Skip to content

Commit

Permalink
Merge pull request #1 from espruino/master
Browse files Browse the repository at this point in the history
Merge
  • Loading branch information
Timothy Skipper committed Jul 20, 2022
2 parents e552f08 + d7e9469 commit 51ac93a
Show file tree
Hide file tree
Showing 29 changed files with 575 additions and 23 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ and which gives information about the app for the Launcher.
```

* name, icon and description present the app in the app loader.
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher` or empty.
* tags is used for grouping apps in the library, separate multiple entries by comma. Known tags are `tool`, `system`, `clock`, `game`, `sound`, `gps`, `widget`, `launcher`, `bluetooth` or empty.
* storage is used to identify the app files and how to handle them
* data is used to clean up files when the app is uninstalled

Expand Down
2 changes: 1 addition & 1 deletion apps/espruinoctrl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion apps/espruinoctrl/app-icon.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/espruinoctrl/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"version": "0.01",
"description": "Send commands to other Espruino devices via the Bluetooth UART interface. Customisable commands!",
"icon": "app.png",
"tags": "",
"tags": "tool,bluetooth",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"custom": "custom.html",
Expand Down
1 change: 1 addition & 0 deletions apps/espruinoprog/ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.01: New App!
42 changes: 42 additions & 0 deletions apps/espruinoprog/README.md
Original file line number Diff line number Diff line change
@@ -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)
1 change: 1 addition & 0 deletions apps/espruinoprog/app-icon.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

90 changes: 90 additions & 0 deletions apps/espruinoprog/app.js
Original file line number Diff line number Diff line change
@@ -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();
Binary file added apps/espruinoprog/app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
145 changes: 145 additions & 0 deletions apps/espruinoprog/custom.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<html>

<head>
<link rel="stylesheet" href="../../css/spectre.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/addon/lint/lint.min.css">
</head>

<body>
<script src="../../core/lib/customize.js"></script>
<script src="../../core/lib/espruinotools.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.32.0/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/mode/javascript/javascript.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/addon/lint/lint.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/addon/lint/javascript-lint.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/addon/hint/javascript-hint.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jshint/2.11.0/jshint.min.js"></script>
<p>Upload code to devices with names starting with:</p>
<p><input type="text" id="nameprefix"></input></p>
<p>Enter your program to upload here:</p>
<p><textarea id="js-code"></textarea></p>
<p>Enter the code to send after upload here:</p>
<p><textarea id="post-code"></textarea></p>
<p>Then click <button id="upload" class="btn btn-primary">Upload</button>&nbsp;<span id="btninfo" style="color:orange"></span> </p>
<p><a id="setdefault">Click here</a> to reset to defaults.</p>
<script>
const LS_JSCODE = "espruinoprog.code";
const LS_POSTCODE = "espruinoprog.postcode";
const LS_NAMEPREFIX = "espruinoprog.namePrefix";
var jseditor,posteditor;

function setDefaults() {
if (localStorage.getItem(LS_JSCODE) === null) {
localStorage.setItem(LS_JSCODE, `// Flash LED2 every 2 seconds
setInterval(function() {
LED2.set();
setTimeout(function() {
LED2.reset();
}, 100);
}, 2000);`);
}
if (localStorage.getItem(LS_POSTCODE) === null) {
localStorage.setItem(LS_POSTCODE, `// Turn red LED on to show programmed
// Bluetooth off to stop this getting re-programmed
LED.set();NRF.sleep();`);
}
if (localStorage.getItem(LS_NAMEPREFIX) === null) {
localStorage.setItem(LS_NAMEPREFIX, "Puck.js");
}
document.getElementById("js-code").value = localStorage.getItem(LS_JSCODE);
if (jseditor) jseditor.setValue(document.getElementById("js-code").value);
document.getElementById("post-code").value = localStorage.getItem(LS_POSTCODE);
if (posteditor) posteditor.setValue(document.getElementById("post-code").value);
document.getElementById("nameprefix").value = localStorage.getItem(LS_NAMEPREFIX);
}
setDefaults();



