mirror of
https://github.com/grinningmosfet/tourneur.js.git
synced 2024-11-24 11:24:02 +01:00
first github release
This commit is contained in:
parent
74608e31b5
commit
8d08ad458f
2 changed files with 171 additions and 0 deletions
33
README.md
33
README.md
|
@ -1,2 +1,35 @@
|
||||||
# tourneur.js
|
# tourneur.js
|
||||||
|
|
||||||
Le Tourneur is a page turner app for Espruini. Perfectly suits musicians, translators or even cooks!
|
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
138
tourneur.js
Normal 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;
|
||||||
|
}
|
Loading…
Reference in a new issue