Implementing Bootloader for Ultra Low Power Arduino Wireless Sensor Node : Part 2

Following the 1st part of bootloader description for ULPNode this article describe what modification have been done to optiboot to be able to drive new Ultra Low Power nodes.


For this, I started from original optiboot (latest trunk from google code) which is version 6.2 at the time of writing.

The process to build the bootloader is quite simple and the organization of the files are very well done so that we can implement customization without touching too much original files (except the optiboot.c source file of course). I created a ulpnode folder under the Arduino bootloaders setup folder (mine is located in Arduino-Install-Dir\hardware\arduino\bootloaders\ulpnode\) and put all necessary files into this folder.

New Makefile, new parameters

To be able to drive the new pins described in the previous article and to use same technique as LED done in the original bootloader I created a new include file with all the definition needed, this file is just a copy of original pin_defs.h that I called ulpnode.h and tweaked for the new pins. With this new file we’re now able to pass the pins parameters on the command line at build exactly the same way it has been done for others parameters such as  LED=B5 LED_START_FLASH=1 ....

The parameters are the following :

  • WS2812=D6 Pin to drive WS2812 LED, PORTD6=D6 of Arduino
  • WAKE_SWITCH=D4 input pin of the manual Wake switch, PORTD4=D4 of Arduino
  • PWR_SENSOR=B1 output pin to power up the sensors, PORTB1=D9 of Arduino
  • PWR_BOOST=C2 output pin to power up the sensors, PORTC2=A2 of Arduino
  • PWR_RF=D7 output pin to power up the radio module, PORTD7=D7 of Arduino

Then I created a new makefile called Makefile.ulpnode containing the build process of the ULPNode bootloader. This is the new file (also on github) :

To be able to compile for ULPNode I needed to edit the original Makefile to include this one. It has a dedicated section for this and I just added the line include Makefile.ulpnode on the section as follow

New parameters, new batch file to build

Now a batch file ulpnode.bat for testing and launch the build process that contains the following

Hummm, I can hear some readers asking what are these fancy frequency 4MHz and 250Kbps baud rate ?? Well the 4MHz frequency is explained in the 1st article (just because ULPNode is running at 4MHz to be able to run until 1.8V) but the 250Kbps isn’t.

Upload yes, but upload right

Ok, it’s time to explain. The serial speed port of Arduino is a derivative of frequency and depending on speed you want to achieve and crystal frequency it can introduce some shifting. And that is exactly why on original optiboot build process, this derivation (called %error)  is checked to be sure you will be able to upload your sketch at the frequency you choose with the on board crystal. Here we’re running at 4MHz and it’s just impossible to get reliable 115 200 kbps default Uno baud rate working (see datasheet below). The error at 4MHz is just 8.5% and still -3.5% in best case at 8MHz

ULPNode UART Speed

Atmega328P UART Speed settings vs Frequency

But looking closer at the datasheet you will find that at 250 000 kbps you always have 0% error. Amazing, since all USB/FTDI chips used to upload sketch are able to do this speed I’m still wondering why it’s not used. I have all my Arduino board flashed with 250 000 kbps uploading. The drawback is that Arduino IDE does not permit Serial monitor to work at this speed even if the serial port is capable of. You can always in your sketch use a Serial.begin(115200);  configuration, but once again, at 4MHz this will not work, so using Arduino serial monitor should be setup with 38 400 kbps. If you want to serial monitor at 250 000 kbps, you will need to use other tool such as putty.

Of course you can stay on the well known speed, for 4MHz, but you will need to upload at 38 400 kbps. It works, uploading is just slower. I’ve tested a workaround, compiling bootloader for 16MHz and just before upload, set clock prescaler to 1 to use 16MHz speed, this is working fine, so why I did not used this technique ?

