Last active
June 20, 2025 14:21
-
-
Save joshtwist/7a13ad0d5967a351de303c984770ac71 to your computer and use it in GitHub Desktop.
puckJS toilet seat code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let monitoring = false; | |
let seatDownAccel = null; | |
let checkInterval = null; | |
let flashInterval = null; | |
let movedSince = null; | |
let flashTimerInterval = null; | |
// === Configuration Constants === | |
const ANGLE_THRESHOLD = 25; // degrees | |
const SEAT_UP_WARNING_DELAY_MS = 15000; // delay before red LED starts flashing | |
const ALERT_PHASE_1_END = 60; // 15s–1min: fast flashing | |
const ALERT_PHASE_2_END = 3600; // 1min–1hr: medium flashing | |
function angleBetween(v1, v2) { | |
const dot = v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; | |
const mag1 = Math.sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); | |
const mag2 = Math.sqrt(v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); | |
const cosTheta = dot / (mag1 * mag2); | |
return Math.acos(Math.min(Math.max(cosTheta, -1), 1)) * (180 / Math.PI); | |
} | |
function flashLED(led, onTime, offTime) { | |
if (flashInterval) clearInterval(flashInterval); | |
LED1.write(0); LED2.write(0); LED3.write(0); | |
flashInterval = setInterval(() => { | |
led.write(1); | |
setTimeout(() => led.write(0), onTime); | |
}, onTime + offTime); | |
} | |
function stopFlashing() { | |
if (flashInterval) clearInterval(flashInterval); | |
flashInterval = null; | |
LED1.write(0); LED2.write(0); LED3.write(0); | |
} | |
function bluePulse() { | |
LED3.write(1); | |
setTimeout(() => LED3.write(0), 1000); | |
} | |
function startFlashingTimer() { | |
if (flashTimerInterval) return; | |
flashTimerInterval = setTimeout(() => { | |
flashInterval = setInterval(() => { | |
const elapsed = (Date.now() - movedSince) / 1000; | |
if (elapsed < ALERT_PHASE_1_END) { | |
flashLED(LED1, 100, 233); // fast | |
} else if (elapsed < ALERT_PHASE_2_END) { | |
flashLED(LED1, 500, 500); // medium | |
} else { | |
flashLED(LED1, 100, 900); // slow | |
} | |
}, 1000); | |
}, SEAT_UP_WARNING_DELAY_MS); | |
} | |
function startMonitoring() { | |
seatDownAccel = Puck.accel().acc; | |
console.log("Calibrated: seat is CLOSED"); | |
movedSince = null; | |
// Blink green 3x | |
let flashes = 0; | |
let flash = setInterval(() => { | |
LED2.write(1); | |
setTimeout(() => LED2.write(0), 200); | |
flashes++; | |
if (flashes === 3) clearInterval(flash); | |
}, 400); | |
checkInterval = setInterval(() => { | |
const current = Puck.accel().acc; | |
if (!current || !seatDownAccel) return; | |
const angle = angleBetween(current, seatDownAccel); | |
if (angle > ANGLE_THRESHOLD) { | |
if (!movedSince) { | |
movedSince = Date.now(); | |
console.log("Seat is UP - movement detected"); | |
bluePulse(); | |
startFlashingTimer(); | |
} | |
} else { | |
if (movedSince) { | |
console.log("Seat is back DOWN - resetting timer"); | |
movedSince = null; | |
stopFlashing(); | |
if (flashTimerInterval) clearTimeout(flashTimerInterval); | |
flashTimerInterval = null; | |
} | |
} | |
}, 1000); | |
} | |
function stopMonitoring() { | |
console.log("Monitoring stopped"); | |
monitoring = false; | |
if (checkInterval) clearInterval(checkInterval); | |
if (flashInterval) clearInterval(flashInterval); | |
if (flashTimerInterval) clearTimeout(flashTimerInterval); | |
flashInterval = null; | |
flashTimerInterval = null; | |
LED1.write(0); LED2.write(0); LED3.write(0); | |
seatDownAccel = null; | |
movedSince = null; | |
checkInterval = null; | |
} | |
function ledDancePowerOn(callback) { | |
let rounds = 4; | |
let baseDelay = 400; | |
function runRound(round) { | |
if (round > rounds) return callback && callback(); | |
LED3.write(1); | |
setTimeout(() => { | |
LED3.write(0); | |
baseDelay *= 0.6; // speed up | |
setTimeout(() => runRound(round + 1), baseDelay); | |
}, baseDelay / 2); | |
} | |
runRound(1); | |
} | |
function ledDancePowerOff(callback) { | |
let rounds = 4; | |
let baseDelay = 100; | |
function runRound(round) { | |
if (round > rounds) return callback && callback(); | |
LED1.write(1); LED2.write(1); // yellow | |
setTimeout(() => { | |
LED1.write(0); LED2.write(0); | |
baseDelay *= 1.5; // slow down | |
setTimeout(() => runRound(round + 1), baseDelay); | |
}, baseDelay / 2); | |
} | |
runRound(1); | |
} | |
// Button toggle | |
setWatch(() => { | |
if (!monitoring) { | |
monitoring = true; | |
ledDancePowerOn(startMonitoring); | |
} else { | |
ledDancePowerOff(stopMonitoring); | |
} | |
}, BTN, { edge: "rising", debounce: 50, repeat: true }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment