self-propagating code

Posted on
  • So this is it.
    Thanks to a few hints from @Gordon my first self-propagating Puck.js code
    (Kind of worm if you like)

    Feedback and improvements welcome.

    setSleepIndicator( LED3 );
    
    function transfer(device, text, callback) {
      var char;
      var result = "";
      console.log( "connecting " + device.name );
      return device.gatt.connect().then(function(d) {
        device = d;
        console.log( "connected" );
        return d.getPrimaryService("6e400001-b5a3-f393-e0a9-e50e24dcca9e");
      }).then(function(s) {
        console.log( "service found" );
        return s.getCharacteristic("6e400002-b5a3-f393-e0a9-e50e24dcca9e");
      }).then(function(c) {
        char = c;
        console.log( "characteristic found, sending..." );
        function sender(resolve, reject) {
          if (text.length) {
            char.writeValue(text.substr(0,20)).then(function() {
              sender(resolve, reject);
            }).catch(reject);
            text = text.substr(20);
          } else  {
            console.log("finished");
            resolve();
          }
        }
        return new Promise( sender );
      }).then(function() {
        device.disconnect();
        if(callback) callback(true);
      }).catch(function() {
        console.log( "error" );
        if(callback) callback(false);
      });
    }
    
    function scan( callback ) {
      var result = [];
      NRF.findDevices(function(list) {
        for( var i = 0; i < list.length; i++ ) {
          var d = list[ i ];
          if( typeof d.name !== "undefined" && d.name.indexOf( "Puck.js" ) === 0 ) {
            console.log( "found " + d.name );
            result.push( d );
          }
        }
        callback( result );
      });
    }
    
    function spread() {
      var code = E.dumpStr();
    
      scan( function( queue ) {
        // recurse through queue
        function process() {
          if( queue.length ) {
              transfer( queue.pop(), code, function( result ) {
                console.log( "done" );
                setTimeout( process, 200 );
              });
          }
        }
        process();
      });
    }
    
    spread();
    

    Output for 4 Pucks:

    found Puck.js c9fc
    found Puck.js f002
    found Puck.js 38a4
    connecting Puck.js 38a4
    connected
    service found
    characteristic found, sending...
    finished
    done
    connecting Puck.js f002
    connected
    service found
    characteristic found, sending...
    finished
    done
    connecting Puck.js c9fc
    connected
    service found
    characteristic found, sending...
    finished
    done
    
  • Still can't figure out why my own example doesn't work when I change some bits, but just a note that line 43 should read

          if( typeof d.name !== "undefined" && d.name.indexOf( "Puck.js" ) === 0 ) {
    

    as typeof returns a string and you're not coercing type.

  • Thanks @trezm for pointing out. Just changed that bit.

    One think I'd like to improve is checking some version number before updating a remote.
    Then omit the update if the version is already there or higher.

    But without reading the response (not possible to receive notifications yet) this is tricky.
    Some thoughts:

    • just execute some verification code and let the peripheral hang up itself. Like if same or newer then disconnect(). Problem here: how can the central see the disconnect and move on? Tried it and catch() was not called. Maybe a watchdog with timeout?
    • implement another service just for reading the version first. More complex and less elegant.
    • waiting for a newer release so the UART response can be used.
      Other ideas?
  • Hi @ChristianW, one idea would be to advertise the installed version number. So when scanning, the sender would skip the pucks which have the same or a higher version number. That's not completely fool proof, when two pucks are sending at the same time with different versions, the older version could in theory win. But it's better than nothing...

  • BTW: This is a seriously cool method to program many Pucks automatically! Very useful

  • Cool. I like it. Will recruit all nearby pucks:-)
    Suggest that as well as being aware of versions it might benefit from 2 things:

    1. A simple white list and/or blacklist to limit pucks of interest( perhaps with wildcard *)
    2. Ability to password protect each puck at the same time to prevent others from capturing your pucks in this way.
  • Hi Gordon,

    How can I get the optimized js code which is deployed on the Puck ?
    This is required when doing mass deployment from Android device for example. Do you think that it is a good idea to have in the Espruino WebIDE "Get deployed code" button ?

    Thank you.

  • You might be able to get it as a string by looking in the 'console' under settings...

    However if you install the command-line tools https://www.npmjs.com/package/espruino you can use -o out.js to output the code directly to a file.

  • Thank you Gordon.

  • Hi @Gordon,

    Along with self-propagating code is it possible to do a firmware update if needed, avoiding user interaction ?
    In my concrete case an Android phone interacts with multiple pucks, which will be really valuable to do both functionalities above.

    Thank you.

  • You need to have physical access to the Puck to put it in to bootloader mode - to do the steps shown here: http://www.espruino.com/Puck.js#firmware-updates

    It's a security thing - by default I leave the Pucks open to make it easy for developers to get started, but allowing anybody to remotely change the interpreter software would be a recipe for disaster.

    The bootloader is protected by a key, but because Espruino is Open Source and people want to be able to use their own firmware the key is public - so anybody can write whatever they want onto their Pucks.

  • Hi @Gordon,

    Please advice how to update the firmware to a fairly huge number of pucks without manual interaction, may be puck password protection is enough for such mode ?

    Thank you.

  • Do you want to update Puck's interpreter firmware, or your JavaScript firmware?

    You won't be able to fully update the interpreter firmware without any manual interaction - it'd be a massive security risk.

    I think it would be possible to remotely send JS code that would totally reset a Puck.js device, and then all someone would have to do is hold the button down while the device reset. It could look like this:

    • Send code to a Puck.js via Bluetooth
    • press button - it could trigger a reset and start the bootloader process
    • software on a PC looks for devices in Bootloader mode and programs them
    • job done

    However if you want a lot of new ones pre-programmed then you could get in touch and if there were enough I could arrange that they were shipped out to you.

    Either that or potentially I could come up with a pogo-pin programmer for you so you'd peel off the silicone top, hold it onto the Puck for ~20 seconds and it'd be programmed.

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

self-propagating code

Posted by Avatar for ChristianW @ChristianW

Actions