Tuesday, August 26, 2014

Wireless switches control (with LabView)


This is another idea I had at work: in the entertainment industry we often install distribution board and we have to walk a long way to switch something on and off. Sometimes concerts and festivals cover a large area and our distribution board can be anywhere. It would be useful to operate them wireless. It would be even better to get some data from them, for example how much current the loads are drawing on each phase, but I'll get there when I'm more expert. In this experiment I just want to control 8 switches with my Arduino, using a LabView interface and XBee radio transmitters. In the distribution board there will be relays to open and close 240V circuits using a 5V signal. In this example I'm switching on and off simple LEDs. When I wire up the distro, I just have to replace the LEDs with relays.
In this case I'm using an Arduino Mega ADK, to make sure I have enough pins to control the switches and an LCD screen.
I've chosen to control 8 switches because the serial communication between LabView and the Arduino uses 256 characters (1 byte), exactly the number of combination we can create with 8 switches (2^8). This way, we only have to send one character (= 1 byte) and we don't need to synchronize the devices or use delays. If we want to control more switches, or we want to send an analog value over 255, we would need to send a string. We will see this in the next experiment.
The wiring is as follow:

LABVIEW INTERFACE


For the LabView interface I've used the code from the example "Advanced Serial Write and Read" downloadable from the National Instrument website. I added 8 switches. They output a boolean value (0 or 1) which is multiplied by the power of 2 correspondent to that switch number:
Switch 1 output multiply by 2^0.
Switch 2 output multiply by 2^1.
Switch 3 output multiply by 2^2.
Switch 4 output multiply by 2^3.
Switch 5 output multiply by 2^4.
Switch 6 output multiply by 2^5.
Switch 7 output multiply by 2^6.
Switch 8 output multiply by 2^7.
The values are then added to form the byte we have to send to the arduino.
This number is between 0 and 255, and each number correspond to a switches configuration. for example: 84 means (1-off, 2-off, 3-on, 4-off, 5-on, 6-off, 7-on, 8-off). This byte is then sent to the serial port through the function VISA.



ARDUINO SKETCH

Now we program the arduino to read the value from the serial port and execute the commands:

#include <LiquidCrystal.h>
LiquidCrystal lcd(26, 27, 28, 29, 30, 31);
int newvalue;
int oldvalue = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
}
void loop()
{
 if(Serial.available() > 0) {
   newvalue = Serial.read();
   
   if (newvalue != oldvalue){
   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print(newvalue);
   oldvalue = newvalue;
   }
    digitalWrite(2, HIGH && (oldvalue & B10000000));
    digitalWrite(3, HIGH && (oldvalue & B01000000));
    digitalWrite(4, HIGH && (oldvalue & B00100000));
    digitalWrite(5, HIGH && (oldvalue & B00010000));
    digitalWrite(6, HIGH && (oldvalue & B00001000));
    digitalWrite(7, HIGH && (oldvalue & B00000100));
    digitalWrite(8, HIGH && (oldvalue & B00000010));
    digitalWrite(9, HIGH && (oldvalue & B00000001));
   }
 delay(5);
}

As usual, I attached an LCD screen to display the value of the byte received by the Arduino through the serial port. It has to be the same displayed on the LabView interface.

The XBees simply replace the USB cable. I just plug them in as I would plug in a USB cable, nothing changes at the end devices. I just have to remember the switch on the Arduino XBee Shield: it has to be on "USB" when I program the controller, and on "micro" when I use the XBee.


CONCLUSIONS



The device works as expected, but it has some limitations: the communication of one byte can only control 8 switches. In the next experiments I will try to send and read strings. Also, the number of switches shouldn't be limited to the number of pins, in the future I will use shift registers to control several switches using one pin.

Monday, August 11, 2014

Remote communication between Arduino and smartphone

I developed this little device to check an analog sensor installed in a remote area using my phone. The idea is to check the level of fuel in generators we install in remote areas. So far, we had to drive there to check and refuel them. Now, we can check the level of fuel in each generator anytime and we can organize a refuelling trip more efficiently, only when necessary. The communication between the Arduino and the phone are simple text SMS.

COMPONENTS AND WIRING

To interface the arduino to the GSM network you need a GSM shield and a SIM card. I recommend the genuine Arduino GSM/GPRS shield, which has more functions, but here I'm using a SIM900 GSM/GPRS shield, which is enough for this project, and much cheaper. In Australia I bought a 20$ Vodafone simcard because it lasts one year without recharging, but I pay more than 28c per SMS (national and international). I'm sure you can get a better deal if you have time to look around.

The sensor: my employer gave me a fuel sensor to play with. It's a floating arm that moves the contact on a resistive rail. I measured the resistance across this resistor: 11 ohms when the arm is at the top (tank full), 185 ohms when the arm is completely down (tank empty). It's very important to know this value. The output value I want to see is a percentage from 0% (tank empty) to 100% (tank full).


If we connect this variable resistance in series with a fixed resistor we create a voltage divider: when we apply 5 volts we'll see a voltage drop across each resistor, and their sum will be 5 volts. The value of the fixed resistor should be about the same range of the variable. In this case I have a 68 ohms resistor. The voltage drop across resistors in series is proportional to their resistance. I wired up the circuit to measure the voltage drop across the fixed resistor with the analog input A5.


