diff --git a/docs/solo_features/screen_lock/screen_lock.md b/docs/solo_features/screen_lock/screen_lock.md index 774760bb..299e85c2 100644 --- a/docs/solo_features/screen_lock/screen_lock.md +++ b/docs/solo_features/screen_lock/screen_lock.md @@ -51,3 +51,14 @@ The display turns off again automatically after 5 seconds of inactivity (or 2 se ### Auto-lock Enable **Auto-lock** in **Settings › Display** to lock the device automatically whenever the display turns off due to auto-off timeout. + +--- + +### Magnetic cover (Hall sensor) + +Optional, user-supplied hardware — no board in this repo has one built in. Wire a Hall-effect or reed sensor to any free GPIO, then set `PIN_HALL_SENSOR` (and `HALL_ACTIVE_HIGH=1`, if your module pulls the pin high rather than low when the magnet is present) as `build_flags` in your own env. No-op entirely unless `PIN_HALL_SENSOR` is defined. + +Fully autonomous, independent of Auto-lock and of any key combo: + +- **Magnet near (cover closed)** — locks and blanks the display immediately, no wake grace. +- **Magnet away (cover opened)** — unlocks and wakes the display right away. diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 01807d86..a6e6f9de 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1404,6 +1404,17 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no _has_cardkb = (Wire1.endTransmission() == 0); #endif +#if defined(PIN_HALL_SENSOR) + // Internal pull matches the default polarity: pulled up so an active-low + // sensor reads HIGH at rest, pulled down so an active-high one reads LOW at + // rest. Most reed/Hall breakouts are open-drain, active-low -- HALL_ACTIVE_HIGH + // is only for modules wired the other way. + pinMode(PIN_HALL_SENSOR, HALL_ACTIVE_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP); + _hall_magnet_present = HALL_ACTIVE_HIGH ? (digitalRead(PIN_HALL_SENSOR) == HIGH) + : (digitalRead(PIN_HALL_SENSOR) == LOW); + if (_hall_magnet_present) _locked = true; // booting with the cover already closed +#endif + #if defined(PIN_USER_BTN) user_btn.begin(); #endif @@ -2237,6 +2248,37 @@ void UITask::pollCardKB() { #endif } +// Level-triggered, like pollCardKB() -- a magnet held near the sensor reads the +// same way every tick, so this only acts on the two edges (closed/opened), not +// on every poll. Fully autonomous: closing locks and blanks the display +// immediately (no wake grace -- the cover is physically over the screen, so +// there's nothing to show), opening unlocks and wakes it, with no combo or +// keypress either way. Independent of Auto-lock (Settings > Display), which is +// a timeout-driven setting -- this is a direct physical event. +void UITask::pollHallSensor() { +#if defined(PIN_HALL_SENSOR) + bool present = HALL_ACTIVE_HIGH ? (digitalRead(PIN_HALL_SENSOR) == HIGH) + : (digitalRead(PIN_HALL_SENSOR) == LOW); + if (present == _hall_magnet_present) return; + _hall_magnet_present = present; + + if (present) { // cover closed + _locked = true; + _lock_wake_until = 0; + if (_display) _display->turnOff(); +#ifdef PIN_LED + digitalWrite(PIN_LED, LOW); // same as the auto-off path -- one less thing lit under a closed cover +#endif + } else { // cover opened + _locked = false; + if (_display && !_display->isOn()) _display->turnOn(); + uint32_t aoff = autoOffMillis(); + if (aoff > 0) _auto_off = millis() + aoff; + } + _next_refresh = 0; +#endif +} + void UITask::loop() { // Background delivery: resend pending on-device DMs whose ACK timed out, and // finalise the ✗ marker — runs regardless of which screen is active. @@ -2335,6 +2377,7 @@ void UITask::loop() { } #endif pollCardKB(); + pollHallSensor(); #ifdef ENV_USE_TCA8418 { extern char tca8418_keypad_read(); // provided by the active variant diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index 37c6ff44..a2ce2411 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -208,6 +208,20 @@ class UITask : public AbstractUITask { #endif void pollCardKB(); + // Optional magnetic "flip cover" lock: a Hall-effect or reed sensor wired to + // any free GPIO, closing (pulling active) when a magnet is near. Entirely + // user-supplied -- no board in this repo ships one, so PIN_HALL_SENSOR must + // be added as a build_flag by whoever wires the sensor up; no-op everywhere + // else. HALL_ACTIVE_HIGH overrides the default polarity for modules that + // pull the pin HIGH (instead of LOW) when the magnet is present. +#if defined(PIN_HALL_SENSOR) +#ifndef HALL_ACTIVE_HIGH + #define HALL_ACTIVE_HIGH 0 +#endif + bool _hall_magnet_present = false; +#endif + void pollHallSensor(); + void setCurrScreen(UIScreen* c); // Centred alert overlay (the showAlert() box). Wraps long text to up to diff --git a/release-notes.md b/release-notes.md index ec08da22..bc6ce158 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,6 +3,7 @@ ### What's new - **On-device community scope, and repeater-side scope filtering.** Settings › Radio gets a **Scope** field — type a region/community name (e.g. `pl`) and every device typing the same name derives the same shared tag, no key exchange needed; it's what your own DM/channel sends carry, previously only settable from a connected app. Tools › Repeater gains **Scope only** (only relay flood traffic matching your own scope, or one of the new **Extra scopes** below — a no-op until a scope is actually set, so it can't silently blackhole all forwarding) and **Extra scopes** (comma-separated additional regions to relay for, without changing what scope this device's own messages send under). +- **Optional magnetic "flip cover" screen lock**, for anyone who wants to wire a Hall-effect or reed sensor to a free GPIO — no board ships one built in. Set `PIN_HALL_SENSOR` as a build flag on your own env and closing the cover locks and blanks the screen instantly, opening it unlocks and wakes it — no combo, independent of Auto-lock. See [Screen Lock](docs/solo_features/screen_lock/screen_lock.md#magnetic-cover-hall-sensor). ### Fixes