Merge branch 'master' into master

pull/889/head^2
Peer David 2021-11-18 16:34:20 +01:00 committed by GitHub
commit 0e5e96ab9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 573 additions and 18 deletions

View File

@ -115,7 +115,7 @@
{ {
"id": "setting", "id": "setting",
"name": "Settings", "name": "Settings",
"version": "0.31", "version": "0.33",
"description": "A menu for setting up Bangle.js", "description": "A menu for setting up Bangle.js",
"icon": "settings.png", "icon": "settings.png",
"tags": "tool,system", "tags": "tool,system",
@ -540,6 +540,21 @@
], ],
"data": [{"name":"trex.score","storageFile":true}] "data": [{"name":"trex.score","storageFile":true}]
}, },
{
"id": "cubescramble",
"name": "Cube Scramble",
"version":"0.01",
"description": "A random scramble generator for the 3x3 Rubik's cube",
"icon": "cube-scramble.png",
"tags": "",
"supports" : ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"allow_emulator": true,
"storage": [
{"name":"cubescramble.app.js","url":"cube-scramble.js"},
{"name":"cubescramble.img","url":"cube-scramble-icon.js","evaluate":true}
]
},
{ {
"id": "astroid", "id": "astroid",
"name": "Asteroids!", "name": "Asteroids!",
@ -4216,7 +4231,40 @@
], ],
"data": [{ "name": "qalarm.json" }] "data": [{ "name": "qalarm.json" }]
}, },
{ "id": "lcars", {
"id": "emojuino",
"name": "Emojuino",
"shortName": "Emojuino",
"version": "0.01",
"description": "Emojis & Espruino: broadcast Unicode emojis via Bluetooth Low Energy.",
"icon": "emojuino.png",
"tags": "emoji",
"supports" : [ "BANGLEJS2" ],
"readme": "README.md",
"storage": [
{ "name": "emojuino.app.js", "url": "emojuino.js" },
{ "name": "emojuino.img", "url": "emojuino-icon.js", "evaluate": true }
]
},
{
"id": "cliclockJS2Enhanced",
"name": "Commandline-Clock JS2 Enhanced",
"shortName": "CLI-Clock JS2",
"version": "0.1",
"description": "Simple CLI-Styled Clock with enhancements. Modes that are hard to use and unneded are removed (BPM, battery info, memory ect) credit to hughbarney for the original code and design",
"icon": "app.png",
"screenshots": [{"url":"screengrab.png"}],
"type": "clock",
"tags": "clock,cli,command,bash,shell",
"supports": ["BANGLEJS","BANGLEJS2"],
"allow_emulator": true,
"storage": [
{"name":"cliclockJS2Enhanced.app.js","url":"app.js"},
{"name":"cliclockJS2Enhanced.img","url":"app.icon.js","evaluate":true}
]
},
{
"id": "lcars",
"name": "LCARS Clock", "name": "LCARS Clock",
"shortName":"LCARS", "shortName":"LCARS",
"icon": "lcars.png", "icon": "lcars.png",

View File

@ -103,7 +103,7 @@ if (Bangle.swipeHandler) {
Bangle.removeListener("swipe", Bangle.swipeHandler); Bangle.removeListener("swipe", Bangle.swipeHandler);
delete Bangle.swipeHandler; delete Bangle.swipeHandler;
} }
if (Bangle.touchandler) { if (Bangle.touchHandler) {
Bangle.removeListener("touch", Bangle.touchHandler); Bangle.removeListener("touch", Bangle.touchHandler);
delete Bangle.touchHandler; delete Bangle.touchHandler;
} }

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AH4A8gAAKC8gKUC7Rf/C/PM5gDBjnBC6EcC4PBDIIbCC5/BAIIXVA4YXXAoRHUC6R3EC6KnEMAbv/C6oAKC8YA/AH4A/AH4Ax"))

View File

@ -0,0 +1,216 @@
var fontsize = g.getWidth()>200 ? 3 : 2;
var fontsizeTime = g.getWidth()>200 ? 4 : 4;
var fontheight = 10*fontsize;
var fontheightTime = 10*fontsizeTime;
var locale = require("locale");
var marginTop = 40;
var flag = false;
var hrtOn = false;
var hrtStr = "Hrt: ??? bpm";
const NONE_MODE = "none";
const ID_MODE = "id";
const VER_MODE = "ver";
const BATT_MODE = "batt";
const MEM_MODE = "mem";
const STEPS_MODE = "step";
const HRT_MODE = "hrt";
const NONE_FN_MODE = "no_fn";
const HRT_FN_MODE = "fn_hrt";
let infoMode = NONE_MODE;
let functionMode = NONE_FN_MODE;
let textCol = g.theme.dark ? "#0f0" : "#080";
function drawAll(){
updateTime();
updateRest(new Date());
}
function updateRest(now){
writeLine(locale.dow(now),1);
writeLine(locale.date(now,1),2);
drawInfo(5);
}
function updateTime(){
if (!Bangle.isLCDOn()) return;
let now = new Date();
writeLine(locale.time(now,1),0);
writeLine(flag?" ":"_",3);
flag = !flag;
if(now.getMinutes() == 0)
updateRest(now);
}
function writeLineStart(line){
if (line==0){
g.drawString(">",0,marginTop+(line)*fontheight);
} else {
g.drawString(">",4,marginTop+(line-1)*fontheight + fontheightTime);
}
}
function writeLine(str,line){
if (line == 0){
var y = marginTop+line*fontheightTime;
g.setFont("6x8",fontsizeTime);
g.setColor(textCol).setFontAlign(-1,-1);
g.clearRect(0,y,((str.length+1)*40),y+fontheightTime-1);
writeLineStart(line);
g.drawString(str,25,y);
} else {
var y = marginTop+(line-1)*fontheight+fontheightTime;
g.setFont("6x8",fontsize);
g.setColor(textCol).setFontAlign(-1,-1);
g.clearRect(0,y,((str.length+1)*20),y+fontheight-1);
writeLineStart(line);
g.drawString(str,25,y);
}
}
function drawInfo(line) {
let val;
let str = "";
let col = textCol; // green
//console.log("drawInfo(), infoMode=" + infoMode + " funcMode=" + functionMode);
switch(functionMode) {
case NONE_FN_MODE:
break;
case HRT_FN_MODE:
col = g.theme.dark ? "#0ff": "#088"; // cyan
str = "HRM: " + (hrtOn ? "ON" : "OFF");
drawModeLine(line,str,col);
return;
}
switch(infoMode) {
case NONE_MODE:
col = g.theme.bg;
str = "";
break;
case HRT_MODE:
str = hrtStr;
break;
case STEPS_MODE:
str = "Steps: " + stepsWidget().getSteps();
break;
case ID_MODE:
val = NRF.getAddress().split(":");
str = "Id: " + val[4] + val[5];
break;
case VER_MODE:
str = "Fw: " + process.env.VERSION;
break;
case MEM_MODE:
val = process.memory();
str = "Memory: " + Math.round(val.usage*100/val.total) + "%";
break;
case BATT_MODE:
default:
str = "Battery: " + E.getBattery() + "%";
}
drawModeLine(line,str,col);
}
function drawModeLine(line, str, col) {
g.setColor(col);
var y = marginTop+line*fontheight;
g.fillRect(0, y, 239, y+fontheight-1);
g.setColor(g.theme.bg).setFontAlign(0, 0);
g.drawString(str, g.getWidth()/2, y+fontheight/2);
}
function changeInfoMode() {
switch(functionMode) {
case NONE_FN_MODE:
break;
case HRT_FN_MODE:
hrtOn = !hrtOn;
Bangle.buzz();
Bangle.setHRMPower(hrtOn ? 1 : 0);
if (hrtOn) infoMode = HRT_MODE;
return;
}
switch(infoMode) {
case NONE_MODE:
if (stepsWidget() !== undefined)
infoMode = hrtOn ? HRT_MODE : STEPS_MODE;
else
infoMode = VER_MODE;
break;
case HRT_MODE:
if (stepsWidget() !== undefined)
infoMode = STEPS_MODE;
else
infoMode = VER_MODE;
break;
case STEPS_MODE:
infoMode = ID_MODE;
break;
case ID_MODE:
infoMode = VER_MODE;
break;
case VER_MODE:
infoMode = BATT_MODE;
break;
case BATT_MODE:
infoMode = MEM_MODE;
break;
case MEM_MODE:
default:
infoMode = NONE_MODE;
}
}
function changeFunctionMode() {
//console.log("changeFunctionMode()");
switch(functionMode) {
case NONE_FN_MODE:
functionMode = HRT_FN_MODE;
break;
case HRT_FN_MODE:
default:
functionMode = NONE_FN_MODE;
}
//console.log(functionMode);
}
function stepsWidget() {
if (WIDGETS.activepedom !== undefined) {
return WIDGETS.activepedom;
} else if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom;
}
return undefined;
}
Bangle.on('HRM', function(hrm) {
if(hrm.confidence > 90){
hrtStr = "Hrt: " + hrm.bpm + " bpm";
} else {
hrtStr = "Hrt: ??? bpm";
}
});
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll();
Bangle.on('lcdPower',function(on) {
if (on) drawAll();
});
var click = setInterval(updateTime, 1000);
// Show launcher when button pressed
Bangle.setUI("clockupdown", btn=>{
if (btn<0) changeInfoMode();
drawAll();
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1 @@
0.01: Initial Release

View File

@ -0,0 +1,18 @@
# Cube Scramble
A random scramble generator for the 3x3 Rubik's cube
## Future features
I'm keen to complete this project with
* Add a timer
* Add the ability for times to be stored and exported
## Requests
Please reach out if you have feature requests or notice bugs.
## Creator
Made by [Nathan Lisgo](https://github.com/nlisgo)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("3YANB54AFgf/+ULCqOw/4ACC6EAxEAC4fwCp2YxGIwEPC4XxGBewqEICwIABGAUQRJWwp3u9xFBAAQUCr3u8gwHgAVBC4ugCwIKDC5nghGAgEODoIKEC44PBBgUAggFDBQZIHhAMB8j4CMgQHBgoDBoB5H3BuC5nM4AqDJAIABgnd7owF3GAhgWBAAJbD8oWBCoIABoAXLqBIBCoNmC4nQC4xGCJAQVBs93u9ghoYDJAmwwFwFwYWBCoIABuBeC7oLBGAmBu0MFgMFDIMHDAZIB6AhBiCoFBgIVBJAQXFAAMv//wI4kBKoJgDSIIVBuwVBgP/AAIXEJoNnuCRFg9gCoPwh4XHTwMHvxHDPQc///xC5PgC4PgCoPFLIQSC+QXEVAb9CunggpYCwMDC4/9C4bnBqFldQWIAAIXH+FNC4deJAMAC4IWCxECC4nybQQXDh3u8vQC4qiEHILxCC4ewF4PdJQIXDO4UwgEYC42wgEOC4NeoAXE+IsBhAFBC4u16EAC4PV8AWCwCpCCwIXDoBGD7rPB7vUC4UQCwRmDBoPQuwXE6ghCgAVEgsBC4fQu4XFD4MN7tE8oWD7oXEu4XE3YXCqHdovuoCgDI4WBC4VwC4YsB6HMgjBBYQKHCC4OKhnAC4K+DJAQXB4DxB9wfBC4cFBYN3tYWDAAO9C4PMC48ABQPAIogADqAMCpwXFhgKCIohJErgNBgoXDqHxC4QWIAAINCg1errmBh4XC4AWJAAIwBg9gonQgf/+QXBFxRJCC4N2OYP/C4VQCxZJCg61Bh4XB+cACxoABC4VvC4PxCx5JBC4k7C6AwCuf/+AWRAANnuX/RZhJIuBFSAAaLMA="))

View File

@ -0,0 +1,74 @@
// Scramble code from: https://raw.githubusercontent.com/bjcarlson42/blog-post-sample-code/master/Rubik's%20Cube%20JavaScript%20Scrambler/part_two.js
const makeScramble = () => {
const options = ["F", "F2", "F'", "R", "R2", "R'", "U", "U2", "U'", "B", "B2", "B'", "L", "L2", "L'", "D", "D2", "D'"];
const numOptions = [0, 1, 2, 3, 4, 5]; // 0 = F, 1 = R, 2 = U, 3 = B, 4 = L, 5 = D
const scrambleMoves = [];
let bad = true;
while (bad) {
let scramble = [];
for (let i = 0; i < 20; i++) {
scramble.push(numOptions[getRandomInt(6)]);
}
// check if moves directly next to each other involve the same letter
for (let i = 0; i < 20 - 1; i++) {
if (scramble[i] == scramble[i + 1]) {
bad = true;
break;
} else {
bad = false;
}
}
}
// switch numbers to letters
let move;
for (let i = 0; i < 20; i++) {
switch (scramble[i]) {
case 0:
move = options[getRandomInt(3)]; // 0,1,2
scrambleMoves.push(move);
break;
case 1:
move = options[getRandomIntBetween(3, 6)]; // 3,4,5
scrambleMoves.push(move);
break;
case 2:
move = options[getRandomIntBetween(6, 9)]; // 6,7,8
scrambleMoves.push(move);
break;
case 3:
move = options[getRandomIntBetween(9, 12)]; // 9,10,11
scrambleMoves.push(move);
break;
case 4:
move = options[getRandomIntBetween(12, 15)]; // 12,13,14
scrambleMoves.push(move);
break;
case 5:
move = options[getRandomIntBetween(15, 18)]; // 15,16,17
scrambleMoves.push(move);
break;
}
}
return scrambleMoves;
};
const getRandomInt = max => Math.floor(Math.random() * Math.floor(max)); // returns up to max - 1
const getRandomIntBetween = (min, max) => Math.floor(Math.random() * (max - min) + min);
const presentScramble = () => {
g.clear();
E.showMessage(makeScramble().join(" "));
};
const init = () => {
presentScramble();
setWatch(() => {
presentScramble();
}, BTN1, {repeat:true});
};
init();

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

1
apps/emojuino/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

28
apps/emojuino/README.md Normal file
View File

@ -0,0 +1,28 @@
# Emojuino
Emojis & Espruino!
## Usage
Select an emoji and then tap to transmit! The emoji will be recognised by [Pareto Anywhere](https://www.reelyactive.com/pareto/anywhere/) open source middleware and any other program which observes the [InteroperaBLE Identifier](https://reelyactive.github.io/interoperable-identifier/) open standard.
## Features
Currently implements a tiny subset of possible [Unicode emojis](https://unicode.org/emoji/charts/full-emoji-list.html) which are advertised as an [InteroperaBLE Identifier](https://reelyactive.github.io/interoperable-identifier/) encapsulated as Eddystone UID.
## Controls
Swipe left/right to select the emoji to broadcast. Tap the screen to initiate the broadcast. Emoji will flash while broadcasting, which lasts for 5 seconds.
## Requests
[Contact reelyActive](https://www.reelyactive.com/contact/) for support/updates.
## Creator
Developed by [jeffyactive](https://github.com/jeffyactive) of [reelyActive](https://www.reelyactive.com)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkBiIAHkUoxGIwUiBxAAGiQVCAAeCkIWNCooADDBYWKDBYWEkc////+cyDBhxDCoQAD+YLDCw0YBQQVFAAYYCwIXFHQRDElGCJYgOCFw8vBwPyOgoJFGAg4BIoQWGDAhJCIwoLBHgYAGJQIjCIwguCnCRFRoeDGAZICAgOPFwaRGDAQfB/AwDBAYuCX44wDAgTrDBoIDBGYP/manBmYFBFYQPDwJeBD4iRGRoQ/FC4QqBEYIbERooTBCAeBNAIjBBQIDDAAggBG4IDDwQXBEQIDDUAgcCHASaBAYQTFMQpcFDYp+EEII9DAARRDFIIfDHIwXBVISlDC4YzD9wA0osFpwIF8lQqgWK8kAgEEBItABIIhGAAfgBoMABIoIChwX0jwED8oNBgoXFqAJBrwHD8IXEBwQNEEIYgFC4wAQ8MRC6sRC+BgULwIwHSINVpwuLC43kaAQABqgaHC4bZHAAkFqhGHGAovFAAYyDCwgwFL4IwGFxAwNFxIwG8lVCoSTEFw7bPCxAYNCxT0LIpIxMCpoyHFhI"))

145
apps/emojuino/emojuino.js Normal file
View File

@ -0,0 +1,145 @@
/**
* Copyright reelyActive 2021
* We believe in an open Internet of Things
*/
// Emojis are integer pairs with the form [ image, Unicode code point ]
// For code points see https://unicode.org/emoji/charts/emoji-list.html
const EMOJIS = [
[ ':)', 0x1f642 ], // Slightly smiling
[ ':|', 0x1f610 ], // Neutral
[ ':(', 0x1f641 ], // Slightly frowning
[ '+1', 0x1f44d ], // Thumbs up
[ '-1', 0x1f44e ], // Thumbs down
[ '<3', 0x02764 ], // Heart
];
const EMOJI_TRANSMISSION_MILLISECONDS = 5000;
const BLINK_PERIOD_MILLISECONDS = 500;
const TRANSMIT_BUZZ_MILLISECONDS = 200;
const CYCLE_BUZZ_MILLISECONDS = 50;
// Non-user-configurable constants
const IMAGE_INDEX = 0;
const CODE_POINT_INDEX = 1;
const BTN_WATCH_OPTIONS = { repeat: true, debounce: 20, edge: "falling" };
const UNICODE_CODE_POINT_ELIDED_UUID = [ 0x49, 0x6f, 0x49, 0x44, 0x55,
0x54, 0x46, 0x2d, 0x33, 0x32 ];
// Global variables
let emojiIndex = 0;
let isToggleOn = false;
let isTransmitting = false;
let lastDragX = 0;
let lastDragY = 0;
// Cycle through emojis
function cycleEmoji(isForward) {
if(isTransmitting) { return; }
if(isForward) {
emojiIndex = (emojiIndex + 1) % EMOJIS.length;
}
else if(--emojiIndex < 0) {
emojiIndex = EMOJIS.length - 1;
}
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
Bangle.buzz(CYCLE_BUZZ_MILLISECONDS);
}
// Handle a touch: transmit displayed emoji
function handleTouch(zone, event) {
if(isTransmitting) { return; }
let emoji = EMOJIS[emojiIndex];
transmitEmoji(emoji[IMAGE_INDEX], emoji[CODE_POINT_INDEX],
EMOJI_TRANSMISSION_MILLISECONDS);
Bangle.buzz(TRANSMIT_BUZZ_MILLISECONDS);
}
// Transmit the given code point for the given duration in milliseconds,
// blinking the image once per second.
function transmitEmoji(image, codePoint, duration) {
let instance = [ 0x00, 0x00, (codePoint >> 24) & 0xff,
(codePoint >> 16) & 0xff, (codePoint >> 8) & 0xff,
codePoint & 0xff ];
require('ble_eddystone_uid').advertise(UNICODE_CODE_POINT_ELIDED_UUID,
instance);
isTransmitting = true;
let displayIntervalId = setInterval(toggleImage, BLINK_PERIOD_MILLISECONDS,
image);
setTimeout(terminateEmoji, duration, displayIntervalId);
}
// Terminate the emoji transmission
function terminateEmoji(displayIntervalId) {
NRF.setAdvertising({ });
isTransmitting = false;
clearInterval(displayIntervalId);
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
}
// Toggle the display between image/off
function toggleImage(image) {
if(isToggleOn) {
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
}
else {
g.clear();
}
isToggleOn = !isToggleOn;
}
// Draw the given emoji
function drawImage(image) {
g.clear();
g.drawString(image, g.getWidth() / 2, g.getHeight() / 2);
g.flip();
}
// Handle a drag event
function handleDrag(event) {
let isFingerReleased = (event.b === 0);
if(isFingerReleased) {
let isHorizontalDrag = (Math.abs(lastDragX) >= Math.abs(lastDragY)) &&
(lastDragX !== 0);
if(isHorizontalDrag) {
cycleEmoji(lastDragX > 0);
}
}
else {
lastDragX = event.dx;
lastDragY = event.dy;
}
}
// Special function to handle display switch on
Bangle.on('lcdPower', (on) => {
if(on) {
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
}
});
// On start: display the first emoji and handle drag and touch events
g.clear();
g.setFont('Vector', 80);
g.setFontAlign(0, 0);
drawImage(EMOJIS[emojiIndex][IMAGE_INDEX]);
Bangle.on('touch', handleTouch);
Bangle.on('drag', handleDrag);

BIN
apps/emojuino/emojuino.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -34,3 +34,5 @@
0.29: Add Customize to Theme menu 0.29: Add Customize to Theme menu
0.30: Move '< Back' to the top of menus 0.30: Move '< Back' to the top of menus
0.31: Remove Bangle 1 settings when running on Bangle 2 0.31: Remove Bangle 1 settings when running on Bangle 2
0.32: Fix 'beep' menu on Bangle.js 2
0.33: Really fix 'beep' menu on Bangle.js 2 this time

View File

@ -38,7 +38,7 @@ function resetSettings() {
quiet: 0, // quiet mode: 0: off, 1: priority only, 2: total silence quiet: 0, // quiet mode: 0: off, 1: priority only, 2: total silence
timeout: 10, // Default LCD timeout in seconds timeout: 10, // Default LCD timeout in seconds
vibrate: true, // Vibration enabled by default. App must support vibrate: true, // Vibration enabled by default. App must support
beep: "vib", // Beep enabled by default. App must support beep: BANGLEJS2?true:"vib", // Beep enabled by default. App must support
timezone: 0, // Set the timezone for the device timezone: 0, // Set the timezone for the device
HID: false, // BLE HID mode, off by default HID: false, // BLE HID mode, off by default
clock: null, // a string for the default clock's name clock: null, // a string for the default clock's name
@ -72,8 +72,37 @@ if (!('qmOptions' in settings)) settings.qmOptions = {}; // easier if this alway
const boolFormat = v => v ? "On" : "Off"; const boolFormat = v => v ? "On" : "Off";
function showMainMenu() { function showMainMenu() {
var beepV = BANGLEJS2 ? [false,true] : [false, true, "vib"]; var beepMenuItem;
var beepN = BANGLEJS2 ? ["Off","On"] : ["Off", "Piezo", "Vibrate"]; if (BANGLEJS2) {
beepMenuItem = {
value: settings.beep!=false,
format: boolFormat,
onchange: v => {
settings.beep = v;
updateSettings();
if (settings.beep) {
analogWrite(VIBRATE,0.1,{freq:2000});
setTimeout(()=>VIBRATE.reset(),200);
} // beep with vibration moter
}
};
} else { // Bangle.js 1
var beepV = [false, true, "vib"];
var beepN = ["Off", "Piezo", "Vibrate"];
beepMenuItem = {
value: Math.max(0 | beepV.indexOf(settings.beep),0),
min: 0, max: beepV.length-1,
format: v => beepN[v],
onchange: v => {
settings.beep = beepV[v];
if (v==1) { analogWrite(D18,0.5,{freq:2000});setTimeout(()=>D18.reset(),200); } // piezo on Bangle.js 1
else if (v==2) { analogWrite(VIBRATE,0.1,{freq:2000});setTimeout(()=>VIBRATE.reset(),200); } // vibrate
updateSettings();
}
};
}
const mainmenu = { const mainmenu = {
'': { 'title': 'Settings' }, '': { 'title': 'Settings' },
'< Back': ()=>load(), '< Back': ()=>load(),
@ -88,17 +117,7 @@ function showMainMenu() {
updateSettings(); updateSettings();
} }
}, },
'Beep': { 'Beep': beepMenuItem,
value: 0 | beepV.indexOf(settings.beep),
min: 0, max: 2,
format: v => beepN[v],
onchange: v => {
settings.beep = beepV[v];
if (v==1) { analogWrite(D18,0.5,{freq:2000});setTimeout(()=>D18.reset(),200); } // piezo
else if (v==2) { analogWrite(D13,0.1,{freq:2000});setTimeout(()=>D13.reset(),200); } // vibrate
updateSettings();
}
},
'Vibration': { 'Vibration': {
value: settings.vibrate, value: settings.vibrate,
format: boolFormat, format: boolFormat,
@ -146,7 +165,7 @@ function showBLEMenu() {
} }
}, },
'HID': { 'HID': {
value: 0 | hidV.indexOf(settings.HID), value: Math.max(0,0 | hidV.indexOf(settings.HID)),
min: 0, max: 3, min: 0, max: 3,
format: v => hidN[v], format: v => hidN[v],
onchange: v => { onchange: v => {