Marc's Public Blog - Arduino Hacking


All | Arduino | Btrfs | Cars | Clubbing | Dining | Diving | Electronics | Exercising | Flying | Hiking | Linux | Linuxha | Public | Rc | Snow | Solar | Trips



Table of Content for arduino:

More pages: January 2015 September 2013 January 2012 December 2011 May 2011 January 2011




2015/01/06 Driver for direct driving single to 3 color LED Matrices with software PWM
π 2015-01-06 00:00 in Arduino
Code download: Multi Color PWM LED Matrix Driver.

Many LED matrices come with a MAX7219 driver chip or equivalent. Those are great since you program the columns and rows, and they do the line by line scan and refresh for you. Unfortunately, you can't do color mixes with different intensities for each color. For instance the Adafruit LED backpack is super easy to use, but you cannot control each color to mix different shades between them.

Then, I also happened to have some raw LED matrices a dual color one and a triple color one ordered from china, equivalent to these two: https://www.sparkfun.com/products/682 and https://www.sparkfun.com/products/683 . Those didn't come with any driver chip, so that gave me an excuse to program my own code to do line scanning and refresh like many examples you find on the net.

My bycolor matrix has common cathode, green and red on the 2 anodes. Like other matrices you have to disable all the lines, set the rows you'd like for each color, and then turn on the common ground to illuminate those pixels for a little while. Then, you go to the next line, and continue. Many examples do this in the main arduino loop, but I wanted to use Adafruit's excellent Adafruit-GFX library. As a result, I wrote an ISR (interrupt routine) to rrefresh the lines, like an old cathodic raw tube, in the background, while leaving the main loop for programming what you want to do and display. This soon allowed me to display the smiley face bitmap from the Adafruit LED Backpack library.

from here
from here

to here
to here

This was pretty major accomplishment for me since I wrote a generic C++ library that could allocate an array of any size (it supports anything, not just 8x8), and do all the work in the background in an ISR. I then got busy with other projects and hobbies.

