Monday, 30 January 2012

Playing with LEDs

I decided to take a break from the work on my wireless sensor network to play around with some components from my ARDX starter kit which I hadn't used yet.

This was all pretty straightforward. I used the following tutorials.
The circuit I ended up with looked like this.


From the left, this has a dimmer switch, two regular LEDs, a light sensor, a push button and on the far right is an RGB LED which oomlout includes for free with orders.

In the end I disabled the regular LEDs and the light sensor and just controlled the RGB LED. In my first configuration I used the button to cycle between the R/G/B LED and the potentiometer to change the brightness of the selected color. This was nice but fiddly so I changed the code to only use the button to start/stop a programmed sequence of colors. The colors are generated by ramping the R/G/B LEDs from 0 up to 255 and back down to 0 again. Each color uses a different increment so that the overall color shifts smoothly through all of the colors which are possible.

The results turn out to be very hard to film because my iPhone does a good job of adjusting the color balance to cancel out the changing LED. However, here are a couple of attempts.

 


As ever, my source code is available here.

Sunday, 29 January 2012

Wireless sensor node - turning off subsystems (5)

In my last post I spoke about the watchdog timer which can be used to enter a deep sleep on the ATtiny85. This time I want to take a quick look at the other power saving options available on the ATtiny85.

The ATtiny has several subsystems which can be separately turned on and off. The datasheet describes all of these systems but we just want to find out how to turn them off! Thankfully, you can skip straight to looking at power.h. This header exposes some methods which make power management more straightforward. From this header file you can quickly find all the subsystems on the ATTiny85 which can be switched off. 
  • power_adc_disable()
  • power_timer0_disable()
  • power_timer1_disable()  
  • power_usi_disable() 
In practice, I can't actually switch all of these off in my project.
  • Timer1 - used by Manchester lib. The datasheet says that timer1 is always stopped while we are asleep
  • ADC - used to read analog value from sensor. ATTINYWATCHDOG already explicitly turns off the ADC when we sleep. 
The ATtiny datasheet has a section on power saving. This recommends that when not in use, pins should be set to input mode so that they are not driving anything. 

The wiring of my project currently connects the VCC of my TX module to the battery VCC line. However, I can improve power use by connecting TX VCC to a pin on the ATtiny. This allows me to power down the TX while the ATtiny sleeps. 

There is one change which I should make which will actually consume more power. I am planning to run an ATtiny from battery power for long periods of time. At some point the battery will start to run out and the battery voltage will drop. The ATtiny can operate over a fairly wide range of voltages. However, but the voltage drops too low the ATtiny can actually be damaged. To avoid this happening, we can enable a Brown Out Detection (BOD) circuit. This is enabled by setting a fuse on the ATtiny. 

The details about how to set the fuse are listed on this page. I used the command line approach which involved the following steps.
  1. Open a command line in: \path\to\arduino-0022\hardware\tools\avr
  2. Run the following command to read the current fuses

    avrdude -C "\path\to\arduino-0022\hardware\tools\avr\etc\avrdude.conf" -p ATtiny85 -c stk500v1 -b 19200 -p COM3 -v


    Note that the stk500v1 works fine when using the Arduino as an ISP. Also note that the fuse values get output twice. For example:

    avrdude: Device signature = 0x1e930b
    avrdude: safemode: lfuse reads as E2
    avrdude: safemode: hfuse reads as DD
    avrdude: safemode: efuse reads as FF

    avrdude: safemode: lfuse reads as E2
    avrdude: safemode: hfuse reads as DD
    avrdude: safemode: efuse reads as FF
    avrdude: safemode: Fuses OK

  3. Use this site to work out your new fuse values. Select "ATtiny85", the default features value is correct. The "current settings" section will likely show fuse values matching what you read from your chip.
  4. To enable BOD, select your desired level, in my case "Brown-out detection level at VCC=2.7 V; [BODLEVEL=101]". Click "Apply feature settings". The updated fuse settings are shown in the "Current settings" section. In my case, the default of DF changed to DD. In binary this means 0b11011111 changes to 0b11011101.
  5. To apply this change, run the following command:

    avrdude -C "\path\to\arduino-0022\hardware\tools\avr\etc\avrdude.conf" -p ATtiny85 -c stk500v1 -b 19200 -p COM3 -U hfuse:w:0xDD:m


    The output of this command should include the following lines:

    avrdude: 1 bytes of hfuse written
    avrdude: 1 bytes of hfuse verified
    avrdude: safemode: Fuses OK
    avrdude done.  Thank you.

The results after making all of these changes are as follows. 
  • Sleep current - 0.025mA (up from 0.006mA). This makes it clear how much current the Brown Out Detection support requires.
  • Wake current - 7.8mA (down from 8.5mA). This is a rough figure - the reading on the multimeter jumps around as the RF TX happens and the ATtiny sleeps between transmits. 7.8mA is about the highest reading I saw so I am going to use that for now.
