dreamstack/devices/waveshare-p4-panel/main/ds_espnow.h

144 lines
5 KiB
C
Raw Normal View History

/**
* DreamStack ESP-NOW Transport Ultra-Low Latency Binary Protocol
*
* Sub-1ms signal delivery over ESP-NOW (WiFi direct, no router).
* Binary packed frames instead of JSON for minimal overhead.
*
* Transport strategy:
* ESP-NOW: real-time signals + events (<1ms, 250 bytes)
* UDP: initial IR push + large payloads (~2ms, 1472 bytes)
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_now.h"
// ─── ESP-NOW Frame Types ───
#define DS_NOW_SIG 0x20 // Single signal update (hub → panel)
#define DS_NOW_SIG_BATCH 0x21 // Batch signal update (hub → panel)
#define DS_NOW_TOUCH 0x30 // Touch event (panel → hub)
#define DS_NOW_ACTION 0x31 // Button/widget action (panel → hub)
#define DS_NOW_PING 0xFE // Heartbeat (bidirectional)
#define DS_NOW_PONG 0xFD // Heartbeat response
// ─── UDP Frame Types ───
#define DS_UDP_IR_PUSH 0x40 // Full IR JSON push (hub → panel)
#define DS_UDP_IR_FRAG 0x41 // IR fragment for payloads > MTU
#define DS_UDP_DISCOVER 0x42 // Panel discovery broadcast
// ─── UDP Port ───
#define DS_UDP_PORT 9200
// ─── ESP-NOW Channel ───
#define DS_ESPNOW_CHANNEL 1
// ─── Max ESP-NOW payload ───
#define DS_ESPNOW_MAX_DATA 250
// ─── Signal Update Frame (7 bytes) ───
// Hub → Panel: update a single signal value
typedef struct __attribute__((packed)) {
uint8_t type; // DS_NOW_SIG
uint16_t signal_id; // which signal (0-65535)
int32_t value; // new value
} ds_sig_frame_t;
// ─── Signal Batch Frame (3 + 6*N bytes) ───
// Hub → Panel: update multiple signals at once
typedef struct __attribute__((packed)) {
uint16_t id;
int32_t val;
} ds_sig_entry_t;
typedef struct __attribute__((packed)) {
uint8_t type; // DS_NOW_SIG_BATCH
uint8_t count; // number of signals (max ~40 in 250B)
uint8_t seq; // sequence number (wrapping u8)
// followed by `count` ds_sig_entry_t entries
} ds_sig_batch_t;
// ─── Touch Event Frame (8 bytes) ───
// Panel → Hub: touch on the display
typedef struct __attribute__((packed)) {
uint8_t type; // DS_NOW_TOUCH
uint8_t node_id; // which UI node (from IR)
uint8_t event; // 0=click, 1=long_press, 2=release, 3=drag
uint8_t seq; // sequence number
uint16_t x; // touch X coordinate
uint16_t y; // touch Y coordinate
} ds_touch_now_t;
// ─── Action Event Frame (4 bytes) ───
// Panel → Hub: widget action (button click, toggle, etc.)
typedef struct __attribute__((packed)) {
uint8_t type; // DS_NOW_ACTION
uint8_t node_id; // which widget
uint8_t action; // 0=click, 1=toggle, 2=slide_change
uint8_t seq; // sequence number
} ds_action_frame_t;
// ─── Heartbeat Frame (2 bytes) ───
typedef struct __attribute__((packed)) {
uint8_t type; // DS_NOW_PING or DS_NOW_PONG
uint8_t seq; // echo back on pong
} ds_heartbeat_t;
// ─── UDP IR Push Header (4 bytes + payload) ───
typedef struct __attribute__((packed)) {
uint8_t magic[2]; // 0xD5, 0x7A
uint16_t length; // JSON payload length
// followed by `length` bytes of IR JSON
} ds_ir_push_t;
// ─── UDP IR Fragment Header (6 bytes + payload) ───
typedef struct __attribute__((packed)) {
uint8_t magic[2]; // 0xD5, 0x7A
uint8_t type; // DS_UDP_IR_FRAG
uint8_t frag_id; // fragment index (0-based)
uint8_t frag_total; // total fragments
uint8_t seq; // group sequence
// followed by fragment data (up to 1466 bytes)
} ds_ir_frag_t;
// ─── Callbacks ───
typedef void (*ds_signal_cb_t)(uint16_t signal_id, int32_t value);
typedef void (*ds_ir_cb_t)(const char *ir_json, size_t length);
// ─── Configuration ───
typedef struct {
uint8_t hub_mac[6]; // Hub MAC address (set to FF:FF:FF:FF:FF:FF for broadcast)
uint8_t channel; // WiFi channel (default: DS_ESPNOW_CHANNEL)
ds_signal_cb_t on_signal; // Called when a signal update arrives
ds_ir_cb_t on_ir_push; // Called when a full IR JSON arrives
} ds_espnow_config_t;
/**
* Initialize ESP-NOW transport.
* Sets up ESP-NOW, registers peer, starts UDP listener.
* WiFi must be initialized first (STA or AP mode, no connection needed).
*/
esp_err_t ds_espnow_init(const ds_espnow_config_t *config);
/**
* Send an action event to the hub (panel hub).
* Encodes as ds_action_frame_t and sends via ESP-NOW.
*/
esp_err_t ds_espnow_send_action(uint8_t node_id, uint8_t action);
/**
* Send a touch event to the hub (panel hub).
*/
esp_err_t ds_espnow_send_touch(uint8_t node_id, uint8_t event,
uint16_t x, uint16_t y);
/**
* Send a heartbeat ping.
*/
esp_err_t ds_espnow_send_ping(void);
/**
* Deinitialize ESP-NOW transport.
*/
void ds_espnow_deinit(void);