Because the WS2812B led driver is included onto the bootloader. This driver code is compiled for a specific speed (and it’s very time critical) and I wanted ULPNode able to light the LED when running at 4MHz (it normal speed) without the need to go to 16MHz speed just to light LED. And as the WS2812B driver code is shared between bootloader and sketch, when called, both need to be at 4MHz. I privileged ULPNode LED code instead of the bootloader one, but it’s just an arbitrary choice that you can change depending on your need.

What is changed on bootloading view ?

Beside including the WS2812B driver code (thank’s Tim) I included in the bootloader the Low Power management to boot without consuming unneeded power. Here the working flow of this new ulpnode bootloader

  • Set PWR_RF I/O as output to disable powering RF Radio Module
  • Set PWR_SENSOR I/O as output to disable powering sensors
  • Set WAKE_SWITCH I/O as input/pull up for reading wake up switch
  • Set PWR_BOOT I/O as output for driving DC booster
  • Enable DC booster
  • Check if FTDI module is connected or if Wake Up switch is pressed
  • Set working speed to 4MHz (remember CLKDIV8 so 2MHz)
  • If Wake Up switch is pressed or FTDI Module connected
    • Go to bootloader for potential upload
    • Flash led with color depending on Switch pressed or FTDI detected
    • blah blah blah, … same as original
    • If nothing to do, reset after 1S and start sketch
  • Else
    • Launch immediately user application (sketch)

This mean, that if no FTDI adapter is connected and the wake up switch is not pushed, the code start the sketch immediately running at 4MHz with the DC booster enabled. If not, the LED will flash (if defined so), bootloader will upload (if needed and found) and the sketch will start with DC booster Enabled

This order is subject to change depending on more testing. I need to test and tweak this with the real ULPNode boards. Of course for the example flashing the LED 3 times is not Low Power even if it’s not at full brightness.It’s fine for testing but for example you could blink 3 times when connected with FTDI powering the board, 1 time only when Wake Up switch is pressed, and no flash if none of the above is happening.

Compile ULPNode bootloader

The compilation is straight forward, just open a command line and go to the folder of ulpnode (mine is located in Arduino-Install-Dir\hardware\arduino\bootloaders\ulpnode\) and issue a ulpnode command. Here the result on my computer :

You have now a file named  ulpnode_250000_4000000Hz.hex in the current folder. For flashing this image, my recommendation is to use Atmel AVR studio. Dealing with fuses with avrdude requires speaking hex fuses language combined with bit action knowledge. I prefer using the GUI since I can see and set the option and also since I bricked some device dealing with avrdude bad fuses setting (ok it was not a avrdude problem, just the human using it). So since I’m using AVR Studio GUI, all is fine for me. The fuses setting for ULPNode should be as follow :

ULPNode Fuses settings

ULPNode Fuses settings

Once fuse are programmed flash the hex file and you’re done. Once again I’m using AVR Studio for this. If all went fine and have a FTDI connected to your board, you should see 3 purple flashes on the WS2812B LED as soon as flash is finished or if you reset your board.

Edit 02/19/2015

I faced some problems during testing when programing large sketch that were uploading data over the bootloader code and thus erased the bootloader code. This even if arduino IDE said that the code size was not “too big”. I opened a thread on Arduino forum about this issue.

The only way to get back was to burn bootloader again, but learning experience from this I looked into Lock settings and found a way to avoid bootloader write by application but keep application calling function into bootloader and able to read function table call from it. Here are the fuse to setup to achieve this

ULPNode Lock Fuses

ULPNode Lock Fuses

End Of Edit

Upload sketch to ULPNode

If you have now your bootloader compiled and flashed, you should be able to upload sketch to ULPNode. If you kept the ULPNode default configuration and didn’t changed the frequency or baud rate, you certainly want to upload a sketch but remember that this board works at 4MHz with 250 000 kbps. No board of this type exist on the Arduino IDE, so we need to declare it. It’s quite simple just add the following section to the boards.txt file located into your Arduino Installation (mine is located in Arduino-Install-Dir\hardware\arduino\).

