26 KiB
OpenSeizureDetector Android App Structure
This document gives new contributors a fast, high-level map of how the Android application is organised: the major Java classes, resource folders, the startup / shutdown lifecycle, and the data + alarm flow.
1. Top-Level Overview
OpenSeizureDetector is an Android foreground-service based application that:
- Collects motion (acceleration) and physiological (heart rate, optionally SpO₂) data from a wearable (Garmin, Pebble, BLE devices, phone sensors, etc.).
- Analyses incoming samples to detect tonic–clonic seizure patterns.
- Raises local (audible) and remote (SMS / phone call) alarms and optionally shares anonymised data with a central server.
- Provides a swipe-based main UI (MainActivity2 + fragments) and a startup checklist screen (StartupActivity) to ensure prerequisites are satisfied before normal operation.
Core runtime logic lives in the SdServer foreground service; Activities and Fragments mostly visualize status and manipulate preferences.
2. Startup Lifecycle (Happy Path)
- User taps the launcher icon -> Android launches
StartupActivity(declared with MAIN/LAUNCHER intent filter inAndroidManifest.xml). StartupActivity:- Applies default preference values from XML (alarm, general, datasource, logging, etc.).
- Requests / validates required runtime permissions (notifications, SMS, location, Bluetooth, activity recognition, etc.).
- Starts (or restarts) the foreground service
SdServerviaOsdUtil.startServer()if not already running. - Binds to the service through
SdServiceConnectionto monitor status (watch connection, settings received, data flowing). - Displays a checklist (ProgressBars + TextViews) updated by a periodic timer until all conditions are OK.
- When all OK, transitions to either
MainActivity2(new UI) or legacyMainActivitydepending on theUseNewUipreference.
SdServer.onStartCommand():- Calls
updatePrefs(); selects concreteSdDataSource*implementation based onDataSourcepreference. - Instantiates and starts the chosen data source (e.g.,
SdDataSourceGarmin,SdDataSourceBLE,SdDataSourcePhone, etc.). - Initialises logging (
LogManager), location (LocationFinderif SMS alarms enabled), timers, embedded HTTP server (SdWebServer), wake lock, and notification channels; enters foreground (persistent notification). - Begins receiving data; populates
SdDataand runs analysis algorithms (e.g., seizure + heart rate) to update alarm state.
- Calls
MainActivity2binds to the already runningSdServerto present live status via Fragments.
3. Shutdown Lifecycle
There are several ways the service stops:
- User selects an "Exit" / stop option (menu action triggers
OsdUtil.stopServer()), which callsstopServiceforSdServer. - System kills the service (low memory or user force-stop) ->
SdServer.onDestroy()releases wake lock, stops data source, timers, web server, and cleans up. - Device reboot triggers
BootBroadcastReceiverwhich, ifAutoStartpreference is true, launchesStartupActivityto restart.
4. Major Java Components
Activities
StartupActivity: Launcher activity; initial permission + readiness checklist; starts/binds the service; routes to main UI.MainActivity2: Modern, swipe-based interface usingViewPager2+ Fragments; shows system/algorithm status, data sharing, web server info, heart rate, ML algorithm, battery, watch signal etc.MainActivity: Legacy UI retained for backward compatibility (optionally used ifUseNewUiis false).PrefActivity: Preferences editor (headers + fragments defined inres/xml/*prefs.xml).BLEScanActivity: Discovers and selects BLE devices when using BLE/BLE2 data source.AuthenticateActivity: Handles login for data sharing / remote API.LogManagerControlActivity,ExportDataActivity,RemoteDbActivity: Data sharing, viewing, exporting, pruning local DB.ReportSeizureActivity,EditEventActivity: Manual event reporting / editing.
Foreground Service
SdServer: Heart of the application. Manages:- Data source selection and life-cycle.
- Alarm evaluation (seizure, heart rate, fall, etc.).
- Notification updates (different channels for service status, events, data sharing issues).
- SMS & phone call alarm orchestration (timers to allow cancellation).
- Logging & data sharing upload scheduling.
- Embedded HTTP server (
SdWebServer) for local access. - Wake lock to keep CPU active during monitoring.
Data Sources (inherit from / similar pattern to SdDataSource)
Each encapsulates a communication protocol + parsing logic:
SdDataSourcePebble– Legacy Pebble watch integration.SdDataSourceAw– Android Wear devices.SdDataSourceGarmin– Garmin watch app (acceleration + heart rate streams).SdDataSourceBLE/SdDataSourceBLE2– Generic BLE device integrations (v1 / v2 protocols for devices like PineTime / BangleJS).SdDataSourceNetwork– Pulls detector data over network from a remote instance.SdDataSourcePhone– Uses phone onboard sensors as the detector.
Algorithms & Data Structures
SdData: Aggregates current sample values, processing results, and metadata (data source name, versions, etc.).SdAlgHr: Heart rate alarm algorithms (simple threshold, adaptive, average-based).SdAlgNn: Neural network / machine learning based seizure detection (seeFragmentMlAlgfor UI).CircBuf: Circular buffer used for windowed averaging and historical metrics.
Core Analysis Pipeline (SdDataSource.doAnalysis())
The principal seizure detection loop lives in the protected method SdDataSource.doAnalysis(), called each time a fresh window of accelerometer samples arrives (vector magnitude or derived from 3D data):
- Phone/watch battery percentages are updated and appended to rolling buffers.
- (Currently hard-coded) sample frequency set (e.g. 25 Hz) and frequency resolution derived from window length (
mNsamp). - FFT performed on the raw acceleration window using JTransforms
DoubleFFT_1D.realForward. - Overall spectrum "power" (
specPower) accumulated up to a cutoff frequency (FreqCutoff), zeroing higher bins to reduce noise. - Region of Interest (ROI) defined by preferences
AlarmFreqMin/AlarmFreqMax; average ROI power (roiPower) and the ratioroiRatio = 10 * roiPower / specPowercomputed. - Simplified spectrum (
simpleSpec[]) built in 1 Hz bins for UI visualisation (scaled byACCEL_SCALE_FACTORto align with historic Pebble scaling). - Populates fields in
mSdData(timestamps,specPower,roiPower, thresholds, ROI freq bounds, simplified spectrum array, flags) and markshaveData. - If the CNN alarm feature is enabled (
mCnnAlarmActive) thennnAnalysis()updatesmPseizureprobability. - Secondary narrow-band motion check via
flapCheck()(arm flapping detection) producing a boolean fed intoalarmCheck(). alarmCheck()applies thresholds (AlarmThresh,AlarmRatioThresh) and enabled algorithm flags (classic OSD, flap, CNN) to set alarm cause/state.- Additional modalities processed:
hrCheck()(heart rate alarms / frozen HR detection),o2SatCheck()(oxygen saturation),fallCheck()(fall detection), andmuteCheck()(user-induced mute logic). - Result dispatched upstream via
mSdDataReceiver.onSdDataReceived(mSdData)(SdServer consumes to raise notifications / alarms).
Key preferences influencing doAnalysis():
AlarmFreqMin/AlarmFreqMax: ROI frequency band.AlarmThresh/AlarmRatioThresh: Power and ratio thresholds for alarm state.- Flap detection thresholds (
FlapThresh,FlapRatioThresh, min/max flap band) when flap alarm active. - Flags enabling algorithms:
mOsdAlarmActive,mFlapAlarmActive,mCnnAlarmActive.
Performance / Extension Notes:
- Current implementation re-allocates FFT arrays each call; optimisation could reuse buffers.
- Sample frequency is hard-coded (25 Hz) inside analysis; aligning it with dynamic settings from watch would improve fidelity.
- Multiple ROIs could be generalised (current flapCheck duplicates spectral processing).
- Scaling (
ACCEL_SCALE_FACTOR) is applied post-hoc; future refactor could normalise early and adopt floating-point consistently for UI.
doAnalysis() Invocation & Alarm Propagation
doAnalysis() is not called in a tight loop by the service; instead each concrete SdDataSource decides when a complete window of samples is ready and then invokes it:
- BLE (
SdDataSourceBLE/SdDataSourceBLE2): Acceleration notifications fill a raw buffer (rawDatalength 125 = 5s @25Hz). When full, the datasource copies buffered samples intomSdData, setsmNsamp, callsdoAnalysis(), then sends a one–byte alarm state back to the device via a status GATT characteristic. - Phone (
SdDataSourcePhone): Collects accelerometer sensor events, performs crude downsampling from ~50Hz to 25Hz. OncerawDatais full it triggersdoAnalysis(), resets counters and continues. - Pebble (
SdDataSourcePebble): The watch app performs analysis on-device and sends already processed results (includingalarmState, spectrum) – sodoAnalysis()is NOT used for Pebble; received data directly callsmSdDataReceiver.onSdDataReceived. - Network (
SdDataSourceNetwork): Fetches remote JSON; if successful passes parsedSdDataupward. Remote faults setalarmStateto NET FAULT (7). LocaldoAnalysis()not used. - Garmin (
SdDataSourceGarmin): Similar pattern (buffer fill ->doAnalysis()).
After doAnalysis() completes in a source that uses it:
- Spectral metrics (
roiPower,specPower, simplified spectrum) and timing fields are populated. flapCheck()optionally computes a narrow-band flap detection boolean.alarmCheck(flapDetected)applies power & ratio thresholds and accumulates time in alarm (mAlarmCount += mSamplePeriod) to transition through:- OK (0) -> WARNING (1) after
mWarnTimeseconds of continuous in-alarm condition. - WARNING (1) -> ALARM (2) after
mAlarmTimeseconds. - Recovery logic: leaving in-alarm state downgrades from ALARM (2) to WARNING (1) (simulating a just-entered warning), or from WARNING (1) to OK (0).
- OK (0) -> WARNING (1) after
- Other modality checks may elevate alarmState:
hrCheck(): If any heart rate alarm stands (simple / adaptive / average) setsalarmState = 2and appends cause tags (HR,HR_ADAPT,HR_AVG). Null HR may either cause alarm or fault depending onmHRNullAsAlarm.o2SatCheck(): Low or null oxygen saturation (with null-as-alarm enabled) sets standing flags and may escalate to ALARM.fallCheck(): SetsfallAlarmStandingand may signal FALL alarm state (3).muteCheck(): Watch/user mute setsalarmState = 6(MUTE) overriding other transient states.- Fault timers (
faultCheck()elsewhere) may set FAULT (4) or NET FAULT (7).
- The datasource calls
mSdDataReceiver.onSdDataReceived(mSdData)(implemented bySdServer).
Alarm State Codes (as observed in code)
| Code | Meaning | Origin / Trigger |
|---|---|---|
| 0 | OK | No current alarm condition or post-recovery. |
| 1 | WARNING | Thresholds exceeded for > warnTime but < alarmTime. |
| 2 | ALARM | Thresholds exceeded for > alarmTime, or HR/O₂/fall promoted, or HR adaptive/average thresholds stand. |
| 3 | FALL | Fall detection logic sets fallAlarmStanding or explicit fall state. |
| 4 | FAULT | Internal fault (e.g., missing data, HR sensor failure without null-as-alarm). |
| 5 | MANUAL ALARM | Raised manually (e.g., SdServer.raiseManualAlarm()). |
| 6 | MUTE | User/watch initiated mute; prevents audible alarm but maintains monitoring. |
| 7 | NET FAULT | Network datasource error / fault condition (SdDataSourceNetwork). |
How SdServer Reacts (onSdDataReceived)
SdServer.onSdDataReceived(sdData) interprets alarmState plus standing flags and performs side-effects:
- OK (0): Clears
alarmStandingunless latched (mLatchAlarms) from previous alarm or fall. - MUTE (6): Sets phrase "MUTE", suppresses alarms and notifications severity.
- WARNING (1): Plays warning tone (
warningBeep()), logs (if enabled), updates notification to warning channel/state. - ALARM (2) or MANUAL ALARM (5): Sets phrase "ALARM", raises
alarmStanding, plays alarm tone (alarmBeep()), shows main UI, posts high-severity notification, initiates latch timer (startLatchTimer()), and sends SMS / phone alarms if enabled (rate-limited to one per minute). - FALL (3 or
fallAlarmStandingtrue): Behaves similarly to ALARM but with phrase "FALL" (alarms + SMS sending). Fall may remain standing until cleared. - HR / O₂ / Adaptive HR / Average HR: These set
alarmState = 2when standing;alarmCauseaccumulates tokens; downstream handling identical to ALARM. - FAULT (4, 7, HR fault, frozen HR fault): Plays fault warning beep (
faultWarningBeep()), shows fault notification; may attempt datasource restart after timer (auto-restart currently disabled for BLE2 to prevent duplicate notifications).
Latching & Reset
With mLatchAlarms enabled, returning to OK does not immediately clear previous ALARM/FALL states; user must manually accept/reset (e.g., via UI actions) or wait for latch timer expiry (mLatchAlarmTimer). Without latching, state machine freely transitions downwards.
Data Sharing & Logging Post-Analysis
Upon each received dataset, SdServer updates internal mSdData, pushes it to SdWebServer for external viewing, and passes it to LogManager (mLm.updateSdData(mSdData)), which may create/append local events (especially on transitions into ALARM states) and schedule remote uploads.
Device Feedback
BLE/BLE2 write a single-byte alarm state back to the watch/device after analysis (executeWriteCharacteristic(mStatusChar, statusVal) or peripheral write) enabling haptic / on-watch UI feedback.
Pebble handles its own alarm transitions internally before sending results.
This separation lets wearable implementations stay lightweight (simple streaming) while centralizing threshold timing, multi-modal fusion, and alarm escalation logic on the phone (except for Pebble legacy analysis).
UI Fragments (used in MainActivity2 ViewPager)
FragmentCommon: Overall status & key indicators.FragmentOsdAlg: Seizure algorithm metrics (spectrum ratio, thresholds, raw/processed values).FragmentHrAlg: Heart rate algorithm status & thresholds.FragmentMlAlg: ML model results / confidence scores.FragmentBatt: Watch + phone battery status.FragmentSystem: System info (permissions, service state, logging flags).FragmentWatchSig: Signal quality / connectivity indicators.FragmentWebServer: Local web server URL / status.FragmentDataSharing: Data sharing setup state, counts of local vs remote events.
Utilities & Helpers
OsdUtil: Starts/stops/binds the service; permission checks; logging helpers; system/environment utilities.LogManager: Handles local + remote logging, event packaging, pruning, and upload scheduling.MlModelManager: Manages loading / inference of ML models (if in use).LocationFinder: Acquires GPS coordinates for SMS alarms.WebApiConnection/WebApiConnection_firebase/WebApiConnection_osdapi: Remote data sharing / API integrations.SdServiceConnection: Wraps service binding / connection callbacks, exposes convenience methods (watchConnected(),hasSdData(),hasSdSettings()).BootBroadcastReceiver: Auto-start on device boot when preference enabled.GattAttributes: BLE UUID constants and attribute names.OsdUncaughtExceptionHandler: Crash reporting path (uses UCE Handler library).SdWebServer: Lightweight embedded HTTP server (for local status / data access).
Data Sharing Module
Under uk/org/openseizuredetector/data/...:
- Repository pattern for authentication:
LoginRepository,LoginDataSource,LoggedInUser,Result(standard wrapper around success/error).
5. Resource Folder Structure
res/
layout/ Activity & Fragment UI XML (e.g., startup_activity, activity_main2, fragment_*).
menu/ Action bar & overflow menus (e.g., main_activity_actions.xml).
values/ Strings (`strings.xml`), styles, colors, dimensions; base resources.
values-XX/ Localized strings (de, es, pl, ru, sl, sv, etc.).
drawable/ Icons and graphics (e.g., star_of_life_48x48). Might also include vector assets.
xml/ Preference definition files and network security config:
- alarm_prefs.xml
- basic_prefs.xml
- general_prefs.xml
- logging_prefs.xml
- pebble_datasource_prefs.xml
- network_datasource_prefs.xml
- network_passive_datasource_prefs.xml
- seizure_detector_prefs.xml
- preference_headers.xml (groups preferences)
- network_security_config.xml
Other notable folders:
assets/(if present) – Additional static assets (not heavily used here).libs/– Third-party JARs (e.g., FFT / chart libraries) bundled with the app.
6. Preferences Flow
- XML files under
res/xmldefine keys and defaults. StartupActivity.onCreate()callsPreferenceManager.setDefaultValues(...)for each preference file (once per install/version).- Classes such as
OsdUtil,SdServer,SdAlgHrinvokeupdatePrefs()to readSharedPreferences(PreferenceManager.getDefaultSharedPreferences(context)), caching operational parameters (thresholds, window lengths, flags). - Preference changes may trigger service restarts or algorithm behavior changes (e.g., enabling SMS alarms requires location permission and
LocationFinder).
7. Data & Alarm Flow
Wearable / Phone Sensors --> Concrete SdDataSource --> SdServer (receives callbacks) -->
Algorithms (SdAlgNn, SdAlgHr, fall detection, etc.) --> Alarm State Transitions -->
Audible ToneGenerator / MP3 playback
Foreground Notification Updates
Timed SMS / Phone Call Alerts (with cancellation window)
Data Logging (local DB) / Remote Sharing (LogManager, WebApiConnection*)
Web Server exposure (SdWebServer)
Heart rate buffering uses CircBuf windows for simple/adaptive thresholding; seizure analysis (frequency spectrum, ratio thresholds) executed inside data source analysis routines (see respective SdDataSource* classes).
8. Foreground Service & Resilience
- Service runs in foreground with a persistent notification (required for stable long-running monitoring on modern Android).
- Wake lock prevents CPU sleep during monitoring sessions (battery intensive but improves reliability).
- Timers manage periodic tasks (event validation checks, remote upload scheduling, alarm muting windows).
- Boot auto-start via broadcast receiver ensures continuity if user opted in.
9. Adding a New Data Source (High-Level Guide)
- Create a new
SdDataSource<YourDevice>class implementing the expected interface / callback pattern (see existing sources for template). - Handle connection, authentication/handshake, data parsing, and call back into
SdServerwith new samples. - Add a selection case in
SdServer.onStartCommand()for yourDataSourcepreference string. - Provide any additional preferences XML (e.g., update period, device address) and add them to default initialization in
StartupActivity. - Update UI fragments if device supplies new metrics.
10. Where to Look for Key Logic
- Service lifecycle & alarm orchestration:
SdServer.java(onStartCommand,onDestroy, timers, notifications). - Startup readiness checklist:
StartupActivity.serverStatusRunnable. - Data source selection:
SdServer.onStartCommand()switch overmSdDataSourceName. - Heart rate algorithms:
SdAlgHr.java. - ML / seizure algorithm UI:
FragmentMlAlg.java+SdAlgNn.java. - Permission checks:
StartupActivity&OsdUtil(Bluetooth, activity recognition, SMS, location). - Logging & data sharing:
LogManager.java,RemoteDbActivity.java,FragmentDataSharing.java. - Embedded web server logic:
SdWebServer.java.
11. Key Preference Examples (Selected)
| Preference Key | Purpose |
|---|---|
DataSource |
Selects which device source (Pebble, Garmin, BLE, Phone, Network). |
AlarmThresh / AlarmRatioThresh |
Seizure detection thresholds (spectrum amplitude / ratio). |
HRThreshMin / HRThreshMax |
Simple heart rate alarm bounds. |
HRAdaptiveAlarmWindowSecs |
Window size for adaptive HR average buffering. |
SMSAlarm / PhoneCallAlarm |
Enable remote alerts. |
LogData / LogDataRemote |
Enable local logging vs remote data sharing. |
UseNewUi |
Switch between legacy and modern main UI. |
AutoStart |
Auto-launch on device boot. |
(See res/xml/*_prefs.xml for full list.)
12. Notifications & Timers
- Multiple channels for service status and events (IDs inside
SdServer). - Timers:
FaultTimer,CheckEventsTimer, SMS countdown (SmsTimer), latch alarm timer, etc., each controlling asynchronous transitions.
13. Data Sharing Flow
- User authenticates (
AuthenticateActivity) -> obtains token stored in preferences. LogManagerpackages events (timestamped, with retention pruning) and attempts periodic uploads (remoteLogPeriod).- Unvalidated remote events prompt UI reminders (
FragmentDataSharing).
14. Crash Handling
UCEHandler integrated in Activities and Service to capture uncaught exceptions and offer sending logs via email.
15. Embedded Web Server
SdWebServer exposes (read-only) status / logged data for local network access; started automatically by SdServer after data source initialisation.
16. Stopping / Restarting Service Programmatically
- Stop:
OsdUtil.stopServer()-> callsstopService(Intent(SdServer)). - Start:
OsdUtil.startServer()->Context.startForegroundService(...)(on modern Android) then service builds notification & begins monitoring. - Restart triggered implicitly if critical permissions change (logic can call stop/start to reinitialise components).
17. Extending Alarms / Algorithms
To add new alarm logic (e.g., oxygen saturation):
- Introduce algorithm class (
SdAlgO2style) storing buffers and thresholds. - Update data source parsing to capture new metric.
- Integrate into
SdServerevaluation loop; amend notification text generation. - Provide preference keys + XML + UI Fragment display.
18. Glossary
- "Latch Alarm": Alarm remains active until explicitly reset (even if underlying condition clears) for a configured duration.
- "Adaptive HR Alarm": Builds a moving average; raises alarm when HR deviates beyond +/- configurable delta.
- "Foreground Service": Long-lived component with persistent notification; less likely to be killed.
- "Data Sharing": User-consented upload of anonymised seizure events / sensor data to central server for algorithm improvement.
19. Useful Entry Points for Debugging
- Set breakpoints in
SdServer.onStartCommand()to inspect data source initialisation. - Use logs emitted via
OsdUtil.writeToSysLogFileto trace state transitions. - Inspect
StartupActivity.serverStatusRunnablefor readiness gating issues.
20. Related Documents
README.md: General project overview, build instructions.DEV_NOTES.txt: Developer notes / historical comments.BLE_Datasource_Specification.md: Protocol specifics for BLE devices.- PDFs under
doc/for algorithm assessment and app structure diagrams.
21. End-to-End Data -> Alarm Flow Diagram
Below are two complementary diagrams (ASCII and Mermaid) showing how a data window travels from the wearable to an alarm being raised.
PNG Version: See FLOW_DIAGRAM.png in the repository root for a downloadable image.
ASCII Flow
Wearable Sensors (Accel / HR / O₂)
|
v
Watch Firmware / Device App
| (Pebble: does analysis + sends results)
| (BLE/Garmin/PineTime/etc.: streams raw samples)
v
SdDataSource (Buffer + Parse + Downsample/Scale)
| (Collect ~125 samples = 5s @25Hz)
v (window full)
doAnalysis()
|-> FFT (JTransforms) & Spectrum Power (specPower)
|-> ROI Power & Ratio (roiPower / roiRatio)
|-> Simplified Spectrum (simpleSpec[])
|-> flapCheck() narrow band detection
|-> nnAnalysis() (if CNN enabled)
|-> hrCheck(), o2SatCheck(), fallCheck(), muteCheck()
v
Populate mSdData (specPower, roiPower, simpleSpec, alarmState, alarmCause, metrics)
v
mSdDataReceiver.onSdDataReceived(mSdData)
v
SdServer.onSdDataReceived()
|-> Alarm state machine (OK/WARNING/ALARM/FALL/FAULT/MUTE)
|-> Latching logic (startLatchTimer if ALARM)
|-> Notifications (foreground + event channels)
|-> Tones (warningBeep / alarmBeep / faultWarningBeep)
|-> SMS / Phone Call (rate-limited, if enabled)
|-> Logging & Data Sharing (LogManager update, remote upload scheduling)
|-> Web Server update (SdWebServer.setSdData)
|-> Write alarmState byte back to device (BLE/Garmin)
v
UI Fragments / Web Server / Remote API Consumers
Mermaid Diagram (optional rendering if supported):
flowchart TD
W[Wearable Sensors\n(Accel / HR / O₂)] --> WF[Watch Firmware / Device App]
WF --> DS{SdDataSource\nBuffer & Parse}
DS -->|Window Full| AN[doAnalysis()]
AN --> FFT[FFT + Spectrum]
FFT --> ROI[ROI Power & Ratio]
ROI --> ALG[flapCheck / nnAnalysis / hrCheck / o2SatCheck / fallCheck / muteCheck]
ALG --> SD[SdData Populated\n(alarmState, metrics)]
SD --> RCV[mSdDataReceiver.onSdDataReceived]
RCV --> SRV[SdServer.onSdDataReceived]
SRV --> ACT[Alarm Actions\nNotification / Tone / SMS / Phone / Latch / Log]
SRV --> FEED[Write alarmState\nback to Device]
ACT --> UI[UI Fragments / Web Server / Data Sharing]
WF -. Pebble path (analysis on watch) .-> SD
Notes:
- Pebble path bypasses local
doAnalysis(); analysis executes on the watch and setsalarmStatebefore dispatch. - Network datasource substitutes "Buffer & Parse" with remote JSON fetch and may directly set NET FAULT (7) on failure.
- Latching prevents immediate clearing of ALARM/FALL states until user intervention or timer expiry.
22. Related Documents
README.md: General project overview, build instructions.DEV_NOTES.txt: Developer notes / historical comments.BLE_Datasource_Specification.md: Protocol specifics for BLE devices.- PDFs under
doc/for algorithm assessment and app structure diagrams.
Questions / Improvements: Feel free to open issues or pull requests on GitHub.