To try out RF communications, we need to go through a number of steps:
- hook up two JeeNode Zero boards, so we can develop on both in parallel
- work out the code needed for the receive and send nodes
- lower the average power consumption of the send node
- install the send code in flash so it can run unattended
- add a coin cell, and turn the sender into a standalone unit
- as a bonus, we’ll also update the node with a modified version
Let’s jump right in… by the end of this one article, you’ll have it all.
1. Preparing two JNZ’s
Here is a 2-node setup, one via a USB-BUB type board, the other via a HyTiny-based SerPlus:
By running two instances of Folie, each in their own terminal session, and making sure that we can connect and easily reset ‘em with Ctrl-C, everything is ready for some Forth development.
2. Setting up RX & TX nodes
On the receiving end, it can’t get any simpler than entering “rf-listen
”.
For sending, the first test is to manually send one packet with “123
rf-txtest
”.
The result is that you should see one packet come in for each one sent out:
TX node, typed in | RX node, printed out |
---|---|
rf-listen | |
123 rf-txtest | |
RF69 21EB2ACA01FDA0803D03 313233 | |
45678 rf-txtest | |
RF69 21EB2AC401FD52803D05 3435363738 | |
9 rf-txtest | |
RF69 21EB2AC301FDC0803D01 39 |
As you can see, the payload contains the ASCII representation of the number sent out, in hex.
Excellent! This shows that all the hardware involved is doing its job.
Next, let’s continuously send one packet per second, with an incrementing counter in it:
: blips 1000000 0 do i rf-txtest 1000 ms loop ;
rf-init 0 rf-power blips
It’s the same rf-txtest
call, but now as part of a loop.
The receiver output will be:
RF69 21EB2AC301FE60803D01 30
RF69 21EB2AC201FF42803D01 31
RF69 21EB2AC301FF4E803D01 32
RF69 21EB2AC301FF9A803D01 33
RF69 21EB2AC301FC92803D01 36
Not all packets are guaranteed to arrive. As you can see, we got packets 0, 1, 2, and 3, missed packets 4 and 5, and then got packet 6. That’s to be expected - it’s not that these sensitive radios are so unreliable, we’re simply placing them far too close together - even at minimal power, the radio waves from the transmitter will verload a receiver placed only a few cm away!
To quit the infinite send loop, we can press Ctrl-C to get the Forth prompt back. Since this resets the JNZ, all code in RAM will be lost.
3. Low-power transmissions
The transmitting node is now sending one packet every second. The rest of the time it’s just twiddling its thumbs and wasting energy. Let’s fix that:
: blips 1000000 0 do i rf-txtest rf-sleep stop1s loop ;
lptim-init rf-init 0 rf-power blips
As before, running this should produce new output once a second on the receiver end.
Actual power consumption measurements will have to wait for another time, but the average current draw is likely to be under 10 µA, some three orders of magnitude lower than before.
Which means we’re almost ready to run the transmit node off a coin cell - but
there’s a snag: the blips
code lives in RAM, so the send node won’t start when
simply powering it up.
4. Unattended operation
For such unattended operation, we need to save the blips
code in flash and add
logic to start it up immediately after power-up (or reset). Appending to flash
is a matter of telling Mecrisp:
compiletoflash
: blips 1000000 0 do i rf-txtest rf-sleep stop1s loop ;
Automatic startup is also easy: Mecrisp looks for an “init
” word on startup,
and launches it.
But it’s also a bit risky. What if there is a mistake in the code, or we want
to change it later? Once a new init
is installed, we lose the default
command-line prompt! Even a hard reset won’t get it back, since it’ll simply
launch that same init
override again.
The solution comes in the form of a special unattended
word, defined in
board.fs.
This is designed specifically for making init
overrides safe:
compiletoflash
: init init unattended lptim-init rf-init 0 rf-power blips ;
Some notes of what’s happening, since it’s fairly critical to get this right:
- we re-define the
init
word, overriding its original definition - first we have to call the original
init
, which is why it appears twice - then we call
unattended
, which will act as a safety escape hatch - lastly, all the code that is to be run on startup follows
The unattended
escape hatch is that when connected to a serial port, anything
after it will be skipped. Instead, this init
definition will immediately
return to the Forth command prompt.
If you run the above and reset the board, you will see that nothing happens.
You still have to start everything up by manually entering “lptim-init rf-init
0 rf-power blips
”.
But that’s exactly the point. Even when a node has been set up to automatically go live without serial FTDI connection, the development workflow remains unaffected.
5. Running off a coin cell
And now the real test - we take the JNZ out of its FTDI connector and insert a coin cell:
(see the red glow? more on that in a moment…)
And here’s some sample output printed by the receiver, while moving away a bit:
RF69 21EB2AC201009C803D02 3231
RF69 21EB2AB801FF34803D02 3232
RF69 21EB2AA3020026803D02 3233
RF69 21EB2AB101FDA6803D02 3234
RF69 21EB2AB40100FE803D02 3235
RF69 21EB2AC0010078803D02 3236
RF69 21EB2A9E02002A803D02 3237
RF69 21EB2A9F02002C803D02 3238
RF69 21EB2A9F02002A803D02 3239
RF69 21EB2A9D020012803D02 3330
It’s alive!
But… whoops, there’s a silly little mistake in there: the LED is still on, drawing some 2 mA. Leaving that LED on would drain the coin cell within a matter of days…
6. Updating the code
So as last step, here is how to fix the LED issue by installing an improved version:
Remove the coin cell (this is important, it cannot coexist with power from FTDI!)
Put the JNZ back in its FTDI adapter, and it’ll show the normal welcome message again.
Remove the code we added to flash by entering “
<<<core>>>
” to restore the flash memory to its previous state. This was explained in the previous article.Improve the code, i.e. we can simply add a call to turn the LED off on startup:
compiletoflash : blips 1000000 0 do i rf-txtest rf-sleep stop1s loop ; : init init unattended led-off lptim-init rf-init 0 rf-power blips ;
Now disconnect from FTDI again, put the coin cell back in, and we’re done.
That’s all. With a mean current consumption in the 10 µA’s (i.e. sleep mode + transmit power use), this node should be able to run at least a year or two on a fresh coin cell. Piece of cake!