Later, I came back to this and added code to support more than one color, and especially support programming an LED array of 1 to 3 separate colors wired either directly or via shift registers, or a combination of the 2 (shift registers save pins, but also make IO 50% slower). By then I was hitting issues where I had to refresh the lines very quickly (200 microseconds) to allow for 16 shades per color and still offer a 40-50Hz refresh rate for the whole array. If my refresh became slower than 200 microseconds, I could not support 16 shades (4 bits per color) without getting too slow and creating an array that would visibly flicker. I fixed this by doing the following:

  • Fast Digital IO to make digitalwrite 3x faster and my ISR routine 2.5x faster
  • Instead of having 16 interruptions for 16 levels per color, I switched to binary code modulation where I could do the same 16 levels of shading with only 4 interrupts instead of 16. This also leaves more time for code in the main loop.
  • I've published my resulting code here: Multi Color PWM LED Matrix Driver. While it uses more resources than the adafruit backpacks, it's cheaper in hardware and ends up giving more flexibility (many more colors).

    *Before you ask me for help, READ THIS*

  • If you don't know the basics of LED matrices and shift registers, go read the links at the bottom of this page which show wiring for direct wiring and shift registers.
  • Or if you want shift registers in a nutshell, go here: https://www.youtube.com/watch?v=bqfPZXEuyuc
  • Shift registers + LED matrix, see: https://www.youtube.com/watch?v=2m3PbCvcqkY
  • For a wiring diagram see: http://learnpic32.blogspot.co.nz/2014/06/controlling-rgb-led-matrix-with-shift.html
  • But basically you should get the basics working before trying my library. It's the fancy icing on top fast one, make sure you can turn on LEDs one by one and then try my library.

    You can look at the results here:




    The original Adafruit::GFX library doesn't support multi color bitmaps, but I added support for it here: https://github.com/adafruit/Adafruit-GFX-Library/pull/39

    After doing the above, I went to add support for Tricolor Matrices, which was not much work, except for adding those 2 bits:

  • allowing shift registers to be wired to rows in reverse order when it makes wiring easier
  • 3 colors at 16pwm values and 40Hz runs against the speed limits of an arduino nano v3
  • My tricolor matrix had a common anode which was opposite from the bicolor with a common cathode.
  • This was all added to my LED-Matrix library here: https://github.com/marcmerlin/LED-Matrix
    If you'd like to buy the tricolor matrix I used, here are some links:

  • specs: http://www.seeedstudio.com/depot/datasheet/2088RGBMatrix.pdf (sold here: http://www.seeedstudio.com/depot/8x8-RGB-LED-Dot-Matrix-Compatible-with-Rainbowduino-p-113.html)
  • http://www.seeedstudio.com/depot/8x8-RGB-LED-Matrix-Square-LED-Dot-p-1730.html seems to be common cathode (the opposite)
  • This is the same matrix I used, sold by sparkfun: https://www.sparkfun.com/products/683
  • only $5 from hobbyking
  • Again, the pictures don't do a good job showing the PWM values because of the CCD trying to capture a consistent amount of light. Also, anything close to white uses all 3 LEDs, this draws too much current from my arduino on the common anode. Before I add FETs or ways to improve current per line, it's still good enogh for demos. This setup uses 2 shift registers for 16 pins (blue and red), while green is connected directly to 8 pins, and 8 pins for common anode (which is where the current for 3 LEDs at once is lacking):



    generating circles with the Adafruit::GFX library
    generating circles with the Adafruit::GFX library


    Here's a video demo:

    Links to other pages about this topic:

  • http://learnpic32.blogspot.co.nz/2014/06/controlling-rgb-led-matrix-with-shift.html
  • http://francisshanahan.com/index.php/2009/how-to-build-a-8x8x3-led-matrix-with-pwm-using-an-arduino
  • http://www.instructables.com/id/64-pixel-RGB-LED-Display-Another-Arduino-Clone
  • http://www.seeedstudio.com/wiki/60mm_square_8*8_LED_matrix_-_super_bright_RGB
  • https://www.sparkfun.com/tutorials/201
  • Sparkfun offers a hardware backpack: https://www.sparkfun.com/products/760 with code here: https://github.com/fornellas/SFRGBLEDMatrix
  • http://playground.arduino.cc/Main/LEDMatrix
  • 2013/09/14 Arduino Training and Hacking at Google
    π 2013-09-14 00:00 in Arduino, Google
    I was able to join an arduino 101 class at Google today. Ok, I wasn't an arduino neophyte, but there are a few useful things I learned during the class, and more importantly got to hack on new IO devices (I2C LCD and I2C LED matrix) which I had never interacted with before, as well as with an IR distance sensor which has pluses I was not aware of compared to the ultrasonic sensors.

    I had good time hacking with this, and in the end was able to make a game where you keep a ball inside the square with an accelerometer while another person can adjust the game speed with the IR sensor. When you lose, you get appropriate sound and screen animation (doesn't look like much, but this is usually what takes the most coding time).

    coworkers at work :)
    coworkers at work :)

    my first try
    my first try

    someone made an almost working pong game
    someone made an almost working pong game

    another keep the ball in the square game
    another keep the ball in the square game

    My finished device in smal form factor fitting on top of the arduino
    My finished device in smal form factor fitting on top of the arduino

    Here's my demo to my coworkers, using my external clean power supply:

    And the same thing in a small form factor, using the noisy arduino voltage ref (this is why it's moving so much in the init phase, despite no input). I'll have to do some analog electronics to clean that up:

    This was definitely time well spent and a good learning experience.

    2012/01/24 Much Improved Pebble v2 Aiko Demo Code
    π 2012-01-24 00:00 in Arduino
    TL;DR: Get code here: https://github.com/marcmerlin/aiko_pebble_v2

    Note, you will need the LiquidCrystal_SR_LCD3 driver from the NewLiquidCrytal Library.

    You will also need the Aiko Library: https://github.com/geekscape/aiko_arduino .

    Pebble v2 requires at least the IDE 0.23 (untested) but 1.0 or better is recommended. The code is of course arduino 1.0 compatible.

    At the LCA 2012 Arduino Miniconf, we got to build a new toy, a Freetronics Pebble v2 (schematics here). This was a refresh of the Pebble V1 (schematics here) which was the target of the very first arduino miniconf I missed in 2010.

    This new Pebble had even more hardware to program against and was a much more compact design due to the use of a bunch of SMT parts (actually there are 2 arduinos on the board, one is dedicated to usb/serial communications, removing the need for an ftdi chip).

    Like good hacking projects and conferences, the design and software were improved until the last minute, and Andy worked all night before the conf to get us his demo code (along with bits from Luke and others).
    Andy wrote the very cool aiko event handler for arduino. You give it a list of callback functions and it'll do its best to call them on the interval you gave it (interval in milliseconds). For arduino folks, with Aiko, code looks like this:

    void setup(void) {
      Serial.begin(DEFAULT_BAUD_RATE);
      // Call LCD init early so that others can write to it.
      lcdInitialize();
    

    Events.addHandler(clockHandler, 1000); Events.addHandler(lightHandler, 1000); // Aim for 250 ms Events.addHandler(rotaryEncoderHandler, 100); Events.addHandler(serialHandler, 30); // Sufficient for 38,400 baud Events.addHandler(temperatureHandler, 1000); // Aim for 250 ms Events.addHandler(touchPanelHandler, 250); // Aim for 50 ms Events.addHandler(rgbLedFadeHandler, 10); // Can run slower. // Call LCD last so that it can display variables that just changed. Events.addHandler(lcdHandler, 1000); // Aim for 100 ms }

    void loop(void) { Events.loop(); }

    So Andy got us working code to test our hardware, but of course it wasn't fully optimized (hell, code never is). So, that gave me a chance to do some arduino programming to see what could be improved. It was a good learning experience.

    After a bunch of work, here is what I was able to do:

  • I happened to have RGB LED demo code I had taken a while to write over Xmas (nice transitions that I went through the trouble of writing so they would work with an Aiko event handler), so I did a bit more hacking to refine the code and plugged it in.
  • The rotary button was being polled every millisecond to catch which was it was turning. First I figured that every 5ms was probably enough, but thanks to sample code from Brett Downing, I was able to write a handler using hardware interrupts on input pin state changes (something I knew nothing about, so it was a good learning experience).
  • Then this brings us to the LCD handler. I had some grand view of writing a popup menu that would let you configure the code at runtime, but that meant a lot of LCD code. I knew arduino had a nice LiquidCrystal library, but it did not work with the shift register setup in the Pebbles. Long story short, I wrote a plugin for the Pebble/LCD3Wires shift register setup in the Pebble. This demo requires this new library (LiquidCrystal_SR_LCD3) to do its work.
  • Here is the end result:

    Again, the code: https://github.com/marcmerlin/aiko_pebble_v2

    Here's a quick man page :)

  • The 4th line shows the color that the RGB LED is changing towards
  • LED 6 means that the color change delay factor is 6 (lower means faster color changing)
  • 3 in LED 6/3 is by how much the color brightness is reduced (clips the color values so that they arne't too bright, mostly useful in dark rooms or when taking pictures).
  • LCD 80 is the analog PWM value for the LCD backlight (higher is brighter).
  • A few custom characters are displayed to show use of the custom character support brought by LiquidCrystal_SR_LCD3

  • pop up menu with selection cursor
    pop up menu with selection cursor

    backlight menu
    backlight menu

    LED color change delay menu
    LED color change delay menu

    Note that I as read on http://www.circuitsathome.com/mcu/rotary-encoder-interrupt-service-routine-for-avr-micros and have seen with multiple decoding errors in my rotary encoder test code (shown below), it is probably a good idea to add a couple of small caps around the encoder's feet:


    And that's it folks, that was fun :)

    Oh, actually I also made a repository of small demo code I wrote for the Pebble v2: https://github.com/marcmerlin/pebble_v2_demos .

  • Full_LiquidCrytal_Demo: This is my NewLiquidCrystal demo code writing for the 20x4 LCD on the Pebble V2.
  • InterruptEncoder_test: This is the standalone/debug interrupt code I used to test the rotary button interrupt code.
  • LCD3WireExample: This allows using the deprecated http://arduino.cc/playground/Code/LCD3wires library with the Pebble v2 (this was just to try it out, there is no reason to use this library, you should be using NewLiquidCrystal's LiquidCrystal_SR_LCD3 library instead: http://marc.merlins.org/perso/arduino/post_2012-01-23_LiquidCrystal-driver-support-LCD3Wire-hardware-_pebble-and-others_.html ).
  • performanceLCD: This is a port of Fernando's LCD example for Pebble v2.
  • 2012/01/23 LiquidCrystal driver support LCD3Wire hardware (pebble and others)
    π 2012-01-23 00:00 in Arduino
    TL;DR: Get code here.

    All the code is arduino 1.0 compatible

    After coming back from the Arduino Miniconf at Linux.conf.au, I ended up with a freshly soldered freetronics pebble v2.

    Like the Pebble V1 (schematics here), it uses MC14094 8 bit shift register to drive the LCD using only 3 wires instead of 6 or more.

    The Pebble designers thankfully used the same shift register wiring than shown on the LCD3wire page on arduino playground. However, the code that came with it only had limited functionality for talking to the LCD, and the code on the LCD3wires page is also limited in functionality (on top of being incompatible with arduino 1.0).

    So, I ended up finding Francisco Malpartida's cool work on virtualizing the stock arduino LiquidCrystal library (very featureful and optimized timings). In turn, Ragnar used that codebase to design his own shift register library for 2 or 3 wires.

    Ragnar's wiring and code is somewhat complex because it exploits some interesting hacks:

  • it is designed to work with cheaper non latching shift registers.
  • it can be made to work with just 2 wires instead of 3 for non latching shift registers.
  • because of the design, the code is less obvious and exploits some tricks, but it sure works :)
  • his code can work for latching shift registers, but I do not recommend using the more complicated wiring and code if you can afford 3 wires and a shift register. Just use the LCD3Wires wiring and the code from this page.
  • In other words, if you have a non latching shift register, use his code and wiring. If you have a latching shift register, you should use the code from this page and the more widespread LCD3Wires wiring.

    So, where does this bring us?
    I used Malpartida and Ragnar's work to make an LCD3Wire hardware driver for the NewLiquidCrystal Library. This brings full library support to the pebbles as well as other folks who use the reference wiring from LCD3wires.

    I called the new library NewLiquidCrystal in the hopes that it will become the new de facto library in standard arduino one day. My code is here: https://github.com/marcmerlin/NewLiquidCrystal and includes the new LiquidCrystal_SR_LCD3 driver I wrote. To be clear, all the code came from Francisco Malpartida's work, I built on top of it to write my LiquidCrystal_SR_LCD3.cpp driver for LCD3Wires compatible hardware.

    I also wrote an extended LiquidCrystal Demo which should work on all liquidcrystal supported devices (baring the character count).



    Here is a demo of my popup menu code that works with a rotary button (cursor showing what is currently selected if you click on it)


    I'm now working with Francisco to get my code included in his tree, and if we're lucky that code may eventually get included in the standard arduino libraries as a faster and more flexible LiquidCrystal replacement.

    2011/12/12 GCC-AVR-4.5.3-2 Breaks Arduino NewSoftSerial
    π 2011-12-12 00:00 in Arduino
    Dear Google, please index this.

    TL;DR: gcc-avr 4.5.3 breaks Arduino NewSoftSerial on 328p. I had to downgrade back to 4.3.5.

    Full story:
    I had my arduino mobsendat board (328P based) working fine and after uploading some changes, it startsending a few of my characters over my Xbee, mixed with garbage. Most characters actually seemed lost.

  • https://github.com/lukeweston/RocketInstrumentation/blob/master/RocketInstrumentation-board.png
  • https://github.com/lukeweston/RocketInstrumentation/blob/master/RocketInstrumentation-schematic.png
  • In other words, I got less traffic than I should, a few were original characters, and some were garbage on a board and Xbee that were sending traffic at 38,400bps just fine for the last year.

    I used my xprotolab to decode the sending pin on the xbee directly to rule out RF issues, and I see the garbage there too. If you want to know what I mean, look at the last picture on http://www.gabotronics.com/product-info/xprotolab-pictures.htm

    So I was bit confused. The Xbee worked fine separately from the board and the serial bits getting to the xbee pins were damaged before they were sent on a simple program like this:

    #include <NewSoftSerial.h>
    

    const int XBEE_SLEEP = 8; // pin 14

    NewSoftSerial mySerial = NewSoftSerial(2, 3);

    void setup(void) { Serial.begin(38400); mySerial.begin(38400); // Switch pin to digital mode with pullup. pinMode(XBEE_SLEEP, OUTPUT); digitalWrite(XBEE_SLEEP, LOW); while (1) { Serial.println("AB12"); mySerial.println("AB12"); delay(1000); } }

    void loop(void) { }

    Arduino IDE is 0.22ubuntu0.1 on ubuntu oneiric. I thought that maybe an update to the arduino IDE broke it when I went from ubuntu marverick/natty to oneiric, but that wasn't it.

    Then I thought that maybe I damaged my hardware (maybe the clock/crystal is messed up, but then I suppose serial over USB wouldn't work either, and it does work), or if upgrading ubuntu recently broke NewSoftSerial in some subtle way, but I verified I had NewSoftSerial 10, and upgrading to 11 beta did not help.

    This is where it got wild: if I listened to the serial traffic going to the xbee from NewSoftSerial (I had version 10), and if I used the RX UART decoding pin of my Xprotolab, it could see the traffic ok. If I used the TX pin on the Xprotolab, then the same traffic mostly looked like garbage (actually most characters were lost). Putting 2 stop bits did not help.

    I talked to Gabriel Anzziani who made the Xprotolab and he said 'The RX and TX decoding are done differently in the Xprotolab. The RX is done by the XMEGA hardware, and the TX is done in firmware.'

    So clearly, there was a timing issue in what NewSoftSerial was doing at 38400 even though it had worked with the same NewSoftSerial library for about a year and NewSoftSerial 11 did not help.

    So, out of desperation, I took the nfs backup from my laptop before I upgraded from ubuntu maverick to ubuntu oneiric. While it was a bit hard to get the arduino IDE to work over an NFS chroot, I eventually got it to run, compile and upload my same Hello world serial program. And it worked!

    From there, it took a binary search to pin this down to the gcc-avr upgrade.

    gandalfthegrey:/var/local/src/arduino# dpkg -i gcc-avr-4.3.5-1.deb
    dpkg: warning: downgrading gcc-avr from 1:4.5.3-2 to 1:4.3.5-1.
    Yes, that really was the fix :(

    My guess is that the new gcc-avr changes some compilation timings/optimizations in a way that NewSoftSerial breaks :(

    2011/05/12 Putting my Mobsendat Board to work to review my Rematee Bumper Belts
    π 2011-05-12 00:00 in Arduino, Osa
    Since my sleep apnea turned out not to be fully cured by my MMA surgery (it helped, but it wasn't entirely enough), the two other known fixes that do not include CPAP are to stop my tongue from being lazy and roll back in and partially my airway, and sleep on my back, which causes the previous point to be worse.

    Funny thing though is that I am used to falling asleep on my tummy (I don't fall asleep easily on my back), but then I will turn on my back while I'm sleeping, every single night. When I half wake up, I'll go back on my tummy and when I sleep again, go on my back again, making my apnea worse (mind you, sleeping on my tummy is not so good for my neck muscles and spine, but I can deal with that). One would say that I should sleep on my side, but that's an unstable position for me and I don't stay there long.

    This is where rematee comes in: I got the Rematee Bumper Belt.
    It is a belt with air pouches that you put on your back and that are supposed to stop you from turning on your back at night. I figured I'd give it a shot, but I soon found out that I quite obviously turned on my back anyway and woke up sleeping on the belt, which hurt my back a fair amount and just gave me an even worse night ("obviously" due to back pain and waking up on my back). One is supposed to wear that belt under the armpits, but for me it didn't make a huge difference, except for where the resulting back pain would be.

    This was discouraging, but I thought I'd give it another shot and got the next size up from rematee. My size is 36, so the large belt with 3 smaller air pouches was technically a little bit too big for me, but my rationale was that once I have the momentum to turn on my back, a few air pouches in my back aren't going to stop me, and apparently I stay sleeping on them and just get a back acke. It made sense at the time that if the air pillows spilled over to my side a bit, they would be more likely to stop my initial rotatioin to my back.

    Long story short, some testing showed that I still turned on my back with the 3 pouch belt. I didn't do detailled testing between the two, but while it seemed to work slightly better, it wasn't good enough.
    At that point, I came to wonder if things would work better with wearing both belts. Long story short is that yes, things worked better with both belts. My suggestion to the designers however, is would for the large belt to have 2 big pouches on the outside to provide maximum leverage against body rotation, and a smaller pouch in the middle to fill the void (the extra large belt does have 3 large air pouches, but that one is just too big for my body).

    both belts
    both belts

    So the big question, is how do you get quantitative data on this? Well, I just happened to be working on an arduino microcontroller board that had a built in 3 axis accelerometer amongst other things. While it was originally meant for attaching to rockets and location tracking with GPS, I figured I could also use it to simply track my body position at night, so I wrote some code to do that (and log it both to the sdcard on the board as well as send the data wirelessly via Xbee to my computer so that I don't have to pull out the card and read it every day).

    You can read this page on Xbee Power Consumption on my Mobsendat Board for details on the board I've been using but basically I get this end result from the board:

    "2011/05/24 06:20:04",,X:0.11,Y:-1.00,Z:0.13,V:4.97,left,rssi:0(DC:2),assoc:843(avg:842)
    "2011/05/24 06:20:09",,X:0.10,Y:-1.00,Z:0.13,V:4.96,left,rssi:0(DC:3),assoc:843(avg:842)
    "2011/05/24 06:20:14",,X:0.04,Y:-0.54,Z:-0.90,V:4.92,down,rssi:903(DC:0),assoc:843(avg:842)
    "2011/05/24 06:20:19",,X:-0.05,Y:0.39,Z:-0.93,V:4.96,down,rssi:903(DC:0),assoc:841(avg:842)

    The important part is the "left" which changed to "down" (Y axis went from -1g to less than .5, and Z went from 0 to -0.9g). I then have a script that parses the output and gives a summary for each night which I then enter in my detailled spreadsheet.

    gandalfthegrey [mc]$ ./scan_file night63_twobelts_home_sun.csv 
    up,     down,   left,   right,  unkwn,  pos changes,     hours sleep
    11.4%,  52.6%,  26.9%,  09.0%,  00.0%,   19 pos chg,     7.7 H sleep

    the microcontroller and battery pack fit nicely inside
    the microcontroller and battery pack fit nicely inside

    what it looks like inside
    what it looks like inside

    And the results are that I slept an average of 26% of my night on my back without belts, 13% with one belt, and down to 7% with both belts. On one side, this is encouraging, although on the other side, it's a bit eery that I still manage to sleep 7% of my night on my back, either by rotating the belts just enough, or by plain sleeping on them. You'll however see if you check the detailled spreadsheet that with the belts on, I have many nights when I didn't sleep on my back at all, and that the average gets messed up by a few nights when I slept more than 20% of the night on my back. It is possible that the optiona shoulder straps will help fix that.

    So how much better is my sleep? That one I don't have quantitative numbers for since I haven't been able to get an SPO2 sensor that I can interface with yet, or easily measure my breathing patterns vs my body position (I did use nose canulas in other take at home sleep tests and I tended to rip them out by sleeping on my face anyway).
    While I don't have exact data, my sleep did feel better with the belts, I also know that my apnea is worse when I'm on my back, so it's fair to say that belts help. How much exactly? Not sure yet, I'll get more data on that later hopefully. I also have to note that my data isn't perfect because at the same time I had to wear a heavy dental piece at night that kept my teeth from moving back to their old place (this is related to my post MMA surgery dental works). I was able to stop using that bulky mouthpiece at day 51, and my sleep improved noticeably at that time.

    So while the data is not perfect since I at least changed the mouth piece part during the samples, here's my summary spreadsheet, and the detailled spreadsheet.

    Interesting bit I got from the data are: I tend to flip-flop about the same amount regardless of whether I have the belts on, and outside of the amount of time on my back, the belts didn't change other sleep parameters I was measuring:

    Position:backtummyleftrightunknownhours sleptposition changesSample Count
    No Belts26.24%49.93%19.44%3.35%1.03%7.4615.0812
    3 cushion belt around chest13.00%51.83%32.07%2.83%0.30%7.4212.507
    both belts6.94%55.91%32.18%4.26%0.67%7.6714.1937
    Average15.39%52.56%27.90%3.48%0.67%7.5213.92

    100_MobSenDat_Rematee 101_MobSenDat_Rematee 102_MobSenDat_Rematee 103_MobSenDat_Rematee 104_MobSenDat_Rematee 105_MobSenDat_Rematee
    2011/05/11 Reducing Xbee Power Consumption on my Mobsendat Board
    π 2011-05-11 00:00 in Arduino
    TL;DR: By using Xbee pin sleep, I was able to bring my board power consumption from 100mA-ish to 35mA-ish average by ensuring that the Xbee radio is off most of the time (unfortunately, it doesn't have a mode where you can just have it be send only and wake up the RF radio only when you give it serial data).

    Longer version:
    I've been using my Mobsendat Board to log my [sleep positions|], and both for fun, and because I'm lazy, I've been using Xbee to send data from the mobsendat to my PC.
    I used some Xbee Pro modules because I wanted to get as long a range as possible, and because the data may have to go through my body, which is some kind of RF shield.

    First, I tried the Xbee 1 modules, because they were easier to get and a bit easier to program since more people were using them. My mobsendat board was using 33mA and with the Xbee 1 module, would go between 80 and 110mA. This is of course a fair amount of power.

    I then upgraded the modules to Xbee 2.5 ZB Pro modules, and that shaved off 10mA or so. It was however clear that I would have to put the modules to sleep to save further.

    To do this, I used some of the free pins on the AT mega 328, and wired up:

  • AT Mega Pin 14 (Digital 8) -> Xbee DTR
  • AT Mega Pin 16 (Analog 2) <- Xbee RSSI
  • AT Mega Pin 17 (Analog 3) <- Xbee Assoc
  • The Xbee was flashed with the latest ZB end device AT firmware with pin sleep (be careful, the default setting of cyclic sleep puts the module to sleep as soon as it's flashed, so even X-Ctu can't re-read its settings without having the module be reset). See my page Xbee Adapter page with reset pin hack.
    I left the pullup resistor enabled in the Xbee, so I was able to use simple wires to connect the pins.


    Xbee sleep works like this: bringing DTR up puts the module to sleep if it's not in the process of sending/resending data (that's nice, you don't have to worry about waiting for an ACK which you can't read in AT mode before putting the device to sleep, you can just fill its buffer, give it the sleep signal and it'll go to sleep when it's done sending).

    This works great: by putting the module to sleep for 95% of the time or so (I transmit every 5 sec), the power use goes back to 33mA for most of the time (with quick peaks to 100mA during send).

    Now, for how to deal with sleep more intelligently:

    If you have AI/RSSI LEDs and your module is not associated, you can easily see that sleep will not put the module to sleep after sending data until all the retries have failed.
    In my case, I wired the RSSI and Assoc outputs to my arduino inputs so that I could read the analog values. If you're sending data from an end device, you can read RSSI as a poor's man ack value by keeping track of last time it was high (defined as between 32 and 1024 on my system since it a signal strength value and anything below 32 has been noise in my experience). What this means is that by reading RSSI multiple loop cycles in a row, I can detect when I'm not receiving any data (and therefore missing acks) and not put the module to sleep at all with the hope that it will "reconnect" on its own.

    One might thing that you can read the Assoc value to guess whether AI=0 (associated) or not. Note that the LED actually shows solid when not associated and will blink when associated, so it's not as easy as just getting an on/off value. To deal with this one, I actually read the LED output value multiple times, average it, and if it's flashing, I'll get less than 800 avg (out of 1024).

    Now, this is where it gets more counter intuitive: AI (association) unfortunately does not go back to unassociated when I remove the router or coordinator in my network. What this means is that I can't detect if my module can't talk to the network by using AI (only getting ACK return values can really tell me that, and I'd have to switch to API mode for that). But, there is a trick: if the module is waiting for ACKs, it will not go to sleep, and AI will normally flash. When that happens, I will read Assoc analog values that are less than 800 and by averaging a few, I can tell that the LED is flashing and therefore that the module isn't going to sleep.
    Neat, eh?

    This is what my data looks like when I have RF issues:

    "2011/05/24 05:53:29",,X:0.13,Y:-1.00,Z:0.11,V:4.96,left,rssi:904(DC:0),assoc:847(avg:844)
    "2011/05/24 05:53:34",,X:0.15,Y:-1.00,Z:0.14,V:4.92,left,rssi:904(DC:0),assoc:844(avg:844)
    "(DC:4),assoc:0(avg:337|ANS)
    "2011/05/24 05:53:59",,X:0.14,Y:-1.00,Z:0.13,V:4.97,left,rssi:0(DC:5),assoc:842(avg:337|ANS)
    "2011/05/24 05:54:04",,X:0.14,Y:-1.00,Z:0.12,V:4.97,left,rssi:0(DC:6|RNS),assoc:842(avg:336|ANS)
    It shows rssi dropping to 0 with DC:5 (down count due to RSSI being 0), and assoc average having dropped from 844 to 337 (we don't see instant low assoc values since they were not transmitted outside of the truncated line).

    Next step is lowering down the Mobsendat power base consumption from 33mA to 12mA by simply disabling two of its status LEDs.

    100_MobSenDat_XBee_Power_Hacking 101_MobSenDat_XBee_Power_Hacking

    More pages: January 2015 September 2013 January 2012 December 2011 May 2011 January 2011