Friday, December 6, 2013

Posting data from Arduino to Xively using bash

I decided to check if it is possible to post a sensor data to xively.com (ex cosm.com ex pachube.com) using bash only without building a complicated system like the one I attempted to build here: My first attempt to build a home monitoring/control system. And, in fact, it turned out to be quite simple.



We are going to need an Arduino with a temperature sensor that is able to answer to a string request with a float number representing sensor value. In my case, Arduino is programmed to send temperature in degrees Celsius if it receives a string "Get" over UART. So, what we need to do is to write a bash script that sends the string "Get" to a comport, recieves data and posts it to xively.com.

We are going to use /dev/ttyUSB0 comport (you may have a different one, so check this first), so let's declare a variable:


SERIAL_DEVICE=/dev/ttyUSB0

Now we can send data to the comport by just doing echo


echo "Get" > $SERIAL_DEVICE

And read data back by just doing read

read TEMP < $SERIAL_DEVICE

Here may be a few problems. Like speed of the port is not matching the speed of Arduino. Or the number of data or stop bits not matching. Or pretty much anything else not matching, so we have to make sure that the comport operates with the same parameters Arduino does. So before sending and receiving data to and from the comport, set its parameters:


stty -F $SERIAL_DEVICE cs8 9600 clocal -crtscts -cstopb -parenb -hup -ixany \
 igncr -ixoff -ixon -isig iexten >/dev/null

I was at this point able to communicate with Arduino, but I still had a problem of the Arduino resetting every time I echo to it. I was not able to overcome this problem, although it seems simple. Options like -hup (or -hupcl) and clocal have to solve the problem of DTR going down every time. But they don't. So what I do, is:
  1. I open the port:
    exec 3<>$SERIAL_DEVICE
    
  2. I wait for 3 seconds whil Arduino reboots:
    sleep 3
    
  3. I do my echo and read
  4. And I close the comport:
    exec 3>&-
    

So now I have the data from the sensor as a string and I just need to send this data to xively.com. I simply do that with cURL:


curl -ssss \
 --request PUT \
 --header "X-ApiKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
 --data "$DATA_STRING" \
 https://api.xively.com/v2/feeds/XXXXXXXXX > /dev/null

where $DATA_STRING is the data in json format:


DATA_STRING='{"version":"1.0.0", "datastreams":[{"id":"TC", "current_value":"'"$TEMP"'"}]}'


Here I had another problem when putting the variable directly into the string. Arduino sends "\r\n" as the end of the line when you use Serial.println(). And that '\r' totally corrupts the line. But we need the '\n' to detect the end of data. To get rid of '\r', you can use stty option igncr. So, the full script now looks as:
#!/bin/bash

#serial
SERIAL_DEVICE=/dev/ttyUSB0

#port setting
stty -F $SERIAL_DEVICE cs8 9600 clocal -crtscts -cstopb -parenb -hup -ixany \
 igncr -ixoff -ixon -isig iexten >/dev/null

sleep 0.1

exec 3<>$SERIAL_DEVICE
sleep 3

echo "Get" > $SERIAL_DEVICE
read TEMP < $SERIAL_DEVICE

DATA_STRING='{"version":"1.0.0", "datastreams":[{"id":"TC", "current_value":"'"$TEMP"'"}]}'
#echo "$DATA_STRING"

exec 3>&-

curl -ssss \
 --request PUT \
 --header "X-ApiKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" \
 --data "$DATA_STRING" \
 https://api.xively.com/v2/feeds/XXXXXXXXXX > /dev/null

So now save your script (I save it as ~/bin/xively), make it executable (chmod +x ~/bin/xively) and add it to the scheduler to execute regularly by typing crontab -e and adding the following line to your crontab:

*/2 * * * * ~/bin/xively

And now your sensor data is in the cloud! Check this out: https://xively.com/feeds/337476649