Skip to content

Latest commit

 

History

226 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

T1DMDROID

A personal Android app for Type 1 Diabetes that passively reads a Microtech/Ottai AiDEX X / LinX continuous glucose monitor over Bluetooth-LE advertisements and runs on-device glucose forecasting. It is advisory-only — it never actuates insulin delivery — and is built for a single arm64 device rather than for general distribution: it is sideloaded, not published to any app store.

Designed by a T1DM patient, informed by lived experience.

Caution

Personal project, research use only. T1DMDROID solves one patient's niche problem — one sensor, one phone — and is published for reference, not for anyone else to install or depend on. It is not a medical device, not clinically validated, and unsupported. Its forecasts and calculators may be wrong and must not be used for medical, diagnostic, or dosing decisions, nor to replace a real CGM, its official app, or professional care. Provided "as is", without warranty; the authors accept no liability.

Table of contents

What it is

The AiDEX X broadcasts its current reading roughly once per minute and the app listens passively — no pairing, no bond, no GATT connection. Activation, calibration and warmup stay with the sensor's official app on a separate phone. Each reading is stamped with the phone's receive time snapped to a 5-minute grid.

A small transformer runs over that feed entirely on the device. It predicts any withheld stretch of glucose rather than only the next two hours, so one artifact fills a gap the sensor left as well as it forecasts, and a low-rank adapter can personalise it from the wearer's own matured forecasts while the exported weights stay frozen. Around it sit meal and insulin logs, advisory statistics, and a deterministic, model-free alarm path for out-of-range and loss-of-signal. Optional integrations add a self-hosted sync server and an encrypted BLE watch accessory.

The Bluetooth, inference, and watch protocols are documented under docs/: CGM.md, INFERENCE.md, WATCH_BLE.md, and T1DMSERVER_API.md.

Features

tall_blood_sugar_graph_that_shows_iob_and_cob.mp4

Blood glucose graph
read_from_multiple_sensors_at_once.mp4

Multiple sensors
paint_on_the_blood_sugar_graph.mp4

Paint
drive_on_your_blood_sugar_curve.mp4

Drive
autoregressive_rolling_for_long_horizon_predictions.mp4

Autoregressive rolling
see_previous_predictions_with_hindsight_mode.mp4

Hindsight
test_infilling_capabilities_of_prediction_models.mp4

Infilling
visualize_model_time_output.mp4

Circadian clock
advanced_model_evaluation.mp4

Model evaluation
lora_finetuning.mp4

LoRA adapters
log_carbohydrates.mp4

Log carbohydrates
log_insulin.mp4

Log insulin
advanced_blood_sugar_statistics.mp4

Statistics
advanced_settings_panel.mp4

Settings

Exercise

Bouts run from a manual start to a manual stop, following GPS during walks and runs and drawing the route on OpenStreetMap tiles. Distance and pace come from accepted fixes; energy expenditure from the ACSM walking and running equations at zero grade, shown only once a body mass has been entered. Each bout's per-five-minute magnitude folds into the same wide sensor series that carries glucose, heart rate, steps, sleep and mood.

Every bout opens its own review: the route, the glucose trace from 30 minutes before to 2 hours after, and a slider sweeping the forecast issued at any instant in that window across the trace that followed it. Where no forecast was issued, the slider shows nothing rather than the nearest one. Tracks are never uploaded; map tiles are fetched for the surrounding area and cached on the device.

Backup and Restore

One gzipped, line-delimited JSON file holds the whole local record — every glucose reading, the wide sensor series, meals, doses, basal schedules, custom foods, saved meals, insulin types, exercise bouts and their tracks, the graph's freehand drawings, and every setting. Automatic backups run on a chosen cadence into a folder outside app storage, so they survive an uninstall, with a configurable number of older archives retained.

Restore merges: a record already present is kept, so importing the same file twice changes nothing and an older archive can never roll back newer data. The server token is never written to a backup — it lives in the Android Keystore rather than in the database.

Architecture

  • UI: Jetpack Compose, organized as a multi-module Gradle build so the CGM-source and model-backend seams stay pluggable.
  • Rust core (t1dm-core, via JNI/NDK): the correctness-critical, hot numerics — AiDEX frame decode and its CRCs, session crypto, the model pre/post pipeline (causal Savitzky-Golay smoothing, normalize/denormalize, the Kovatchev risk transform, quantile assembly), and the watch AES-128-GCM. Kotlin keeps the UI, BLE plumbing, storage, and orchestration. The core is tested bit-for-bit against golden vectors in CI.
  • On-device inference: ExecuTorch. One exported model runs on two backends behind a clean seam — CPU (XNNPACK, fp32) as the reference authority, and the GPU (Vulkan compute delegate, fp16) as a measured shadow whose agreement with the CPU path is measured before it may inform anything. The Vulkan delegate comes from a custom ExecuTorch build vendored under third_party/; the stock runtime registers XNNPACK only.
  • Storage & orchestration: Room on the bundled SQLite driver; an always-on foreground service plus WorkManager run the passive scan, the 5-minute grid, inference, sync, and the alarm path off the main thread.

