Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Knob Button app for Puck.js (v2) #5

Merged
merged 1 commit into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps.json
Original file line number Diff line number Diff line change
Expand Up @@ -277,5 +277,17 @@
"storage": [
{"name":".bootcde","url":"app.js"}
]
},
{ "id": "knobbutton",
"name": "Knob Button",
"icon": "icon.png",
"version":"0.01",
"description": "Use the Puck.js (v2) as an anywhere knob: push the button to transmit the angle of rotation in BLE advertising packets.",
"tags": "bluetooth",
"readme": "README.md",
"needsFeatures":["BLE","ACCEL"],
"storage": [
{"name":".bootcde","url":"app.js"}
]
}
]
1 change: 1 addition & 0 deletions apps/knobbutton/ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.01: New App!
26 changes: 26 additions & 0 deletions apps/knobbutton/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Knob Button

Use the Puck.js (v2) as an on-demand knob: rotate to the desired angle and press the button. The angle of rotation will be advertised to any Bluetooth Low Energy receivers in range.

## Usage

Load the app onto the Puck.js (v2) and then:
- hold the Puck.js in a vertical position (ex: against a wall)
- rotate to the desired angle
- press the button
- observe the green LED flash
- observe BLE advertising packets with the angle of rotation data for ~5 seconds

## How it works

The Puck.js (v2) will wake on button press and read the accelerometer. The angle of rotation will be calculated based on the accelerometer's X-axis and Y-axis readings. The value is advertised as a JSON string in a manufacturer-specific data packet using the Espruino company code (0x590), for example:

{angleOfRotation:123}

[Pareto Anywhere](https://www.reelyactive.com/pareto/anywhere/) open source IoT middleware will automatically interpret these packets using its [advlib-ble-manufacturers](https://github.com/reelyactive/advlib-ble-manufacturers) library which supports Espruino advertising packets.

Following a button press sequence, the Puck.js (v2) will return to low-power sleep, waking again on any subsequent button press. It will also periodically advertise the name "Knob.js".

## Adapt the code

See the reelyActive's [Puck.js Development Guide](https://reelyactive.github.io/diy/puckjs-dev/) to load the source code in the Espruino IDE and adapt it to meet your needs!
103 changes: 103 additions & 0 deletions apps/knobbutton/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Copyright reelyActive 2022-2024
* We believe in an open Internet of Things
*/


// User-configurable constants
const LED_BLINK_MILLISECONDS = 50;
const STABLE_ACCELERATION_TOLERANCE_G = 0.1;
const ANGLE_ADVERTISING_DURATION_MILLISECONDS = 5000;
const ANGLE_ADVERTISING_PERIOD_MILLISECONDS = 500;
const NAME_ADVERTISING_PERIOD_MILLISECONDS = 5000;


// Non-user-configurable constants
const ACC_SAMPLE_RATE_HZ = 12.5; // Valid values are 1.6, 12.5, 26, 52, 104, 208
const ACC_PER_G = 8192;
const DEG_PER_RAD = 180 / Math.PI;


// Global variables
let advertisingTimeoutId;


// Calculate the angle of rotation based on the given accelerometer reading
function calculateAngleOfRotation(acc) {
let ratioXY = ((acc.y === 0) ? Infinity : Math.abs(acc.x / acc.y));
let ratioYX = ((acc.x === 0) ? Infinity : Math.abs(acc.y / acc.x));

if((acc.x >= 0) && (acc.y >= 0)) {
return Math.round(Math.atan(ratioYX) * DEG_PER_RAD);
}
if((acc.x <= 0) && (acc.y >= 0)) {
return Math.round(90 + (Math.atan(ratioXY) * DEG_PER_RAD));
}
if((acc.x <= 0) && (acc.y <= 0)) {
return Math.round(180 + (Math.atan(ratioYX) * DEG_PER_RAD));
}
if((acc.x >= 0) && (acc.y <= 0)) {
return Math.round(270 + (Math.atan(ratioXY) * DEG_PER_RAD));
}
}


// Advertise the name "Knob.js"
function advertiseName() {
NRF.setAdvertising({}, {
showName: false,

Check warning on line 48 in apps/knobbutton/app.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 4 spaces but found 6
manufacturer: 0x0590,

Check warning on line 49 in apps/knobbutton/app.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 4 spaces but found 6
manufacturerData: JSON.stringify({ name: "Knob.js" }),

Check warning on line 50 in apps/knobbutton/app.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 4 spaces but found 6
interval: NAME_ADVERTISING_PERIOD_MILLISECONDS

Check warning on line 51 in apps/knobbutton/app.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 4 spaces but found 6
});
}


// Advertise the angle of rotation for a specific period
function advertiseAngleOfRotation(angleOfRotation) {
if(advertisingTimeoutId) {
clearTimeout(advertisingTimeoutId);
}

NRF.setAdvertising({}, {
showName: false,

Check warning on line 63 in apps/knobbutton/app.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 4 spaces but found 6
manufacturer: 0x0590,

Check warning on line 64 in apps/knobbutton/app.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 4 spaces but found 6
manufacturerData: JSON.stringify({ angleOfRotation: angleOfRotation }),

Check warning on line 65 in apps/knobbutton/app.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 4 spaces but found 6
interval: ANGLE_ADVERTISING_PERIOD_MILLISECONDS

Check warning on line 66 in apps/knobbutton/app.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 4 spaces but found 6
});

advertisingTimeoutId = setTimeout(advertiseName,
ANGLE_ADVERTISING_DURATION_MILLISECONDS);

Check warning on line 70 in apps/knobbutton/app.js

View workflow job for this annotation

GitHub Actions / test

Expected indentation of 4 spaces but found 36
}


// Handle a button press: blink green LED and initiate accelerometer readings
function handleButton() {
Puck.accelOn(ACC_SAMPLE_RATE_HZ);
LED2.write(true);
setTimeout(function() { LED2.write(false); }, LED_BLINK_MILLISECONDS);
}


// Handle accelerometer reading: terminate accelerometer readings and advertise
// angle of rotation once magnitude is stable
function handleAcceleration(data) {
let magnitude = Math.sqrt((data.acc.x * data.acc.x) +
(data.acc.y * data.acc.y) +
(data.acc.z * data.acc.z)) / ACC_PER_G;
let isStableMagnitude = (magnitude < 1.0 + STABLE_ACCELERATION_TOLERANCE_G) &&
(magnitude > 1.0 - STABLE_ACCELERATION_TOLERANCE_G);

if(isStableMagnitude) {
let angleOfRotation = calculateAngleOfRotation(data.acc);

Puck.accelOff();
advertiseAngleOfRotation(angleOfRotation);
}
}


// Advertise "Knob.js", wake on button press and handle accelerometer readings
advertiseName();
Puck.on('accel', handleAcceleration);
setWatch(handleButton, BTN, { edge: "rising", repeat: true, debounce: 50 });
Binary file added apps/knobbutton/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading