Arduino EtherShield DS18B20 Temperature Logger

I've been using an Arduino, an EtherShield and a DS18B20 temperature sensor to log the temperatures outside my window for about 6 months now, and it has worked well. I've also been getting a couple of emails looking for the source code to my program. The original code was rather quickly hacked together and I was reluctant to publish it (although I did send it to those that emailed).

I got some spare time over the holidays and did a major refactor of the code, including moving from the EtherShield library to the ethercard library. I've published the code to github, and the raw sketch file is available here.

Arduino Float to String

If you have ever tried to use sprintf() on an Arduino to convert from a float to a string, you will notice it doesn't work.

sprintf(buf,"%f", floatvar);

The above function will most likely return a "?" to your char buffer.

If you google around, you'll see various functions that people have written to overcome this, but all of them seem broken in one way or another. The alternative is to use dtostrf(), a standard avr-libc function. This provides a simple, tested function to convert from a float to a string.

To use this function, simply replace sprintf() with dtostrf(), along with the self explanatory parameters below.

dtostrf(floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, charBuf);

As the function name suggests, this will also work for the conversion of doubles to strings.

Arduino Modbus RTU ADC

Modbus is an industry standard communications protocol for electronic devices. Given that most industrial sensors and meters provide their output by varying the voltage of the output pin between 0-10 Volts, Modbus compatible analog to digital converters are popular devices. Modbus is surprisingly resilient to interference. This is because it is based around the RS-485 standard. RS-485 is essentially a multi-point, balanced, version of RS-232 (which people might recognise as the serial port protocol), which is point-to-point, and unbalanced.

Given that the cheapest industrial unit I found retails for €75 ex.VAT, it is interesting to know that an Arduino UNO + MAX485 can do the same thing for €25 inc.VAT, ⅓ of the price.

The Arduino UNO has 5 analog inputs, which means it is capable of sampling 5 analog values and storing them storing them in 5 sequential registers. The Modbus master device can then query Arduino slave, and retrieve these values from the registers. If you find that you don't have enough device inputs with 5 analog inputs, consider using an Arduino Mega, which has 16 analog inputs.

Most sensors output a range between 0-10 V, while the Arduino accepts a maximum of 5 V. By passing the sensor output through a pair of resistors in parallel, we will reduce the voltage range from 0-10 V to 0-5 V, which is suitable for the Arduino.

The Arduino only has full duplex serial lines, so a MAX485 IC chip must be used to convert them into the half-duplex differential lines for RS-485. Due to the the fact that the MAX485 is half duplex, Pin2 is used as an output enable pin.

The code is based on this library by jpmzometa which must be downloaded and placed in the Arduino/Libraries directory.


/* create new mbs instance */
ModbusSlave mbs;

