2009-09-27

Peppers update

My chilly crop has had its ups and downs. Some time ago the leaves of most stems where becoming yellowish and falling, latter the cause was known to be over-watering of the plants. Since reducing the water to once every three/four days (from once every day) the leaves started growing again and of a vivid green color.

When the temperatures are forecast to reach below 5 degrees centigrade during the night I bring the vase inside, otherwise most of the time the vase is outside facing southwest/west.

Nevertheless, our strange looking pepper is only now becoming reddish, it is supposed to be a Jalapeño, but it is curlier than most peppers I've ever seen. Most people don't know but before peppers become red and ripe, they go through a stage where they are mostly brown/black. Since the strange pepper is a bit bigger than most the color transition takes more time and you can see the color grading from green to red going through brown.

The leaves of this particular pepper are still yellowish, but at least they're not falling... I hope it recovers soon and reaches maturity A.S.A.P.

Meanwhile a new flower has open... so probably more peppers are expected for Christmas...

Feeding data to Pachube from Arduino

After having done the Arduino Home monitor I wanted to put data on a webpage and do some graphics. Alex suggested putting it at Pachube, as he also has some of his house data there and he had an invitation "to offer". (Thanks Alex!).
Patcube can be feed with data by your application from time to time or Pachube's server requests data from time to time to your "Web-enabled-application". Since I wanted to keep my arduino serving my "human-readable-webpages", I thought of creating a "virtual file" that would Pachube would fetch to get the data. On their Tutorial pages, Pachube has a sample application for feeding data from an Arduino to Pachube. The Tutorial example posts data to the Pachube server from time to time. Since I still wanted to keep my normal webserver and check the temperature online I decided to go the other way and create a comma separated values (csv) page that Pachube would fetch.

This was a bit more difficult than I expected. Before to any access to port 80, the Arduino would reply with my info webpage, but now I had to determine which webpage was accessed. I had to parse the GET command from the browser/server and provide the requested page.
After implementing this "virtual file system" I had another hurdle to overcome, Pachube was always complaining that my CSV page didn't have the correct format. After reading the quickstart a few more times and the http/html specification I added the correct header and an empty line at the end, and it finally Pachube started collecting data correctly.

Finally I wanted to increase the precision of the analog to digital converter (ADC) of the Arduino, specially when reading the LM35 temperature. The LM35 outputs 10mV per degree Centigrade so it will output a full volt at 100 degrees Centigrade, in order to increase the resolution of the ADC the reference voltage must be lowered to close the value in range.
The Arduino provides the option of using either an internal reference (of 1.1V), an external reference (selected externaly) or the default supply voltage (5V in the Arduino Duemilanove). The internal reference is 1.1V so it would beter to use it for the LM35, but for the Light sensor the best is the 5V reference.
So I needed to change reference between conversions, there are a couple of problems when trying to change reference. When default is selected there is a low impedance connection between the reference and the ADC, so when the reference is changed the ATMega168 datasheet says that at least one measurement will be incorrect (I do two for safety). The problem with the internal reference is that it has a high impedance connection to the ADC, so any change takes more time to charge referece input of the ADC, the problem is that the datasheet does not say how much... so I change the reference to internal first and wait for the next webpage request and do the temperature conversion first, this increases the precision of the measurements if the webpage requests are not too frequent.

/*
* Web Server
* based on the original webserver example.
* processes the GET command from the http client.
* supports two pages, one readable for humans /info.html other in CSV for computers /csv.html
*/
/*
*==== Typical HTTP request ====
*GET / HTTP/1.0[CRLF]
*Host: www.google.com[CRLF]
*Connection: close[CRLF]
*User-Agent: Web-sniffer/1.0.29 (+http://web-sniffer.net/)[CRLF]
*Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7[CRLF]
*Cache-Control: no[CRLF]
*Accept-Language: de,en;q=0.7,en-us;q=0.3[CRLF]
*Referer: http://web-sniffer.net/[CRLF]
*[CRLF]
*==== Typical HTTP request ====
*/

#include <ethernet.h>
#define DEBUG

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 2, 253 };
//These were not needed, i.e. without them the ethernet shield works ok with my router
//byte gateway[] = { 192, 168, 2, 254 };
//byte subnet[] = { 255, 255, 255, 0 };

