Tuesday, August 11, 2009

a wireless sensor network

update: when not using encryption or any sleep modes I am able to remotely reset the funnelio and upload the firmware automatically. it's awesome.

I jumpered DTR from UARTSB to DIO3 of the UARTSB. (and solder SJ1 on the funnelio) with that simple setup it is possible to use the end_device.pro / coordinator_auto_reset.pro from funnel as is.

--

the 1980s were characterized by the chips that made computers affordable for households. lasers that enabled long range high speed communications in optic fiber defined the 1990s. ubiquituous sensors may be the defining technology of the current decade. mobile phones contain a number of sensors. android and iphone development environments allow easy access to the collected data, and soon even SIM cards will have sensors and be easier to program than some phones.

I went for a simpler sensor network. the network has four nodes that measure temperature and report to a central collector which publishes the data.

I used funnelIO boards, xbee series 1 radios, an uartsb, and ds18b20 temperature sensors.

I chose the original funnelios from sparkfun, over the remixed ones from seeedstudio. the seeedstudio version has a 2.54mm jst plug for the power. they don't sell batteries with those plugs, and neither does anyone else.

there are a number of tutorials on uploading your program wirelessly to an arduino. the tutorials go into much detail but they require you to do exactly what they do with exactly the same hardware and software. there are alternative ways of uploading wirelessly, but they aren't covered. the sparkfun tutorial actually requires you to reprogram your microcontroller. which is difficult in the case of the funnelio.

everything starts with the closing of the solder jumper on the funnelio. the explanation is in the schematic of the funnelio. sj1 connects dio3 of the xbee with the rst pin of the arduino. the arduino is reset when digital input/output number 3 of the xbee is triggered. dio3 of the receiver is configured as DO HIGH (digital out), which is equivalent to holding down the reset button until that pin is triggered. the rest deals with setting up the sending side so that when a request to send(RTS), not the same thing as RST(reset) is sent to the serial on the sending side, that information is passed on to the receiver's reset pin.

if everything works out, you can upload your arduino stuff as if the remote device was connected to your computer with the usb cable. unsurprisingly there are a number of caveats. the xbee must be powered on, it must associate with the network, it cannot be using encryption, the arduino can connect to a single serial port at a time: either xbee or the wired one.

the uartsb actually has 5 pins instead of 6 on the serial port. RTS is not available. RTS is however wired to DIO6, which is right next to DIO3. when using the wired connection for uploading to a funnelio, the DTR can be connected to the RST pin and everything will work. I tried all combinations of connecting DTR and RTS to DIO3 and DIO6 and configuring DIO6 and DIO3 as inputs on the sending side, and configuring the change detect on the receiving side to 8, 0x40, and 0xff. but I could not get the arduino ide to reset the funnelio properly. but!

you can upload your arduino code without autoreset! you just have to make sure that the xbee is powered on, it is associated, encryption is disabled, you press the reset button by hand and you release it at a good time. further, you have to setup DIO3 and change detect so that the default setting of DO HIGH does not hold your reset line high all the time. or avoid closing the solder jumper. the thing to watch out for is the tx and rx lights on the uartsb. when the leds first blink one after the other and then start blinking continuously you're doing good!

I ended up not uploading wirelessly, instead I used the wired interface. all you have to do is unplug the xbee everytime you upload because the funnelio has no switch to choose the serial interface and the xbee has priority over the wires.

802.15.4 or zigbee does not support mesh networks. in this context a mesh networks would be one that allowed multihop routing and self-healing properties. instead there is a coordinator and end devices. my coordinator is the uartsb and the end devices are the sensors.

I wanted to conserve power and to use encryption. I went through a number of different combinations of who should wake up who and whether I wanted to buffer data before sending and so on. it turns out that it is easier for the xbee to wakeup the arduino than the arduino to wakeup the xbee. especially with the way the funnelio's xbee socket is wired. pin 9 is not connected to anything, and it's not trivial to jumper it to the pins of the at168.

