A high-resolution sensor node Dec 2016
The JeeNode Zero is intended as sensor node in a Wireless Sensor Network. This requires:
- a sensor we can read out periodically, such as the BME280
- being able to sleep with a very low current, as recently described
- the ability to run the node unattended, i.e. without FTDI cable
- formatting and sending the collected sensor readings over RF
So far, all development has been performed through FTDI. To run unattended, we’ll need to cut that umbilical cord and make the node start up by itself from a battery.
Battery-powered operation is essentially just a matter of placing a CR2032 coin cell in the on-board coin cell holder, but – as so often – there’s a bit more to it than that:
- you can’t have both FTDI connected and a coin cell inserted at the same time, because they share one circuit - that’d put 3.3V across the 3.0V coin cell, it’s not meant to charge!
- there are some differences when running on 3.0V, especially with the A/D and D/A converters, which both operate in ratiometric mode, i.e. relative to the supply voltage
- it is no longer possible to keep the code in RAM, everything has to be stored in flash memory to survive the power cycle while switching to battery use
- we lose the ability to run a command interactively, so the node needs to be set up to automatically launch its built-in “application” code
- it’d be nice if we could still reconnect the node to FTDI (with the coin cell removed)
A trick which can be quite useful during development, is to connect the JeeNode Zero to FTDI with all pins except the 5V power feed, using a modified 6-pin “stacking” header:
This way, a coin cell can be used as power source, while keeping the serial interface available for uploads, resets, and debugging (this is a JNZ rev1, which had a BME280 on top).
Mecrisp Forth supports auto-execution by re-defining
init, which is always
automatically started up after a reset. We might therefore consider implementing our node as
compiletoflash : main <application code ...> ; : init init main ;
Note the call to
init inside our redefined
init. This is extremely
important, since we have to make sure that all the original initialisations are
still carried out. Within a redefinition of “
x”, any reference to “x” is
always treated as a call to its previous definition (not as recursion!).
But there’s a problem… what if the code has a bug, or we simply want to replace it?
With the above code, we have effectively shut down Forth’s default interactive
interpreter. This code is no longer listening for incoming serial data! To make
matters worse, adding a check using
key? is not good enough: if the code
crashes and hangs (which you can expect to happen all the time during
development), then it won’t reach that key check either.
One solution is to include a special word called “
unattended”, which is
: init init unattended main ;
This has the following effect:
unattendedchecks whether the RX pin is connected, and if so, it exits its caller, which is
initin this case - as a result,
mainwill not even be started when FTDI is connected
mainis still assumed to be written as a loop with “
begin ... key? again”, so that when
mainis started interactively, it can easily be stopped
- as always with FTDI connected, runaway code can be aborted with a reset (i.e. CTRL-C in SerPlus), so crashes and hangs are no big deal
unattendeddoes not detect a serial connection, it replaces the
key?handler to check for a re-connect of the serial receive pin and treat that as an incoming char / key press
The end effect is that
main will only run automatically when not plugged
into FTDI. When plugged in, you will need to start it by typing
addition, when an unattended running JeeNode Zero is plugged in, it’ll react
as if a key has been pressed, and exit its main loop.
It is probably still possible to lock yourself out of the command prompt, but
the chance of this happening is now much lower. You can always get a JeeNode
Zero back to interactive mode by re-flashing a new copy of Mecrisp Forth onto
it, using Folie’s built-in “upload” command (
The final step is to get the data into a packet and send it out using
rf-send. The mechanism which is starting to become standard practice at
JeeLabs, is to use a variable integer
encoding which compresses ints to a fairly compact binary format. The utility
make this relatively easy - although the API and code for this may look a bit
: send-packet ( vprev vcc tint lux humi pres temp -- ) 2 <pkt hwid u+> n+> 6 0 do u+> loop pkt>rf ;
Basically, we’re sending out a packet of type “2”, followed by the node’s unique hardware ID, followed by a signed int and 6 unsigned ints - all popped in reverse from the data stack. The result is then broadcast as RF packet. Further details will be covered in a future article.
The full implementation of this first sensor node can be found in
– by changing the
DEBUG value to 1, you can make it report sensor values over
serial instead of over RF.
To load this application in flash for unattended remote use, send
the JNZ – this includes main as well as the special
init override, with
unattended taking care of the details.
Note: this was coded before the sleep mode power savings were ready. In this implementation, the node still draws about 1.7 mA, too much for long term coin cell battery use. But it worked fine as initial test for a fridge node and ran for two weeks before the coin cell went dead.
Here are some results from that early test run of the fridge node:
Temperature and humidity are both reported with an impressive resolution of two decimal places. The big dip is the cooler in action. The tiny blip was caused by opening the door.
Some more values - including the internal temperature sensor of the STM32L052 µC itself. It’s surprisingly usable due to the pre-calibrated parameters in ROM:
Another very useful feature of the STM32L052, is that it is possible to accurately estimate the voltage at which it is running - by measuring the internal bandgap voltage in reference to Vcc and then performing a little inverse calculation. Again, these values are of a surprisingly good quality, due to the µC’s built-in calibration parameters for the bandgap:
The first graph is measured right after coming out of sleep mode, when the coin cell has presumably recovered from the TX power drain, while the second graph shows the measurements made right after the TX packet send completes. As you can see, coin cells have a hard time with 15..25 mA current pulses. These tests were made with a rev1 JeeNode Zero, which had a 22 µF supply capacitor.
The glitches in the left-side graph might be caused by not waiting long enough for the ADC to stabilise after coming out of stop mode (it used the 16 MHz HSI clock). Needs investigation.
Note how the battery voltage pattern is almost identical to the temperature curve in the fridge. At these low temperatures, batteries tend to have a very difficult time generating electricity!