From d7e9469909e769fcf9b69081682740d09f92e77d Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Tue, 19 Jul 2022 17:13:01 +0100 Subject: [PATCH] Added automatic Espruino programmer tool --- apps/espruinoctrl/README.md | 2 +- apps/espruinoprog/ChangeLog | 1 + apps/espruinoprog/README.md | 42 +++++++++ apps/espruinoprog/app-icon.js | 1 + apps/espruinoprog/app.js | 90 ++++++++++++++++++++ apps/espruinoprog/app.png | Bin 0 -> 9645 bytes apps/espruinoprog/custom.html | 145 ++++++++++++++++++++++++++++++++ apps/espruinoprog/metadata.json | 17 ++++ 8 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 apps/espruinoprog/ChangeLog create mode 100644 apps/espruinoprog/README.md create mode 100644 apps/espruinoprog/app-icon.js create mode 100644 apps/espruinoprog/app.js create mode 100644 apps/espruinoprog/app.png create mode 100644 apps/espruinoprog/custom.html create mode 100644 apps/espruinoprog/metadata.json diff --git a/apps/espruinoctrl/README.md b/apps/espruinoctrl/README.md index a7bca662c..7b2e434e7 100644 --- a/apps/espruinoctrl/README.md +++ b/apps/espruinoctrl/README.md @@ -17,7 +17,7 @@ showing available Espruino devices is popped up. device being connected to. Use this if you want to print data - eg: `print(E.getBattery())` When done, click 'Upload'. Your changes will be saved to local storage -so they'll be remembered next time you upload from the same device.s +so they'll be remembered next time you upload from the same device. ## Usage diff --git a/apps/espruinoprog/ChangeLog b/apps/espruinoprog/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/espruinoprog/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/espruinoprog/README.md b/apps/espruinoprog/README.md new file mode 100644 index 000000000..a8f15d3e9 --- /dev/null +++ b/apps/espruinoprog/README.md @@ -0,0 +1,42 @@ +# Espruino Programmer + +Finds Bluetooth devices with a specific name (eg `Puck.js`), connects and uploads code. Great for programming many devices at once! + +**WARNING:** This will reprogram **any matching Espruino device within range** while +the app is running. Unless you are careful to remove other devices from the area or +turn them off, you could find some of your devices unexpectedly get programmed! + +## Customising + +Click on the Customise button in the app loader to set up the programmer. + +* First you need to choose the kind of devices you want to upload to. This is +the text that should match the Bluetooth advertising name. So `Puck.js` for Puck.js +devices, or `Bangle.js` for Bangles. +* Now paste in the code you want to write to the device. This is automatically +written to flash (`.bootcde`). See https://www.espruino.com/Saving#save-on-send-to-flash- +for more information. +* Now enter the code that should be sent **after** programming. This code +should make the device so it doesn't advertise on Bluetooth with the Bluetooth +name you entered for the first item. It may also help if it indicates to you that +the device is programmed properly. + * You could turn advertising off with `NRF.sleep()` + * You could change the advertising name with `NRF.setAdvertising({},{name:"Ok"});` + * On a Bangle, you could turn it off with `Bangle.off()` +* Finally scroll down and click `Upload` +* Now you can run the new `Programmer` app on the Bangle. + +## Usage + +Just run the app, and as soon as it starts it'll start scanning for +devices to upload to! + +To stop scanning, long-press the button to return to the clock. + +## Notes + +* Right now the Espruino Tools used here are unaware of the device they're writing to, +and as a result they don't use Storage and so the size of the files you can +write to the device are quite limited. You should be find with up to 4k of code. +* Currently, code is not minified before upload (so you need to supply pre-minified + code if you want that) diff --git a/apps/espruinoprog/app-icon.js b/apps/espruinoprog/app-icon.js new file mode 100644 index 000000000..532c60eea --- /dev/null +++ b/apps/espruinoprog/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4cA/4AB7wJB8/5uX+7uUgH41lSKf4AKpMkyQCCggEDAQVtCAMCCNWUx9JufSrmkCJeKqsiytICJtFkWRCJWAEaARCI5BkEoAGBymJ9eSvXkCJZ9JCLI1DyM9uQRLNYWRpRZMR5ARWAwSPCuWR9MuCJZZIgARGPouTCIcSA4OQAoMW7dt2wCEEZECCI1oCJAADrZyBAAcDuQRByOABQkKDAvbtwRBxu24AMFAAcGCIY/B7AQIhpOC3MjKIVsCJe3jYRCwiPEkARBQg227ieDAQO0CJPhCKHJCK1N0ARI28JCIjUDEY4OBzWRfAoRG3ARBygRH3oPBswRB4QjFfAYgCt9pYoJoEkmbCJONCI1ACJGSiQRE7TXDCIuQEYkmCIhpDEYSCFCIj2DCIOTrYRE6ARDAH4AHA")) diff --git a/apps/espruinoprog/app.js b/apps/espruinoprog/app.js new file mode 100644 index 000000000..b939d4007 --- /dev/null +++ b/apps/espruinoprog/app.js @@ -0,0 +1,90 @@ +var uart; // require("ble_uart") +var device; // BluetoothDevice +var uploadTimeout; // a timeout used during upload - if we disconnect, kill this +Bangle.loadWidgets(); + +var json = require("Storage").readJSON("espruinoprog.json",1); +/*var json = { // for example + namePrefix : "Puck.js ", + code : "E.setBootCode('digitalPulse(LED2,1,100);')", + post : "LED.set();NRF.sleep()", +};*/ + +if ("object" != typeof json) { + E.showAlert("JSON not found","Programmer").then(() => load()); + throw new Error("JSON not found"); + // stops execution +} + +// Set up terminal +var R = Bangle.appRect; +var termg = Graphics.createArrayBuffer(R.w, R.h, 1, {msb:true}); +termg.setFont("6x8"); +var term; + +function showTerminal() { + E.showMenu(); // clear anything that was drawn + if (term) term.print(""); // redraw terminal +} + +function scanAndConnect() { + termg.clear(); + term = require("VT100").connect(termg, { + charWidth : 6, + charHeight : 8 + }); + term.print = str => { + for (var i of str) term.char(i); + g.reset().drawImage(termg,R.x,R.y); + }; + term.print(`\r\nScanning...\r\n`); + NRF.requestDevice({ filters: [{ namePrefix: json.namePrefix }] }).then(function(dev) { + term.print(`Found ${dev.name||dev.id.substr(0,17)}\r\n`); + device = dev; + + term.print(`Connect to ${dev.name||dev.id.substr(0,17)}...\r\n`); + device.removeAllListeners(); + device.on('gattserverdisconnected', function(reason) { + if (!uart) return; + term.print(`\r\nDISCONNECTED (${reason})\r\n`); + uart = undefined; + device = undefined; + if (uploadTimeout) clearTimeout(uploadTimeout); + uploadTimeout = undefined; + setTimeout(scanAndConnect, 1000); + }); + require("ble_uart").connect(device).then(function(u) { + uart = u; + term.print("Connected...\r\n"); + uart.removeAllListeners(); + uart.on('data', function(d) { term.print(d); }); + uart.write(json.code+"\n").then(() => { + term.print("\r\nUpload Complete...\r\n"); + // main upload completed - wait a bit + uploadTimeout = setTimeout(function() { + term.print("\r\nFinal Upload...\r\n"); + // now upload the code to run after... + uart.write(json.post+"\n").then(() => { + term.print("\r\nDone.\r\n"); + // now wait and disconnect (if not already done!) + uploadTimeout = setTimeout(function() { + term.print("\r\nDisconnecting...\r\n"); + if (uart) uart.disconnect(); + }, 500); + }); + }, 1000); + }); + }); + }).catch(err => { + if (err.toString().startsWith("No device found")) { + // expected - try again + scanAndConnect(); + } else + term.print(`\r\ERROR ${err.toString()}\r\n`); + }); +} + +// now start +Bangle.drawWidgets(); +showTerminal(); +scanAndConnect(); diff --git a/apps/espruinoprog/app.png b/apps/espruinoprog/app.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b435f04bfbc91e77a1ff7b8895a48357381b12 GIT binary patch literal 9645 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 literal 0 HcmV?d00001 diff --git a/apps/espruinoprog/custom.html b/apps/espruinoprog/custom.html new file mode 100644 index 000000000..c994aaea7 --- /dev/null +++ b/apps/espruinoprog/custom.html @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + +

Upload code to devices with names starting with:

+

+

Enter your program to upload here:

+

+

Enter the code to send after upload here:

+

+

Then click  

+

Click here to reset to defaults.

+ + + + diff --git a/apps/espruinoprog/metadata.json b/apps/espruinoprog/metadata.json new file mode 100644 index 000000000..75fcf5a79 --- /dev/null +++ b/apps/espruinoprog/metadata.json @@ -0,0 +1,17 @@ +{ + "id": "espruinoprog", + "name": "Espruino Programmer", + "shortName": "Programmer", + "version": "0.01", + "description": "Finds Bluetooth devices with a specific name (eg 'Puck.js'), connects and uploads code. Great for programming many devices at once!", + "icon": "app.png", + "tags": "tool,bluetooth", + "supports": ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "custom": "custom.html", + "storage": [ + {"name":"espruinoprog.app.js","url":"app.js"}, + {"name":"espruinoprog.img","url":"app-icon.js","evaluate":true}, + {"name":"espruinoprog.json"} + ] +}