Files
ESP32-WijiBoard/web/js/ble.js
T
PROFERIS - Mi³osz Stocki 67b4eda6aa Initial commit
2026-07-06 11:34:03 +02:00

140 lines
5.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* WijiBoard BLE Singleton
* web/js/ble.js
*
* Provides a singleton BLE object with:
* BLE.connect() → connects to device, gets characteristics
* BLE.disconnect() → disconnects
* BLE.write(cmd) → writes ASCII command (write-without-response)
* BLE.isConnected() → boolean
* BLE.on(event, cb) → subscribe to 'connected' | 'disconnected' | 'status'
* BLE.off(event, cb) → unsubscribe
*
* Command characteristic (WRITE_NR):
* S1+<n> → step motor 1 clockwise n steps
* S1-<n> → step motor 1 counter-clockwise n steps
* S2+<n> → step motor 2 clockwise n steps
* S2-<n> → step motor 2 counter-clockwise n steps
* SPD:<n> → set max speed (steps/sec)
* ACC:<n> → set acceleration (steps/sec²)
* HOME → zero both steppers
* POS → request position report
*
* Status characteristic (NOTIFY):
* P:<s1>,<s2> → current step positions for motor 1 & 2
*/
// ── UUIDs must match src/main.cpp exactly ──────────────────────
const SERVICE_UUID = 'a0b1c2d3-e4f5-6789-abcd-ef0123456700';
const CMD_UUID = 'a0b1c2d3-e4f5-6789-abcd-ef0123456701';
const STATUS_UUID = 'a0b1c2d3-e4f5-6789-abcd-ef0123456702';
const DEVICE_NAME = 'WijiBoard';
const BLE = (() => {
// ── Internal state ──────────────────────────────────────────
let device = null;
let server = null;
let cmdChar = null;
let statusChar = null;
const encoder = new TextEncoder();
// ── Tiny event emitter ──────────────────────────────────────
const listeners = {};
function emit(event, ...args) {
(listeners[event] || []).forEach(cb => {
try { cb(...args); } catch (e) { console.error('[BLE] listener error', e); }
});
}
function on(event, cb) {
if (!listeners[event]) listeners[event] = [];
listeners[event].push(cb);
}
function off(event, cb) {
if (!listeners[event]) return;
listeners[event] = listeners[event].filter(fn => fn !== cb);
}
// ── Disconnect handler ──────────────────────────────────────
function onDisconnected() {
console.log('[BLE] Device disconnected');
device = null;
server = null;
cmdChar = null;
statusChar = null;
emit('disconnected');
}
// ── Status notification handler ─────────────────────────────
function onStatusNotification(event) {
const value = new TextDecoder().decode(event.target.value);
emit('status', value.trim());
}
// ── connect() ───────────────────────────────────────────────
async function connect() {
if (!navigator.bluetooth) {
throw new Error('Web Bluetooth API not available. Use Chrome/Edge over HTTPS or localhost.');
}
emit('connecting');
device = await navigator.bluetooth.requestDevice({
filters: [{ name: DEVICE_NAME }],
optionalServices: [SERVICE_UUID],
});
device.addEventListener('gattserverdisconnected', onDisconnected);
server = await device.gatt.connect();
const service = await server.getPrimaryService(SERVICE_UUID);
cmdChar = await service.getCharacteristic(CMD_UUID);
// Status notifications (optional firmware may not have this char yet)
try {
statusChar = await service.getCharacteristic(STATUS_UUID);
await statusChar.startNotifications();
statusChar.addEventListener('characteristicvaluechanged', onStatusNotification);
} catch {
console.warn('[BLE] Status characteristic not available notifications disabled');
}
emit('connected', device.name);
}
// ── disconnect() ────────────────────────────────────────────
async function disconnect() {
if (device?.gatt?.connected) {
device.gatt.disconnect();
}
}
// ── write() ─────────────────────────────────────────────────
async function write(cmd) {
if (!cmdChar) throw new Error('BLE not connected');
await cmdChar.writeValueWithoutResponse(encoder.encode(cmd));
emit('sent', cmd);
}
// ── isConnected() ───────────────────────────────────────────
function isConnected() {
return !!(device?.gatt?.connected && cmdChar);
}
// ── getDeviceName() ─────────────────────────────────────────
function getDeviceName() {
return device?.name ?? null;
}
return {
on, off, connect, disconnect, write, isConnected, getDeviceName,
SERVICE_UUID, CMD_UUID, STATUS_UUID, DEVICE_NAME,
};
})();
export default BLE;