syntax = "proto3"; import "nanopb.proto"; package alox; enum MessageType { UNKNOWN = 0; ACK = 1; ECHO = 2; VERSION = 3; CLIENT_INFO = 4; CLIENT_INPUT = 5; ACCEL_DEADZONE = 6; ESPNOW_UNICAST_TEST = 7; LED_RING = 8; OTA_START = 16; OTA_PAYLOAD = 17; OTA_END = 18; OTA_STATUS = 19; OTA_START_ESPNOW = 20; OTA_SLAVE_PROGRESS = 21; FIND_ME = 22; RESTART = 23; ACCEL_SNAPSHOT = 24; ACCEL_STREAM = 25; } message UartMessage { MessageType type = 1; oneof payload { Ack ack_payload = 2; EchoPayload echo_payload = 3; VersionResponse version_response = 4; ClientInfoResponse client_info_response = 5; ClientInputResponse client_input_response = 6; OtaStartPayload ota_start = 7; OtaPayload ota_payload = 8; OtaEndPayload ota_end = 9; OtaStatusPayload ota_status = 10; AccelDeadzoneRequest accel_deadzone_request = 11; AccelDeadzoneResponse accel_deadzone_response = 12; EspNowUnicastTestRequest espnow_unicast_test_request = 13; EspNowUnicastTestResponse espnow_unicast_test_response = 14; OtaSlaveProgressRequest ota_slave_progress_request = 15; OtaSlaveProgressResponse ota_slave_progress_response = 16; LedRingProgressRequest led_ring_progress_request = 17; LedRingProgressResponse led_ring_progress_response = 18; EspNowFindMeRequest espnow_find_me_request = 19; EspNowFindMeResponse espnow_find_me_response = 20; RestartRequest restart_request = 21; RestartResponse restart_response = 22; AccelSnapshotRequest accel_snapshot_request = 23; AccelSnapshotResponse accel_snapshot_response = 24; AccelStreamRequest accel_stream_request = 25; AccelStreamResponse accel_stream_response = 26; } } message Ack {} message EchoPayload { bytes data = 1; } message VersionResponse { uint32 version = 1; string git_hash = 2; /** Active OTA app partition label, e.g. "ota_0" or "ota_1". */ string running_partition = 3; } message ClientInfo { uint32 id = 1; bool available = 2; bool used = 3; bytes mac = 4; uint32 last_ping = 5; uint32 last_success_ping = 6; uint32 version = 7; /** Master: ESP-NOW accel stream enabled for this slave. */ bool accel_stream_enabled = 8; } message ClientInfoResponse { repeated ClientInfo clients = 1; } message ClientInput { uint32 id = 1; float lage_x = 2; float lage_y = 3; uint32 bitmask = 4; } message ClientInputResponse { repeated ClientInput clients = 1; } // write=false: read deadzone; write=true: apply deadzone (LSB per axis, raw accel units). // client_id 0 = local BMA456 on this node; >0 = slave id on master; ignored on slave. // all_clients = true (master only): push deadzone to every registered slave via ESP-NOW. message AccelDeadzoneRequest { bool write = 1; uint32 deadzone = 2; uint32 client_id = 3; bool all_clients = 4; } message AccelDeadzoneResponse { uint32 deadzone = 1; uint32 client_id = 2; bool success = 3; uint32 slaves_updated = 4; } // Host → master: enable/disable slave accel ESP-NOW stream (~16 ms per slave). // write=false: read; write=true: apply. client_id 0 invalid for write (use >0 or all_clients). message AccelStreamRequest { bool write = 1; bool enable = 2; uint32 client_id = 3; bool all_clients = 4; } message AccelStreamResponse { bool enabled = 1; uint32 client_id = 2; bool success = 3; uint32 slaves_updated = 4; } // Host → master: read cached accel samples from slaves (only while stream enabled). // client_id 0 = all registered slaves; otherwise one slave. message AccelSnapshotRequest { uint32 client_id = 1; } message AccelSample { uint32 client_id = 1; bool valid = 2; sint32 x = 3; sint32 y = 4; sint32 z = 5; /** Milliseconds since last ESP-NOW sample from this slave. */ uint32 age_ms = 6; } message AccelSnapshotResponse { repeated AccelSample samples = 1 [(nanopb).max_count = 16]; } message EspNowUnicastTestRequest { uint32 client_id = 1; uint32 seq = 2; } message EspNowUnicastTestResponse { bool success = 1; uint32 seq = 2; } // Host → device: LED ring display (progress bar, digit, clear, blink, or find-me). // mode: 0=clear, 1=progress (0–100 %), 2=digit (0–10), 3=blink full ring, 4=find-me (R/G/B ×3 @ full brightness). message LedRingProgressRequest { uint32 mode = 1; /** 0–100: fraction of ring LEDs to light (mode=progress) */ uint32 progress = 2; /** 0–10 (mode=digit) */ uint32 digit = 3; uint32 r = 4; uint32 g = 5; uint32 b = 6; /** 0–255 brightness scale; 0 = firmware default (~5 %) */ uint32 intensity = 7; /** Pulse length in ms (mode=blink, default 350) */ uint32 blink_ms = 8; /** Number of pulses (mode=blink, default 1) */ uint32 blink_count = 9; } message LedRingProgressResponse { bool success = 1; uint32 mode = 2; uint32 progress = 3; uint32 digit = 4; } /** Host → master: find-me on local ring (client_id=0) or ESP-NOW unicast to one slave. */ message EspNowFindMeRequest { uint32 client_id = 1; } message EspNowFindMeResponse { bool success = 1; uint32 client_id = 2; } /** Host → master: restart local node (client_id=0) or ESP-NOW unicast to one slave. */ message RestartRequest { uint32 client_id = 1; } message RestartResponse { bool success = 1; uint32 client_id = 2; } // Host → device: begin UART OTA (erase inactive OTA slot; device replies OTA_STATUS). message OtaStartPayload { uint32 total_size = 1; } // Host → device: firmware chunk (up to 200 bytes); device buffers 4 KiB before flash write. message OtaPayload { uint32 seq = 1; bytes data = 2 [(nanopb).max_size = 200]; } // Host → device: no more payload; device flushes buffer and finalizes OTA. message OtaEndPayload {} // Device → host status (also used as ACK after each 4 KiB written). // status: 1=preparing, 2=ready, 3=block_ack, 4=success, 5=failed, 6=distributing message OtaStatusPayload { uint32 status = 1; uint32 bytes_written = 2; uint32 target_slot = 3; uint32 error = 4; } // Host → master: query ESP-NOW slave OTA progress (client_id 0 = all slaves in session). message OtaSlaveProgressRequest { uint32 client_id = 1; } message OtaSlaveProgressEntry { uint32 client_id = 1; uint32 bytes_written = 2; uint32 total_bytes = 3; /** 0=idle, 1=preparing, 2=ready, 3=distributing, 4=success, 5=failed */ uint32 status = 4; uint32 error = 5; } message OtaSlaveProgressResponse { bool active = 1; uint32 total_bytes = 2; uint32 aggregate_bytes = 3; uint32 slave_count = 4; repeated OtaSlaveProgressEntry slaves = 5 [(nanopb).max_count = 16]; }