• 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);
    
About

Avatar for tom.gidden @tom.gidden started