Module map

Module Responsibility
:app Composition root, the always-on foreground service, notifications, widgets, navigation
:cgm Passive AiDEX X advertisement scan, recognition, and the CGM-source registry
:inference The forecasting cycle: context build, backend dispatch, decode, degeneracy gating
:sensors Step counter, GPS track recording, and other phone sensors
:calc Advisory bolus/basal and statistics calculators
:alerts The deterministic, model-free alarm engine (out-of-range, loss-of-signal, device temperature)
:sync Durable-outbox sync with the self-hosted server
:watch Encrypted BLE link to the optional ESP32-C3 watch
:data Room database, repositories, curve reconstruction, the backup archive codec
:core:common, :core:model, :core:design, :core:native Shared dispatchers, domain types, theming, and the Rust-core JNI bindings
:ui:graph The custom Compose blood-glucose graph
:feature:* Screen features — dashboard, stats, models, hardware, network, meals, insulin, security, settings, logs, backup

Building

  • Android SDK 36 and the NDK, JDK 21.
  • A Rust toolchain with the aarch64-linux-android target and cargo-ndk installed and on PATH — the native build silently skips if cargo-ndk is missing.

The app targets arm64-v8a only, minSdk 34, targetSdk 36.

./gradlew :app:assemblePersonalRelease

Two product flavors: personal (the daily build) and public (installs under a .pub application id). Release builds are R8-minified and resource-shrunk; without a keystore.properties they fall back to the debug signing key, so a fresh checkout still produces an installable APK.

Running on Xiaomi HyperOS / MIUI

HyperOS manages background apps far more aggressively than stock Android, and an always-on passive CGM reader is exactly the kind of app it curtails. The setup below is required for reliable operation, and a system update or reboot can silently reset parts of it.

Battery and autostart

In Settings → Apps, for T1DMDROID:

  • Autostart on — this also lets it start on boot.
  • Battery mode No restrictions; the default "Battery saver" level throttles background work.
  • Pause app activity if unused off.
  • The standard Android battery-optimization exemption granted as well.

Also set the system Bluetooth app to Unrestricted (Settings → Apps → show system apps → Bluetooth → battery usage) — easy to miss, and the scan depends on it. Lock the app in Recents (drag its card down until it shows a padlock) so "clear all" and the memory cleaner cannot evict it. Performance power mode helps as well.

Background collection while the screen is off

On Android 14+, and especially on HyperOS, the system suspends a background app's Bluetooth-LE scan when the screen turns off. To keep collecting, the app uses offloaded batch scanning: the Bluetooth controller buffers the sensor's advertisements in hardware regardless of screen state, and HyperOS flushes those batches on roughly a five-minute timer.

So while the phone is locked, new readings — and therefore any alarms — can lag by up to about five minutes. This is an OS-imposed floor for a passive-advertisement sensor, and does not apply while the screen is on. Each batched reading is timestamped at its true capture instant, so no 5-minute grid slot is lost.

Glucose on the lock screen

The app posts a persistent, silent notification with the current glucose value and trend. HyperOS hides silent notifications from the lock screen by default and removes the corresponding control from Settings, so the notification appears in the shade but not on the lock screen until that control is re-enabled once:

adb shell settings put secure lock_screen_show_silent_notifications 1

An "Activity Launcher"-type app reaches the same control: com.android.settings.Settings$ConfigureNotificationSettingsActivityNotifications on lock screenShow conversations, default and silent.

The setting is device-wide, persists across reboots, and is cleared by a factory reset — no app can set it on the user's behalf.

Target device

The build targets a single phone: a Redmi K90 Max (MediaTek Dimensity 9500 / MT6993) running Android 16 / HyperOS, arm64-v8a. Accelerated inference runs on the GPU through Vulkan; the SoC's APU is not used, since the MediaTek NeuroPilot runtime ships through Play feature delivery and a sideloaded build cannot fetch it. Other devices are untested and unsupported.

Related projects

  • T1DMSIM — the behavioral simulator whose synthetic traces pretrain the model this app runs.
  • T1DMAI — the training and ExecuTorch export pipeline that produces the artifact and descriptor loaded here.
  • T1DMSERVER — the optional sync backend this app pushes to, phone-authoritative (protocol in docs/T1DMSERVER_API.md).

License

MIT — see LICENSE.

Contributors

Languages