// handle requests at port 80
Server server(80);

// values of temperature and light
long tmprtr,lght;

char cmmnd; /* received command = {G|P} for GET or PUT */
char args[20]; /* requested file,arguments */
char host[20]; /* host ip address*/

void setup()
{
Ethernet.begin(mac, ip);
server.begin();
#ifdef DEBUG
Serial.begin(9600); // serial port for debug
#endif
}

//
// Get and convert analog input values
//
void get_values(void)
{
tmprtr=((long)analogRead(0)*1100)/1024;
analogReference(DEFAULT);
analogRead(1); /* discard value */
analogRead(1); /* discard value */
lght=(long)analogRead(1)*1000/1024;
analogReference(INTERNAL);
analogRead(0); /* discard value - see ATMEGA168 datasheet on change reference */
analogRead(0); /* discard value - for safety*/
}

//
// Begin of HTML page, fixed page title
//
void http_head(Client & client)
{
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
client.println("<html>");
client.println("<title>Arduino Home monitor</title>");
client.println("<body>");
}

//
// End of HTML page
//
void http_end(Client & client)
{
client.println("</html>");
client.println("</body>");
}
//
// reply to root request or /info.html
//
void http_root(Client & client)
{
http_head(client);
client.print("Temperature is ");
client.print(tmprtr / 10);
client.print(".");
client.print(tmprtr % 10);
client.println("<br />");
client.print("Light at home is ");
client.print(lght/10);
client.print("%");
client.println("<br />");
http_end(client);
}
//
// reply to /csv.html
//
void http_csv(Client & client)
{
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/csv");
client.println();
client.print(tmprtr / 10);
client.print(".");
client.print(tmprtr % 10);
client.print(",");
client.println(lght/10);
client.println();
}
//
// Work on a http reply, check request file, produce output
//
void http_reply(Client & client)
{
#ifdef DEBUG
Serial.println("==== debug out ===");
Serial.println(cmmnd);
Serial.println(args);
Serial.println(host);
Serial.println("==== debug eot ===");
#endif
get_values();
if ((args[1]=='c') && (args[2]=='s') && (args[3]=='v')) http_csv(client);
else http_root(client);
}

void loop()
{
Client client = server.available();
#define LN_BUF 40
char inp_ln[LN_BUF]; /* input line, crop at 40 chars */
char chr; /* input char */
byte inp_ln_ptr,i;

if (client) {
inp_ln_ptr=0; /* line is empty */

while (client.connected()) { /* while client is connected process lines */
if (client.available()) { /* is there a char available */
chr = client.read(); /* get it */
if (chr == '\n' && inp_ln_ptr<2) {
http_reply(client); /* received a blank line, create return page */
break; /* exit while, !! find a more logical way to do this */
}
if (chr == '\n') { /* end of line */
inp_ln[inp_ln_ptr]=0; /* end of line */
Serial.println(inp_ln);
if ((inp_ln[0]=='G') && (inp_ln[1]=='E') && (inp_ln[2]=='T')) {
cmmnd='G';
// get arguments
for (i=0;i<sizeof(args);i++) {
args[i]=inp_ln[4+i];
if (args[i] == ' ') break;
}
}

if ((inp_ln[0]=='P') && (inp_ln[1]=='U') && (inp_ln[2]=='T')) {
cmmnd='P';
// get arguments
for (i=0;i<sizeof(args);i++) {
args[i]=inp_ln[4+i];
if (args[i] == ' ') break;
}
}
if ((inp_ln[0]=='H') && (inp_ln[1]=='o') && (inp_ln[2]=='s') && (inp_ln[3]=='t')) {
// get arguments
for (i=0;i<sizeof(host);i++) {
host[i]=inp_ln[5+i];
if ((host[i] == ' ') || (host[i] =='\n') ||(host[i] =='\r')) {
host[i]='\0';
break;
}
}
}
inp_ln_ptr=0; /* ptr ready for next line */
}
else if (inp_ln_ptr<LN_BUF-1) inp_ln[inp_ln_ptr++]=chr;
}
}
// give the web browser time to receive the data
delay(1);
client.stop();
}
}