Finally, there is one mode thing which I could do in software to improve power usage. This would be to rewrite my code to be interrupt driven. This would allow the ATtiny to spend more time asleep. However, the resulting code would be more complex and I don't need the extra power savings at the moment.

Friday, 27 January 2012

Wireless sensor node - code management

I have moved the Manchester library into its own GitHub repository. Full instructions for using the library can be found on that page.

This new repository now includes Mike's latest changes to allow a variable number of bytes to be transmitted.

Monday, 23 January 2012

Wireless sensor node - sleeping (4)

Now that I have RF comms working I can start to consider the other aspects of building wireless sensor nodes. 

The next item on my todo list is to improve the battery life of my wireless sensors. Note: The content of this article was heavily based on the following blog posts:
The first step in this process is to understand the expected battery life before I make any optimisations. The amount of energy stored in batteries is normally measured in milliamp hours (mAh). Therefore, to work out battery life we just need to measure the current used by the circuit. Current is measured by placing a multimeter (e.g. this one) in series with the circuit. This is how I do this on my circuit. 



Using this setup I can see that my circuit uses about 8.5mA (Note: the first picture actually shows a lower current as I took it at the end of this article). To work out the battery life from this number you just divide the available mAh by this reading. Note that using multiple batteries only increases the voltage, it does not increase the mAh and if each battery is different, the overall mAh available is the lowest value. 

My 3 AA batteries are all 1700mAh and I measured a current of 8.5mA. This gives a battery life of 1700/8.5 = 200 hours ~ 8 days. Not bad but not ideal - I don't want a new weekly chore of changing batteries! Especially if I deploy 5+ of these nodes!

Thankfully there is a straightforward code change we can make to dramatically reduce our current usage. The key point is that I don't plan to take readings constantly. I expect I will configure each wireless sensor to take readings no more often than once every 5 minutes. The rest of the time, the code will just sit in a delay() call. However, during this period of inactivity we can actually take advantage of a feature of the ATtiny85 to use much less power. 

The ATtiny85 supports being put into a sleep mode. In this mode, the power usage is MUCH lower and no code is executed. We can configure this sleep to end when either an input pin changes or when a fixed amount of time has passed. The second of these is what I will use. The timeout which wakes us is called the watchdog timer. Full details about the ATtiny including how to use the watchdog timer can be seen in the datasheet.

Without further ado, here is the code which I have added to make use of the watchdog timer. This comes in several parts.

1) Includes and defines:

#include <avr/sleep.h>
#include <avr/wdt.h>

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

2) Prep work. Note that I have excluded a lengthy comment from this quote which is itself a cutdown extract from the datasheet. The interested reader should refer to the ATTiny85 datasheet as the proper reference for what this code is doing. 

// Watchdog timeout values
// 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
// 6=1sec, 7=2sec, 8=4sec, 9=8sec
void setup_watchdog(int ii)

 // The prescale value is held in bits 5,2,1,0
 // This block moves ii itno these bits
 byte bb;
 if (ii > 9 ) ii=9;
 bb=ii & 7;
 if (ii > 7) bb|= (1<<5);
 bb|= (1<<WDCE);
 
 // Reset the watchdog reset flag
 MCUSR &= ~(1<<WDRF);
 // Start timed sequence
 WDTCR |= (1<<WDCE) | (1<<WDE);
 // Set new watchdog timeout value
 WDTCR = bb;
 // Enable interrupts instead of reset
 WDTCR |= _BV(WDIE);
}

void setup()
{  
  ...
  setup_watchdog(8);
}

3) Support methods:

void system_sleep()
{
 cbi(ADCSRA,ADEN); // Switch Analog to Digital converter OFF
 set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode
 sleep_mode(); // System sleeps here
 sbi(ADCSRA,ADEN);  // Switch Analog to Digital converter ON
}

// wait for totalTime ms
// the wait interval is to the nearest 4 seconds
void deepsleep(int waitTime)
{
  // Calculate the delay time
  int waitCounter = 0;
  while (waitCounter != waitTime)
  {
    system_sleep();
    waitCounter++;
  }
}

4) Actually going to sleep - note that deepsleep replaced delay. 

void loop()
{
  Tdata +=1;
  sendMsg(Tdata);
  // deep sleep for 2 * 4 seconds = 8 seconds
  deepsleep(2);
}

The result of all this is that the ATtiny spends most of its time asleep. Using a multimeter I measured the sleep current of my circuit to be 0.006mA. This can be used to calculate the overall battery life by entering the following figures into this website:
  • Battery capacity: 1700mAh
  • Sleep current consumption: 0.006mA
  • Wake current consumption: 8.5mA
  • Duration of wakeup: 2000ms
  • Number of wakeups per hour: 12 (once every 5 minutes)
The result: 2.6 years! Much better! 120x better than the previous battery life which I calculated.

As ever, the full source code is available here.

Sunday, 22 January 2012

Wireless sensor node - code fixes (3 - redux)

In my last post I realised a few things which needed to be fixed. These are now done!
  • My RX code no longer checks the range of the node ID and msg number which by definition were always in range.
  • The Manchester RX code now checks for low/high frequency interference during the data reception.
  • I have fixed a bug in the Manchester RX code which didn't properly check the timings of the pre-amble.