// The code editor
var lintFlags = {
esversion: 6, // Enable ES6 for literals, arrow fns, binary
evil: true, // don't warn on use of strings in setInterval
laxbreak: true, // don't warn about newlines in expressions
laxcomma: true // don't warn about commas at the start of the line
};
jseditor = CodeMirror.fromTextArea(document.getElementById("js-code"), {
width: "100%",
height: "auto",
matchBrackets: true,
mode: { name: "javascript", globalVars: false },
lineWrapping: true,
showTrailingSpace: true,
lint: lintFlags,
gutters: ["CodeMirror-linenumbers", "CodeMirror-lint-markers"],
lineNumbers: true
});
posteditor = CodeMirror.fromTextArea(document.getElementById("post-code"), {
width: "100%",
height: "auto",
matchBrackets: true,
mode: { name: "javascript", globalVars: false },
lineWrapping: true,
showTrailingSpace: true,
lint: lintFlags,
gutters: ["CodeMirror-linenumbers", "CodeMirror-lint-markers"],
lineNumbers: true
});
function hasWarnings() {
return jseditor.state.lint.marked.length!=0 || posteditor.state.lint.marked.length!=0;
}
var editorChangedTimeout;
function editorChanged() {
if (editorChangedTimeout) clearTimeout(editorChangedTimeout);
editorChangedTimeout = setTimeout(function() {
if (hasWarnings()) {
document.getElementById("btninfo").innerHTML = "There are warnings in the code to be uploaded";
document.getElementById("upload").classList.add("disabled");
} else {
document.getElementById("btninfo").innerHTML = "";
document.getElementById("upload").classList.remove("disabled");
}
}, 500);
}

jseditor.on("change", editorChanged);
posteditor.on("change", editorChanged);

document.getElementById("upload").addEventListener("click", function() {
if (!hasWarnings()) {
var jscode = jseditor.getValue();
var postcode = posteditor.getValue();
var namePrefix = document.getElementById("nameprefix").value;
localStorage.setItem(LS_JSCODE, jscode);
localStorage.setItem(LS_POSTCODE, postcode);
localStorage.setItem(LS_NAMEPREFIX, namePrefix);

Espruino.transform(jscode, {
SET_TIME_ON_WRITE : false, // time would just be out of date
SAVE_ON_SEND : 1, // save to flash
LOAD_STORAGE_FILE : 0, // do not load from storage after saving
// PRETOKENISE : true,
// MINIFICATION_LEVEL : "ESPRIMA", // maybe?
}).then(content => {
sendCustomizedApp({
storage: [{ name: "espruinoprog.json", content: JSON.stringify({
namePrefix : namePrefix,
code : Espruino.Core.CodeWriter.reformatCode(content),
post : Espruino.Core.CodeWriter.reformatCode(postcode)
})}]
});
});
}
});
document.getElementById("setdefault").addEventListener("click", function(e) {
e.preventDefault();
localStorage.removeItem(LS_JSCODE);
localStorage.removeItem(LS_POSTCODE);
localStorage.removeItem(LS_NAMEPREFIX);
setDefaults();
});
</script>
</body>

</html>
17 changes: 17 additions & 0 deletions apps/espruinoprog/metadata.json
Original file line number Diff line number Diff line change
@@ -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"}
]
}
1 change: 1 addition & 0 deletions apps/espruinoterm/ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.01: New App!
22 changes: 22 additions & 0 deletions apps/espruinoterm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Espruino Terminal

Send commands to other Espruino devices via the Bluetooth UART interface and
see the result on a terminal.

## Customising

Once installed and you're connected to the Bangle you can click the button next to the app in the app loader
to change the commands (they will be read from the device).

When done, click `Save to Bangle.js` and your changes will be saved to the same device.

## Usage

* Load the app and after a few seconds you'll see a menu with Espruino devices
in the vicinity.
* Tap on the device you want to connect to
* A terminal will pop up showing `Connecting...` and then `Connected`
* Now tap on the right (or press the button) to bring up a menu with options for commands, or the option to disconnect.

You can also choose `Custom` in which case a keyboard (using the currently installed text input method) will
be displayed and you can enter the command you would like to send.
1 change: 1 addition & 0 deletions apps/espruinoterm/app-icon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwcCpMkyQC/AVW//4AK/oR/COD8LCP4R/CK8DCKNsCKFt2BHPhu2CJ8BCKAjQI4OQNaIUB23bsCPMCJzp/CP4Rf/4AKCKwC/AVIA=="))
Loading

0 comments on commit 51ac93a

Please sign in to comment.