Configuration
Configuration is carried by the root config oneof using ConfigMessage.
Commands
| Command | Client intent | Device response |
|---|---|---|
GET |
read current config | result=OK, current=<snapshot> |
SET |
apply a partial patch | result=OK, INVALID, or STORE_ERROR, plus current=<snapshot> |
RESET |
reset to defaults | result=OK, INVALID, or STORE_ERROR, plus current=<snapshot> |
NOTIFY |
not a normal client request; used by firmware for change notifications | current=<snapshot> when processed |
The firmware currently handles any incoming ConfigMessage regardless of the root Reaction.type; test tooling sends GET as Query and SET/RESET as Command.
Patch Semantics
ConfigPatch is partial. Only fields present in the patch are applied.
Color patches are also partial. For example, a patch can update only red inside power_led_color.
After applying a patch, firmware validates the full candidate config before saving it. If validation fails, the previous config remains active and the response result is INVALID.
Defaults
Defaults from DeviceConfig:
| Field | Default |
|---|---|
schema_version |
1 |
revision |
0 |
timer_60_seconds |
60 |
timer_120_seconds |
120 |
timer_530_single_seconds |
5 |
timer_530_double_seconds |
30 |
contest_seconds |
150 |
contest_pause_seconds |
30 |
timer_expired_holdoff_ms |
2000 |
pairing_timeout_ms |
60000 |
contest_gap_ms |
4000 |
buzzer_volume_percent |
100 |
power_led_color |
{ red: 0, green: 0, blue: 20 } |
active_top_red_team |
{ red: 25, green: 0, blue: 0 } |
active_top_green_team |
{ red: 0, green: 20, blue: 0 } |
active_front_red_team |
{ red: 40, green: 0, blue: 0 } |
active_front_green_team |
{ red: 0, green: 20, blue: 0 } |
idle_front_red_team |
{ red: 1, green: 0, blue: 0 } |
idle_front_green_team |
{ red: 0, green: 1, blue: 0 } |
idle_top_red_brightness |
1 |
idle_top_green_brightness |
1 |
ble_read_only_color |
{ red: 24, green: 12, blue: 0 } |
ble_write_authorized_color |
{ red: 0, green: 24, blue: 0 } |
ble_pairing_color |
{ red: 0, green: 20, blue: 20 } |
ble_pin_max_failures |
3 |
ble_pin_lockout_ms |
300000 |
ble_stale_bond_quick_timeout_ms |
3000 |
ble_stale_bond_strikes |
2 |
bleed_mode_enabled |
false |
Validation Rules
Validation runs after protobuf fields are converted into the C++ DeviceConfig types. Some protobuf uint32 fields are cast to uint8_t before validation, including colors, buzzer volume, idle top brightness, PIN failure count, and stale-bond strike count. Values above 255 can wrap during that conversion rather than being rejected as large protobuf values.
After conversion, the current firmware rejects configs when:
schema_versionis not1- any timer duration, timer holdoff, or pairing timeout listed in validation is zero
buzzer_volume_percentis greater than100contest_gap_msis greater than30000ble_pin_max_failuresis zero or greater than10ble_pin_lockout_msis less than1000or greater than3600000ble_stale_bond_quick_timeout_msis less than100or greater than30000ble_stale_bond_strikesis zero or greater than10
Color validation currently accepts all converted color values. In C++, colors are stored as uint8_t, and protobuf color components are cast to that width before validation.
Change Notifications
When a config update is committed and BLE peers are connected, the BLE task sends a config notification:
Reaction {
type: Update
config {
command: NOTIFY
result: OK
current: ...
}
}
If no BLE peers are connected, the firmware observes the config change but skips the notification.