When mounted horizontally the Ethernet shield and the voltage regulator produce enough heat to raise the temperature in the sensor to about 30 degrees Centigrade. When placed vertically convection keeps the heat away and the sensor reads more "normal" temperatures.
The Power supply of the Arduino is supplied by the router that has a USB port, this way an extra wall-adapter is not needed.
The pachube feed is here, where you can see both graphics light and temperature. It is also possible to embed the sensor readings in the blog layout...

2009-09-15

Building GCC toolchain for AVR

Recently, along with my Arduino-ethernet-shield I bought a Arduino-Diecimilia fitted with a ATMega328, only to find out that my compiler set-up wouldn't work. A quick investigation (here and here) showed me that my current gcc toolchain (4.2.2) didn't support the '328.
OpenSuse repositories provide an avr-gcc toolchain that can be used with the Arduino IDE for Arduinos fitted with the ATMega8 (very old) to the ATMega168 (old), for the ATMega328 you must build the toolchain yourself (at least for now).
I tried to follow general cross gcc building instructions (more on this on another day), but it didn't work. Using gcc bleeding edge of technology sometimes has its problems. Also there are some patches that must be applied to the source.
So I decided to follow the build script provided by the AVR-Freaks forum. You must become a member in order to download the script. It is still not the latest version of GCC 4.4.1, but it is recent enough (4.3.3) for ATMega328.
The pre-requisites are quite important, you can install binutils-devel (BFD support) from the Opensuse repositories and download the mpfr from here. Compile and install mpfr.

$./configure
$make
$su
#make install
#exit
$

Then run the scripts in order:

$ ./getfiles.sh
$ ./get-patches.sh
$ su
# ./buildavr-no-insight.sh
# ./buildinsight.sh
# exit
$

The build scripts ask you a couple of questions and in the end suggest some clean-up, once it starts you have time for few coffees...
Finally test your configuration:

$ avr-ld -v
GNU ld (GNU Binutils) 2.19.1 + coff-avr-patch (20050630)
$ avr-gcc -v
Using built-in specs.
Target: avr
Configured with: ../../source/gcc-4.3.3/configure -v --target=avr --disable-nls --prefix=/usr/local/avr --with-gnu-ld --with-gnu-as --enable-languages=c,c++ --disable-libssp --with-dwarf2
Thread model: single
gcc version 4.3.3 (GCC)

And you're good to go...

UPDATE: 2011.03.13
I recently installed OpenSUSE 11.3 and redid the installation. It works very well but you still need to do the following (these changes are, or may not be OpenSUSE specific):
- as su create and edit a /etc/bash.bashrc.local, add the following lines:

# User specific environment and startup programs
PREFIX=/usr/local/avr
export PREFIX

PATH=$PATH:$HOME/bin:$PREFIX/bin
export PATH

This will enable all users to access the build tools.
Then you need to do the following:

# chmod 777 /var/lock
# chmod 777 /dev/ttyUSB0
# chmod 777 /dev/ttyS0
# chmod 777 /dev/ttyS1

these last two are for my serialports.. I need to remind myself of doing it for minicom.
and finally create a udev rule for avrisp mk ii.
created a file 89-usbprog.rules in the rules.d directory (as su) and add the lines:

# udev rules file for some usb connected mcu programmers

# Atmel devices
#
ATTR{idVendor}=="03eb", ATTR{idProduct}=="2104", MODE="0666", GROUP="users", SYMLINK+="avrispmkII"
ATTR{idVendor}=="03eb", ATTR{idProduct}=="2103", MODE="0666", GROUP="users", SYMLINK+="avrunknown-%n"
ATTR{idVendor}=="03eb", ATTR{idProduct}=="2107", MODE="0666", GROUP="users", SYMLINK+="avrdragon-%n"

# Microchip devices
#
ATTR{idVendor}=="04d8", ATTR{idProduct}=="0033", MODE="0666", GROUP="users", SYMLINK+="pickit2"

as seen here with minor adaptations and your done...
test it with the arduino-IDE, compile and download the blink example.
test avrisp with a simple command:
avrdude -p t15 -c avrisp2 -P usb

PS. this solution also works

2009-09-13

Arduino Home Monitor

