syntax = "proto3"; import "nanopb.proto"; package alox; enum EspNowMessageType { ESPNOW_UNKNOWN = 0; ESPNOW_DISCOVER = 1; ESPNOW_SLAVE_INFO = 2; ESPNOW_HEARTBEAT = 3; ESPNOW_SET_ACCEL_DEADZONE = 4; ESPNOW_UNICAST_TEST = 5; ESPNOW_OTA_START = 6; ESPNOW_OTA_PAYLOAD = 7; ESPNOW_OTA_END = 8; ESPNOW_OTA_STATUS = 9; ESPNOW_FIND_ME = 10; } message EspNowUnicastTest { uint32 seq = 1; } /** Master → slave: locate pod (LED ring R/G/B ×3 @ full brightness). */ message EspNowFindMe { /** 0 = any slave; otherwise only slave_id must match */ uint32 client_id = 1; } message EspNowDiscover { uint32 network = 1; } message EspNowSlavePresence { uint32 network = 1; bytes mac = 2; uint32 version = 3; uint32 slave_id = 4; bool available = 5; bool used = 6; } message EspNowAccelDeadzone { uint32 deadzone = 1; uint32 client_id = 2; // 0 = all slaves; otherwise only matching slave_id applies } // Master → slave: begin OTA (erase inactive slot; slave replies ESPNOW_OTA_STATUS). message EspNowOtaStart { uint32 total_size = 1; } // Master → slave: firmware chunk (up to 200 bytes); slave buffers 4 KiB before flash write. message EspNowOtaPayload { uint32 seq = 1; bytes data = 2 [(nanopb).max_size = 200]; } // Master → slave: finalize OTA (flush, esp_ota_end, set boot partition). message EspNowOtaEnd {} // Slave → master: status / block ACK (same status codes as UART OtaStatusPayload). message EspNowOtaStatus { uint32 status = 1; uint32 bytes_written = 2; uint32 error = 3; } message EspNowMessage { EspNowMessageType type = 1; oneof payload { EspNowDiscover discover = 2; EspNowSlavePresence slave_info = 3; EspNowSlavePresence heartbeat = 4; EspNowAccelDeadzone accel_deadzone = 5; EspNowUnicastTest unicast_test = 6; EspNowOtaStart ota_start = 7; EspNowOtaPayload ota_payload = 8; EspNowOtaEnd ota_end = 9; EspNowOtaStatus ota_status = 10; EspNowFindMe find_me = 11; } }