Then restart Arduino IDE, you just need to select the new board type  ULPNode 4MHz (250K upload)  and flash your sketch. Do not forget that at 4MHz you won’t be able to serial console at 115 200 kbps. Use 38 400 kbps from Arduino IDE (but upload still goes at 250 000 kbps).

I’ve also added a 16MHz version in case you want to use ULPNode as classic “Arduino” but with upload up to 250 kbps.

How declare bootloader function as exportable to sketch ?

To be able to call a function located into the bootloader section, we need to know the address of the function and do a call to this function. To obtain the function address, the basic and weird way is to take a look to the .lst file generated when compiled the bootloader, and, put this address in you sketch.

Here is an example, the function to light the WS2812B led is called  __bl_showLED  found in the generated .lst file.

You can see that the function address is identified at 0x7eea by the line  00007eea <__bl_showLED>: . But this is not really convenient since each modification on the bootloader code could change this address and you will need to report back this change in your sketch code.

Any tip to avoid this ? Sure !, the idea is to put on the bootloader area a mapping table with the addresses of the functions to export. And set this area on a well know defined address. Here I choose to place this table just before the optiboot version number, at the end of the flash area.

The optiboot version number is two bytes located at the end of the flash area, at address 0x7ffe/0x7ffff. Our function table will be located on a new section starting at 0x7ff0 to 0x7ffd which let us to put 7 functions addresses (one address is two bytes). Adding a section for compiler and linker is an easy and convenient way to reserve this space.

Now let’s look closer to our Makefile.ulpnode file. The first interesting line for this is the following :

I just added  -Wl,--section-start=.functable=0x7ff0 to define our section .functable.

Now, for this to be working I also needed to add the following file to the Makefile.ulpnode. If it was not done it was not working (but compiled fine). Don’t ask me why, I duplicated the job done by optiboot to the section .version, may be related to the linker to create and allocate this space. If someone knows, it would be nice to tell me what it really does.

That’s all for the build process of this new section. Now let’s be using this new section in optiboot.c file

To identify bootlader exported function I prefixed them with __bl_ which make them more speaking when used in code or just reading it. The function to call to drive WS2812B leds is called __bl_showLED and the prototype is as follow

To place the address of this function to the function table in section .functable this instruction has been added to optiboot.c, just putting the address of __bl_showLED into our section

Imagine you created another exportable function with different prototyping, for example :

then the previous declaration would be

As you can see, there is no function prototype defined in the .functable section, only the function address, the prototype will be defined in the sketch file.

But how to call the exported function from sketch ?

To call the bootloader function to light WS2812B onboard LED, we just need to declare the address of the function table, read the address of the function from the table and define the function prototype pointing to this address. For this, we use the macro pgm_read_word that I included in a more friendly macro called   __BL_FUNCTION_ADDRESS with the number of the function we need to retrieve (1 for the 1st declared in optiboot.c and so on). Then we need to call the the macro  __BL_FUNCTION_ADDRESS(1) to get the address of the 1st function located in bootloader table. Here it is what you’re sketch could look.

Once this is done, you can call the function using the showLED function pointer as follow :

The above sketch will blink the WS2812B led in purple every second. You can see full sketch on github repo.

This is the end of this quite long article. I hope you found this article interesting. It’s the last concerning the bootloader stuff for ULPNode. It has been made for ULPNode but I think you can easily use it for any kind of Arduino board by tweaking the configuration parameters. It can be a good starting point.

You can find all files of this article on the dedicated ULPNode github repo

If you find any bugs, a better implementation or just add some features, just drop me a comment or better open a topic on the ULPNode forum.

Reference used

  • Thank’s Tim for ws2812 avr library driver working on 4MHz devices
  • Thank’s WestfW for original optiboot bootloader
  • Optiboot wiki documentation
  • Original optiboot working flow