I use sleep mode 4, or cyclic sleep - the remote xbees periodically wake up, send a packet to the coordinator, and the coordinator either sends a buffered packet to them or tells them to go back to sleep. after a period of inactivity the xbee goes back to sleep. if a packet is received it is given to the arduino. if the arduino was sleeping, the serial activity wakes it up. the arduino can keep sending on the xbee as long as it doesn't allow the xbee to time out. in case of timeout, the connection is lost, and sent data is not buffered. with cyclic sleep the remotes also become hard to reprogram wirelessly as the xbee radio may be off or unassociated at the time the sender wishes to upload the program. with a short sleep timeout and a long sleep period reconfiguring the xbees with X-CTU also becomes difficult because the xbees will go to sleep and become unavailable. eventually the only way to reprogram them is to unplug the uartsb, shutdown x-ctu, plugin uartsb, start x-ctu, and choose "always update firmware".

The xbee uses 60mA at 3.3v when it is active, 0mA when it sleeping, and the multimeter flashes to about 15mA for a split second when it checks for the packet. the funnelio is an atmega168 running at 8mhz on 3.3v. it uses 6ma when it is calling delay(1000) and 2.5mA when it is sleeping. in this sleep mode wakeup is instantaneous. the batteries I am using are 1000mAh or 1100mAh depending on who you believe. sending a packet back and forth every minute, with timeout at 250ms and temperature measurement taking about 200ms, approximately 200mAseconds are consumed every minute giving an average consumption of 3mA. the batteries should therefore last for about 2 weeks. ofcourse it will take atleast two weeks to find out, unless the batteries run out earlier.

the ds18b20 is a solid state non-thermistor temperature sensor. it uses a proprietary protocol called 1-wire. it has 3 wires, and requires "no external components". that's excluding the 4.7kohm resistor which it requires. the sensor seems to heat up faster than it cools down. I don't know how that's possible. the sensor returns the temperature as a two-byte fixed point reading. it takes 750ms to "convert" the temperature to a 12-bit value, with 4 bits after the decimal point. 3 bits of resolution takes 375ms, 2 bits takes 187,5ms and so forth. I am using the 2 bit resolution, so I get readings in quarter degrees celcius. the accuracy of the sensor is +/- 0.5 degrees and the maximum resolution is 1/16th of a degree. apparently the accuracy discrepancy is fixed over the measurement range, or atleast between consecutive measurements, so there is no noise in consecutive readings. if the measurement changes 1/16th of a degree, the temperature really changed, but it might be between 25.0 and 25.0625 or 24.5 and 24.5625. my four sensors produce results within one degree of each other if placed nearby on a table. the onewire library for arduino doesn't live in version control, and there have been some bugs that have been fixed, but not in the official release. unlike the other examples floating around I didn't use parasite power for the ds18b20. measurement takes the same fixed time, but instead of calling delay(200), I call sensor.read() and wait for it to return something other than zero.

the default mode for xbee is a delightful abstraction: there is no way to tell whether you are connected via radio or if you are at the end of a cable. there are however two problems, and these are addressed by Digi's API mode. in cyclic sleep mode, broadcasting is not supported. the coordinator has to address messages to each remote separately. API mode allows this to be performed without entering command mode. when receiving messages, the identity of the sender is unknown. the data would have to be somehow tagged with the sender identity, and the data might further have to be tagged with some semantic information: for example high byte or low byte of temperature measurement. doing both of these at byte level would require significant overhead. entering command mode to change recipient addresses would also be difficult if there were concurrent incoming messages from remotes, and while in command mode some data might be lost. API mode, whose use is easy enough with the xbee-arduino library, allows data to be grouped into packets which are delivered atomically and with the sender identity automatically present.

I ended up having to set Mac mode to 1. The default mode of zero, which has ACKs and a Digi header got screwy when AES was enabled. Messages would be delivered as double to the remotes, even though only one ACK would be received on the sender side, and worse, after a couple of messages the sender would somehow get clogged and unable to send any more messages or even to reset itself.

I wrote the coordinator code in Ruby with the serialport gem. on Linux and Windows usb-to-serial stuff works out of the box but on OSX a separate driver is required. the driver is included in the arduino download for osx. I send out a message to the remotes every minute or so, they respond with the current temperature and I write it out in json. I rsync the JSON to a server and draw some graphs with HTML5's delightful canvas tag.

updated: the code
--

1 kommenttia:

Eero A said...

Kovaa kamaa! Onko kolvi toiminut hyvin?