Making an always-on device Mar 2017
add these two lines at the start of the source file:
add this line at the end, assuming that the main routine is called
: init init unattended read-enc ;
That’s it. Now the source will first clear any definitions in flash after the
<<<core>>> words, and then set up to compile to flash. As the last step, the
init word is redefined to call the core setup, and then our code.
In between sits the crucial
unattended word which allows us to switch between
development mode and unattended / untethered mode. The distinction is made on
startup by checking whether the serial RX input pin on the FTDI header is
floating. If so,
read-enc will be started.
This way, we’ll always get back to a prompt when plugged in - allowing us to update the flash.
While still attached to FTDI, we simply type
read.enc to start it up. So even
when in flash and ready to be used in detached mode, development is
straight-forward: 1) press Ctrl-C to regain control, 2) send new code with “
...”, and 3) enter “
read-enc” to launch the new code.
So much for making the node work unattended. The above is sufficient to allow unplugging it from FTDI and inserting a coin cell (remember: never connect power from both FTDI and the coin cell at the same time!). A coin cell will work fine, but… it’ll it’ll be drained within days!
We really need to get power consumption down if this “rotary knob node” is to be used for a long time, and even more so if it is to remain in always-on mode.
The first step is to pick the low-hanging fruit to take care of the main “power
hogs” (relatively speaking). This can be done with a single line added to the
led-off 2.1MHz only-msi 10 systick-hz
But first, let’s set up a way to measure power consumption:
That’s a µCurrent from EEVblog, which is very convenient for measuring low currents with a standard multimeter - in the back you can see the receiving node (running unmodified code).
The baseline measured with the code from
jz4/ex/rot5.fs is 5 mA. That’s
quite a lot for a CR2032 coin cell, nominally rated at just 200..230 mAh: only ≈
40 hours of run time, ouch!
The above one-line change takes care of the two main consumers: LED and µC. The LED is simply turned off, saving ≈ 2 mA, and the µC is set to run at 2.1 MHz instead of its default 16 MHz. The result is a ten-fold reduction: 460 µA. The coin cell will now last for ≈ 18 days.
Still not good enough, but we can take the µC clock rate quite a bit lower by using this line:
led-off 2.1MHz only-msi 65KHz 10 systick-hz
Note that this slow 65 KHz clock rate can only be reached by first switching to the 2.1 MHz clock and that we also lower the SysTick timer rate to 10 Hz. The result: 45 µA, i.e. 6 months.
Here is the full sender-side code, which can also be found in
<<<core>>> compiletoflash PA3 constant ENC-A PA5 constant ENC-B PA4 constant ENC-C \ common 1000 variable counter : ab-pins ( -- n ) \ read current A & B pin state as bits 1 and 0 ENC-A io@ %10 and ENC-B io@ %01 and or ; : step ( n -- ) counter +! 7 <pkt counter @ +pkt pkt>rf ; : read-enc IMODE-HIGH ENC-A io-mode! IMODE-HIGH ENC-B io-mode! OMODE-PP ENC-C io-mode! ENC-C ioc! rf-init led-off 2.1MHz only-msi 65KHz 10 systick-hz %11 \ previous state, stays on the stack begin 2 lshift \ prev pins in bits 3 and 2 ab-pins tuck \ new pins, also save as previous for next cycle or \ combines prev-a/prev-b/curr-a/curr-b into a 4-bit value \ process this 4-bit value and leave only prev state on stack case %0001 of -1 step endof %0010 of 1 step endof %0100 of 1 step endof %0111 of -1 step endof %1000 of -1 step endof %1011 of 1 step endof %1101 of 1 step endof %1110 of -1 step endof endcase again ; : init init unattended read-enc ;
Not bad for a one-line change. Can we do better? First of all, the rotary encoder readout is starting to become erratic again at this very low clock rate. And second: having to insert a fresh coin cell every six months is really still a bit too often. Yes, we can fix both, stay tuned…