The simple assembly code I wrote to get V3.0 up and running works fine. You can configure the brightness in code, then download a new image to the PIC to change the battery lifetime. This is OK if you are building these devices for your own use, but inconvenient for people who just want to buy them. So, it would be nice to have a simple user interface on the board to select the runtime.
I was initially concerned that the runtime might get accidentally changed over the years, then @jaromir.sukuba suggested a configuration mode that was only accessible briefly after the battery was inserted. After that period, the brightness (and battery life) are locked-in until you remove the battery.
I wrote some C code that does this yesterday - you can see it in the GitHub repo. Well, it's mostly C-code. There are two helper functions I had to write in assembly to access the high-endurance flash (HEF) portion of program memory to store the current mode setting.
The user interface is simple. After inserting the battery, the LED blinks out the runtime in years: 5 blinks = 5 years, etc. To increment the runtime, you short pin 4 to pin 8 (RA3 to GND). This "switch" is properly debounced in software. I found some 2.6x1.6 mm switches, and I think I'm going to spin a set of V3.1 boards with them. The code blinks out the runtime 10 times, then enters the running mode, and can't be re-configured without removing the battery.
The only downside to this approach is that you sometimes have to wait a long time for the capacitors to discharge. At 10-year run-times (or more), the mere 11uF of capacitance on board can power the LED for many seconds!
There are a few configurable parameters in the code controlling the interface:
// number of cyles of blinks to show before exiting config mode
#define N_CYCLES 10
// number of pulses per "blink" in config mode, higher = brighter blinks
#define N_PULSES 10
// year run-times and corresponding PWM periods
#define N_MODES 10
struct run_time
{
uint8_t years; // number of years of run-time or mode number
uint16_t pwm_period; // PWM period (in units of 1/31000 s)
};
// Note: frequencies converted to pwm periods
const struct run_time period_table[N_MODES] =
{{1, (int)(31000./326. + 0.5)},
{2, (int)(31000./155. + 0.5)},
{3, (int)(31000./98. + 0.5)},
{4, (int)(31000./69. + 0.5)},
{5, (int)(31000./52. + 0.5)},
{6, (int)(31000./41. + 0.5)},
{7, (int)(31000./33. + 0.5)},
{8, (int)(31000./27. + 0.5)},
{9, (int)(31000./22. + 0.5)},
{10, (int)(31000./15. + 0.5)}
};
// default mode index
#define DEFAULT_MODE_IDX 1
The run-times and associated PWM frequencies are stored in a table in the flash. In this case, you can select from 1 to 10 year lifetime in 1-year increments. These values can be manually determined, or you can use a script and some hardware to find them automatically (I'll be publishing this script soon, I'm just cleaning it up now). The table shown here is not quite right: there is no need for the frequencies to be integers. Frequencies with greater precision (e.g. 21.7 vs 22), will result in more accurate PWM period counts (e.g. 1428 vs 1409). The differences can be significant for multi-year run times.
I guess I just promised at least two more logs: the auto-tuning script, and V3.1 PCBs with a tiny switch. It might feel like work if I didn't love it :-)
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
If you apply the economics of "£|$ per use", the button now dominates the BoM over the lifetime of the battery!
Are you sure? yes | no
I know :-) I was reluctant to have a switch at all, but I like being able to set the run-time.
The switch I'm using for testing was free:
https://cdn.hackaday.io/files/288331233591072/improvised_button.jpg
Are you sure? yes | no