As usual, I connected a LCD to display the value in real time on the device.
The power comes from a 12V DC power supply in parallel with a 9.6V battery pack (8 x rechargable AA). When the generator is off, the batteries power the Arduino. When the generator is on, the power supply powers the Arduino and charges the batteries. To do that the power supply must have a higher voltage than the battery. The input voltage range (recommended) for the Arduino Uno is 7V to 12V.
It took me many hours to understand which pins are not used by the GSM shield. After many unsuccessful tries I was able to use pin 13,12,11,6,5,4 to run the LCD, without interfering with the operations of the shield.


SKETCH

We know the value read in the analog input is 0 to 1023, where 1023 correspond to 5 Volts. We have to apply Kirchoff Law to calculate the minimum and maximum value across the resistance, and then map this value to give an output value between 0 and 100%.

1023 * 68 / (185+68) = 275
1023 * 68 / (11+68) = 880

There is always a little error so, after few tries, I changed those values to 280 and 850 to give exactly 0 to 100% (it was close enough anyway).

I don't want to store the SMS on the SIM card, so I delete everything at the start of the program. This makes sure there is enough space to receive a command. Without this it stopped working suddenly and I couldn't understand why: the SIM was full.

A friend created an Android app to send a message to the device just pressing "test". The app sends a SMS starting with "a". The Arduino receive the message and it knows that "a" means "how much fuel is left?". It replies to the sender with the reading from the sensor.

Also, I want it to send an alarm SMS to a set "emergency phone" when the level of fuel passes the 50%, 25% and 10%.

#include <LiquidCrystal.h>
#include "SIM900.h"
#include <SoftwareSerial.h>
#include "sms.h"
SMSGSM sms;

LiquidCrystal lcd(13, 12, 11, 6, 5, 4);

int numdata;
boolean started=false;
char smsbuffer[160];
char n[20];
char sms_position;
char phone_number[20];
char sms_text[100];
int i;
char alarm_phone[20] = "+61439021813";

String dataString;
boolean alarm50 = false;
boolean alarm25 = false;
boolean alarm10 = false;


void setup()
{
  lcd.begin(16, 2);
  lcd.print("Testing...");
//  Serial.begin(9600);
//  Serial.println("GSM Shield testing.");
  if (gsm.begin(4800))
    {
//    Serial.println("\nstatus=READY");
    started=true;
    }
//    else Serial.println("\nstatus=IDLE");
  for(i=1;i<=20;i++)
    {
    sms.DeleteSMS(i);
    }
}

void loop()
{
  int val=(map(analogRead(A5), 280, 850, 0, 100));
  constrain (val, 0, 100);
  lcd.clear();
  lcd.print("Fuel:");
  lcd.setCursor(6,0);
  lcd.print(val);
  lcd.setCursor(9,0);
  lcd.print("%");
  delay(500);
 
  if(started)
    {
    sms_position=sms.IsSMSPresent(SMS_UNREAD);
    if (sms_position)
      {
//     Serial.print("SMS postion:");
//     Serial.println(sms_position,DEC);
      sms.GetSMS(sms_position, phone_number, sms_text, 100);
//      Serial.println(phone_number);
//      Serial.println(sms_text);
      sms.DeleteSMS(1);
      if(sms_text[0] == 'a')
        {
        dataString = "";
        dataString += "Fuel: ";
        dataString += (val);
        dataString += "%";
//        Serial.println(dataString);
        char charBuf[50];
        dataString.toCharArray(charBuf, 50);
        if (sms.SendSMS(phone_number, charBuf));
//        Serial.println("\nSMS sent OK");  
        }
      }
      else
      {
        if(val < 50 && !alarm50)
          {
          if (sms.SendSMS(alarm_phone, "Fuel < 50%"));
//          Serial.println("\nAlarm50 sent OK");
          alarm50 = true;
          }
          if(val < 25 && !alarm25)
          {
          if (sms.SendSMS(alarm_phone, "Fuel < 50%"));
//          Serial.println("\nAlarm25 sent OK");
          alarm25 = true;
          }
          if(val < 10 && !alarm10)
          {
          if (sms.SendSMS(alarm_phone, "Fuel < 50%"));
//          Serial.println("\nAlarm10 sent OK");
          alarm10 = true;
          }
//        Serial.println("NO NEW SMS,WAITING");
      }
  }
}

VIDEOS



Sunday, August 10, 2014

Binary to Gray, Gray to binary conversion


During my PLC course I had to convert binary numbers to Gray code and viceversa. I made this spreadsheet to help me with that.





