Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions products/go/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,8 @@ Both states: Button 1 long press → Shutdown. Releasing after the long
press powers off; keeping the button held re-wakes the BQ25629 through
`/QON` and cold-boots the device — see [Shutdown](#shutdown).

Inactivity timeout while unlocked auto-lock. The timeout value is
configurable via settings (minimum 5 seconds).
Inactivity while unlocked triggers auto-lock after the selected Auto Lock
timeout (10, 30, or 60 seconds). Setting Auto Lock to 0 disables it.

While locked, the display periodically updates sensor values and status on
the dashboard. The refresh interval is configurable and can be disabled
Expand Down Expand Up @@ -814,7 +814,7 @@ Settings fields:
- GPS mode (AlwaysOff / OnWhenTracking / AlwaysOn)
- Operating mode (Portable / Stationary / Offline; default: Portable)
- Auto-lock timeout (0 = disabled, 10 s / 30 s / 60 s)
- Inactivity timeout, GPS interval, device name
- GPS interval

### Display Service (E-Paper)

Expand Down
12 changes: 5 additions & 7 deletions products/go/docs/ble_service.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ All characteristic payloads use CBOR (RFC 8949) encoded with TinyCBOR's
|---|---|---|---|
| Measures | ~120B | ~135B | Yes |
| Status | ~95B | ~115B | Yes |
| Config (read, 18 keys) | — | <512B | Yes (Read-Long) |
| Config (read, 16 keys) | — | <512B | Yes (Read-Long) |
| Config (notify, one field + type) | — | <180B | Yes |
| History control (CBOR) | ~40B | ~180B | Yes |
| History data (binary, 4 pts) | 223B | 223B | Yes |
Expand Down Expand Up @@ -376,20 +376,18 @@ config**, **set config values**, and **execute commands**.

### Read (phone reads characteristic)

Returns the full device configuration as an 18-key CBOR map. The BLE service
Returns the full device configuration as a 16-key CBOR map. The BLE service
keeps this value updated whenever the orchestrator calls `update_config()`.

#### CBOR Payload (Map) — 18 Keys
#### CBOR Payload (Map) — 16 Keys

| Key | CBOR Type | `GoSettings` field | Encoded with |
|---|---|---|---|
| `"meas_int"` | uint | `measure_interval_seconds` | `cbor_encode_uint` (1–3600 seconds) |
| `"temp_f"` | bool | `use_fahrenheit` | `cbor_encode_boolean` |
| `"pm_aqi"` | bool | `pm_use_usaqi` | `cbor_encode_boolean` |
| `"gps_mode"` | text | `gps_mode` | See mapping below |
| `"inact_to"` | uint | `inactivity_timeout_seconds` | `cbor_encode_uint` |
| `"auto_lock"` | uint | `auto_lock_seconds` | `cbor_encode_uint` |
| `"dev_name"` | text | `device_name` | `cbor_encode_text_stringz` |
| `"op_mode"` | text | `operating_mode` | See mapping below |
| `"fled"` | uint | `front_led_brightness` | `cbor_encode_uint` (0–3) |
| `"bled"` | uint | `back_led_brightness` | `cbor_encode_uint` (0–3) |
Expand Down Expand Up @@ -895,7 +893,7 @@ failed `setup_ble()` is non-fatal (advertise without OTA). See
| `notify_tracking_status(power, gps, tracking, session_id)` | Refreshes the full 9-key snapshot via `update_status()` (Read stays full), then pushes a `{tracking, session}` transition delta via `notify(data, len)`. Used for urgent tracking transitions (start success, start failure, manual stop). Best-effort delivery — Read remains authoritative. |
| `notify_charging_status(power, gps, tracking, session_id)` | Refreshes the full 9-key snapshot via `update_status()` (Read stays full), then pushes a `{charging, bat_pct, bat_v}` power delta via `notify(data, len)`. Used for charging transitions (plug in, unplug, charge complete). Disjoint keys from the tracking delta, no `"type"` discriminator — client merges by key. |
| `notify_disconnect(reason)` | Pushes a NOTIFY-only `{disc}` delta via `notify(data, len)` (snapshot untouched) announcing an imminent link drop and why (`overheat`/`low_batt`/`user`/`op_stationary`/`op_offline`). Called from `change_mode()` (leaving Portable) and `shutdown()`; gated on `is_connected()`; the caller settles before teardown so it can drain. |
| `update_config(settings)` | Encode the full snapshot via `encode_config()` (18 keys, no `"type"`), `set_value()` only. Sole writer of the Config snapshot; buffer sized to the 512-byte ATT ceiling. |
| `update_config(settings)` | Encode the full snapshot via `encode_config()` (16 keys, no `"type"`), `set_value()` only. Sole writer of the Config snapshot; buffer sized to the 512-byte ATT ceiling. |
| `notify_config(prev, cur)` | Refreshes the snapshot via `update_config(cur)`, then sends the changed-fields delta (`encode_config_delta()`: `"type":"config"` + changed keys) via `notify(data, len)`. |
| `notify_command_progress(cmd)` | Inline CBOR encoding (2 keys: type + cmd), `notify(data, len)` (stored value untouched). Sent before long-running commands. |
| `notify_command_result(cmd, success, error)` | Inline CBOR encoding (3-4 keys), `notify(data, len)` (stored value untouched). |
Expand Down Expand Up @@ -1241,7 +1239,7 @@ cover:

- **CBOR encoding**: `encode_measures()` (field omission, GPS inclusion),
`encode_status()` (all 9 keys, battery clamping) and `encode_status_transition()`
(2-key delta), `encode_config()` (full 18-key snapshot, no `"type"`) and
(2-key delta), `encode_config()` (full 16-key snapshot, no `"type"`) and
`encode_config_delta()` (`"type":"config"` + changed keys only),
`notify_config(prev, cur)` (delta via `notify(data, len)`, Read stays full,
snapshot refreshed first), `notify_command_result()` /
Expand Down
7 changes: 0 additions & 7 deletions products/go/docs/orchestrator.md
Original file line number Diff line number Diff line change
Expand Up @@ -1046,13 +1046,6 @@ through `UIManager::handle_input()` returning `UIActionResult`. The
triggers — for example, BLE `start_tracking` / `stop_tracking` commands
dispatch through the same `start_tracking()` / `stop_tracking()` methods.

### auto_lock_seconds vs inactivity_timeout_seconds

The inactivity timer uses `GoSettings::auto_lock_seconds` because this is
the field controlled by the UI "Auto Lock" setting and persisted correctly
through `save_go_settings()`. The `inactivity_timeout_seconds` field exists
in `GoSettings` but is not connected to any UI control.

### Invalid Sentinel Initialization

`_raw_measures` and `_corrected_measures` are initialized to invalid sentinel
Expand Down
4 changes: 0 additions & 4 deletions products/go/docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ See [`go_settings.h`](../main/go_settings.h) for full signatures.
| `pm_use_usaqi` | `"pmu"` | `bool` | `false` | — | PM display format (false=µg/m³, true=USAQI) |
| `gps_mode` | `"gpm"` | `int` (stored) / `GpsMode` (in struct) | `OnWhenTracking` (1) | 0 .. 2 | GPS operating mode: AlwaysOff / OnWhenTracking / AlwaysOn |
| `operating_mode` | `"opm"` | `int` (stored) / `OperatingMode` (in struct) | `Portable` (0) | 0 .. 2 | Serialized as int; cast to `OperatingMode` on load |
| `inactivity_timeout_seconds` | `"ito"` | `int` | `5` | 5 .. 600 | Persisted and exposed over BLE; not currently used by the runtime auto-lock path |
| `auto_lock_seconds` | `"als"` | `int` | `10` | 0, 10, 30, 60 | Runtime auto-lock timeout; `0` = disabled |
| `device_name` | `"dn"` | `std::string` | `"airgradient-go"` | 1 .. 64 chars | Advertised name for BLE/WiFi |
| `disable_cloud` | `"dc"` | `bool` | `false` | — | Outbound cloud transport kill switch. Suppresses POST, FETCH, and Stationary OTA checks; does not disable the local API. |
| `configuration_control` | `"cc"` | `int` (stored) / `ConfigurationControl` (in struct) | `Both` (2) | 0 .. 2 | Remote configuration authority: `Cloud`, `Local`, or `Both`. Does not control measurement POST. |
| `co2_abc_days` | `"cad"` | `int` | `7` | -1 or 1 .. 200 | Automatic background calibration period. `-1` disables it; positive values are applied as 24 hours per day to supported S12 and SCD41 CO2 sensors. |
Expand Down Expand Up @@ -125,13 +123,11 @@ All validation is implemented in an anonymous namespace in `go_settings.cpp`
| Field | Rule |
|---|---|
| `measure_interval_seconds` | `>= 1 && <= 3600` |
| `inactivity_timeout_seconds` | `>= 5 && <= 600` |
| `use_fahrenheit` | No range check (bool) |
| `pm_use_usaqi` | No range check (bool) |
| `gps_mode` | Underlying int in `0 .. 2` (matches `GpsMode` enum values) |
| `operating_mode` | Underlying int in `0 .. 2` (matches `OperatingMode` enum values) |
| `auto_lock_seconds` | `0`, `10`, `30`, or `60` |
| `device_name` | Non-empty and `<= 64` characters |
| `disable_cloud` | No range check (bool) |
| `configuration_control` | Underlying int in `0 .. 2`; `Cloud` is invalid when `disable_cloud == true` |
| `co2_abc_days` | `-1`, or `>= 1 && <= 200` |
Expand Down
1 change: 0 additions & 1 deletion products/go/feature_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ Common configurable settings include:
- AQI LED brightness.
- Touch LED brightness.
- Buzzer enabled or disabled.
- Device name.
- Static IP and cloud preference for Stationary mode.

Action-style settings are also available from the device or app, including CO2
Expand Down
18 changes: 6 additions & 12 deletions products/go/go_ble_client.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,15 @@ Read-Long / Read Blob operations.

Notifications are single ATT PDUs and are never fragmented by the application.
The Config snapshot is therefore not a notification payload: it is a Read-Long
value and is typically about 239 bytes with correction schema version 1. Clients
value and is typically about 205 bytes with correction schema version 1. Clients
must support Read-Long and must not assume that one Read Response contains the
full snapshot.

| Characteristic | Typical Size | Max/Limit | Transport |
|---|---:|---:|---|
| Measures | ~120 B | ~135 B | One notification when MTU is at least 138 |
| Status Read | ~95 B | ~115 B | Read; notifications carry small deltas |
| Config Read | ~239 B | <512 B | Read-Long / Read Blob |
| Config Read | ~205 B | <512 B | Read-Long / Read Blob |
| Config Notify | — | <180 B | One notification when MTU is at least 185 |
| History control | ~40 B | ~180 B | One notification per response |
| History data | 227 B | 227 B | One notification when MTU is at least 230 |
Expand Down Expand Up @@ -510,17 +510,15 @@ This characteristic supports three operations:

Read the characteristic to receive the full device configuration.

#### Payload (18-key CBOR map)
#### Payload (16-key CBOR map)

| Key | Type | Description |
|---|---|---|
| `"meas_int"` | uint | Measurement interval in seconds (1–3600). All sensors measured together at this cadence. |
| `"temp_f"` | bool | `true` = Fahrenheit, `false` = Celsius |
| `"pm_aqi"` | bool | `true` = US AQI for PM, `false` = raw ug/m3 |
| `"gps_mode"` | text | GPS mode (see table below) |
| `"inact_to"` | uint | Inactivity timeout (seconds) |
| `"auto_lock"` | uint | Auto-lock timeout (seconds) |
| `"dev_name"` | text | User-defined device name |
| `"op_mode"` | text | Operating mode (see table below) |
| `"fled"` | uint | Front (display) LED brightness: 0=Off, 1=Dim, 2=Mid, 3=Bright |
| `"bled"` | uint | Back (AQI) LED brightness: 0=Off, 1=Dim, 2=Mid, 3=Bright |
Expand Down Expand Up @@ -568,9 +566,7 @@ them and persisted loading canonicalizes them.
"temp_f": false,
"pm_aqi": false,
"gps_mode": "tracking",
"inact_to": 300,
"auto_lock": 60,
"dev_name": "My AGo",
"op_mode": "portable",
"fled": 3,
"bled": 3,
Expand Down Expand Up @@ -616,9 +612,7 @@ silently ignored for backward compatibility. They do not modify any setting.
| `"temp_f"` | bool | |
| `"pm_aqi"` | bool | |
| `"gps_mode"` | text | `"off"`, `"tracking"`, or `"always"` |
| `"inact_to"` | uint | |
| `"auto_lock"` | uint | |
| `"dev_name"` | text | Max 64 characters |
| `"op_mode"` | text | `"portable"`, `"stationary"`, or `"offline"` |
| `"fled"` | uint | 0–3 (front LED brightness) |
| `"bled"` | uint | 0–3 (back LED brightness) |
Expand Down Expand Up @@ -809,7 +803,7 @@ which normally changes one setting at a time) yields a 2-key map:
Merge the changed key(s) into your local model. Production sends no Config
notification for a no-op write. The full config is always available via **Read /
Read-Long** (no `"type"` key) — re-read it on connect to establish the baseline.
The snapshot is typically about 239 bytes with schema version 1, so clients must
The snapshot is typically about 205 bytes with schema version 1, so clients must
collect Read-Long fragments when the negotiated MTU cannot carry the complete
value. The `"type"` key distinguishes this from command notifications (all
arrive on the same characteristic; Read always returns the config snapshot
Expand Down Expand Up @@ -1611,8 +1605,8 @@ negotiated interval; only its speed is affected.

### Required MTUs by operation

- **Config Read**: the full 18-key snapshot is typically about 239 bytes and is
bounded by a 512-byte characteristic buffer. Use Read-Long / Read Blob and
- **Config Read**: the full 16-key snapshot is bounded by a 512-byte
characteristic buffer. Use Read-Long / Read Blob and
collect all fragments. Config Read does not require MTU 512, but it does
require a client API that supports long reads.
- **Config and Status notifications**: notifications contain deltas, not the
Expand Down
33 changes: 0 additions & 33 deletions products/go/main/go_ble.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1815,15 +1815,9 @@ static void enc_pm_aqi(CborEncoder &m, const GoSettings &s) {
static void enc_gps_mode(CborEncoder &m, const GoSettings &s) {
cbor_encode_text_stringz(&m, gps_mode_to_wire(s.gps_mode));
}
static void enc_inact_to(CborEncoder &m, const GoSettings &s) {
cbor_encode_uint(&m, static_cast<uint64_t>(s.inactivity_timeout_seconds));
}
static void enc_auto_lock(CborEncoder &m, const GoSettings &s) {
cbor_encode_uint(&m, static_cast<uint64_t>(s.auto_lock_seconds));
}
static void enc_dev_name(CborEncoder &m, const GoSettings &s) {
cbor_encode_text_stringz(&m, s.device_name.c_str());
}
static void enc_op_mode(CborEncoder &m, const GoSettings &s) {
cbor_encode_text_stringz(&m, operating_mode_to_wire(s.operating_mode));
}
Expand Down Expand Up @@ -1871,15 +1865,9 @@ static bool dif_pm_aqi(const GoSettings &a, const GoSettings &b) {
static bool dif_gps_mode(const GoSettings &a, const GoSettings &b) {
return a.gps_mode != b.gps_mode;
}
static bool dif_inact_to(const GoSettings &a, const GoSettings &b) {
return a.inactivity_timeout_seconds != b.inactivity_timeout_seconds;
}
static bool dif_auto_lock(const GoSettings &a, const GoSettings &b) {
return a.auto_lock_seconds != b.auto_lock_seconds;
}
static bool dif_dev_name(const GoSettings &a, const GoSettings &b) {
return a.device_name != b.device_name;
}
static bool dif_op_mode(const GoSettings &a, const GoSettings &b) {
return a.operating_mode != b.operating_mode;
}
Expand Down Expand Up @@ -1925,9 +1913,7 @@ static const ConfigField CONFIG_FIELDS[] = {
{BLE_KEY_TEMP_F, enc_temp_f, dif_temp_f},
{BLE_KEY_PM_AQI, enc_pm_aqi, dif_pm_aqi},
{BLE_KEY_GPS_MODE, enc_gps_mode, dif_gps_mode},
{BLE_KEY_INACT_TO, enc_inact_to, dif_inact_to},
{BLE_KEY_AUTO_LOCK, enc_auto_lock, dif_auto_lock},
{BLE_KEY_DEV_NAME, enc_dev_name, dif_dev_name},
{BLE_KEY_OP_MODE, enc_op_mode, dif_op_mode},
{BLE_KEY_FRONT_LED, enc_fled, dif_fled},
{BLE_KEY_BACK_LED, enc_bled, dif_bled},
Expand Down Expand Up @@ -2211,14 +2197,6 @@ BleConfigDecodeResult BleService::decode_config_write(const uint8_t *buf, size_t
else if (key_is(BLE_KEY_PM_INT) || key_is(BLE_KEY_OTHER_INT) || key_is(BLE_KEY_DISP_INT)) {
cbor_value_advance(&it);
handled = true;
} else if (key_is(BLE_KEY_INACT_TO)) {
cbor_value_advance(&it);
result.recognized_config_key_count++;
uint64_t v = 0;
if (cbor_value_is_unsigned_integer(&it) && cbor_value_get_uint64(&it, &v) == CborNoError) {
settings.inactivity_timeout_seconds = static_cast<uint32_t>(v);
}
handled = true;
} else if (key_is(BLE_KEY_AUTO_LOCK)) {
cbor_value_advance(&it);
result.recognized_config_key_count++;
Expand Down Expand Up @@ -2388,17 +2366,6 @@ BleConfigDecodeResult BleService::decode_config_write(const uint8_t *buf, size_t
settings.operating_mode = str_to_operating_mode(text);
}
handled = true;
} else if (key_is(BLE_KEY_DEV_NAME)) {
cbor_value_advance(&it);
result.recognized_config_key_count++;
char text[65] = {};
if (cbor_value_is_text_string(&it)) {
size_t slen = sizeof(text) - 1;
cbor_value_copy_text_string(&it, text, &slen, nullptr);
text[slen] = '\0';
settings.device_name = text;
}
handled = true;
}
// --- Aiding command payload fields ---
else if (key_is(BLE_KEY_LAT)) {
Expand Down
2 changes: 0 additions & 2 deletions products/go/main/go_ble_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,7 @@ inline constexpr const char *BLE_KEY_DISP_INT = "disp_int";
inline constexpr const char *BLE_KEY_TEMP_F = "temp_f";
inline constexpr const char *BLE_KEY_PM_AQI = "pm_aqi";
inline constexpr const char *BLE_KEY_GPS_MODE = "gps_mode";
inline constexpr const char *BLE_KEY_INACT_TO = "inact_to";
inline constexpr const char *BLE_KEY_AUTO_LOCK = "auto_lock";
inline constexpr const char *BLE_KEY_DEV_NAME = "dev_name";
inline constexpr const char *BLE_KEY_OP_MODE = "op_mode";
inline constexpr const char *BLE_KEY_FRONT_LED = "fled";
inline constexpr const char *BLE_KEY_BACK_LED = "bled";
Expand Down
Loading
Loading