• Hi,

    I'm trying to figure out the most effective way to trigger an action on a Raspberry Pi when the button is pressed on a puck. It feels like an obvious thing to do, but I couldn't find an example in the forum or the tutorials.

    My first attempt involved turning the puck into a HID keyboard and capture the corresponding key event on the pi.

    I used the first example code from here: https://www.espruino.com/Puck.js+Keyboar­d, verified it worked when connected as a HID keyboard to my Mac. That worked. But then I couldn't get it to pair as a HID keyboard with the Pi. Following various guides, I've got to the point where bluetoothctl reports the devices as follows:

    [bluetooth]# info F1:41:42:B4:0D:3B
    Device F1:41:42:B4:0D:3B
        Name: Puck.js 0d3b
        Alias: Puck.js 0d3b
        Paired: yes
        Trusted: yes
        Blocked: no
        Connected: yes
        LegacyPairing: no
        UUID: Generic Access Profile    (00001800-0000-1000-8000-00805f9b34fb)
        UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
        UUID: Human Interface Device    (00001812-0000-1000-8000-00805f9b34fb)
        UUID: Vendor specific           (6e400001-b5a3-f393-e0a9-e50e24dcca9e)

    You can see it is both connected and paired and that it has a HID UUID - but I see no new keyboard events when I hit the button.

    Question #1 - has anyone get the HID Keyboard stuff to work with a Pi (in this instance, a Pi3 and its onboard bluetooth).

    I've also tried EspruinoHub and doing something hacky around NRF.setServices() and overloading the 'temperature' value, but in some quick experiments I couldn't get that to work - the value would never change.

    Question #2 - Is there a better approach I should be using going down the EspruinoHub route?

  • I'm trying to figure out the same thing. See this comment for my thoughts on how it might be solved. I'm sure Gordon will be here soon with a helpful answer, as always :)

    If you're comfortable with Node, you might try using noble and Espruino's UART service over BLE like I did (node code, espruino code). Though, as Gordon pointed out, a continuous BLE connection uses much more power, so I'm looking for a better solution.

  • @joakim thanks for that. A couple questions...

    where is the Bluetooth object documented? It looks like a Serial object, but I don't see it mentioned on the reference page - https://www.espruino.com/Reference

    I've got the node.js code working to the extent it connects to my puck, gets the appropriate characteristic and waits for data to be sent over. However nothing ever arrives from the puck. I've confirmed the puck side is working as, in the web ide, when I press the button, the Bluetooth.write() does result in something appearing in the web console.

  • @joakim finally found the mistake I'd made in the node.js code... missed off the subscribe call on the rx characteristic. All working now.

    Next task is, as you've said, figure out if there's a more energy efficient way of doing this.

  • HID on Pi 3

    Strange. Personally I haven't had it working, but I have definitely done it on Desktop linux so I wonder what's up.

    Could it be that something like EspruinoHub is effectively 'claiming' the BLE interface and breaking things? IIRC for Bleno to work you basically have to disable bluetoothd, and I guess it's bluetoothd that handles all the HID stuff.

    where is the Bluetooth object documented?

    Hmm. Looks like it isn't. I'll fix that, but yes - it's basically just a 'Serial' object

    Glad you got it working with Node.js in the end. I will eventually add something that'll get lower power consumption on the Puck when it's connected.

    Personally I'd say to use NRF.setAdvertising and work on received advertising data. EspruinoHub should pick that up without a connection (or it's pretty easy to do with Noble directly or even hcitool), and it's not actually using substantially more power than the usual advertising it does by default.

  • Not sure about the HID stuff - wasn't running EspruinoHub at the time.

    Have moved over to setAdvertising and got it working well. Took a bit of time to find a succinct example of noble code that showed how to get the data, but once I found it, it was trivial enough. Code below emits changes to any advertised data from my puck. Right now it assumes a single byte payload in the data - sufficient for my needs, but easily extended to do things properly.

    var noble = require('noble');
    noble.on('stateChange', function(state) {
        if (state === 'poweredOn') {
            // pass true so duplicate events are emitted
        } else {
    var lastData = {};
    noble.on('discover', function(peripheral) {
        if (peripheral.id !== 'f14142b40d3b') return;
        var data = peripheral.advertisement.serviceData;
        for (var i=0;i<data.length;i++) {
            var d = data[i];
            if (lastData[d.uuid] !== d.data[0]) {
                lastData[d.uuid] = d.data[0];
  • Great, thanks! And what are you using on Puck.js for that? Just something like:

      0x180F : [95]
  • Pretty much, yes.

    I set it to 10, 20 or 30 depending on whether I single, double or triple tap the button, resetting it back to 0 after 3 seconds.

    Need to tidy up the code as it is littered with previous attempts and experiments, but will share it later on.

  • @knolleary Did you ever make any code publicly available? Got the basics working in the same way as your example above, but would be really useful to see some more complete code if you've got it.

  • Thanks, @knolleary, that's really useful!

    One question - Did you see somewhat erratic times between the .on('discover') event firing? I'm running essentially the same code as you linked and I'm seeing anywhere between 1.5 seconds and 20 seconds between each .on('discover') event.

    Not ideal when I'm trying to use the Puck.js as a light switch!

  • How far away is your Puck.js device? It could be the signal strength is low enough that you're right on the edge of what can be reliably received.

    You can fix that to some extent by using NRF.setTxPower(4), and you could also increase the advertising rate so more advertisements get sent per second.

    Also, in other posts I've suggested advertising a value that is the number of button presses - so even if you miss an advertising packet you still know the button has been pressed at some point.

  • Currently the puck is right next to the MacBook, maybe 15 cm away. When I come to actually use it, however, it will be on top of a table with a Raspberry Pi 3 underneath the table, maybe 40cm away.

    I'll have a look at the NRF.setTxPower function and see if that helps. How would I go about increasing the advertising rate? Currently I'm just calling NRF.setAdvertising() once, should it be inside a setInterval to call it repeatedly?

    If it's of any use at all, here's a gist of the code.

    Regarding the button presses, I'm currently doing the same sort of thing as knolleary suggested, which is to advertise 0, increment to 10 on first push, 20 on second etc, then reset to 0 after a few seconds. Maybe I will try having a basic counter that increments the number by 1 on each press, then just look for increases in my node script. At least I can guarantee that I'll notice the button press, no matter how long it takes.

  • Ahh great, I didn't realise that's basically what knolleary was doing already :)

    Have a look at http://www.espruino.com/Reference#l_NRF_­setAdvertising

    Basically all you need to do is change NRF.setAdvertising(data); to NRF.setAdvertising(data,{interval:100});­

    It's actually possible that your Mac's WiFi is causing slightly intermittent reception - could you have been downloading anything in the background? WiFi and BLE usually use the same aerial I think, so when WiFi is sending data it's unlikely BLE will be able to receive anything.

    If you're on a Pi 3 with Ethernet (or at least very rare WiFi comms) the situation will be miles better.

  • Great, thanks, I'll try that this evening.

    I wasn't knowingly downloading anything, but of course the OS could have been doing something in the background. I've got a Pi 3 coming in the next few days, so we'll see how it goes with that too.

    Massive thanks for your help, both!

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

Triggering an action on a Pi when the button is pressed

Posted by Avatar for knolleary @knolleary