diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 00d1e79..1bdcaa3 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -225,12 +225,12 @@ Unabhängig von Protobuf — reines Bytestream-Framing: | Feld | Wert | |------|------| | Start | `0xAA` | -| Länge | 1 Byte (1–252) | +| Länge | 2 Bytes little-endian (1–1024) | | Payload | `length` Bytes | | Prüfsumme | XOR aller Payload-Bytes | | Stopp | `0xCC` | -**Parameter:** `main/uart.h` — `UART_NUM_1`, `921600` Baud, TX GPIO **2**, RX GPIO **3**, `MAX_PAYLOAD_SIZE` 248. +**Parameter:** `main/uart.h` — `UART_NUM_1`, `921600` Baud, TX GPIO **2**, RX GPIO **3**, `MAX_PAYLOAD_SIZE` 1024. ### 5.2 Nutzlast (Anwendung) diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md index 1770b08..426fe8c 100644 --- a/docs/DOCUMENTATION.md +++ b/docs/DOCUMENTATION.md @@ -124,10 +124,10 @@ idf.py -p /dev/ttyUSB0 flash monitor | Byte | Inhalt | |------|--------| | 0 | `0xAA` Start | -| 1 | Länge N (1–252) | -| 2…N+1 | Payload | -| N+2 | XOR-Checksum über Payload | -| N+3 | `0xCC` Stopp | +| 1–2 | Länge N little-endian (1–1024) | +| 3…N+2 | Payload | +| N+3 | XOR-Checksum über Payload | +| N+4 | `0xCC` Stopp | Implementierung: `uart.c` — `parse_uart_byte()`, `uart_send_framed()`. diff --git a/docs/adding-a-feature.md b/docs/adding-a-feature.md index 9c5d798..f4191da 100644 --- a/docs/adding-a-feature.md +++ b/docs/adding-a-feature.md @@ -287,7 +287,7 @@ Entspricht `main/README.md`: | Byte | Inhalt | |------|--------| -| Frame | `0xAA`, Länge, Payload, XOR-Checksum, `0xCC` | +| Frame | `0xAA`, Länge (uint16 LE), Payload, XOR-Checksum, `0xCC` | | Payload[0] | `MessageType` (z.B. `22` = FIND_ME) | | Payload[1…] | Nanopb-`UartMessage` | diff --git a/goTool/uart/frame.go b/goTool/uart/frame.go index a49e07b..6878eda 100644 --- a/goTool/uart/frame.go +++ b/goTool/uart/frame.go @@ -10,8 +10,10 @@ import ( const ( StartMarker = 0xAA StopMarker = 0xCC - // Must match main/uart.h MAX_PAYLOAD_SIZE (MAX_BUF_SIZE - 4). - MaxPayload = 248 + // Must match main/uart.h MAX_PAYLOAD_SIZE. + MaxPayload = 1024 + // AA | len_lo | len_hi | payload | xor | CC + FrameOverhead = 5 ) var ( @@ -19,14 +21,15 @@ var ( ErrTimeout = errors.New("read timeout") ) -// EncodeFrame builds a framed packet: 0xAA len payload xor(payload) 0xCC. +// EncodeFrame builds a framed packet: 0xAA len_lo len_hi payload xor(payload) 0xCC. func EncodeFrame(payload []byte) ([]byte, error) { if len(payload) == 0 || len(payload) > MaxPayload { return nil, fmt.Errorf("payload length %d out of range 1..%d", len(payload), MaxPayload) } - frame := make([]byte, 0, 4+len(payload)) - frame = append(frame, StartMarker, byte(len(payload))) + n := len(payload) + frame := make([]byte, 0, FrameOverhead+n) + frame = append(frame, StartMarker, byte(n&0xff), byte((n>>8)&0xff)) var checksum byte for _, b := range payload { frame = append(frame, b) @@ -39,6 +42,7 @@ func EncodeFrame(payload []byte) ([]byte, error) { type Parser struct { state int len int + lenLo byte payload []byte index int checksum byte @@ -46,7 +50,8 @@ type Parser struct { const ( stateStart = iota - stateLen + stateLenLo + stateLenHi stateData stateChecksum stateStop @@ -63,14 +68,19 @@ func (p *Parser) Feed(b byte) (payload []byte, ok bool, err error) { if b == StartMarker { p.index = 0 p.checksum = 0 - p.state = stateLen + p.state = stateLenLo } - case stateLen: - if b == 0 || b > MaxPayload { + case stateLenLo: + p.lenLo = b + p.state = stateLenHi + case stateLenHi: + n := int(p.lenLo) | (int(b) << 8) + if n == 0 || n > MaxPayload { p.state = stateStart } else { - p.len = int(b) + p.len = n p.payload = make([]byte, p.len) + p.index = 0 p.state = stateData } case stateData: @@ -102,7 +112,7 @@ func (p *Parser) Feed(b byte) (payload []byte, ok bool, err error) { // maxWait bounds total wait time; zero means no limit (serial read timeouts retry forever). func ReadFrame(r io.Reader, buf []byte, maxWait time.Duration) ([]byte, error) { if buf == nil { - buf = make([]byte, 256) + buf = make([]byte, 1024) } parser := NewParser() diff --git a/main/README.md b/main/README.md index 3683c57..94c0d6f 100644 --- a/main/README.md +++ b/main/README.md @@ -166,7 +166,7 @@ Hardware: **UART1**, **921600** baud, **TX = GPIO3**, **RX = GPIO2** (adapter TX | Field | Value | |-------|--------| | Start | `0xAA` | -| Length | 1 byte, payload size 1–252 | +| Length | 2 bytes little-endian, payload size 1–1024 | | Payload | `length` bytes | | Checksum | XOR of all payload bytes | | Stop | `0xCC` | @@ -178,7 +178,7 @@ Hardware: **UART1**, **921600** baud, **TX = GPIO3**, **RX = GPIO2** (adapter TX | 0 | Command ID (`MessageType` / `msg_id`) | | 1… | Arguments (handler receives bytes after ID only) | -Example VERSION request: single-byte payload `03` → frame `AA 01 03 03 CC`. +Example VERSION request: single-byte payload `03` → frame `AA 01 00 03 03 CC`. Logging: diff --git a/main/ble_stream.c b/main/ble_stream.c index 10eb8c7..9147486 100644 --- a/main/ble_stream.c +++ b/main/ble_stream.c @@ -113,8 +113,8 @@ static esp_err_t send_tick_truncated(alox_UartMessage *msg) { size_t payload_len = stream.bytes_written + 1; uint16_t max_pay = ble_host_max_payload(); - /* Framed size = payload + 4; notify must fit ATT MTU-3. */ - size_t frame_len = payload_len + 4; + /* Framed size = payload + UART_FRAME_OVERHEAD; notify must fit ATT MTU-3. */ + size_t frame_len = payload_len + UART_FRAME_OVERHEAD; if (payload_len <= MAX_PAYLOAD_SIZE && frame_len <= max_pay) { uint8_t payload[MAX_PAYLOAD_SIZE]; payload[0] = (uint8_t)msg->type; @@ -197,7 +197,7 @@ void ble_stream_update(void) { if (want && !s_running) { s_running = true; - if (xTaskCreate(ble_stream_task, "ble_stream", 4096, NULL, 5, &s_task) != + if (xTaskCreate(ble_stream_task, "ble_stream", 8192, NULL, 5, &s_task) != pdPASS) { s_running = false; s_task = NULL; diff --git a/main/host_transport.c b/main/host_transport.c index 2a57f27..c2e4927 100644 --- a/main/host_transport.c +++ b/main/host_transport.c @@ -22,14 +22,14 @@ void host_transport_set_cmd_queue(QueueHandle_t cmd_queue) { QueueHandle_t host_transport_cmd_queue(void) { return s_cmd_queue; } -bool host_enqueue_packet(const uint8_t *payload, uint8_t len) { +bool host_enqueue_packet(const uint8_t *payload, size_t len) { if (s_cmd_queue == NULL || payload == NULL || len == 0) { return false; } generic_msg_t msg = { .msg_id = payload[0], - .len = len > 1 ? (size_t)(len - 1) : 0, + .len = len > 1 ? (len - 1) : 0, .payload = NULL, }; @@ -63,10 +63,11 @@ esp_err_t host_send_framed(const uint8_t *payload, size_t len) { checksum ^= payload[i]; } - uint8_t frame[4 + MAX_PAYLOAD_SIZE]; + uint8_t frame[UART_FRAME_OVERHEAD + MAX_PAYLOAD_SIZE]; size_t pos = 0; frame[pos++] = START_MARKER; - frame[pos++] = (uint8_t)len; + frame[pos++] = (uint8_t)(len & 0xff); + frame[pos++] = (uint8_t)((len >> 8) & 0xff); memcpy(&frame[pos], payload, len); pos += len; frame[pos++] = checksum; diff --git a/main/host_transport.h b/main/host_transport.h index 269683a..4c2f6f0 100644 --- a/main/host_transport.h +++ b/main/host_transport.h @@ -19,7 +19,7 @@ void host_transport_set_cmd_queue(QueueHandle_t cmd_queue); QueueHandle_t host_transport_cmd_queue(void); /** - * Build AA|len|payload|xor|CC and send via the registered TX backend. + * Build AA|len_lo|len_hi|payload|xor|CC and send via the registered TX backend. * Same semantics as uart_send_framed. */ esp_err_t host_send_framed(const uint8_t *payload, size_t len); @@ -31,6 +31,6 @@ esp_err_t host_send_framed(const uint8_t *payload, size_t len); void host_rx_bytes(const uint8_t *data, size_t len); /** Enqueue a completed uart_packet_t (payload[0]=msg type). */ -bool host_enqueue_packet(const uint8_t *payload, uint8_t len); +bool host_enqueue_packet(const uint8_t *payload, size_t len); #endif diff --git a/main/uart.c b/main/uart.c index 3179c07..5ea477e 100644 --- a/main/uart.c +++ b/main/uart.c @@ -81,18 +81,27 @@ bool parse_uart_byte(uint8_t byte, uart_packet_t *p) { if (byte == START_MARKER) { p->index = 0; p->checksum = 0; - p->state = STATE_LEN; + p->len = 0; + p->state = STATE_LEN_LO; } break; - case STATE_LEN: - if (byte > MAX_PAYLOAD_SIZE || byte == 0) { + case STATE_LEN_LO: + p->len = byte; + p->state = STATE_LEN_HI; + break; + + case STATE_LEN_HI: { + uint16_t len = (uint16_t)(p->len | ((uint16_t)byte << 8)); + if (len == 0 || len > MAX_PAYLOAD_SIZE) { p->state = STATE_START; } else { - p->len = byte; + p->len = len; + p->index = 0; p->state = STATE_DATA; } break; + } case STATE_DATA: p->payload[p->index++] = byte; diff --git a/main/uart.h b/main/uart.h index 4d3f1d7..fbef2f1 100644 --- a/main/uart.h +++ b/main/uart.h @@ -22,13 +22,18 @@ #define UART_DRIVER_TX_BUF_SIZE 4096 #define START_MARKER 0xAA #define STOP_MARKER 0xCC -#define MAX_BUF_SIZE 252 -#define MAX_PAYLOAD_SIZE \ - MAX_BUF_SIZE - 4 // Buffer overhead, Start, Len, CRC, End +/** Frame: AA | len_lo | len_hi | payload[len] | xor | CC */ +#define UART_FRAME_OVERHEAD 5 +/** + * Max application payload (type byte + protobuf). Sized for CLIENT_REGISTRY_MAX + * aggregations (CLIENT_INFO / BATTERY_STATUS with 16+ samples). + */ +#define MAX_PAYLOAD_SIZE 1024 typedef enum { STATE_START, - STATE_LEN, + STATE_LEN_LO, + STATE_LEN_HI, STATE_DATA, STATE_CHECKSUM, STATE_STOP @@ -36,8 +41,8 @@ typedef enum { typedef struct { uint8_t payload[MAX_PAYLOAD_SIZE]; - uint8_t len; - uint8_t index; + uint16_t len; + uint16_t index; uint8_t checksum; parser_state_t state; } uart_packet_t; diff --git a/main/uart_proto.c b/main/uart_proto.c index 148151b..a8e7fa4 100644 --- a/main/uart_proto.c +++ b/main/uart_proto.c @@ -1,18 +1,26 @@ #include "uart_proto.h" +#include "esp_log.h" #include "pb_encode.h" #include "uart.h" #include +static const char *TAG = "[UART_PROTO]"; + esp_err_t uart_send_uart_message(const alox_UartMessage *msg) { uint8_t pb_buf[MAX_PAYLOAD_SIZE]; pb_ostream_t stream = pb_ostream_from_buffer(pb_buf, sizeof(pb_buf)); if (!pb_encode(&stream, alox_UartMessage_fields, msg)) { + ESP_LOGE(TAG, "pb_encode failed type=%u (%s)", (unsigned)msg->type, + PB_GET_ERROR(&stream)); return ESP_FAIL; } uint8_t payload[MAX_PAYLOAD_SIZE]; if (stream.bytes_written + 1 > sizeof(payload)) { + ESP_LOGE(TAG, "payload too large type=%u need=%u max=%u", + (unsigned)msg->type, (unsigned)(stream.bytes_written + 1), + (unsigned)sizeof(payload)); return ESP_ERR_NO_MEM; }