Last week I received my Arduino-Ethernet-shield, although I couldn't use my Decimillia with it (due to a compiler problem), I went ahead with my old NG in order to try it out.
I've build a simple system with a LM35 temperature sensor connected to analog input 0(zero) through a low pass filter (100K/ 100nF) , to measure the ambient temperature.
To measure the ambient lighting I connected to the analog input 1 to the middle point of a simple LDR circuit (LDR in series with a 10K resistor, middle point connected to analog input).

The ethernet shield was the first one issued, based on the W5100 ethernet TCP adaptor. Some of the TCP stack is processed in the W5100 so the Arduino spends less time processing the packets and has some more time to do what you want him to do. Also the libraries included in the Arduino IDE work "out of the box". I added my "breadboard" shield on top of the ethernet shield plug in and went on to try it out.


My program is based on the original "ethernet/analog input" example but I had to do some calibration and conversion from the sensors.
I also tried to change the analogReference during runtime, but I didn't get good results. The LM35CZ output will change from 200mV to 300mV, for 20 degrees centigrade to 30 C respectively, therefore I would get the best of the ADC resolution if I used the INTERNAL reference of 1.1V. For some reason the values were not stable, so I abandoned it and used the 5V reference (DEFAULT) for both measures.
I also tried to "correct" the HTML output so that it could be read by a friend's Freecom MusicPal, but we never made it. I need to learn a bit more of HTML parsing and building the page.
Here is the code:

/*
* Web Server
*
* A simple web server that shows the value of the analog input pins.
*/

#include <ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 2, 253 };
//byte gateway[] = { 192, 168, 2, 254 };
//byte subnet[] = { 255, 255, 255, 0 };

Server server(80);

void setup()
{
Ethernet.begin(mac, ip);
server.begin();
}

void loop()
{
long tmp;

Client client = server.available();
if (client) {
// an http request ends with a blank line
boolean current_line_is_blank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
// if we've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so we can send a reply
if (c == '\n' && current_line_is_blank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
client.println("<HTML>");
client.println("<title>Arduino Home monitor</title>");
client.println("<BODY>");
// output the value of each analog input pin

client.print("Temperature is ");
analogReference(DEFAULT);
delay(10);
tmp=((long)analogRead(0)*5000)/1024;
client.print(tmp / 10);
client.print(".");
client.print(tmp % 10);
client.println("
");

analogReference(DEFAULT);
delay(10);
client.print("Light at home is ");
tmp=(long)analogRead(1)*1000/1024;
client.print(tmp/10);
client.print("%");
client.println("
");

client.println("<BODY>");
client.println("<HTML>");
break;
}
if (c == '\n') {
// we're starting a new line
current_line_is_blank = true;
} else if (c != '\r') {
// we've gotten a character on the current line
current_line_is_blank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
client.stop();
}
}

P.S. I had to change the > and < by the html code & g t ; and & l t ; so I don't know if the above code will cut/paste correctly.
Most of my problems were in fact related to the my provider's ADSL access box, it has only one fixed IP address available x.x.x.253 (it must be outside the DHCP range, which I cannot change), and then enabling the routing of port 80 from the outside world to the Arduino in the inside network.
With my IP address I can check the temperature and light levels (?) at home from any computer!(possibly later I could use a free dynamicdns)...
All this in the same weekend we place a new kitchen floor...
Uf... I though it never ended...

2009-09-08

The Origin of Species

Finally I had the time and patience to finish it... It was hard but I got to the end at last! To all those future readers of Darwin's book a few words of advice:
- spare a couple of months, it is not easy ready as there is no action, it is basically a Ph.D. thesis presenting the Hypothesis of evolution and rebating some old (or still existing in revised form) different points of view.
- Naturally the book is biased towards the concept of evolution, when reading try to have an open mind and place yourself in Victorian times.

It was hard but fun, I must admit that I am surprised not only on how our knowledge has evolved (pun intended) but also how some things we now know for sure, were still unexplained at the time.
In the book Darwin suspects that earth started as continuous continent and only later the continents have separated, in order to explain the same species in different continents or in distant islands, we now call it the plate tectonics theory.
The markings in the book are to avoid dog ears in the pages, I try to mark important passages as I go through the book and later revise them..