/* slave registers */
enum {        
        MB_A1,        /* analogIn 1 */
        MB_A2,        /* analogIn 2 */
        MB_A3,        /* analogIn 3 */
        MB_A4,        /* analogIn 4 */
        MB_A5,        /* analogIn 5 */
        MB_REGS       /* dummy register. using 0 offset to keep size of array */

int regs[MB_REGS];
unsigned long wdog = 0;         /* watchdog */

void setup(){
  /* Modbus slave configuration parameters */
  const unsigned char SLAVE = 10;      /* slaveId */
  const long BAUD = 19200;             /* baud rate */
  const char PARITY = 'n';             /* n=none; e=even; o=odd */
  const char TXENPIN = 2;              /* output driver enable pin */
  /* configure msb with config settings */


void loop()
/* pass current register values to mbs */
  if(mbs.update(regs, MB_REGS))
  wdog = millis();

  /* ADC reads are slow. sample every 5 seconds */  
  if ((millis() - wdog) > 5000)  {      
    regs[MB_A1] = analogRead(A1); /* read input A1 */
    regs[MB_A2] = analogRead(A2); /* read input A2 */
    regs[MB_A3] = analogRead(A3); /* read input A3 */
    regs[MB_A4] = analogRead(A4); /* read input A4 */
    regs[MB_A5] = analogRead(A5); /* read input A5 */

Arduino Duemilanove Optiboot

You may have noticed that the new Arduino Uno uses the optiboot bootloader. This new bootloader brings two main advantages:

  • Smaller bootloader footprint - An additional 1.5kB of space for sketches.
  • Increased bootloader baudrate - Faster sketch uploading.

Fortunately for us, these bootloader improvements can be backported to the Arduino Duemilanove. If you have more than one Duemilanove to hand, then is it trivial to flash the new bootloader. Follow this tutorial, and use one Arduino as an In-System Programmer to flash the Uno bootloader to the other.

If on the otherhand, you only have one Arduino, then you must use another method to flash the bootloader. You might have noticed 4 unsoldered connections on your Duemilanove labelled X3. As you can see from the schematic, these are the CTS, DSR, DCD and RI signals from the FTDI FT232RL USB to serial converter. These pins are not used for anything, but we may toggle them high and low as required (bit bang mode) to emulate the SPI interface required to burn the bootloader.

Desolder these pads and solder in 4 pin headers into X3. These 4 headers should then we wired to the 6 pin ISCP header. The MISO, MOSI, SCK and RESET lines can be connected to the 4 X3 pins in an arbitary fashion. We can match up the lines in avrdude.conf later.

If you happen to have a FTDI breakout board from Sparkfun or what not, you can also use this to program your ATmega328. Simply update the entry in avrdude.conf as required.

The basic instructions for flashing your bootloader are explained nicely on this page, and so, there no point in me reposting.

Instead of using the instructions given on the page above for the Duemilanove, change the fuse settings as follows:

  • High Fuse: 0xD6
  • Low Fuse: 0xFF
  • Extended Fuse: 0x05
  • Lock Bit: 0x0F

The bootloader file that you wish to flash with is arduino-0021\hardware\arduino\bootloaders\optiboot\optiboot_atmega328.hex, located in the Arduino 0021 folder.

Click Erase - Write - Verify, and go have a cup of coffee. This process can take up to 10 minutes (only running at 4800 baud)!

Once programmed, disconnect your ICSP header from the X3 port, and fire up the Arduino IDE, and try to download the blink example. If successful, you should have a blinking LED near Digital 13. If you are getting stk500 errors from avrdude, ensure that you have specified the correct type of Arduino in the Tools/Boards menu. Because you have flashed with a new bootloader, you should be selecting Arduino Uno rather than Arduino Duemilanove.

ENC28J60 Ethernet Breakout Board

One of the first things I wanted to buy when I got my Arduino board was an Ethernet shield. The ability to interface with the internet opens up a raft of possible applications for the device.

After picking my jaw up off the ground after seeing the price of the of the official Wiznet W5100 based Arduino Ethernet shield, I decided to go for the cheaper MicroChip ENC28J60 based nuelectronics Ethernet shield. The only problem was that by the time I had the funds to buy the shield, it had gone out of stock.

The solution was to either buy the ENC28J60 parts and build my own on a breadboard, or buy a ENC28J60 breakout board. I decided to splash out with the cash I saved compared to the official shield, and get both. :)

The nice thing about the breakout board compared to a Arduino shield, is that if I ever decide to change to another type of microcontroller, I can simply plug in and interface using the SPI bus, without being tied into the non-breadboard friendly Arduino shield format.

The my schematic is essentially the same as the nuelectronics schematic, only I replaced the SN74HCT08D with a SN74HC125N simply because I had one to hand.

The ENC28J60 runs at 3.3, while the ATmega328 runs at 5V. Normally this is a major problem, but luckily for us, the ENC28J60 is 5V tolerant. That means no level shifting is needed on the 5V lines controlled by the ATmega328 (CS, SCK, SI). The Tristate Buffer is used to shift the lines controlled by the ENC28J60 (SO, INT) from 3.3V to 5V. The lines never need to be put in a high impedance mode, so just tie the output enable line to ground.

Once connected, I downloaded Andrew Lindsay's EtherShield library, and uploaded the example webserver program to the Arduino, and voila...

user@host ~
$ ping
PING ( 56 data bytes
64 bytes from icmp_seq=0 ttl=64 time=2 ms
64 bytes from icmp_seq=1 ttl=64 time=1 ms
64 bytes from icmp_seq=2 ttl=64 time=1 ms
64 bytes from icmp_seq=3 ttl=64 time=1 ms

---- PING Statistics----
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip (ms)  min/avg/max/med = 1/1/2/1