[ERROR] Controlling Other Pucks

Posted on
Page
of 2
/ 2
Next
  • Hi,
    I just got my Pucks and decided to start playing with them, I'm really impressed and I really like the quality of the hardware and how easy/smooth it was to get up and running.

    Following the tutorial Controlling Other Pucks I ran into an error:

    Uncaught Error: Unhandled promise rejection: undefined at line 34 col
    26 txType.writeValue('Puck.js d5b2').then(()=>{ in function called
    from system

    The code runs perfect initially, but when I click the central Puck after a couple minutes of inactivity I get the previous error. I wont post the source code since it's a copy paste from the tutorial.

    I find the error message confusing since the Promise seems to be handled?

    txCharacteristic.writeValue("toggle()\n"­).then(function() {
            digitalPulse(LED2, 1, 500); // light green to show it worked
            busy = false;
          }).catch(function() {
            digitalPulse(LED1, 1, 500); // light red if we had a problem
            busy = false;
          });
    

    The peripheral Puck seems to be working OK. I modified the source to handle manual press

    var on = 0;
    function toggle() {
      on = !on;
      digitalWrite(LED, on);
    }
    
    setWatch(toggle, BTN, {edge:'rising', debounce:50, repeat: true});
    
  • What firmware are you using? Have you updated firmware? There was a bug around promise rejection that was fixed by @Gordon link

    The reason for the rejection is the pucks will disconnect from each other after a minute or two. When you press the button the code thinks it's still connected and tried to use the characteristic and fails. Then the bug cause the exception.

  • @dklinkman thanks for the reply. No, I have not updated the firmware. Will do.

  • This is an updated function. Added 2 lines to the 2nd catch. It handles the disconnected case better. You'll still get a red blink because the write fails. But on next press it will try and reconnect and you'll be back in business.

    // Function to call 'toggle' on the other Puck
    function sendToggle() {
      if (!busy) {
        busy = true;
        if (!connected) {
          NRF.requestDevice({ filters: [{ name: 'Puck.js d958' }] }).then(function(device) {
            return device.gatt.connect();
          }).then(function(d) {
            connected = d;
            return d.getPrimaryService("6e400001-b5a3-f393-­e0a9-e50e24dcca9e");
          }).then(function(s) {
            return s.getCharacteristic("6e400002-b5a3-f393-­e0a9-e50e24dcca9e");
          }).then(function(c) {
            txCharacteristic = c;
            busy = false;
            // Now actually send the toggle command
            sendToggle();
          }).catch(function() {
            if (connected) connected.disconnect();
            connected=false;
            digitalPulse(LED1, 1, 500); // light red if we had a problem
            busy = false;
          });
        } else {
          txCharacteristic.writeValue("toggle()\n"­).then(function() {
            digitalPulse(LED2, 1, 500); // light green to show it worked
            busy = false;
          }).catch(function() {
            if (connected) connected.disconnect();
            connected=false;
            digitalPulse(LED1, 1, 500); // light red if we had a problem
            busy = false;
          });
        }
      }
    }
    
    
  • There was a bug around promise rejection that was fixed by @Gordon link

    I have upgraded to 1v90.5 and I the issue seems to persist.

    The reason for the rejection is the pucks will disconnect from each other after a minute or two

    This makes sense.

    --
    I noticed that the version provided in the link- 1v90.5- is not present in the "binaries folder".

  • That's correct, you have to download from the forum. That firmware should have fixed the promise rejection exception. Here's an even newer one: link

    When I saw your original post I loaded v90 onto a puck and confirmed the same error, just to see. Then reloaded the 90.12 and it worked fine.

  • Ok, 1v90.12 fixes the error and the updated script.

    I see it fail, and then on next press it tries to connect again. However, quite often it will fail- red LED lights up- a few times in a row before the command it's sent successfully.

  • I experience the same results. I've been tweaking the example code to make it more resilient. The promise paradigm makes that tricky, at least for me. I'll post that code tonight or tomorrow. I find that even when communicating with the remote puck every 5 seconds over an established connection, the connection will just drop after a minute or so.

    I've got the EspruinoHub up and running with some changes I made to it. Connecting to a puck seems very fast and reliable. At least with my experience with it. So I wonder if the inconsistency we see is with the puck originating the connection, and not so much with the puck accepting the connection.

  • So I wonder if the inconsistency we see is with the puck originating the connection, and not so much with the puck accepting the connection.

    I'm wondering the same thing. I also wonder if caching the connection makes it less reliable. Will make a quick test and see.

    I've got the EspruinoHub up and running with some changes I made to it. Connecting to a puck seems very fast and reliable.

    I actually saw your cloned repo, and I'm testing that right now. One thing I noticed is when you look at the history of the files you modified you can't see the actual changes you made since the whole file seems to be different- due to formatting changes, maybe. I think it would be good if you contributed back your changes upstream, but I personally wouldn't accept a PR in the current state. Just my 2c.

  • Yeah I noticed that my editor changed all the line endings on the files I changed. So I committed another update that corrected that. So depending on which commits you are comparing it can look like the whole file changed.

  • Here's my variation on the Controlling Other Pucks tutorial code. It does some console logging, plus keeps track of attempts, retries, etc. There's a setInterval that simulates a button press every 5 seconds. That can be commented out. What it shows, at least in my case, is that the connection from one puck to another only lasts about 70 seconds, even when being frequently used. Another thing that I did was add some basic retry logic. For each button 'press' the logic will try up to a total of 3 times to connect and toggle the other puck before giving up and calling it a failure. Also attached is a log file that shows my results over a few minutes. Very consistent disconnect, retry pattern.

    var busy = false;
    var connected = false;
    var txCharacteristic = false;
    var attempts = 0;
    var retries = 0;
    var failures = 0;
    
    function writeToggle() {
      attempts++;
      return new Promise(function(fulfill, reject) {
        var emsg = "did not find device";
        if(!busy) {
          if (!connected) {
            busy = true;
            // be sure to change the filter to match your puck!
            NRF.requestDevice({ filters: [{ name: 'Puck.js d958' }] }).then(function(device) {
              console.log("got device, connecting...");
              return device.gatt.connect();
            }).then(function(d) {
              console.log("connected!");
              emsg = "did not get service";
              connected = d;
              return d.getPrimaryService("6e400001-b5a3-f393-­e0a9-e50e24dcca9e");
            }).then(function(s) {
              console.log("got service");
              emsg = "did not get characteristic";
              return s.getCharacteristic("6e400002-b5a3-f393-­e0a9-e50e24dcca9e");
            }).then(function(c) {
              console.log("got characteristic");
              emsg = "unable to write value";
              txCharacteristic = c;
              return txCharacteristic.writeValue("toggle()\n"­);
            }).then(function() {
              console.log("wrote toggle");
              emsg = "did not disconnect";
            }).then(function() {
              digitalPulse(LED2, 1, 500); // light green to show it worked
              busy = false;
              console.log("success!");
              fulfill();
            }).catch(function(error) {
              console.log(emsg + ": " + error);
              digitalPulse(LED1, 1, 500); // light red if we had a problem
              if (connected) {
                connected.disconnect();
                connected = false;
              }
              busy = false;
              reject();
            });
          } 
          else {
            emsg = "unable to write value";
            txCharacteristic.writeValue("toggle()\n"­).then(function() {
              digitalPulse(LED2, 1, 500); // light green to show it worked
              busy = false;
              fulfill();
            }).catch(function(error) {
              digitalPulse(LED1, 1, 500); // light red if we had a problem
              console.log(emsg + ": " + error);
              if (connected) {
                connected.disconnect();
                connected = false;
              }
              busy = false;
              reject();
            });
          }
        }
      });
    }
    
    function success() { /*console.log("ok");*/ }
    function retry() {
      console.log("error, retrying...");
      retries++; console.log("attempts: " + attempts + ", retries: " + retries + ", failures: " + failures);
      return writeToggle();
    }
    
    function start() {
      writeToggle()
        .then(success, retry)
        .then(success, retry)
        .catch(function(error) {
          console.log("oops, failed! " + error !== undefined ? error : "");
          failures++;
        });
    }
    
    setInterval(function() { start(); }, 5000);
    setWatch(start, BTN, { edge:"rising", debounce:50, repeat: true });
    
    

    1 Attachment

  • So you can only get 70 seconds, pretty consistently?

    What about if you are not connected to the other Puck that's doing the connection? Obviously it's harder to debug that way but you could log the attempts and then just connect later to see what happens.

  • Yes very consistently, like 100%. But that's with the console connected. I'll try it disconnected and see what it does. I can use the LEDs for feedback, green on connect, red on disconnect, long red for error, etc

  • @Gordon, can confirm 70 seconds or so connection time even when the web ide is disconnected. Based on visual feedback from the LEDs. In this mode the lead puck is sending a toggle() to the receiving puck every 5 seconds.

  • Sorry for the delay - just to say I haven't forgotten. This is now fixed, and will be available in the 1v92 build.

  • Hi @Gordon,

    I would like to write value on another Puck following example at https://www.espruino.com/Puck.js+Control­ling+Other+Pucks

    When I write value sometimes I have :

    Uncaught Error: Got BLE error 0x8 (INVALID_STATE)
    at line 192 col 10});

    but in most of the cases it says that the write value is executed successfully, but on the controlled puck nothing happens ? I am using 1v95.71

    Thank you!

  • Which example are you using? This one?

    // Are we busy?
    var busy = false;
    
    // Function to call 'toggle' on the other Puck
    function sendToggle() {
      if (!busy) {
        busy = true;
        digitalPulse(LED3, 1, 500); // light blue to show we're working
        NRF.requestDevice({ filters: [{ name: 'Puck.js 7fcf' }] }).then(function(device) {
          require("ble_simple_uart").write(device,­ "toggle()\n", function() {
            digitalPulse(LED2, 1, 500); // light green to show it worked
            busy = false;
          });
        }).catch(function() {
          digitalPulse(LED1, 1, 500); // light red if we had a problem
          busy = false;
        });
      }
    }
    
    // Call sendToggle when the button is pressed
    setWatch(sendToggle, BTN, { edge:"rising", debounce:50, repeat: true });
    

    It is attempting to call toggle() on the other Puck - so if you haven't uploaded the function to it, it won't work. You could try replacing the text with "LED.set()\n" which will work regardless of what's on the Puck you connect to.

    I'd highly recommend that you try running the most recent firmware (1v97) though. 1v96 especially had some really big improvements as I moved to using a new version of Nordic's BLE libraries, which fixed quite a few potential BLE issues.

  • Hi @Gordon,

    Everything is working fine, it is side effect of my assumption that NRF.requestDevice could filter by id which is the mac address, but it is not. As a result my device is connected to the first found Puck and tries to execute this function there which is not available.

    Is it possible to support NRF.requestDevice filtration by id ?

    Thank you!

  • If you just want to connect by ID then you don't even need to scan - just use NRF.connect: http://www.espruino.com/Reference#l_NRF_­connect

    Other Puck.js use 'random' addresses though, so make sure you supply an address that looks like: "e7:e0:57:ad:36:a2 random"

  • Hi @Gordon,

    I have two devices:

    Device1 has:

    var s = false;
    var c = 0;
    function o() {
      c++;
      s=!s;
      LED2.write(s);
    }
    

    Device2 has:

    var busy = false;
    var gatt;
    setInterval(function() {
      console.log("Trying to connect...");
      if (!busy) {
        busy = true;
        NRF.connect("xx:xx:xx:xx:xx:xx random").then(function(g) {
          gatt = g;
          return gatt.getPrimaryService("6e400001-b5a3-f3­93-e0a9-e50e24dcca9e");
        }).then(function(s) {
          return s.getCharacteristic("6e400002-b5a3-f393-­e0a9-e50e24dcca9e");
        }).then(function(c) {
          return c.writeValue("o()\n");
        }).then(function() {
          busy=false;
          if(gatt !== undefined)gatt.disconnect();
          console.log("Write value success!");
        }).catch(function(e) {
          busy=false;
          if(gatt !== undefined)gatt.disconnect();
          console.log("Write value error. ", e);
        });
      } else {
        console.log("Device is busy!");
      }
    },1000);
    

    Device 2 end dump is:

    Uncaught Error: Unhandled promise rejection: Error: Got BLE error 0x8 (INVALID_STATE)
    Trying to connect...
    Trying to connect...
    Device is busy!
    Trying to connect...
    Device is busy!
    Write value success!
    Trying to connect...
    Trying to connect...
    Device is busy!
    Trying to connect...
    Device is busy!
    Write value success!
    Trying to connect...
    ERROR: Ctrl-C while processing interval - removing it.
    Execution Interrupted during event processing.
    New interpreter error: CALLBACK,MEMORY
    Write value success!

    In this concrete test Device 2 succeeded to call function on Device 1 successfully 87 times and crashed, but sometimes it is 75, 78.

    Please advice!
    Thank you!

  • Have you tried updating firmware? This is just the sort of thing that got fixed in firmwares 1v96 and later

  • Hi @Gordon,

    I tried with 1v97.5 version of the firmware, it stopped on the 89 iteration with same error.

    Thank you!

  • You mean Uncaught Error: Unhandled promise rejection: Error: Got BLE error 0x8 (INVALID_STATE), or New interpreter error: CALLBACK,MEMORY

  • Hi @Gordon,

    I mean that on the 89 successful execution of a function to the remote device, the current device receive New interpreter error: CALLBACK,MEMORY and the device stops working.

    Thank you!

  • Ok, thanks - and it's literally just this code and nothing else:

    var busy = false;
    var gatt;
    setInterval(function() {
      console.log("Trying to connect...");
      if (!busy) {
        busy = true;
        NRF.connect("xx:xx:xx:xx:xx:xx random").then(function(g) {
          gatt = g;
          return gatt.getPrimaryService("6e400001-b5a3-f3­93-e0a9-e50e24dcca9e");
        }).then(function(s) {
          return s.getCharacteristic("6e400002-b5a3-f393-­e0a9-e50e24dcca9e");
        }).then(function(c) {
          return c.writeValue("o()\n");
        }).then(function() {
          busy=false;
          if(gatt !== undefined)gatt.disconnect();
          console.log("Write value success!");
        }).catch(function(e) {
          busy=false;
          if(gatt !== undefined)gatt.disconnect();
          console.log("Write value error. ", e);
        });
      } else {
        console.log("Device is busy!");
      }
    },1000);
    

    Because that error means that the Puck has run out of memory - so if it's the code above it'll be a memory leak in Puck.js, but if it's other code it could just be that you're storing a bunch of data somewhere?

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

[ERROR] Controlling Other Pucks

Posted by Avatar for goliatone @goliatone

Actions