Matrix solver (Cramer's rule)

This spreadsheet solve three equations with three variables using the matrix method (Cramer's rule).



Thursday, August 7, 2014

Brewduino

PURPOSE

I just finished testing my new experiment: a device that keeps the temperature of a fermenter between two set values and display and record the data over time. It takes about 6-7 days to ferment a home brew beer, and the temperature must be kept between 18 and 24 degrees (it can change slightly depending on the beer). If it's too cold the fermentation stops, if it's too warm the beer goes bad. I bought a 30W heating belt to wrap the fermenter, but I wanted it to go on and off automatically when needed, so I set up my Arduino Uno, a temperature sensor (DHT11) and a relay to control the AC current using a DC signal of 5 Volts. Additionally I attached an LCD to see the temperature read by the sensor and compare it with the analog thermometer, just to double check. I also like to analyse data so I attached an SD card module to record the temperature and state of the heating belt (on and off) over time.

SCHEMATICS



As you can see from the schematics, I'm using pin 5 to send the signal to the relay that controls the AC power to the heating belt. I decided to switch the active wire (brown): switching the neutral (blue) would work exactly the same but it wouldn't be safe in case of a fault. You can set the relay "normally open" (it will activate the circuit when the signal is present), or "normally closed" (it will stop the circuit when the signal is present) using different pins.
The SD card module uses 4 pins that are set in the relative library, so I didn't change them: they are 13,12,11,10 plus power and ground. These modules are supposed to work with both 3,3V and 5V but I can't get it to work with 5V so I had to use 3,3V. 
The sensor DHT11 has its own library and it only uses one pin (in this case pin 3) plus power and ground.
The connection of the LCD is always the same, with the potentiometer to control the brightness.

SKETCH

#include <LiquidCrystal.h>
#include <DHT11.h>
#include <SD.h>

LiquidCrystal lcd(9, 8, 7, 6, 4, 2);

int pin=3;
DHT11 dht11(pin); 

const int chipSelect = 10;
int t,h;

long previousMillis = 0;
long interval = 30000;

void setup(){
  Serial.begin(9600);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
  lcd.begin(16, 2);
  if (!SD.begin(chipSelect)) {
    return;
  }
}

void loop(){
  int err;
  float temp, humi;
  if((err=dht11.read(humi, temp))==0)
  {
    t = (int) temp;
    h = (int) humi;
    
  lcd.print(t);

  if(t < 19){digitalWrite(5,HIGH);}
  if(t > 21){digitalWrite(5,LOW);}
    
  int state = digitalRead(5);
  lcd.setCursor(8, 0);
  lcd.print(state);

//  Serial.println(dataString);

  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval) {
    
    String dataString = "";
    dataString += "temperature:";
    dataString += String(t);
    dataString += ",";
    dataString += "humidity:";
    dataString += String(h);
    dataString += ",";
    dataString += String(state);
        
    File dataFile = SD.open("datalog.txt", FILE_WRITE);
    if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    }
    previousMillis = currentMillis;   
   }
  }
    delay(1000);
    lcd.clear();
}


To write the sketch I had to include the 3 libraries to control the LCD, DHT11 and SD module. I had to use pin 9,8,7,6,4,2 to control the LCD because I'm using 13,12,11,10 to control the SD module (and I can't change that unless I change the library). The "chipSelect" is pin 10 in the Arduino Uno (it's different in the Mega). Pin 3 is used by the sensor and pin 5 is the pin that controls the relay.
The sketch itself is pretty simple, I took parts from the datalogger sketch. I had to introduce a different kind of delay: I want the program to run every second, but to record data on the SD only every 30 seconds. To do that I can't use the function delay(30000), because it would stop the whole program for 30 seconds. I'm using the function millis() that counts the milliseconds passed since the start of the program. This value is compared to the set interval and when it's larger than that, it execute the part of the program that writes the data into "datalog.txt" on the SD card.
The function digitalRead() can also read an output pin. For example here I'm reading the state of pin 5, even though I'm using it as an output.

TEST

Because I don't want to waste beer, I filled up the fermenter with water for the purpose of this test. 


When you deal with higher voltages it's a good idea to enclose your devices in a box. 


After about 4 days I stopped the device and analized the data recorded on the SD card: I had 10545 values that correspond at about 88 hours of recording. As you can see from the chart, the temperature was kept between 19 and 21 degrees (the analog thermometer was reading the same). The last number in the log is the state of the heater (0=off, 1=on). The same value is displayed on the LCD, next to the temperature.

I imported this data into Excel and plotted a chart of temperature over time: as you can see the first day I had it set to keep the temperature between 18 and 24 degrees. The blue line indicates the temperature. The red line shows when the heater was on.


The heat belt was on for 11 hours to increase the temperature to 24 degrees, and the temperature decreased quickly after the heat belt went off. Then I changed the settings to make it work more efficiently: I just want to keep the temperatures over 18 degrees, so I set it to start the heat belt when the temp was under 19 and to stop when it was over 21. The second night it went on for a shorter time, enough to increase the temperature by 2 degrees (about 2 hours), the third night it didn't even go on because it wasn't cold enough and the fourth night it went on about 2.5 hours.

Analyzing data is useful to understand if the device is working efficiently or it can be improved. For example, I can observe that it takes about 1.5 hours to increase 23 liters by one degree, using about 30W of power.
I also plugged in a power meter at the heat belt to check the consumption: it uses maximum 40W power and at the end of the experiment the total energy consumption was about 680Wh.