These changes can be seen here.

Wireless sensor node - manchester lib (3)

Last night I spent several hours trying to improve the RF comms which I am using.

MANCHESTER encoding

The first step was to add indenting and more details comments to MANCHESTER.cpp. I am quite pleased that I spent the time doing this as I now understand how the library works. I will try and describe what I have learnt here for my future reference.

RF comms can be tricky due to a combination of interference and trying to ensure that the TX and RX don't drift out of sync. For example consider a TX which wants to send the number 0 as a 16 bit number. In a naive scheme this would involve TXing 0 for the duration of 16 bits. However, if the receiver is running a bit too fast or slow they could easily read this as 15x 0 or 17x 0. The naive approach is also vulnerable to low frequency interference. The RX could detect bit transitions that were too fast and assume this was interference but there is no limit to how far apart bit transitions can be so low frequency interference will not be detected.

Manchester encoding addresses this by sending each bit as a transition. Zero is HI to LO. One is LO to HI. This means that the midpoint of every sent bit will always contain a transition. The edges of a bit will only contain a transition if the previous bit was the same. For example
  • 00 - (HI,LO)(HI,LO)
  • 01 - (HI,LO)(LO,HI)
To correctly receive these bits the RX must get in sync with the TX. This ensures that HI,LO,HI,LO is correctly interpreted as 00 instead of the RX missing the initial HI, reading a 1 and then discarding the final LO.

The MANCHESTER library which I am using sends messages as follows.

TX sends
  • 14x 0 followed by 1x1 - this is the sync pattern
  • 16x data bits
  • 2x 0 - I'm not sure why these are sent at the end
RX listens for
  • 10x 0 followed by 1x 1
  • 16x data bits
The RX listens for a shorter sync sequence because the first few bits are likely to be corrupted. The RX chip uses an amplifier to receive faint signals. This means that when there is no signal the RX will amplify background RF noise until it detects some ones and zeros. When the TX starts sending a message the RX chip needs a few transitions to adjust its amplifier to the correct level.

The reformatted and better commented MANCHESTER code is available here.

Reliable transmission

The Manchester library is really good and over short distances the RF link is good enough that the transmission is very reliably. However, when I have tested moving the TX into another room at the other end of my house I have found the link gets much more lossy and sometimes messages come through corrupted.

I don't think there is much more that could be done at the MANCHESTER level. As I wrote this I realised that the sync sequence explicitly checks for high/low frequency interference. However, the data receiving code doesn't do this checking so a clean sync followed by interference will lead to corrupted data. I will need to take a look at improving this.

I decided to add an extra layer of functionality on top of the MANCHESTER library. This extra layer would do the following.
  • Send each message 3 times with a random delay between each retransmission.
  • Add a message level pre-amble, node ID, message ID and checksum to each transmission.
The detailed message format is best described by the code I wrote to send it.

// Send a message with the following format
  // 6 bits pre-amble
  // 5 bit node ID
  // 5 bit reading number
  // 16 bit data
  // 16 bit data (repeated)
  //
  // This is a total of 3x unsigned ints
  unsigned int preamble = (0b010101 << 10);
  unsigned int nodeID = ((NODE_ID & 0b11111) << 5);
  unsigned int firstByte = preamble | nodeID | (msgNum & 0b11111);
  
  MANCHESTER.Transmit(firstByte);
  MANCHESTER.Transmit(data);
  MANCHESTER.Transmit(data);

Since I only have a single unsigned int of data, sending the data twice is essentially an XOR checksum. I previously XOR'd the data byte with the firstByte but this caused problems because for any data values with the top 6 bits set to 0, the checksum ended up starting with the 6 bit pre-emable which confused the RX code.

The RX code stores an array of node ID to message Nums. This allows it to ignore retransmissions. The RX code also checks for the pre-emable, checks the checksum, checks the nodeID and readingNum are within the expected range. As I write this I have realised that these last two checks are silly because these numbers are constrained to 5 bits each so actually HAVE to be in the correct expected range.

The full code for my TX and RX code can be seen here:
Pictures

Every blog post should have pictures. I will conclude this post by pointing out a mistake I made when wiring my circuit up again last night.



This is the small breadboard I use for my TX circuit. I had real issues getting my TX to work last night. I eventually realised that the broken red line along the bottom means that the left half and right half of the power line are disconnected. I initially had my battery plugged in on the left.where it was powering nothing. Moving it to the right got everything working!

Saturday, 21 January 2012

A couple of interesting links

Here are a couple of excellent websites which post a LOT of content about Arduino/ATtiny related topics.

http://jeelabs.org/
This is a blog run by someone who has invented their own platform similar to Arduino but called a JeeNode. The main difference is that a JeeNode includes an RF link. This is actually pretty cool as a complete JeeNode is only EUR18.50!

http://hackaday.com/category/attiny-hacks/
This site is frequently updated with interesting hacks which people have built around ATtinys.