After exhausting a CR2032 in about a month, I've been tweaking my page-turner trying to make it more efficient.
I'm wondering about the best approach for saving energy between clicks as well as between sessions. I've got a timeout of ~10 seconds that is reset every time the button is clicked; when the timeout does activate, it performs an NRF.sleep(). Then, when the button is clicked, it does NRF.setServices(...hid...) and NRF.setAdvertising(...).
I've tried doing NRF.wake() in the button's watch function, as well as NRF.disconnect() during the sleep routine in an attempt to cut off the connection so the iPad doesn't keep thinking it's attached to a keyboard (and so hide the on-screen keyboard until I switch off Bluetooth)
However, I get some weird bugs. With NRF.disconnect() reconnecting is unreliable, and sometimes all of the LED light flashes are slowed down, which makes me think there's something else going on.
When NRF.wake() is included, I get:
NRF ERROR 0x8 at ?:0
REBOOTING.
I assume setDeepSleep() is not relevant on the Puck, but what would you see to be the right sequence of events for this application?
It's to be woken by a button click, and doesn't really require discovery when the device is "cold", so sleeping all of the BLE stuff seems reasonable; I'm not sure if an entire shutdown of the softdevice is in order. Disconnecting any HID connections when the device isn't being used is also desirable for the on-screen keyboard reason given above.
My code's attached, with the NRF.wake() and NRF.disconnect() calls commented out for the time being.
Any thoughts?
var hid = require("ble_hid_keyboard");
var long_click = 2000; // 2 seconds
var double_click = 250; // 0.25 second
// Detect short press ( < 2 seconds)
var is_short = false;
var short_timeout = false;
// Detect fast-click count ( < 0.25 seconds)
var clickcount = 0;
var clearclick_timeout;
// Sleep when not in use. Needs to be long enough that it doesn't
// interfere with attempts to connect to it via the IDE. Saying that,
// a triple click will postpone the sleep.
var sleep_timeout;
var sleep_delay = 10000; // 30 seconds
var sleep_long_delay = 600000; // 10 minutes
function setupSleep(is_long) {
if (undefined !== sleep_timeout)
clearTimeout(sleep_timeout);
// Schedule the sleep function to run in the future. A button click
// will reset this timer.
sleep_timeout = setTimeout(sleep, is_long ? sleep_long_delay : sleep_delay);
}
function sleep() {
// Flicker the LED to indicate sleep. This is really for debugging
digitalWrite([LED1,LED2,LED3], 0x101);
setTimeout(function () {
// Clear the LED
digitalWrite([LED1,LED2,LED3], 0);
// Clear the timeout
sleep_timeout = undefined;
//// Disconnect any running HID sessions -- problematic; doesn't reconnect well.
// NRF.disconnect();
// And shut down the chip
NRF.sleep();
}, 250);
}
// Wake is called for two reasons: woken by a button press; and called by
// init() for a full reset.
function wake() {
//// This is VERY problematic: big-time crash in 1v92
// NRF.wake();
// Set up HID (keyboard) services
NRF.setServices(undefined, { hid : hid.report });
// Name it
NRF.setAdvertising({},{name:"Page-Turn-O-Matic 4000"});
}
// Upon reset of the device
function init() {
// Clear any current button event handlers
clearWatch();
// Just in case, we want to make sure this is off.
Puck.magOff();
// Set up BLE
wake();
// Set up button event handlers for both rising and falling. These will
// also take it out of sleepage.
setWatch(btnPressed, BTN, { repeat:true, edge:'rising', debounce : 50 });
setWatch(btnReleased, BTN, { repeat:true, edge:'falling', debounce : 50 });
}
// The primary action, on a single click
function primary() {
hid.tap(hid.KEY.RIGHT, 0);
}
// The secondary action, on a double click
function secondary() {
hid.tap(hid.KEY.LEFT, 0);
}
// Triple-click
function tertiary() {
// Postpone the sleep cycle for a long time (ten minutes) for debug purposes
setupSleep(true);
}
// The reset function, on a long click (>2s)
function long() {
NRF.disconnect();
init();
}
function btnPressed() {
// When the button's pressed, we clear the LEDs. Any
// LED activity is on _release_
digitalWrite([LED1,LED2,LED3], 0);
// Count clicks in chain
clickcount ++;
// Reset execution of short-click chain
if (undefined !== clearclick_timeout) {
clearTimeout(clearclick_timeout);
clearclick_timeout = undefined;
}
// Assume it's a short press
is_short = true;
// Set a timeout for two seconds to recognise a long press
short_timeout = setTimeout(function () {
// It's been two seconds, so...
// Long press detected
is_short = false;
short_timeout = null;
// Full blast RGB
digitalWrite([LED1,LED2,LED3], 0x111);
// and don't do anything until release...
}, long_click);
}
// Once a chain of repeated rapid clicks is over (ie.
// the 0.25 second threshold has passed)...
function chainEnded() {
var o = clickcount;
clickcount = 0;
clearclick_timeout = undefined;
// Reset the sleep watchdog
wake();
setupSleep();
switch (o) {
case 1:
// Simple click; GREEN
digitalWrite([LED1,LED2,LED3], 0b010);
primary();
break;
case 2:
// Double-click; RED
digitalWrite([LED1,LED2,LED3], 0b100);
secondary();
break;
// Triple-click, etc. can be added as additional `case`s.
case 3:
// Triple-click; YELLOW?
digitalWrite([LED1,LED2,LED3], 0b110);
tertiary();
break;
default:
// Too many clicks. Ignore.
break;
}
}
function btnReleased() {
// `short_timeout` is there to _deny_ a short
// click. If it times out, then it means the button
// has been held longer than a short click.
//
// So, for a short click, if the timeout is still going
// then clear it: `short` should remain whatever it is,
// including `true`.
if (short_timeout) {
clearTimeout(short_timeout);
short_timeout = null;
}
if (is_short) {
// Set a timeout to process short clicks
clearclick_timeout = setTimeout(chainEnded, 250);
}
else {
// Long press: reset
digitalWrite([LED1,LED2,LED3], 0x001);
clearclick_timeout = undefined;
long();
}
// And clear any LEDs after a reasonable period.
setTimeout(function () {
digitalWrite([LED1,LED2,LED3], 0);
}, double_click);
}
// Set the initialisation function
E.on('init', init);
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
After exhausting a CR2032 in about a month, I've been tweaking my page-turner trying to make it more efficient.
I'm wondering about the best approach for saving energy between clicks as well as between sessions. I've got a timeout of ~10 seconds that is reset every time the button is clicked; when the timeout does activate, it performs an
NRF.sleep()
. Then, when the button is clicked, it doesNRF.setServices(...hid...)
andNRF.setAdvertising(...)
.I've tried doing
NRF.wake()
in the button's watch function, as well asNRF.disconnect()
during the sleep routine in an attempt to cut off the connection so the iPad doesn't keep thinking it's attached to a keyboard (and so hide the on-screen keyboard until I switch off Bluetooth)However, I get some weird bugs. With
NRF.disconnect()
reconnecting is unreliable, and sometimes all of the LED light flashes are slowed down, which makes me think there's something else going on.When
NRF.wake()
is included, I get:I assume
setDeepSleep()
is not relevant on the Puck, but what would you see to be the right sequence of events for this application?It's to be woken by a button click, and doesn't really require discovery when the device is "cold", so sleeping all of the BLE stuff seems reasonable; I'm not sure if an entire shutdown of the softdevice is in order. Disconnecting any HID connections when the device isn't being used is also desirable for the on-screen keyboard reason given above.
My code's attached, with the
NRF.wake()
andNRF.disconnect()
calls commented out for the time being.Any thoughts?