Compare commits
2 Commits
953edd4065
...
94f38975bd
| Author | SHA1 | Date | |
|---|---|---|---|
| 94f38975bd | |||
| c325336508 |
@@ -26,14 +26,13 @@ BLECharacteristic cfgGitHash (0x1239); // GitHash R 8 bytes (7-char ha
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Charge mode
|
||||
// Charge mode
|
||||
void applyChargeMode(ChargeMode mode) {
|
||||
switch (mode) {
|
||||
case CHARGE_OFF: pinMode(PIN_HICHG, INPUT_PULLUP); break;
|
||||
case CHARGE_SLOW: pinMode(PIN_HICHG, OUTPUT); digitalWrite(PIN_HICHG, HIGH); break;
|
||||
case CHARGE_FAST: pinMode(PIN_HICHG, OUTPUT); digitalWrite(PIN_HICHG, LOW); break;
|
||||
case CHARGE_SLOW: pinMode(PIN_HICHG, INPUT); break;
|
||||
case CHARGE_FAST: pinMode(PIN_HICHG, OUTPUT); digitalWrite(PIN_HICHG, LOW); break;
|
||||
}
|
||||
const char* n[] = {"OFF (~0mA)", "SLOW (50mA)", "FAST (100mA)"};
|
||||
const char* n[] = {"SLOW (50mA)", "FAST (100mA)"};
|
||||
Serial.print("[CHG] "); Serial.println(n[mode]);
|
||||
}
|
||||
|
||||
@@ -113,7 +112,7 @@ void onConfigBlobWrite(uint16_t h, BLECharacteristic* c, uint8_t* d, uint16_t l)
|
||||
cfg.accelStrength = b->accelStrength;
|
||||
if (b->curve <= 2) cfg.curve = (CurveType)b->curve;
|
||||
cfg.axisFlip = b->axisFlip;
|
||||
if (b->chargeMode <= 2) { cfg.chargeMode = (ChargeMode)b->chargeMode; applyChargeMode(cfg.chargeMode); }
|
||||
if (b->chargeMode <= 1) { cfg.chargeMode = (ChargeMode)b->chargeMode; applyChargeMode(cfg.chargeMode); }
|
||||
#ifdef FEATURE_TAP_DETECTION
|
||||
if (b->tapThreshold >= 1 && b->tapThreshold <= 31) {
|
||||
cfg.tapThreshold = b->tapThreshold;
|
||||
|
||||
+2
-2
@@ -54,7 +54,7 @@
|
||||
|
||||
// Persistence
|
||||
#define CONFIG_FILENAME "/imu_mouse_cfg.bin"
|
||||
#define CONFIG_MAGIC 0xDEAD123DUL
|
||||
#define CONFIG_MAGIC 0xDEAD123EUL
|
||||
|
||||
// Physical button sentinel
|
||||
#define BTN_PIN_NONE 0xFF // Stored in btn*Pin when that button is disabled
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
// Enums
|
||||
enum CurveType : uint8_t { CURVE_LINEAR=0, CURVE_SQUARE=1, CURVE_SQRT=2 };
|
||||
enum ChargeMode : uint8_t { CHARGE_OFF=0, CHARGE_SLOW=1, CHARGE_FAST=2 };
|
||||
enum ChargeMode : uint8_t { CHARGE_SLOW=0, CHARGE_FAST=1 };
|
||||
enum ChargeStatus: uint8_t { CHGSTAT_DISCHARGING=0, CHGSTAT_CHARGING=1, CHGSTAT_FULL=2 };
|
||||
|
||||
// Tap action types
|
||||
|
||||
+32
-7
@@ -16,7 +16,7 @@ const FLAG_AUTO_RECAL_ENABLED = 0x04;
|
||||
const FLAG_ALL_DEFAULT = FLAG_TAP_ENABLED | FLAG_TEMP_COMP_ENABLED | FLAG_AUTO_RECAL_ENABLED;
|
||||
|
||||
// Local shadow of the current config (kept in sync with device)
|
||||
const config = { sensitivity:600, deadZone:0.06, accelStrength:0.08, curve:0, axisFlip:0, chargeMode:1,
|
||||
const config = { sensitivity:600, deadZone:0.06, accelStrength:0.08, curve:0, axisFlip:0, chargeMode:0,
|
||||
tapThreshold:12, tapAction:0, tapKey:0, tapMod:0, tapFreezeEnabled:1, jerkThreshold:2000,
|
||||
featureFlags:FLAG_ALL_DEFAULT,
|
||||
btnLeftPin:0xFF, btnRightPin:0xFF, btnMiddlePin:0xFF };
|
||||
@@ -37,7 +37,10 @@ let _gattDepth = 0;
|
||||
|
||||
function _withTimeout(promise, ms) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const t = setTimeout(() => reject(new Error(`GATT timeout (${ms}ms)`)), ms);
|
||||
const t = setTimeout(() => {
|
||||
console.warn(`[GATT] operation timed out after ${ms}ms`);
|
||||
reject(new Error(`GATT timeout (${ms}ms)`));
|
||||
}, ms);
|
||||
promise.then(v => { clearTimeout(t); resolve(v); },
|
||||
e => { clearTimeout(t); reject(e); });
|
||||
});
|
||||
@@ -45,6 +48,7 @@ function _withTimeout(promise, ms) {
|
||||
|
||||
function _enqueue(fn) {
|
||||
if (_gattDepth >= GATT_MAX_DEPTH) {
|
||||
console.warn(`[GATT] write dropped - queue depth ${_gattDepth} >= max ${GATT_MAX_DEPTH}`);
|
||||
return Promise.reject(new Error('GATT queue full - device unreachable?'));
|
||||
}
|
||||
_gattDepth++;
|
||||
@@ -57,6 +61,7 @@ function gattWrite(char, value) { return _enqueue(() => char.writeValueWithRespo
|
||||
function gattCmd (char, value) { return _enqueue(() => char.writeValueWithoutResponse(value)); }
|
||||
|
||||
function gattQueueReset() {
|
||||
console.log('[GATT] queue reset');
|
||||
// Drain the chain so a reconnect starts with a fresh resolved promise
|
||||
_gattQueue = Promise.resolve();
|
||||
_gattDepth = 0;
|
||||
@@ -133,7 +138,9 @@ async function discoverServices() {
|
||||
|
||||
// Telemetry notify (1 Hz) - also carries chargeStatus
|
||||
chars.telemetry.addEventListener('characteristicvaluechanged', e => parseTelemetry(e.target.value));
|
||||
console.log('[TELEM] subscribing to notifications');
|
||||
await chars.telemetry.startNotifications();
|
||||
console.log('[TELEM] subscribed, reading initial value');
|
||||
// Initial read so values show immediately. Also force updateChargeUI() here
|
||||
// because parseTelemetry() only calls it on a *change*, and currentChargeStatus
|
||||
// starts at 0 (discharging) - so a discharging device would never trigger the
|
||||
@@ -210,7 +217,7 @@ async function checkHashMatch() {
|
||||
// ConfigBlob read / write
|
||||
// ConfigBlob layout (25 bytes LE):
|
||||
// float sensitivity [0], float deadZone [4], float accelStrength [8]
|
||||
// uint8 curve [12], uint8 axisFlip [13], uint8 chargeMode [14]
|
||||
// uint8 curve [12], uint8 axisFlip [13], uint8 chargeMode [14] (0=SLOW 1=FAST)
|
||||
// uint8 tapThreshold [15], uint8 tapAction [16], uint8 tapKey [17], uint8 tapMod [18], uint8 tapFreezeEnabled [19]
|
||||
// float jerkThreshold [20], uint8 featureFlags [24]
|
||||
|
||||
@@ -384,15 +391,15 @@ function setChargeMode(val) {
|
||||
config.chargeMode = val;
|
||||
setChargeModeUI(val);
|
||||
writeConfigBlob();
|
||||
log(`Charge → ${['OFF','SLOW 50mA','FAST 100mA'][val]}`,'warn');
|
||||
log(`Charge → ${['SLOW 50mA','FAST 100mA'][val]}`,'warn');
|
||||
}
|
||||
function setChargeModeUI(val) {
|
||||
[['chgOff','off'],['chgSlow','slow'],['chgFast','fast']].forEach(([id,cls],i) => {
|
||||
[['chgSlow','slow'],['chgFast','fast']].forEach(([id,cls],i) => {
|
||||
const b = document.getElementById(id);
|
||||
b.classList.remove('active','off','slow','fast');
|
||||
b.classList.remove('active','slow','fast');
|
||||
if (i===val) b.classList.add('active', cls);
|
||||
});
|
||||
document.getElementById('ciMode').textContent = ['Off (0mA)','50 mA','100 mA'][val] ?? '--';
|
||||
document.getElementById('ciMode').textContent = ['50 mA','100 mA'][val] ?? '--';
|
||||
}
|
||||
|
||||
function onCapTapChange(enabled) {
|
||||
@@ -487,6 +494,7 @@ function parseTelemetry(dv) {
|
||||
// chargeStatus is now delivered via telemetry (no separate characteristic)
|
||||
if (chargeStatus !== currentChargeStatus) {
|
||||
currentChargeStatus = chargeStatus;
|
||||
console.log('[TELEM] charge status:', ['discharging','charging','full'][chargeStatus] ?? chargeStatus);
|
||||
updateChargeUI();
|
||||
}
|
||||
|
||||
@@ -547,6 +555,7 @@ let debugLiveRing = [];
|
||||
let debugT0 = 0;
|
||||
|
||||
function openDebugModal() {
|
||||
console.log('[DEBUG] modal opened (imuSubscribed=' + imuSubscribed + ')');
|
||||
debugModalOpen = true;
|
||||
debugT0 = Date.now();
|
||||
debugLiveRing = [];
|
||||
@@ -555,6 +564,7 @@ function openDebugModal() {
|
||||
if (!imuSubscribed && chars.imuStream) vizSetPaused(false);
|
||||
}
|
||||
function closeDebugModal() {
|
||||
console.log('[DEBUG] modal closed');
|
||||
debugModalOpen = false;
|
||||
document.getElementById('debugOverlay').classList.remove('show');
|
||||
}
|
||||
@@ -706,6 +716,7 @@ const TRAIL_LEN = 120;
|
||||
let cursorX = canvas.width/2, cursorY = canvas.height/2, trail = [];
|
||||
let vizPaused = true;
|
||||
let imuSubscribed = false;
|
||||
let _prevIdle = false;
|
||||
|
||||
// Stream diagnostics
|
||||
let streamPktCount = 0; // packets received this second
|
||||
@@ -715,7 +726,9 @@ let streamLastRateT = 0; // timestamp of last rate log
|
||||
let streamFreezeTimer = null; // fires if no packet for >1s while subscribed
|
||||
|
||||
function streamDiagReset() {
|
||||
console.log('[STREAM] diagnostics reset');
|
||||
streamPktCount = streamPktTotal = streamLastPktT = streamLastRateT = 0;
|
||||
_prevIdle = false;
|
||||
if (streamFreezeTimer) { clearTimeout(streamFreezeTimer); streamFreezeTimer = null; }
|
||||
}
|
||||
|
||||
@@ -771,6 +784,7 @@ function vizUpdateIndicator() {
|
||||
async function vizSetPaused(paused) {
|
||||
vizPaused = paused;
|
||||
if (!paused && chars.imuStream && !imuSubscribed) {
|
||||
console.log('[STREAM] requesting subscribe');
|
||||
try {
|
||||
await chars.imuStream.startNotifications();
|
||||
imuSubscribed = true;
|
||||
@@ -778,12 +792,15 @@ async function vizSetPaused(paused) {
|
||||
log('IMU stream subscribed','ok');
|
||||
} catch(e) { log(`IMU stream start failed: ${e.message}`,'err'); vizPaused = true; }
|
||||
} else if (paused && imuSubscribed) {
|
||||
console.log(`[STREAM] requesting unsubscribe (total rx: ${streamPktTotal})`);
|
||||
try {
|
||||
await chars.imuStream.stopNotifications();
|
||||
imuSubscribed = false;
|
||||
streamDiagReset();
|
||||
document.getElementById('orientLabel').textContent = '- not streaming -';
|
||||
} catch(e) { log(`IMU stream stop failed: ${e.message}`,'err'); }
|
||||
} else {
|
||||
console.log(`[STREAM] vizSetPaused(${paused}) - no action (imuSubscribed=${imuSubscribed})`);
|
||||
}
|
||||
vizUpdateIndicator();
|
||||
}
|
||||
@@ -817,6 +834,7 @@ function parseImuStream(dv) {
|
||||
feedDebugRow(gyroX, gyroZ, accelX, accelY, accelZ, moveX, moveY, flags);
|
||||
|
||||
if (vizPaused) return;
|
||||
|
||||
const idle = !!(flags & 0x01);
|
||||
const single = !!(flags & 0x02);
|
||||
const dbl = !!(flags & 0x04);
|
||||
@@ -835,6 +853,13 @@ function parseImuStream(dv) {
|
||||
|
||||
streamDiagPkt();
|
||||
|
||||
if (idle !== _prevIdle) {
|
||||
console.log(`[STREAM] idle → ${idle ? 'idle' : 'active'} (pkt #${streamPktTotal})`);
|
||||
_prevIdle = idle;
|
||||
}
|
||||
if (single) console.log(`[STREAM] single tap (pkt #${streamPktTotal})`);
|
||||
if (dbl) console.log(`[STREAM] double tap (pkt #${streamPktTotal})`);
|
||||
|
||||
if (single) flashTap('Left');
|
||||
if (dbl) flashTap('Right');
|
||||
drawViz(idle);
|
||||
|
||||
+2
-3
@@ -86,9 +86,8 @@
|
||||
<div class="param" style="border-bottom:none;padding:0">
|
||||
<div><div class="param-label">Charge Mode</div><div class="param-desc">BQ25100 ISET via P0.13 (HICHG)</div></div>
|
||||
<div class="segmented charge-seg" style="grid-column:2/4">
|
||||
<button class="seg-btn off" id="chgOff" onclick="setChargeMode(0)" disabled>OFF</button>
|
||||
<button class="seg-btn slow" id="chgSlow" onclick="setChargeMode(1)" disabled>SLOW · 50mA</button>
|
||||
<button class="seg-btn fast" id="chgFast" onclick="setChargeMode(2)" disabled>FAST · 100mA</button>
|
||||
<button class="seg-btn slow" id="chgSlow" onclick="setChargeMode(0)" disabled>SLOW · 50mA</button>
|
||||
<button class="seg-btn fast" id="chgFast" onclick="setChargeMode(1)" disabled>FAST · 100mA</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="charge-info" id="chargeInfo">
|
||||
|
||||
@@ -199,7 +199,6 @@
|
||||
.seg-btn:last-child { border-right:none; }
|
||||
.seg-btn.active { background:var(--accent); color:var(--bg); font-weight:bold; }
|
||||
.seg-btn:disabled { cursor:not-allowed; opacity:0.35; }
|
||||
.charge-seg .seg-btn.active.off { background:var(--dim); color:#fff; }
|
||||
.charge-seg .seg-btn.active.slow { background:var(--warn); color:var(--bg); }
|
||||
.charge-seg .seg-btn.active.fast { background:var(--accent2);color:#fff; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user