first github release

This commit is contained in:
grinningmosfet 2024-04-18 13:58:09 +02:00 committed by GitHub
parent 74608e31b5
commit 8d08ad458f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 171 additions and 0 deletions

View file

@ -1,2 +1,35 @@
# tourneur.js
Le Tourneur is a page turner app for Espruini. Perfectly suits musicians, translators or even cooks!
## Operation
### Setup
Insert a CR2032 battery into your Puck.js, pair it with the Bluetooth device you'd like to control and you are ready to go!
### Button interface
- **Short** press to skip one page. (It simulates the press of the **right** keyboard key.)
- **Long** press to return one page. (It simulates the press of the **left** keyboard key.)
The long press delay can be customized (see _Variables_ below).
### Bilking indicators
- **First blink**: no Bluetooth connection if **red**, skip if **green**, return if **yellow** (red + green).
- **Second blink** (red): battery level is under threshold.
### Variables
Som compile-time parameters are exposed. They are reported in the table below:
| Variable | Default | Description |
|---------------------|---------|---------------------------------------------------------------------------|
| `EMPTY_BATT_THRESH` | `20` | Battery level in % below which low battery indicator turns on. |
| `LONG_PRESS_MILLIS` | `300` | Minimal press duration in milliseconds for a long press to be registered. |
## Tips & Tricks
Keep the virtual keyboard under Android
: As Le Tourneur acts like a keyboard, but without all the letters and numbers keys, you need to tell Android to keep showing the virtual keyboard in case you want to fill in a text field. This option exists in the Android settings.

138
tourneur.js Normal file
View file

@ -0,0 +1,138 @@
/**
* tourneur.js
*
* Le Tourneur is a page turner app for Espruini.
*
* @author Pierre "grinningmosfet" Guillod (maintainer) and contributors
* @version 1.2.0
*
* This file is part of Le Tourneur.
*
* Le Tourneur is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Le Tourneur is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with Le Tourneur. If not, see <https://www.gnu.org/licenses/>.
*/
var EMPTY_BATT_THRESH = 20;
var LONG_PRESS_MILLIS = 300;
var kb = require("ble_hid_keyboard");
NRF.setServices(undefined, { hid : kb.report });
var timoutObj = null;
/**
* btnPressed
* Callback for button press
*
* Starts a timeout that will issue
* a long press action after timing
* out.
*
* \return void
*/
function btnPressed() {
timoutObj = setTimeout(function(){
timoutObj = null;
try {
kb.tap(kb.KEY.LEFT,0);
ledsAnim("rev");
} catch (error) {
ledsAnim("error");
}
}, LONG_PRESS_MILLIS);
}
/**
* btnReleased
* Callback for button release
*
* If the timeout us still running,
* the long press delay was not reached.
* Therefore, the timeout needs to be
* stopped and a forward action must
* be issued.
*
* \return void
*/
function btnReleased() {
if (timoutObj) {
clearTimeout(timoutObj);
timoutObj = null;
try {
kb.tap(kb.KEY.RIGHT,0);
ledsAnim("fwd");
} catch (error) {
ledsAnim("error");
}
}
}
setWatch(btnPressed, BTN, {edge:"rising", repeat:true,debounce:50});
setWatch(btnReleased, BTN, {edge:"falling",repeat:true,debounce:50});
/**
* ledsAnim
* LEDs animations handler
*
* \param level: characterizes the animation <"feedback"|"error">
* \return void
*/
function ledsAnim(level) {
var BLACK = 0;
var RED = 1;
var GREEN = 2;
var BLUE = 4;
var WHITE = RED + GREEN + BLUE;
var YELLOW = RED + GREEN;
switch(level) {
case "fwd":
digitalWrite([LED3,LED2,LED1],GREEN);
setTimeout(function(){digitalWrite([LED3,LED2,LED1],BLACK);}, 5);
if(isBattUnderThresh(EMPTY_BATT_THRESH)) {
setTimeout(function(){digitalWrite([LED3,LED2,LED1],RED);}, 200);
setTimeout(function(){digitalWrite([LED3,LED2,LED1],BLACK);}, 205);
}
break;
case "rev":
digitalWrite([LED3,LED2,LED1],YELLOW);
setTimeout(function(){digitalWrite([LED3,LED2,LED1],BLACK);}, 5);
if(isBattUnderThresh(EMPTY_BATT_THRESH)) {
setTimeout(function(){digitalWrite([LED3,LED2,LED1],RED);}, 200);
setTimeout(function(){digitalWrite([LED3,LED2,LED1],BLACK);}, 205);
}
break;
case "error":
digitalWrite([LED3,LED2,LED1],RED);
setTimeout(function(){digitalWrite([LED3,LED2,LED1],BLACK);}, 5);
if(isBattUnderThresh(EMPTY_BATT_THRESH)) {
setTimeout(function(){digitalWrite([LED3,LED2,LED1],RED);}, 200);
setTimeout(function(){digitalWrite([LED3,LED2,LED1],BLACK);}, 205);
}
break;
}
}
/**
* isBattUnderThresh
* Compares battery level to arbitrary threshold.
*
* \param thresh: threshold
* \return boolean: if the current battery level is under the threshold
*/
function isBattUnderThresh(thresh) {
return E.getBattery() < thresh;
}