Add selectable ESP-NOW send path for accel/tap input.

Expose best_effort/fast/wait/reliable via UART, dashboard, and CLI so
loss vs latency can be compared under real multi-pod load.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-01 22:34:03 +01:00
co-authored by Cursor
parent 178ab46cec
commit 1adaddd6c8
25 changed files with 1178 additions and 255 deletions
+1
View File
@@ -38,6 +38,7 @@ go run . -port /dev/ttyUSB0 clients
| `find-me` | 22 | Locate pod (`-client 0` master, `>0` slave via ESP-NOW) | | `find-me` | 22 | Locate pod (`-client 0` master, `>0` slave via ESP-NOW) |
| `restart` | 23 | Reboot master or slave (`-client 0` / `>0`) | | `restart` | 23 | Reboot master or slave (`-client 0` / `>0`) |
| `log-level` | `0x1f` | Get/set master ESP-IDF log level for tag `"*"` (`-set`, `-level` 0–5); output on UART0 debug, not host UART | | `log-level` | `0x1f` | Get/set master ESP-IDF log level for tag `"*"` (`-set`, `-level` 0–5); output on UART0 debug, not host UART |
| `input-send-mode` | `0x23` | Get/set slave accel/tap ESP-NOW TX path (`-set -mode 0..3`, `-all` or `-client N`): best_effort/fast/wait/reliable |
`clients` requires slaves to have responded to master discover broadcasts first. `clients` requires slaves to have responded to master discover broadcasts first.
+100
View File
@@ -0,0 +1,100 @@
package main
import (
"encoding/json"
"net/http"
pb "powerpod/gotool/pb"
)
type inputSendModeAPIResponse struct {
Success bool `json:"success"`
Mode uint32 `json:"mode"`
ModeName string `json:"mode_name"`
ClientID uint32 `json:"client_id,omitempty"`
SlavesUpdated uint32 `json:"slaves_updated,omitempty"`
Error string `json:"error,omitempty"`
}
func inputSendModeName(mode pb.InputSendMode) string {
switch mode {
case pb.InputSendMode_INPUT_SEND_BEST_EFFORT:
return "best_effort"
case pb.InputSendMode_INPUT_SEND_FAST:
return "fast"
case pb.InputSendMode_INPUT_SEND_WAIT:
return "wait"
case pb.InputSendMode_INPUT_SEND_RELIABLE:
return "reliable"
default:
return "unknown"
}
}
func mountInputSendModeAPI(mux *http.ServeMux, link *managedSerial) {
mux.HandleFunc("/api/input-send-mode", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
serveInputSendModeGet(w, link)
case http.MethodPut, http.MethodPost:
serveInputSendModePut(w, r, link)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
})
}
func serveInputSendModeGet(w http.ResponseWriter, link *managedSerial) {
resp, err := link.InputSendMode(&pb.InputSendModeRequest{Write: false})
if err != nil {
writeJSON(w, http.StatusBadGateway, inputSendModeAPIResponse{Error: err.Error()})
return
}
writeJSON(w, http.StatusOK, inputSendModeAPIResponse{
Success: resp.GetSuccess(),
Mode: uint32(resp.GetMode()),
ModeName: inputSendModeName(resp.GetMode()),
})
}
func serveInputSendModePut(w http.ResponseWriter, r *http.Request, link *managedSerial) {
var body struct {
Mode uint32 `json:"mode"`
ClientID uint32 `json:"client_id"`
AllClients bool `json:"all_clients"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
writeJSON(w, http.StatusBadRequest, inputSendModeAPIResponse{Error: "invalid JSON"})
return
}
if body.Mode > uint32(pb.InputSendMode_INPUT_SEND_RELIABLE) {
writeJSON(w, http.StatusBadRequest, inputSendModeAPIResponse{Error: "mode must be 0..3"})
return
}
if !body.AllClients && body.ClientID == 0 {
// Dashboard default: apply to every registered slave.
body.AllClients = true
}
resp, err := link.InputSendMode(&pb.InputSendModeRequest{
Write: true,
Mode: pb.InputSendMode(body.Mode),
ClientId: body.ClientID,
AllClients: body.AllClients,
})
if err != nil {
writeJSON(w, http.StatusBadGateway, inputSendModeAPIResponse{Error: err.Error()})
return
}
status := http.StatusOK
if !resp.GetSuccess() {
status = http.StatusBadRequest
}
writeJSON(w, status, inputSendModeAPIResponse{
Success: resp.GetSuccess(),
Mode: uint32(resp.GetMode()),
ModeName: inputSendModeName(resp.GetMode()),
ClientID: resp.GetClientId(),
SlavesUpdated: resp.GetSlavesUpdated(),
})
}
+1
View File
@@ -102,6 +102,7 @@ func mountServeAPI(mux *http.ServeMux, link *managedSerial, hub *wsHub, streamCt
mountBleTestAPI(mux, link) mountBleTestAPI(mux, link)
mountLiveStreamAPI(mux, hub) mountLiveStreamAPI(mux, hub)
mountAccelStreamAPI(mux, link, hub, streamCtl) mountAccelStreamAPI(mux, link, hub, streamCtl)
mountInputSendModeAPI(mux, link)
mountTapAPI(mux, link, hub, tapCtl) mountTapAPI(mux, link, hub, tapCtl)
mountTapConfigAPI(mux, link) mountTapConfigAPI(mux, link)
mountLedRingAPI(mux, link) mountLedRingAPI(mux, link)
+8 -4
View File
@@ -156,7 +156,7 @@ func (m *managedSerial) TapNotifyPoll(req *pb.TapNotifyRequest) (*pb.TapNotifyRe
func (m *managedSerial) tapNotifyExchange(poll bool, req *pb.TapNotifyRequest) (*pb.TapNotifyResponse, error) { func (m *managedSerial) tapNotifyExchange(poll bool, req *pb.TapNotifyRequest) (*pb.TapNotifyResponse, error) {
msg := &pb.UartMessage{ msg := &pb.UartMessage{
Type: pb.MessageType_TAP_NOTIFY, Type: pb.MessageType_TAP_NOTIFY,
Payload: &pb.UartMessage_TapNotifyRequest{TapNotifyRequest: req}, Payload: &pb.UartMessage_TapNotifyRequest{TapNotifyRequest: req},
} }
body, err := proto.Marshal(msg) body, err := proto.Marshal(msg)
@@ -216,7 +216,7 @@ func decodeCacheStatusPayload(payload []byte) (*pb.CacheStatusResponse, error) {
func (m *managedSerial) accelStreamExchange(poll bool, req *pb.AccelStreamRequest) (*pb.AccelStreamResponse, error) { func (m *managedSerial) accelStreamExchange(poll bool, req *pb.AccelStreamRequest) (*pb.AccelStreamResponse, error) {
msg := &pb.UartMessage{ msg := &pb.UartMessage{
Type: pb.MessageType_ACCEL_STREAM, Type: pb.MessageType_ACCEL_STREAM,
Payload: &pb.UartMessage_AccelStreamRequest{AccelStreamRequest: req}, Payload: &pb.UartMessage_AccelStreamRequest{AccelStreamRequest: req},
} }
body, err := proto.Marshal(msg) body, err := proto.Marshal(msg)
@@ -262,7 +262,7 @@ func (m *managedSerial) TapConfigPoll(req *pb.TapConfigRequest) (*pb.TapConfigRe
func (m *managedSerial) tapConfigExchange(poll bool, req *pb.TapConfigRequest) (*pb.TapConfigResponse, error) { func (m *managedSerial) tapConfigExchange(poll bool, req *pb.TapConfigRequest) (*pb.TapConfigResponse, error) {
msg := &pb.UartMessage{ msg := &pb.UartMessage{
Type: pb.MessageType_TAP_CONFIG, Type: pb.MessageType_TAP_CONFIG,
Payload: &pb.UartMessage_TapConfigRequest{TapConfigRequest: req}, Payload: &pb.UartMessage_TapConfigRequest{TapConfigRequest: req},
} }
body, err := proto.Marshal(msg) body, err := proto.Marshal(msg)
@@ -292,7 +292,7 @@ func (m *managedSerial) tapConfigExchange(poll bool, req *pb.TapConfigRequest) (
func (m *managedSerial) accelDeadzoneExchange(poll bool, req *pb.AccelDeadzoneRequest) (*pb.AccelDeadzoneResponse, error) { func (m *managedSerial) accelDeadzoneExchange(poll bool, req *pb.AccelDeadzoneRequest) (*pb.AccelDeadzoneResponse, error) {
msg := &pb.UartMessage{ msg := &pb.UartMessage{
Type: pb.MessageType_ACCEL_DEADZONE, Type: pb.MessageType_ACCEL_DEADZONE,
Payload: &pb.UartMessage_AccelDeadzoneRequest{AccelDeadzoneRequest: req}, Payload: &pb.UartMessage_AccelDeadzoneRequest{AccelDeadzoneRequest: req},
} }
body, err := proto.Marshal(msg) body, err := proto.Marshal(msg)
@@ -606,6 +606,10 @@ func (m *managedSerial) SetLogLevel(write bool, level uint32) (*pb.SetLogLevelRe
return hostSetLogLevel(m, write, level) return hostSetLogLevel(m, write, level)
} }
func (m *managedSerial) InputSendMode(req *pb.InputSendModeRequest) (*pb.InputSendModeResponse, error) {
return hostInputSendMode(m, req)
}
func (m *managedSerial) Restart(clientID uint32) error { func (m *managedSerial) Restart(clientID uint32) error {
if err := hostRestart(m, clientID); err != nil { if err := hostRestart(m, clientID); err != nil {
return err return err
+42
View File
@@ -0,0 +1,42 @@
package main
import (
"flag"
"fmt"
pb "powerpod/gotool/pb"
)
func runInputSendModeCmd(h hostConn, args []string) error {
fs := flag.NewFlagSet("input-send-mode", flag.ExitOnError)
write := fs.Bool("set", false, "write mode to slave(s)")
mode := fs.Uint("mode", 0, "0=best_effort 1=fast 2=wait 3=reliable")
clientID := fs.Uint("client", 0, "slave client_id (required unless -all)")
all := fs.Bool("all", false, "apply to every registered slave")
_ = fs.Parse(args)
req := &pb.InputSendModeRequest{
Write: *write,
Mode: pb.InputSendMode(*mode),
ClientId: uint32(*clientID),
AllClients: *all,
}
if *write && !*all && *clientID == 0 {
return fmt.Errorf("need -client N or -all when using -set")
}
if *write && *mode > uint(pb.InputSendMode_INPUT_SEND_RELIABLE) {
return fmt.Errorf("mode must be 0..3")
}
r, err := hostInputSendMode(h, req)
if err != nil {
return err
}
fmt.Printf("input-send-mode: success=%v mode=%d (%s) client_id=%d slaves_updated=%d\n",
r.GetSuccess(), r.GetMode(), inputSendModeName(r.GetMode()), r.GetClientId(),
r.GetSlavesUpdated())
if *write && !r.GetSuccess() {
return fmt.Errorf("input-send-mode rejected")
}
return nil
}
+32
View File
@@ -400,6 +400,37 @@ go run . -port /dev/ttyUSB0 log-level -set -level 3
Dashboard: Master card → dropdown **ESP Log-Level** with **Lesen** / **Setzen**. Dashboard: Master card → dropdown **ESP Log-Level** with **Lesen** / **Setzen**.
### Input send mode (slave accel/tap ESP-NOW TX path)
Debug A/B for how slaves transmit `ACCEL_SAMPLE` / `TAP_EVENT`. Default on slaves is `best_effort` (0). Master remembers the last mode for GET; write pushes via ESP-NOW.
```http
GET /api/input-send-mode
PUT /api/input-send-mode
Content-Type: application/json
{"mode": 2, "all_clients": true}
```
| `mode` | Path |
|--------|------|
| 0 | `best_effort` (fire-and-forget, quiet) |
| 1 | `fast` (fire-and-forget, logs failures) |
| 2 | `wait` (send-done + retries) |
| 3 | `reliable` (OTA-style retries; can stall high-rate accel) |
```json
{"success": true, "mode": 2, "mode_name": "wait", "slaves_updated": 2}
```
**CLI:**
```bash
go run . -port /dev/ttyUSB0 input-send-mode
go run . -port /dev/ttyUSB0 input-send-mode -set -mode 2 -all
```
Dashboard: ESP-NOW Slaves toolbar → **Input-Send** dropdown + **Setzen** / **Lesen**.
### OTA (master host upload, UART or BLE) ### OTA (master host upload, UART or BLE)
```http ```http
@@ -455,5 +486,6 @@ Same as external API:
| Unicast test | `POST /api/unicast-test` | | Unicast test | `POST /api/unicast-test` |
| Echo ping | `POST /api/echo-ping` (per-slave latency; UI shows Host ms + ESP ms) | | Echo ping | `POST /api/echo-ping` (per-slave latency; UI shows Host ms + ESP ms) |
| Master log level | `GET` / `POST /api/log-level` or CLI `log-level` | | Master log level | `GET` / `POST /api/log-level` or CLI `log-level` |
| Input-Send mode | `GET` / `PUT /api/input-send-mode` or CLI `input-send-mode` |
| Tap notify S/D/T | `PUT /api/clients/{id}/tap-notify` | | Tap notify S/D/T | `PUT /api/clients/{id}/tap-notify` |
| Tap receive (UI) | Live stream + tap notify; see WebSocket doc for external API | | Tap receive (UI) | Live stream + tap notify; see WebSocket doc for external API |
+28 -1
View File
@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
"time" "time"
pb "powerpod/gotool/pb"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
pb "powerpod/gotool/pb"
) )
// hostConn is the UART or BLE host transport used by CLI commands. // hostConn is the UART or BLE host transport used by CLI commands.
@@ -160,6 +160,33 @@ func hostSetLogLevel(h hostConn, write bool, level uint32) (*pb.SetLogLevelRespo
return r, nil return r, nil
} }
func hostInputSendMode(h hostConn, req *pb.InputSendModeRequest) (*pb.InputSendModeResponse, error) {
msg := &pb.UartMessage{
Type: pb.MessageType_INPUT_SEND_MODE,
Payload: &pb.UartMessage_InputSendModeRequest{
InputSendModeRequest: req,
},
}
body, err := proto.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("encode: %w", err)
}
payload := append([]byte{byte(pb.MessageType_INPUT_SEND_MODE)}, body...)
respPayload, err := h.exchangePayload(payload, "INPUT_SEND_MODE")
if err != nil {
return nil, err
}
var respMsg pb.UartMessage
if err := proto.Unmarshal(respPayload[1:], &respMsg); err != nil {
return nil, fmt.Errorf("decode: %w", err)
}
r := respMsg.GetInputSendModeResponse()
if r == nil {
return nil, fmt.Errorf("missing input_send_mode_response")
}
return r, nil
}
func hostLedRing(h hostConn, req *pb.LedRingProgressRequest) (*pb.LedRingProgressResponse, error) { func hostLedRing(h hostConn, req *pb.LedRingProgressRequest) (*pb.LedRingProgressResponse, error) {
msg := &pb.UartMessage{ msg := &pb.UartMessage{
Type: pb.MessageType_LED_RING, Type: pb.MessageType_LED_RING,
+5 -2
View File
@@ -34,7 +34,8 @@ func usage() {
fmt.Fprintf(os.Stderr, " led-ring set LED ring progress bar (0–100%%, rgb, intensity)\n") fmt.Fprintf(os.Stderr, " led-ring set LED ring progress bar (0–100%%, rgb, intensity)\n")
fmt.Fprintf(os.Stderr, " find-me blink LED ring red/green/blue (3× each, full brightness)\n") fmt.Fprintf(os.Stderr, " find-me blink LED ring red/green/blue (3× each, full brightness)\n")
fmt.Fprintf(os.Stderr, " restart reboot master or slave (ESP-NOW)\n") fmt.Fprintf(os.Stderr, " restart reboot master or slave (ESP-NOW)\n")
fmt.Fprintf(os.Stderr, " log-level get/set master ESP-IDF log level (global)\n\n") fmt.Fprintf(os.Stderr, " log-level get/set master ESP-IDF log level (global)\n")
fmt.Fprintf(os.Stderr, " input-send-mode get/set slave accel/tap ESP-NOW send path\n\n")
flag.PrintDefaults() flag.PrintDefaults()
} }
@@ -73,7 +74,7 @@ func main() {
bleName: *bleName, bleName: *bleName,
settle: time.Duration(*bleSettle * float64(time.Second)), settle: time.Duration(*bleSettle * float64(time.Second)),
}, flag.Args()[1:]) }, flag.Args()[1:])
case "version", "clients", "client-info", "clients-reset-ids", "clients_reset_ids", "deadzone", "accel-deadzone", "tap-config", "tap_config", "tap-notify", "tap_notify", "cache-status", "cache_status", "accel-stream", "accel_stream", "unicast-test", "unicast_test", "echo-ping", "echo_ping", "ble-rtt", "ble_rtt", "ble-stream", "ble_stream", "led-ring", "led_ring", "find-me", "find_me", "restart", "log-level", "log_level", "ota", "ota-progress", "ota_progress": case "version", "clients", "client-info", "clients-reset-ids", "clients_reset_ids", "deadzone", "accel-deadzone", "tap-config", "tap_config", "tap-notify", "tap_notify", "cache-status", "cache_status", "accel-stream", "accel_stream", "unicast-test", "unicast_test", "echo-ping", "echo_ping", "ble-rtt", "ble_rtt", "ble-stream", "ble_stream", "led-ring", "led_ring", "find-me", "find_me", "restart", "log-level", "log_level", "input-send-mode", "input_send_mode", "ota", "ota-progress", "ota_progress":
if *useBLE && *portName != "" { if *useBLE && *portName != "" {
fmt.Fprintf(os.Stderr, "use either -port or -ble, not both\n") fmt.Fprintf(os.Stderr, "use either -port or -ble, not both\n")
os.Exit(2) os.Exit(2)
@@ -179,6 +180,8 @@ func main() {
break break
} }
runErr = runLogLevel(sp, flag.Args()[1:]) runErr = runLogLevel(sp, flag.Args()[1:])
case "input-send-mode", "input_send_mode":
runErr = runInputSendModeCmd(h, flag.Args()[1:])
case "ota": case "ota":
t, ok := h.(otaTransport) t, ok := h.(otaTransport)
if !ok { if !ok {
File diff suppressed because it is too large Load Diff
+71
View File
@@ -553,6 +553,8 @@
<strong>Live-Stream</strong>: UART ≈ <code>CACHE_STATUS</code>-Poll (~16&nbsp;ms); BLE ≈ <strong>Live-Stream</strong>: UART ≈ <code>CACHE_STATUS</code>-Poll (~16&nbsp;ms); BLE ≈
<code>BLE_STREAM_TICK</code>-Push. Pro Slave <strong>Accel</strong> aktiviert den ESP-NOW-Stream; <code>BLE_STREAM_TICK</code>-Push. Pro Slave <strong>Accel</strong> aktiviert den ESP-NOW-Stream;
Tap-Notify (S/D/T) steuert die Tap-Arten auf dem Slave. Tap-Notify (S/D/T) steuert die Tap-Arten auf dem Slave.
<strong>Input-Send</strong> wählt den ESP-NOW-TX-Pfad für Accel/Tap auf den Slaves
(<code>best_effort</code> Default; <code>wait</code>/<code>reliable</code> blockieren länger).
</p> </p>
<div class="px-3 pb-2 d-flex flex-wrap gap-2 align-items-center"> <div class="px-3 pb-2 d-flex flex-wrap gap-2 align-items-center">
<button type="button" <button type="button"
@@ -564,6 +566,28 @@
<span class="text-muted small font-monospace" x-show="isBLEHost() && state.live_stream" <span class="text-muted small font-monospace" x-show="isBLEHost() && state.live_stream"
x-text="bleTickLabel()" x-text="bleTickLabel()"
title="Median Δt zwischen BLE_STREAM_TICK (esp_timestamp_us)"></span> title="Median Δt zwischen BLE_STREAM_TICK (esp_timestamp_us)"></span>
<label class="text-muted small mb-0" for="input-send-mode">Input-Send:</label>
<select id="input-send-mode" class="form-select form-select-sm"
style="width: auto; min-width: 9rem;"
x-model.number="inputSendMode"
:disabled="busy || !hostUp() || !(state.clients || []).length">
<option :value="0">best_effort</option>
<option :value="1">fast</option>
<option :value="2">wait</option>
<option :value="3">reliable</option>
</select>
<button type="button" class="btn btn-outline-secondary btn-sm"
@click="setInputSendMode(inputSendMode)"
:disabled="busy || !hostUp() || !(state.clients || []).length"
title="Modus an alle registrierten Slaves pushen">
Setzen
</button>
<button type="button" class="btn btn-outline-secondary btn-sm"
@click="readInputSendMode()"
:disabled="busy || !hostUp()"
title="Zuletzt gesetzten Modus vom Master lesen">
Lesen
</button>
<span class="text-muted small">Tap alle Slaves:</span> <span class="text-muted small">Tap alle Slaves:</span>
<label class="tap-toggle"><input type="checkbox" x-model="allTapSingle" :disabled="busy"> S</label> <label class="tap-toggle"><input type="checkbox" x-model="allTapSingle" :disabled="busy"> S</label>
<label class="tap-toggle"><input type="checkbox" x-model="allTapDouble" :disabled="busy"> D</label> <label class="tap-toggle"><input type="checkbox" x-model="allTapDouble" :disabled="busy"> D</label>
@@ -913,6 +937,7 @@
masterDz: 100, masterDz: 100,
masterDzSeeded: false, masterDzSeeded: false,
masterLogLevel: 0, masterLogLevel: 0,
inputSendMode: 0,
masterTapCfg: { masterTapCfg: {
tap_sens_thres: 5, tap_sens_thres: 5,
max_gest_dur: 100, max_gest_dur: 100,
@@ -1796,6 +1821,52 @@
this.busy = false; this.busy = false;
} }
}, },
formatInputSendMode(mode) {
const labels = ['best_effort', 'fast', 'wait', 'reliable'];
if (mode == null || mode < 0 || mode > 3) return '—';
return labels[mode];
},
async readInputSendMode() {
this.busy = true;
try {
const r = await fetch('/api/input-send-mode');
const data = await r.json();
if (!r.ok || !data.success) {
this.flash(data.error || 'Input-Send lesen fehlgeschlagen', false);
return;
}
this.inputSendMode = data.mode ?? 0;
this.flash(`Input-Send: ${data.mode_name || this.formatInputSendMode(data.mode)}`, true);
} catch (e) {
this.flash(String(e), false);
} finally {
this.busy = false;
}
},
async setInputSendMode(mode) {
this.busy = true;
try {
const r = await fetch('/api/input-send-mode', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mode, all_clients: true })
});
const data = await r.json();
if (!r.ok || !data.success) {
this.flash(data.error || 'Input-Send setzen fehlgeschlagen', false);
return;
}
this.inputSendMode = data.mode ?? mode;
this.flash(
`Input-Send ${data.mode_name || this.formatInputSendMode(data.mode)} → ${data.slaves_updated || 0} Slaves`,
true
);
} catch (e) {
this.flash(String(e), false);
} finally {
this.busy = false;
}
},
patchClientAccelStream(clientId, enabled) { patchClientAccelStream(clientId, enabled) {
const clients = (this.state.clients || []).map((c) => { const clients = (this.state.clients || []).map((c) => {
if (c.id !== clientId) return c; if (c.id !== clientId) return c;
+1
View File
@@ -22,6 +22,7 @@ idf_component_register(
"cmd/cmd_client_info.c" "cmd/cmd_client_info.c"
"cmd/cmd_accel_deadzone.c" "cmd/cmd_accel_deadzone.c"
"cmd/cmd_accel_stream.c" "cmd/cmd_accel_stream.c"
"cmd/cmd_input_send_mode.c"
"cmd/cmd_tap_notify.c" "cmd/cmd_tap_notify.c"
"cmd/cmd_tap_config.c" "cmd/cmd_tap_config.c"
"cmd/cmd_cache_status.c" "cmd/cmd_cache_status.c"
+3 -1
View File
@@ -124,7 +124,8 @@ Schema: `proto/esp_now_messages.proto`. Encode/decode: `esp_now_proto.c`. The ES
| `ESPNOW_UNICAST_TEST` | Master → slave | `EspNowUnicastTest` (`seq`) | | `ESPNOW_UNICAST_TEST` | Master → slave | `EspNowUnicastTest` (`seq`) |
| `ESPNOW_FIND_ME` | Master → slave | `EspNowFindMe` (`client_id` filter) — LED locate sequence | | `ESPNOW_FIND_ME` | Master → slave | `EspNowFindMe` (`client_id` filter) — LED locate sequence |
| `ESPNOW_RESTART` | Master → slave | `EspNowRestart` (`client_id` filter) — reboot slave | | `ESPNOW_RESTART` | Master → slave | `EspNowRestart` (`client_id` filter) — reboot slave |
| `ESPNOW_ACCEL_SAMPLE` | Slave → master | `EspNowAccelSample` (`slave_id`, `x`, `y`, `z` raw LSB) — ~every 8 ms, best-effort | | `ESPNOW_ACCEL_SAMPLE` | Slave → master | `EspNowAccelSample` (`slave_id`, `x`, `y`, `z` raw LSB) — ~every 10 ms; TX path via `INPUT_SEND_MODE` (default best-effort) |
| `ESPNOW_SET_INPUT_SEND_MODE` | Master → slave | `EspNowInputSendMode` (`mode` 0..3 = best_effort/fast/wait/reliable) |
| `ESPNOW_SET_TAP_NOTIFY` | Master → slave | `EspNowTapNotify` (`client_id`, `single`, `double_tap`, `triple`) — which tap kinds to forward | | `ESPNOW_SET_TAP_NOTIFY` | Master → slave | `EspNowTapNotify` (`client_id`, `single`, `double_tap`, `triple`) — which tap kinds to forward |
| `ESPNOW_TAP_EVENT` | Slave → master | `EspNowTapEvent` (`client_id`, `kind`) — on BMA456 tap interrupt if notify enabled | | `ESPNOW_TAP_EVENT` | Slave → master | `EspNowTapEvent` (`client_id`, `kind`) — on BMA456 tap interrupt if notify enabled |
| `ESPNOW_BATTERY_REPORT` | Slave → master | `EspNowBatteryReport` (`client_id`, `lipo1/2` mV) — ~every 30 s; cached in `client_registry` | | `ESPNOW_BATTERY_REPORT` | Slave → master | `EspNowBatteryReport` (`client_id`, `lipo1/2` mV) — ~every 30 s; cached in `client_registry` |
@@ -233,6 +234,7 @@ Host and master speak nanopb-encoded `UartMessage` inside UART frames (byte 0 =
| 22 | `FIND_ME` | Implemented (`cmd/cmd_espnow_find_me.c`) — `client_id=0` local ring, `>0` ESP-NOW to slave | | 22 | `FIND_ME` | Implemented (`cmd/cmd_espnow_find_me.c`) — `client_id=0` local ring, `>0` ESP-NOW to slave |
| 23 | `RESTART` | Implemented (`cmd/cmd_restart.c`) — `client_id=0` reboot master, `>0` ESP-NOW reboot slave | | 23 | `RESTART` | Implemented (`cmd/cmd_restart.c`) — `client_id=0` reboot master, `>0` ESP-NOW reboot slave |
| 25 | `ACCEL_STREAM` | Implemented — enable/disable slave ESP-NOW accel stream to master | | 25 | `ACCEL_STREAM` | Implemented — enable/disable slave ESP-NOW accel stream to master |
| 35 | `INPUT_SEND_MODE` | Implemented — get/set slave accel/tap ESP-NOW send path (best_effort/fast/wait/reliable) |
| 27 | `TAP_NOTIFY` | Implemented (`cmd/cmd_tap_notify.c`) — get/set which tap kinds notify via ESP-NOW | | 27 | `TAP_NOTIFY` | Implemented (`cmd/cmd_tap_notify.c`) — get/set which tap kinds notify via ESP-NOW |
| 29 | `CACHE_STATUS` | Implemented (`cmd/cmd_cache_status.c`) — subscribed accel + tap cache (one UART round-trip) | | 29 | `CACHE_STATUS` | Implemented (`cmd/cmd_cache_status.c`) — subscribed accel + tap cache (one UART round-trip) |
| 30 | `ESPNOW_ECHO_PING` | Implemented (`cmd/cmd_espnow_echo_ping.c`) — ESP-NOW timestamp echo (latency test) | | 30 | `ESPNOW_ECHO_PING` | Implemented (`cmd/cmd_espnow_echo_ping.c`) — ESP-NOW timestamp echo (latency test) |
+4
View File
@@ -63,6 +63,10 @@ static const char *message_type_name(uint16_t id) {
return "SET_LOG_LEVEL"; return "SET_LOG_LEVEL";
case alox_MessageType_TAP_CONFIG: case alox_MessageType_TAP_CONFIG:
return "TAP_CONFIG"; return "TAP_CONFIG";
case alox_MessageType_CLIENT_ID_MAP_RESET:
return "CLIENT_ID_MAP_RESET";
case alox_MessageType_INPUT_SEND_MODE:
return "INPUT_SEND_MODE";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }
+109
View File
@@ -0,0 +1,109 @@
#include "cmd_input_send_mode.h"
#include "client_registry.h"
#include "esp_log.h"
#include "esp_now_comm.h"
#include "uart_cmd.h"
static const char *TAG = "[INPUT_SEND]";
/** Last mode pushed from host (GET returns this; slaves may lag until re-push). */
static alox_InputSendMode s_mode = alox_InputSendMode_INPUT_SEND_BEST_EFFORT;
static bool valid_mode(alox_InputSendMode mode) {
return mode <= alox_InputSendMode_INPUT_SEND_RELIABLE;
}
static void reply(bool success, alox_InputSendMode mode, uint32_t client_id,
uint32_t slaves_updated) {
alox_UartMessage response;
uart_cmd_init_response(&response, alox_MessageType_INPUT_SEND_MODE,
alox_UartMessage_input_send_mode_response_tag);
response.payload.input_send_mode_response.success = success;
response.payload.input_send_mode_response.mode = mode;
response.payload.input_send_mode_response.client_id = client_id;
response.payload.input_send_mode_response.slaves_updated = slaves_updated;
uart_cmd_send(&response, TAG);
}
static esp_err_t push_mode_to_slave(const client_info_t *client,
alox_InputSendMode mode) {
if (client == NULL) {
return ESP_ERR_INVALID_ARG;
}
return esp_now_comm_send_input_send_mode(client->mac, client->id,
(uint32_t)mode);
}
static void handle_input_send_mode(const uint8_t *data, size_t len) {
alox_UartMessage uart_msg;
alox_InputSendModeRequest req = alox_InputSendModeRequest_init_zero;
if (len > 0) {
if (uart_cmd_decode(data, len, &uart_msg) != ESP_OK) {
ESP_LOGW(TAG, "decode failed");
reply(false, s_mode, 0, 0);
return;
}
const alox_InputSendModeRequest *req_ptr =
UART_CMD_REQ(&uart_msg, alox_UartMessage_input_send_mode_request_tag,
input_send_mode_request);
if (req_ptr == NULL) {
ESP_LOGW(TAG, "missing input_send_mode_request");
reply(false, s_mode, 0, 0);
return;
}
req = *req_ptr;
}
if (req.write) {
if (!valid_mode(req.mode)) {
ESP_LOGW(TAG, "invalid mode %lu", (unsigned long)req.mode);
reply(false, s_mode, req.client_id, 0);
return;
}
if (req.all_clients) {
uint32_t sent = 0;
for (size_t i = 0; i < client_registry_count(); i++) {
const client_info_t *client = client_registry_at(i);
if (client == NULL) {
continue;
}
if (push_mode_to_slave(client, req.mode) == ESP_OK) {
sent++;
}
}
s_mode = req.mode;
ESP_LOGI(TAG, "input send mode %lu for %u slaves",
(unsigned long)req.mode, (unsigned)sent);
reply(sent > 0 || client_registry_count() == 0, req.mode, 0, sent);
return;
}
if (req.client_id == 0) {
ESP_LOGW(TAG, "client_id required (or all_clients)");
reply(false, req.mode, 0, 0);
return;
}
const client_info_t *client = client_registry_find_by_id(req.client_id);
if (client == NULL) {
ESP_LOGW(TAG, "client id %lu not found", (unsigned long)req.client_id);
reply(false, req.mode, req.client_id, 0);
return;
}
esp_err_t err = push_mode_to_slave(client, req.mode);
if (err == ESP_OK) {
s_mode = req.mode;
}
reply(err == ESP_OK, req.mode, req.client_id, err == ESP_OK ? 1u : 0u);
return;
}
reply(true, s_mode, 0, 0);
}
void cmd_input_send_mode_register(void) {
uart_cmd_register(alox_MessageType_INPUT_SEND_MODE, handle_input_send_mode);
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef CMD_INPUT_SEND_MODE_H
#define CMD_INPUT_SEND_MODE_H
void cmd_input_send_mode_register(void);
#endif
+4
View File
@@ -13,6 +13,10 @@ esp_err_t esp_now_comm_init(const app_config_t *config);
esp_err_t esp_now_comm_send_accel_stream(const uint8_t mac[CLIENT_MAC_LEN], esp_err_t esp_now_comm_send_accel_stream(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t client_id, bool enable); uint32_t client_id, bool enable);
/** Master: set accel/tap ESP-NOW send path on one slave (InputSendMode). */
esp_err_t esp_now_comm_send_input_send_mode(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t client_id, uint32_t mode);
/** Master: configure tap notify flags on one slave. */ /** Master: configure tap notify flags on one slave. */
esp_err_t esp_now_comm_send_tap_notify(const uint8_t mac[CLIENT_MAC_LEN], esp_err_t esp_now_comm_send_tap_notify(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t client_id, bool single, uint32_t client_id, bool single,
+28
View File
@@ -37,6 +37,16 @@ static esp_err_t send_accel_stream(const uint8_t *dest_mac, uint32_t client_id,
return esp_now_core_send(dest_mac, &msg); return esp_now_core_send(dest_mac, &msg);
} }
static esp_err_t send_input_send_mode(const uint8_t *dest_mac,
uint32_t client_id, uint32_t mode) {
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
msg.type = alox_EspNowMessageType_ESPNOW_SET_INPUT_SEND_MODE;
msg.which_payload = alox_EspNowMessage_input_send_mode_tag;
msg.payload.input_send_mode.client_id = client_id;
msg.payload.input_send_mode.mode = mode;
return esp_now_core_send(dest_mac, &msg);
}
static esp_err_t send_accel_deadzone(const uint8_t *dest_mac, uint32_t client_id, static esp_err_t send_accel_deadzone(const uint8_t *dest_mac, uint32_t client_id,
uint32_t deadzone) { uint32_t deadzone) {
alox_EspNowMessage msg = alox_EspNowMessage_init_zero; alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
@@ -446,6 +456,24 @@ esp_err_t esp_now_comm_send_accel_stream(const uint8_t mac[CLIENT_MAC_LEN],
return err; return err;
} }
esp_err_t esp_now_comm_send_input_send_mode(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t client_id, uint32_t mode) {
if (mac == NULL || !esp_now_core_is_master()) {
return ESP_ERR_INVALID_STATE;
}
char mac_str[18];
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
esp_err_t err = send_input_send_mode(mac, client_id, mode);
if (err == ESP_OK) {
ESP_LOGI(TAG, "unicast SET_INPUT_SEND_MODE to %s: mode=%lu client_id=%lu",
mac_str, (unsigned long)mode, (unsigned long)client_id);
} else {
ESP_LOGW(TAG, "unicast SET_INPUT_SEND_MODE to %s failed: %s", mac_str,
esp_err_to_name(err));
}
return err;
}
esp_err_t esp_now_comm_send_tap_notify(const uint8_t mac[CLIENT_MAC_LEN], esp_err_t esp_now_comm_send_tap_notify(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t client_id, bool single, uint32_t client_id, bool single,
bool double_tap, bool triple) { bool double_tap, bool triple) {
+66 -7
View File
@@ -24,9 +24,9 @@
/** While master or slave OTA is in progress (discover may be sparse). */ /** While master or slave OTA is in progress (discover may be sparse). */
#define SLAVE_MASTER_OTA_GRACE_MS 300000u #define SLAVE_MASTER_OTA_GRACE_MS 300000u
/** /**
* Best-effort gaming input cadence (loss OK; send_best_effort). * Accel stream cadence. TX path is selectable (INPUT_SEND_MODE; default
* Must be >= one FreeRTOS tick (CONFIG_FREERTOS_HZ=100 → 10 ms); shorter * best_effort). Must be >= one FreeRTOS tick (CONFIG_FREERTOS_HZ=100 → 10 ms);
* values become 0 ticks and assert in vTaskDelayUntil. * shorter values become 0 ticks and assert in vTaskDelayUntil.
*/ */
#define ESPNOW_ACCEL_INTERVAL_MS 10 #define ESPNOW_ACCEL_INTERVAL_MS 10
#define ESPNOW_BATTERY_INTERVAL_MS 30000 #define ESPNOW_BATTERY_INTERVAL_MS 30000
@@ -40,6 +40,9 @@ static bool s_accel_stream_enabled;
static bool s_tap_notify_single; static bool s_tap_notify_single;
static bool s_tap_notify_double; static bool s_tap_notify_double;
static bool s_tap_notify_triple; static bool s_tap_notify_triple;
/** Accel/tap TX path; default matches historical best-effort gaming input. */
static uint32_t s_input_send_mode =
(uint32_t)alox_InputSendMode_INPUT_SEND_BEST_EFFORT;
static uint8_t s_master_mac[ESP_NOW_ETH_ALEN]; static uint8_t s_master_mac[ESP_NOW_ETH_ALEN];
static uint32_t s_last_discover_ms; static uint32_t s_last_discover_ms;
/** Logical id assigned by current/last master (0 = unbound). */ /** Logical id assigned by current/last master (0 = unbound). */
@@ -161,6 +164,21 @@ static void send_presence(const uint8_t *dest_mac, alox_EspNowMessageType type)
esp_now_core_send(dest_mac, &msg); esp_now_core_send(dest_mac, &msg);
} }
static esp_err_t send_input_message(const uint8_t *dest_mac,
const alox_EspNowMessage *msg) {
switch (s_input_send_mode) {
case (uint32_t)alox_InputSendMode_INPUT_SEND_FAST:
return esp_now_core_send_fast(dest_mac, msg);
case (uint32_t)alox_InputSendMode_INPUT_SEND_WAIT:
return esp_now_core_send_wait(dest_mac, msg);
case (uint32_t)alox_InputSendMode_INPUT_SEND_RELIABLE:
return esp_now_core_send_reliable(dest_mac, msg);
case (uint32_t)alox_InputSendMode_INPUT_SEND_BEST_EFFORT:
default:
return esp_now_core_send_best_effort(dest_mac, msg);
}
}
static esp_err_t send_accel_sample(const uint8_t *dest_mac, uint32_t slave_id, static esp_err_t send_accel_sample(const uint8_t *dest_mac, uint32_t slave_id,
int16_t x, int16_t y, int16_t z) { int16_t x, int16_t y, int16_t z) {
alox_EspNowMessage msg = alox_EspNowMessage_init_zero; alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
@@ -170,8 +188,7 @@ static esp_err_t send_accel_sample(const uint8_t *dest_mac, uint32_t slave_id,
msg.payload.accel_sample.x = x; msg.payload.accel_sample.x = x;
msg.payload.accel_sample.y = y; msg.payload.accel_sample.y = y;
msg.payload.accel_sample.z = z; msg.payload.accel_sample.z = z;
/* Fire-and-forget: controller input prefers low latency over delivery. */ return send_input_message(dest_mac, &msg);
return esp_now_core_send_best_effort(dest_mac, &msg);
} }
static esp_err_t send_tap_event(const uint8_t *dest_mac, uint32_t slave_id, static esp_err_t send_tap_event(const uint8_t *dest_mac, uint32_t slave_id,
@@ -181,7 +198,7 @@ static esp_err_t send_tap_event(const uint8_t *dest_mac, uint32_t slave_id,
msg.which_payload = alox_EspNowMessage_tap_event_tag; msg.which_payload = alox_EspNowMessage_tap_event_tag;
msg.payload.tap_event.slave_id = slave_id; msg.payload.tap_event.slave_id = slave_id;
msg.payload.tap_event.kind = kind; msg.payload.tap_event.kind = kind;
return esp_now_core_send_best_effort(dest_mac, &msg); return send_input_message(dest_mac, &msg);
} }
static esp_err_t send_battery_report(const uint8_t *dest_mac, static esp_err_t send_battery_report(const uint8_t *dest_mac,
@@ -429,6 +446,43 @@ static void handle_accel_stream(const uint8_t *master_mac,
cfg->enable ? "on" : "off", mac_str, (unsigned long)my_client_id()); cfg->enable ? "on" : "off", mac_str, (unsigned long)my_client_id());
} }
static const char *input_send_mode_name(uint32_t mode) {
switch (mode) {
case (uint32_t)alox_InputSendMode_INPUT_SEND_BEST_EFFORT:
return "best_effort";
case (uint32_t)alox_InputSendMode_INPUT_SEND_FAST:
return "fast";
case (uint32_t)alox_InputSendMode_INPUT_SEND_WAIT:
return "wait";
case (uint32_t)alox_InputSendMode_INPUT_SEND_RELIABLE:
return "reliable";
default:
return "unknown";
}
}
static void handle_input_send_mode(const uint8_t *master_mac,
const alox_EspNowInputSendMode *cfg) {
if (!client_id_matches(cfg->client_id)) {
return;
}
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
return;
}
if (cfg->mode > (uint32_t)alox_InputSendMode_INPUT_SEND_RELIABLE) {
ESP_LOGW(TAG, "input send mode %lu invalid (id=%lu)",
(unsigned long)cfg->mode, (unsigned long)my_client_id());
return;
}
s_input_send_mode = cfg->mode;
char mac_str[18];
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
ESP_LOGI(TAG, "input send mode %s (%lu) from master %s (id=%lu)",
input_send_mode_name(s_input_send_mode),
(unsigned long)s_input_send_mode, mac_str,
(unsigned long)my_client_id());
}
static void handle_tap_notify(const uint8_t *master_mac, static void handle_tap_notify(const uint8_t *master_mac,
const alox_EspNowTapNotify *cfg) { const alox_EspNowTapNotify *cfg) {
if (!client_id_matches(cfg->client_id)) { if (!client_id_matches(cfg->client_id)) {
@@ -585,7 +639,7 @@ static void check_master_timeout(void) {
static void accel_stream_task(void *param) { static void accel_stream_task(void *param) {
(void)param; (void)param;
ESP_LOGI(TAG, "accel stream task (interval %u ms, best-effort)", ESP_LOGI(TAG, "accel stream task (interval %u ms, mode selectable)",
(unsigned)ESPNOW_ACCEL_INTERVAL_MS); (unsigned)ESPNOW_ACCEL_INTERVAL_MS);
TickType_t last_wake = xTaskGetTickCount(); TickType_t last_wake = xTaskGetTickCount();
@@ -733,6 +787,11 @@ void esp_now_slave_on_recv(const esp_now_recv_info_t *info, const uint8_t *data,
handle_tap_notify(info->src_addr, &msg.payload.tap_notify); handle_tap_notify(info->src_addr, &msg.payload.tap_notify);
} }
break; break;
case alox_EspNowMessage_input_send_mode_tag:
if (from_joined_master(info->src_addr)) {
handle_input_send_mode(info->src_addr, &msg.payload.input_send_mode);
}
break;
case alox_EspNowMessage_battery_query_tag: case alox_EspNowMessage_battery_query_tag:
if (from_joined_master(info->src_addr)) { if (from_joined_master(info->src_addr)) {
handle_battery_query(info->src_addr, &msg.payload.battery_query); handle_battery_query(info->src_addr, &msg.payload.battery_query);
+2
View File
@@ -2,6 +2,7 @@
#include "cmd_handler.h" #include "cmd_handler.h"
#include "cmd_accel_deadzone.h" #include "cmd_accel_deadzone.h"
#include "cmd_accel_stream.h" #include "cmd_accel_stream.h"
#include "cmd_input_send_mode.h"
#include "cmd_tap_notify.h" #include "cmd_tap_notify.h"
#include "cmd_tap_config.h" #include "cmd_tap_config.h"
#include "cmd_cache_status.h" #include "cmd_cache_status.h"
@@ -221,6 +222,7 @@ void app_main(void) {
cmd_client_info_register(); cmd_client_info_register();
cmd_accel_deadzone_register(); cmd_accel_deadzone_register();
cmd_accel_stream_register(); cmd_accel_stream_register();
cmd_input_send_mode_register();
cmd_tap_notify_register(); cmd_tap_notify_register();
cmd_tap_config_register(); cmd_tap_config_register();
cmd_cache_status_register(); cmd_cache_status_register();
+3
View File
@@ -54,6 +54,9 @@ PB_BIND(alox_EspNowTapConfig, alox_EspNowTapConfig, AUTO)
PB_BIND(alox_EspNowTapEvent, alox_EspNowTapEvent, AUTO) PB_BIND(alox_EspNowTapEvent, alox_EspNowTapEvent, AUTO)
PB_BIND(alox_EspNowInputSendMode, alox_EspNowInputSendMode, AUTO)
PB_BIND(alox_EspNowBatteryReport, alox_EspNowBatteryReport, AUTO) PB_BIND(alox_EspNowBatteryReport, alox_EspNowBatteryReport, AUTO)
+32 -4
View File
@@ -36,7 +36,9 @@ typedef enum _alox_EspNowMessageType {
/* * Master → slave: assigned logical client id after join/presence. */ /* * Master → slave: assigned logical client id after join/presence. */
alox_EspNowMessageType_ESPNOW_ASSIGN_ID = 22, alox_EspNowMessageType_ESPNOW_ASSIGN_ID = 22,
/* * Master → slave: forget persisted master/id binding (debug reset). */ /* * Master → slave: forget persisted master/id binding (debug reset). */
alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING = 23 alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING = 23,
/* * Master → slave: accel/tap ESP-NOW send path (best_effort/fast/wait/reliable). */
alox_EspNowMessageType_ESPNOW_SET_INPUT_SEND_MODE = 24
} alox_EspNowMessageType; } alox_EspNowMessageType;
/* Struct definitions */ /* Struct definitions */
@@ -147,6 +149,14 @@ typedef struct _alox_EspNowTapEvent {
uint32_t kind; uint32_t kind;
} alox_EspNowTapEvent; } alox_EspNowTapEvent;
/* *
Master → slave: which esp_now_core_send_* path to use for ACCEL_SAMPLE / TAP_EVENT.
Values match uart InputSendMode (0=best_effort … 3=reliable). */
typedef struct _alox_EspNowInputSendMode {
uint32_t client_id; /* 0 = all slaves; otherwise only matching slave_id applies */
uint32_t mode;
} alox_EspNowInputSendMode;
/* * Slave → master: LiPo voltages (periodic ~30 s and on query). */ /* * Slave → master: LiPo voltages (periodic ~30 s and on query). */
typedef struct _alox_EspNowBatteryReport { typedef struct _alox_EspNowBatteryReport {
uint32_t client_id; uint32_t client_id;
@@ -221,6 +231,7 @@ typedef struct _alox_EspNowMessage {
alox_EspNowTapConfig tap_config; alox_EspNowTapConfig tap_config;
alox_EspNowAssignId assign_id; alox_EspNowAssignId assign_id;
alox_EspNowClearIdBinding clear_id_binding; alox_EspNowClearIdBinding clear_id_binding;
alox_EspNowInputSendMode input_send_mode;
} payload; } payload;
} alox_EspNowMessage; } alox_EspNowMessage;
@@ -231,8 +242,9 @@ extern "C" {
/* Helper constants for enums */ /* Helper constants for enums */
#define _alox_EspNowMessageType_MIN alox_EspNowMessageType_ESPNOW_UNKNOWN #define _alox_EspNowMessageType_MIN alox_EspNowMessageType_ESPNOW_UNKNOWN
#define _alox_EspNowMessageType_MAX alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING #define _alox_EspNowMessageType_MAX alox_EspNowMessageType_ESPNOW_SET_INPUT_SEND_MODE
#define _alox_EspNowMessageType_ARRAYSIZE ((alox_EspNowMessageType)(alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING+1)) #define _alox_EspNowMessageType_ARRAYSIZE ((alox_EspNowMessageType)(alox_EspNowMessageType_ESPNOW_SET_INPUT_SEND_MODE+1))
@@ -276,6 +288,7 @@ extern "C" {
#define alox_EspNowTapNotify_init_default {0, 0, 0, 0} #define alox_EspNowTapNotify_init_default {0, 0, 0, 0}
#define alox_EspNowTapConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define alox_EspNowTapConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define alox_EspNowTapEvent_init_default {0, 0} #define alox_EspNowTapEvent_init_default {0, 0}
#define alox_EspNowInputSendMode_init_default {0, 0}
#define alox_EspNowBatteryReport_init_default {0, 0, 0, 0, 0} #define alox_EspNowBatteryReport_init_default {0, 0, 0, 0, 0}
#define alox_EspNowLedRing_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define alox_EspNowLedRing_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define alox_EspNowOtaStart_init_default {0} #define alox_EspNowOtaStart_init_default {0}
@@ -299,6 +312,7 @@ extern "C" {
#define alox_EspNowTapNotify_init_zero {0, 0, 0, 0} #define alox_EspNowTapNotify_init_zero {0, 0, 0, 0}
#define alox_EspNowTapConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define alox_EspNowTapConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define alox_EspNowTapEvent_init_zero {0, 0} #define alox_EspNowTapEvent_init_zero {0, 0}
#define alox_EspNowInputSendMode_init_zero {0, 0}
#define alox_EspNowBatteryReport_init_zero {0, 0, 0, 0, 0} #define alox_EspNowBatteryReport_init_zero {0, 0, 0, 0, 0}
#define alox_EspNowLedRing_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define alox_EspNowLedRing_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define alox_EspNowOtaStart_init_zero {0} #define alox_EspNowOtaStart_init_zero {0}
@@ -349,6 +363,8 @@ extern "C" {
#define alox_EspNowTapConfig_enable_triple_tag 10 #define alox_EspNowTapConfig_enable_triple_tag 10
#define alox_EspNowTapEvent_slave_id_tag 1 #define alox_EspNowTapEvent_slave_id_tag 1
#define alox_EspNowTapEvent_kind_tag 2 #define alox_EspNowTapEvent_kind_tag 2
#define alox_EspNowInputSendMode_client_id_tag 1
#define alox_EspNowInputSendMode_mode_tag 2
#define alox_EspNowBatteryReport_client_id_tag 1 #define alox_EspNowBatteryReport_client_id_tag 1
#define alox_EspNowBatteryReport_lipo1_valid_tag 2 #define alox_EspNowBatteryReport_lipo1_valid_tag 2
#define alox_EspNowBatteryReport_lipo2_valid_tag 3 #define alox_EspNowBatteryReport_lipo2_valid_tag 3
@@ -394,6 +410,7 @@ extern "C" {
#define alox_EspNowMessage_tap_config_tag 22 #define alox_EspNowMessage_tap_config_tag 22
#define alox_EspNowMessage_assign_id_tag 23 #define alox_EspNowMessage_assign_id_tag 23
#define alox_EspNowMessage_clear_id_binding_tag 24 #define alox_EspNowMessage_clear_id_binding_tag 24
#define alox_EspNowMessage_input_send_mode_tag 25
/* Struct field encoding specification for nanopb */ /* Struct field encoding specification for nanopb */
#define alox_EspNowUnicastTest_FIELDLIST(X, a) \ #define alox_EspNowUnicastTest_FIELDLIST(X, a) \
@@ -502,6 +519,12 @@ X(a, STATIC, SINGULAR, UINT32, kind, 2)
#define alox_EspNowTapEvent_CALLBACK NULL #define alox_EspNowTapEvent_CALLBACK NULL
#define alox_EspNowTapEvent_DEFAULT NULL #define alox_EspNowTapEvent_DEFAULT NULL
#define alox_EspNowInputSendMode_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, client_id, 1) \
X(a, STATIC, SINGULAR, UINT32, mode, 2)
#define alox_EspNowInputSendMode_CALLBACK NULL
#define alox_EspNowInputSendMode_DEFAULT NULL
#define alox_EspNowBatteryReport_FIELDLIST(X, a) \ #define alox_EspNowBatteryReport_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, client_id, 1) \ X(a, STATIC, SINGULAR, UINT32, client_id, 1) \
X(a, STATIC, SINGULAR, BOOL, lipo1_valid, 2) \ X(a, STATIC, SINGULAR, BOOL, lipo1_valid, 2) \
@@ -572,7 +595,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,echo_ping,payload.echo_ping), 20) \
X(a, STATIC, ONEOF, MESSAGE, (payload,echo_pong,payload.echo_pong), 21) \ X(a, STATIC, ONEOF, MESSAGE, (payload,echo_pong,payload.echo_pong), 21) \
X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config,payload.tap_config), 22) \ X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config,payload.tap_config), 22) \
X(a, STATIC, ONEOF, MESSAGE, (payload,assign_id,payload.assign_id), 23) \ X(a, STATIC, ONEOF, MESSAGE, (payload,assign_id,payload.assign_id), 23) \
X(a, STATIC, ONEOF, MESSAGE, (payload,clear_id_binding,payload.clear_id_binding), 24) X(a, STATIC, ONEOF, MESSAGE, (payload,clear_id_binding,payload.clear_id_binding), 24) \
X(a, STATIC, ONEOF, MESSAGE, (payload,input_send_mode,payload.input_send_mode), 25)
#define alox_EspNowMessage_CALLBACK NULL #define alox_EspNowMessage_CALLBACK NULL
#define alox_EspNowMessage_DEFAULT NULL #define alox_EspNowMessage_DEFAULT NULL
#define alox_EspNowMessage_payload_discover_MSGTYPE alox_EspNowDiscover #define alox_EspNowMessage_payload_discover_MSGTYPE alox_EspNowDiscover
@@ -598,6 +622,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,clear_id_binding,payload.clear_id_bi
#define alox_EspNowMessage_payload_tap_config_MSGTYPE alox_EspNowTapConfig #define alox_EspNowMessage_payload_tap_config_MSGTYPE alox_EspNowTapConfig
#define alox_EspNowMessage_payload_assign_id_MSGTYPE alox_EspNowAssignId #define alox_EspNowMessage_payload_assign_id_MSGTYPE alox_EspNowAssignId
#define alox_EspNowMessage_payload_clear_id_binding_MSGTYPE alox_EspNowClearIdBinding #define alox_EspNowMessage_payload_clear_id_binding_MSGTYPE alox_EspNowClearIdBinding
#define alox_EspNowMessage_payload_input_send_mode_MSGTYPE alox_EspNowInputSendMode
extern const pb_msgdesc_t alox_EspNowUnicastTest_msg; extern const pb_msgdesc_t alox_EspNowUnicastTest_msg;
extern const pb_msgdesc_t alox_EspNowEchoPing_msg; extern const pb_msgdesc_t alox_EspNowEchoPing_msg;
@@ -615,6 +640,7 @@ extern const pb_msgdesc_t alox_EspNowBatteryQuery_msg;
extern const pb_msgdesc_t alox_EspNowTapNotify_msg; extern const pb_msgdesc_t alox_EspNowTapNotify_msg;
extern const pb_msgdesc_t alox_EspNowTapConfig_msg; extern const pb_msgdesc_t alox_EspNowTapConfig_msg;
extern const pb_msgdesc_t alox_EspNowTapEvent_msg; extern const pb_msgdesc_t alox_EspNowTapEvent_msg;
extern const pb_msgdesc_t alox_EspNowInputSendMode_msg;
extern const pb_msgdesc_t alox_EspNowBatteryReport_msg; extern const pb_msgdesc_t alox_EspNowBatteryReport_msg;
extern const pb_msgdesc_t alox_EspNowLedRing_msg; extern const pb_msgdesc_t alox_EspNowLedRing_msg;
extern const pb_msgdesc_t alox_EspNowOtaStart_msg; extern const pb_msgdesc_t alox_EspNowOtaStart_msg;
@@ -640,6 +666,7 @@ extern const pb_msgdesc_t alox_EspNowMessage_msg;
#define alox_EspNowTapNotify_fields &alox_EspNowTapNotify_msg #define alox_EspNowTapNotify_fields &alox_EspNowTapNotify_msg
#define alox_EspNowTapConfig_fields &alox_EspNowTapConfig_msg #define alox_EspNowTapConfig_fields &alox_EspNowTapConfig_msg
#define alox_EspNowTapEvent_fields &alox_EspNowTapEvent_msg #define alox_EspNowTapEvent_fields &alox_EspNowTapEvent_msg
#define alox_EspNowInputSendMode_fields &alox_EspNowInputSendMode_msg
#define alox_EspNowBatteryReport_fields &alox_EspNowBatteryReport_msg #define alox_EspNowBatteryReport_fields &alox_EspNowBatteryReport_msg
#define alox_EspNowLedRing_fields &alox_EspNowLedRing_msg #define alox_EspNowLedRing_fields &alox_EspNowLedRing_msg
#define alox_EspNowOtaStart_fields &alox_EspNowOtaStart_msg #define alox_EspNowOtaStart_fields &alox_EspNowOtaStart_msg
@@ -663,6 +690,7 @@ extern const pb_msgdesc_t alox_EspNowMessage_msg;
#define alox_EspNowEchoPing_size 22 #define alox_EspNowEchoPing_size 22
#define alox_EspNowEchoPong_size 22 #define alox_EspNowEchoPong_size 22
#define alox_EspNowFindMe_size 6 #define alox_EspNowFindMe_size 6
#define alox_EspNowInputSendMode_size 12
#define alox_EspNowLedRing_size 60 #define alox_EspNowLedRing_size 60
#define alox_EspNowOtaEnd_size 0 #define alox_EspNowOtaEnd_size 0
#define alox_EspNowOtaPayload_size 209 #define alox_EspNowOtaPayload_size 209
+12
View File
@@ -31,6 +31,8 @@ enum EspNowMessageType {
ESPNOW_ASSIGN_ID = 22; ESPNOW_ASSIGN_ID = 22;
/** Master → slave: forget persisted master/id binding (debug reset). */ /** Master → slave: forget persisted master/id binding (debug reset). */
ESPNOW_CLEAR_ID_BINDING = 23; ESPNOW_CLEAR_ID_BINDING = 23;
/** Master → slave: accel/tap ESP-NOW send path (best_effort/fast/wait/reliable). */
ESPNOW_SET_INPUT_SEND_MODE = 24;
} }
message EspNowUnicastTest { message EspNowUnicastTest {
@@ -138,6 +140,15 @@ message EspNowTapEvent {
uint32 kind = 2; uint32 kind = 2;
} }
/**
* Master → slave: which esp_now_core_send_* path to use for ACCEL_SAMPLE / TAP_EVENT.
* Values match uart InputSendMode (0=best_effort … 3=reliable).
*/
message EspNowInputSendMode {
uint32 client_id = 1; // 0 = all slaves; otherwise only matching slave_id applies
uint32 mode = 2;
}
/** Slave → master: LiPo voltages (periodic ~30 s and on query). */ /** Slave → master: LiPo voltages (periodic ~30 s and on query). */
message EspNowBatteryReport { message EspNowBatteryReport {
uint32 client_id = 1; uint32 client_id = 1;
@@ -208,5 +219,6 @@ message EspNowMessage {
EspNowTapConfig tap_config = 22; EspNowTapConfig tap_config = 22;
EspNowAssignId assign_id = 23; EspNowAssignId assign_id = 23;
EspNowClearIdBinding clear_id_binding = 24; EspNowClearIdBinding clear_id_binding = 24;
EspNowInputSendMode input_send_mode = 25;
} }
} }
+8
View File
@@ -42,6 +42,12 @@ PB_BIND(alox_AccelStreamRequest, alox_AccelStreamRequest, AUTO)
PB_BIND(alox_AccelStreamResponse, alox_AccelStreamResponse, AUTO) PB_BIND(alox_AccelStreamResponse, alox_AccelStreamResponse, AUTO)
PB_BIND(alox_InputSendModeRequest, alox_InputSendModeRequest, AUTO)
PB_BIND(alox_InputSendModeResponse, alox_InputSendModeResponse, AUTO)
PB_BIND(alox_BatteryStatusRequest, alox_BatteryStatusRequest, AUTO) PB_BIND(alox_BatteryStatusRequest, alox_BatteryStatusRequest, AUTO)
@@ -158,3 +164,5 @@ PB_BIND(alox_OtaSlaveProgressResponse, alox_OtaSlaveProgressResponse, 2)
+86 -5
View File
@@ -44,9 +44,23 @@ typedef enum _alox_MessageType {
/* * /* *
Host → device: clear master client-id NVS map + live registry (and notify Host → device: clear master client-id NVS map + live registry (and notify
online slaves), or on a slave clear local master/id binding only. */ online slaves), or on a slave clear local master/id binding only. */
alox_MessageType_CLIENT_ID_MAP_RESET = 34 alox_MessageType_CLIENT_ID_MAP_RESET = 34,
/* *
Host → master: get/set slave ESP-NOW send path for accel/tap
(best_effort / fast / wait / reliable). */
alox_MessageType_INPUT_SEND_MODE = 35
} alox_MessageType; } alox_MessageType;
/* *
Slave ESP-NOW TX path for ACCEL_SAMPLE / TAP_EVENT (debug A/B).
Maps 1:1 to esp_now_core_send_*. */
typedef enum _alox_InputSendMode {
alox_InputSendMode_INPUT_SEND_BEST_EFFORT = 0,
alox_InputSendMode_INPUT_SEND_FAST = 1,
alox_InputSendMode_INPUT_SEND_WAIT = 2,
alox_InputSendMode_INPUT_SEND_RELIABLE = 3
} alox_InputSendMode;
typedef enum _alox_TapKind { typedef enum _alox_TapKind {
alox_TapKind_TAP_NONE = 0, alox_TapKind_TAP_NONE = 0,
alox_TapKind_TAP_SINGLE = 1, alox_TapKind_TAP_SINGLE = 1,
@@ -136,6 +150,23 @@ typedef struct _alox_AccelStreamResponse {
uint32_t slaves_updated; uint32_t slaves_updated;
} alox_AccelStreamResponse; } alox_AccelStreamResponse;
/* Host → master: get/set input send mode on slaves.
write=false: return last mode remembered on master (client_id ignored).
write=true: push mode via ESP-NOW (all_clients or one client_id). */
typedef struct _alox_InputSendModeRequest {
bool write;
alox_InputSendMode mode;
uint32_t client_id;
bool all_clients;
} alox_InputSendModeRequest;
typedef struct _alox_InputSendModeResponse {
bool success;
alox_InputSendMode mode;
uint32_t client_id;
uint32_t slaves_updated;
} alox_InputSendModeResponse;
/* * Host → master: read LiPo ADC voltages (master local and/or slaves via ESP-NOW). */ /* * Host → master: read LiPo ADC voltages (master local and/or slaves via ESP-NOW). */
typedef struct _alox_BatteryStatusRequest { typedef struct _alox_BatteryStatusRequest {
/* * 0 = master only; >0 = one slave; ignored when all_clients */ /* * 0 = master only; >0 = one slave; ignored when all_clients */
@@ -380,7 +411,7 @@ typedef struct _alox_TapConfigResponse {
bool enable_triple; bool enable_triple;
} alox_TapConfigResponse; } alox_TapConfigResponse;
/* Host → device: begin UART OTA (erase inactive OTA slot; device replies OTA_STATUS). */ /* Host → device: begin host OTA over UART or BLE (erase inactive OTA slot; device replies OTA_STATUS). */
typedef struct _alox_OtaStartPayload { typedef struct _alox_OtaStartPayload {
uint32_t total_size; uint32_t total_size;
} alox_OtaStartPayload; } alox_OtaStartPayload;
@@ -471,6 +502,8 @@ typedef struct _alox_UartMessage {
alox_BleStreamTick ble_stream_tick; alox_BleStreamTick ble_stream_tick;
alox_ClientIdMapResetRequest client_id_map_reset_request; alox_ClientIdMapResetRequest client_id_map_reset_request;
alox_ClientIdMapResetResponse client_id_map_reset_response; alox_ClientIdMapResetResponse client_id_map_reset_response;
alox_InputSendModeRequest input_send_mode_request;
alox_InputSendModeResponse input_send_mode_response;
} payload; } payload;
} alox_UartMessage; } alox_UartMessage;
@@ -481,8 +514,12 @@ extern "C" {
/* Helper constants for enums */ /* Helper constants for enums */
#define _alox_MessageType_MIN alox_MessageType_UNKNOWN #define _alox_MessageType_MIN alox_MessageType_UNKNOWN
#define _alox_MessageType_MAX alox_MessageType_CLIENT_ID_MAP_RESET #define _alox_MessageType_MAX alox_MessageType_INPUT_SEND_MODE
#define _alox_MessageType_ARRAYSIZE ((alox_MessageType)(alox_MessageType_CLIENT_ID_MAP_RESET+1)) #define _alox_MessageType_ARRAYSIZE ((alox_MessageType)(alox_MessageType_INPUT_SEND_MODE+1))
#define _alox_InputSendMode_MIN alox_InputSendMode_INPUT_SEND_BEST_EFFORT
#define _alox_InputSendMode_MAX alox_InputSendMode_INPUT_SEND_RELIABLE
#define _alox_InputSendMode_ARRAYSIZE ((alox_InputSendMode)(alox_InputSendMode_INPUT_SEND_RELIABLE+1))
#define _alox_TapKind_MIN alox_TapKind_TAP_NONE #define _alox_TapKind_MIN alox_TapKind_TAP_NONE
#define _alox_TapKind_MAX alox_TapKind_TAP_TRIPLE #define _alox_TapKind_MAX alox_TapKind_TAP_TRIPLE
@@ -501,6 +538,10 @@ extern "C" {
#define alox_InputSendModeRequest_mode_ENUMTYPE alox_InputSendMode
#define alox_InputSendModeResponse_mode_ENUMTYPE alox_InputSendMode
@@ -554,6 +595,8 @@ extern "C" {
#define alox_AccelDeadzoneResponse_init_default {0, 0, 0, 0} #define alox_AccelDeadzoneResponse_init_default {0, 0, 0, 0}
#define alox_AccelStreamRequest_init_default {0, 0, 0, 0, 0} #define alox_AccelStreamRequest_init_default {0, 0, 0, 0, 0}
#define alox_AccelStreamResponse_init_default {0, 0, 0, 0} #define alox_AccelStreamResponse_init_default {0, 0, 0, 0}
#define alox_InputSendModeRequest_init_default {0, _alox_InputSendMode_MIN, 0, 0}
#define alox_InputSendModeResponse_init_default {0, _alox_InputSendMode_MIN, 0, 0}
#define alox_BatteryStatusRequest_init_default {0, 0} #define alox_BatteryStatusRequest_init_default {0, 0}
#define alox_LipoReading_init_default {0, 0} #define alox_LipoReading_init_default {0, 0}
#define alox_BatterySample_init_default {0, false, alox_LipoReading_init_default, false, alox_LipoReading_init_default, 0} #define alox_BatterySample_init_default {0, false, alox_LipoReading_init_default, false, alox_LipoReading_init_default, 0}
@@ -603,6 +646,8 @@ extern "C" {
#define alox_AccelDeadzoneResponse_init_zero {0, 0, 0, 0} #define alox_AccelDeadzoneResponse_init_zero {0, 0, 0, 0}
#define alox_AccelStreamRequest_init_zero {0, 0, 0, 0, 0} #define alox_AccelStreamRequest_init_zero {0, 0, 0, 0, 0}
#define alox_AccelStreamResponse_init_zero {0, 0, 0, 0} #define alox_AccelStreamResponse_init_zero {0, 0, 0, 0}
#define alox_InputSendModeRequest_init_zero {0, _alox_InputSendMode_MIN, 0, 0}
#define alox_InputSendModeResponse_init_zero {0, _alox_InputSendMode_MIN, 0, 0}
#define alox_BatteryStatusRequest_init_zero {0, 0} #define alox_BatteryStatusRequest_init_zero {0, 0}
#define alox_LipoReading_init_zero {0, 0} #define alox_LipoReading_init_zero {0, 0}
#define alox_BatterySample_init_zero {0, false, alox_LipoReading_init_zero, false, alox_LipoReading_init_zero, 0} #define alox_BatterySample_init_zero {0, false, alox_LipoReading_init_zero, false, alox_LipoReading_init_zero, 0}
@@ -680,6 +725,14 @@ extern "C" {
#define alox_AccelStreamResponse_client_id_tag 2 #define alox_AccelStreamResponse_client_id_tag 2
#define alox_AccelStreamResponse_success_tag 3 #define alox_AccelStreamResponse_success_tag 3
#define alox_AccelStreamResponse_slaves_updated_tag 4 #define alox_AccelStreamResponse_slaves_updated_tag 4
#define alox_InputSendModeRequest_write_tag 1
#define alox_InputSendModeRequest_mode_tag 2
#define alox_InputSendModeRequest_client_id_tag 3
#define alox_InputSendModeRequest_all_clients_tag 4
#define alox_InputSendModeResponse_success_tag 1
#define alox_InputSendModeResponse_mode_tag 2
#define alox_InputSendModeResponse_client_id_tag 3
#define alox_InputSendModeResponse_slaves_updated_tag 4
#define alox_BatteryStatusRequest_client_id_tag 1 #define alox_BatteryStatusRequest_client_id_tag 1
#define alox_BatteryStatusRequest_all_clients_tag 2 #define alox_BatteryStatusRequest_all_clients_tag 2
#define alox_LipoReading_valid_tag 1 #define alox_LipoReading_valid_tag 1
@@ -846,6 +899,8 @@ extern "C" {
#define alox_UartMessage_ble_stream_tick_tag 41 #define alox_UartMessage_ble_stream_tick_tag 41
#define alox_UartMessage_client_id_map_reset_request_tag 42 #define alox_UartMessage_client_id_map_reset_request_tag 42
#define alox_UartMessage_client_id_map_reset_response_tag 43 #define alox_UartMessage_client_id_map_reset_response_tag 43
#define alox_UartMessage_input_send_mode_request_tag 44
#define alox_UartMessage_input_send_mode_response_tag 45
/* Struct field encoding specification for nanopb */ /* Struct field encoding specification for nanopb */
#define alox_UartMessage_FIELDLIST(X, a) \ #define alox_UartMessage_FIELDLIST(X, a) \
@@ -887,7 +942,9 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config_request,payload.tap_confi
X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config_response,payload.tap_config_response), 40) \ X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config_response,payload.tap_config_response), 40) \
X(a, STATIC, ONEOF, MESSAGE, (payload,ble_stream_tick,payload.ble_stream_tick), 41) \ X(a, STATIC, ONEOF, MESSAGE, (payload,ble_stream_tick,payload.ble_stream_tick), 41) \
X(a, STATIC, ONEOF, MESSAGE, (payload,client_id_map_reset_request,payload.client_id_map_reset_request), 42) \ X(a, STATIC, ONEOF, MESSAGE, (payload,client_id_map_reset_request,payload.client_id_map_reset_request), 42) \
X(a, STATIC, ONEOF, MESSAGE, (payload,client_id_map_reset_response,payload.client_id_map_reset_response), 43) X(a, STATIC, ONEOF, MESSAGE, (payload,client_id_map_reset_response,payload.client_id_map_reset_response), 43) \
X(a, STATIC, ONEOF, MESSAGE, (payload,input_send_mode_request,payload.input_send_mode_request), 44) \
X(a, STATIC, ONEOF, MESSAGE, (payload,input_send_mode_response,payload.input_send_mode_response), 45)
#define alox_UartMessage_CALLBACK NULL #define alox_UartMessage_CALLBACK NULL
#define alox_UartMessage_DEFAULT NULL #define alox_UartMessage_DEFAULT NULL
#define alox_UartMessage_payload_ack_payload_MSGTYPE alox_Ack #define alox_UartMessage_payload_ack_payload_MSGTYPE alox_Ack
@@ -928,6 +985,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,client_id_map_reset_response,payload
#define alox_UartMessage_payload_ble_stream_tick_MSGTYPE alox_BleStreamTick #define alox_UartMessage_payload_ble_stream_tick_MSGTYPE alox_BleStreamTick
#define alox_UartMessage_payload_client_id_map_reset_request_MSGTYPE alox_ClientIdMapResetRequest #define alox_UartMessage_payload_client_id_map_reset_request_MSGTYPE alox_ClientIdMapResetRequest
#define alox_UartMessage_payload_client_id_map_reset_response_MSGTYPE alox_ClientIdMapResetResponse #define alox_UartMessage_payload_client_id_map_reset_response_MSGTYPE alox_ClientIdMapResetResponse
#define alox_UartMessage_payload_input_send_mode_request_MSGTYPE alox_InputSendModeRequest
#define alox_UartMessage_payload_input_send_mode_response_MSGTYPE alox_InputSendModeResponse
#define alox_Ack_FIELDLIST(X, a) \ #define alox_Ack_FIELDLIST(X, a) \
@@ -1014,6 +1073,22 @@ X(a, STATIC, SINGULAR, UINT32, slaves_updated, 4)
#define alox_AccelStreamResponse_CALLBACK NULL #define alox_AccelStreamResponse_CALLBACK NULL
#define alox_AccelStreamResponse_DEFAULT NULL #define alox_AccelStreamResponse_DEFAULT NULL
#define alox_InputSendModeRequest_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, write, 1) \
X(a, STATIC, SINGULAR, UENUM, mode, 2) \
X(a, STATIC, SINGULAR, UINT32, client_id, 3) \
X(a, STATIC, SINGULAR, BOOL, all_clients, 4)
#define alox_InputSendModeRequest_CALLBACK NULL
#define alox_InputSendModeRequest_DEFAULT NULL
#define alox_InputSendModeResponse_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, success, 1) \
X(a, STATIC, SINGULAR, UENUM, mode, 2) \
X(a, STATIC, SINGULAR, UINT32, client_id, 3) \
X(a, STATIC, SINGULAR, UINT32, slaves_updated, 4)
#define alox_InputSendModeResponse_CALLBACK NULL
#define alox_InputSendModeResponse_DEFAULT NULL
#define alox_BatteryStatusRequest_FIELDLIST(X, a) \ #define alox_BatteryStatusRequest_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, client_id, 1) \ X(a, STATIC, SINGULAR, UINT32, client_id, 1) \
X(a, STATIC, SINGULAR, BOOL, all_clients, 2) X(a, STATIC, SINGULAR, BOOL, all_clients, 2)
@@ -1312,6 +1387,8 @@ extern const pb_msgdesc_t alox_AccelDeadzoneRequest_msg;
extern const pb_msgdesc_t alox_AccelDeadzoneResponse_msg; extern const pb_msgdesc_t alox_AccelDeadzoneResponse_msg;
extern const pb_msgdesc_t alox_AccelStreamRequest_msg; extern const pb_msgdesc_t alox_AccelStreamRequest_msg;
extern const pb_msgdesc_t alox_AccelStreamResponse_msg; extern const pb_msgdesc_t alox_AccelStreamResponse_msg;
extern const pb_msgdesc_t alox_InputSendModeRequest_msg;
extern const pb_msgdesc_t alox_InputSendModeResponse_msg;
extern const pb_msgdesc_t alox_BatteryStatusRequest_msg; extern const pb_msgdesc_t alox_BatteryStatusRequest_msg;
extern const pb_msgdesc_t alox_LipoReading_msg; extern const pb_msgdesc_t alox_LipoReading_msg;
extern const pb_msgdesc_t alox_BatterySample_msg; extern const pb_msgdesc_t alox_BatterySample_msg;
@@ -1363,6 +1440,8 @@ extern const pb_msgdesc_t alox_OtaSlaveProgressResponse_msg;
#define alox_AccelDeadzoneResponse_fields &alox_AccelDeadzoneResponse_msg #define alox_AccelDeadzoneResponse_fields &alox_AccelDeadzoneResponse_msg
#define alox_AccelStreamRequest_fields &alox_AccelStreamRequest_msg #define alox_AccelStreamRequest_fields &alox_AccelStreamRequest_msg
#define alox_AccelStreamResponse_fields &alox_AccelStreamResponse_msg #define alox_AccelStreamResponse_fields &alox_AccelStreamResponse_msg
#define alox_InputSendModeRequest_fields &alox_InputSendModeRequest_msg
#define alox_InputSendModeResponse_fields &alox_InputSendModeResponse_msg
#define alox_BatteryStatusRequest_fields &alox_BatteryStatusRequest_msg #define alox_BatteryStatusRequest_fields &alox_BatteryStatusRequest_msg
#define alox_LipoReading_fields &alox_LipoReading_msg #define alox_LipoReading_fields &alox_LipoReading_msg
#define alox_BatterySample_fields &alox_BatterySample_msg #define alox_BatterySample_fields &alox_BatterySample_msg
@@ -1433,6 +1512,8 @@ extern const pb_msgdesc_t alox_OtaSlaveProgressResponse_msg;
#define alox_EspNowFindMeResponse_size 8 #define alox_EspNowFindMeResponse_size 8
#define alox_EspNowUnicastTestRequest_size 12 #define alox_EspNowUnicastTestRequest_size 12
#define alox_EspNowUnicastTestResponse_size 8 #define alox_EspNowUnicastTestResponse_size 8
#define alox_InputSendModeRequest_size 12
#define alox_InputSendModeResponse_size 16
#define alox_LedRingProgressRequest_size 64 #define alox_LedRingProgressRequest_size 64
#define alox_LedRingProgressResponse_size 32 #define alox_LedRingProgressResponse_size 32
#define alox_LipoReading_size 8 #define alox_LipoReading_size 8
+35
View File
@@ -42,6 +42,11 @@ enum MessageType {
* online slaves), or on a slave clear local master/id binding only. * online slaves), or on a slave clear local master/id binding only.
*/ */
CLIENT_ID_MAP_RESET = 34; CLIENT_ID_MAP_RESET = 34;
/**
* Host → master: get/set slave ESP-NOW send path for accel/tap
* (best_effort / fast / wait / reliable).
*/
INPUT_SEND_MODE = 35;
} }
message UartMessage { message UartMessage {
@@ -85,6 +90,8 @@ message UartMessage {
BleStreamTick ble_stream_tick = 41; BleStreamTick ble_stream_tick = 41;
ClientIdMapResetRequest client_id_map_reset_request = 42; ClientIdMapResetRequest client_id_map_reset_request = 42;
ClientIdMapResetResponse client_id_map_reset_response = 43; ClientIdMapResetResponse client_id_map_reset_response = 43;
InputSendModeRequest input_send_mode_request = 44;
InputSendModeResponse input_send_mode_response = 45;
} }
} }
@@ -167,6 +174,34 @@ message AccelStreamResponse {
uint32 slaves_updated = 4; uint32 slaves_updated = 4;
} }
/**
* Slave ESP-NOW TX path for ACCEL_SAMPLE / TAP_EVENT (debug A/B).
* Maps 1:1 to esp_now_core_send_*.
*/
enum InputSendMode {
INPUT_SEND_BEST_EFFORT = 0;
INPUT_SEND_FAST = 1;
INPUT_SEND_WAIT = 2;
INPUT_SEND_RELIABLE = 3;
}
// Host → master: get/set input send mode on slaves.
// write=false: return last mode remembered on master (client_id ignored).
// write=true: push mode via ESP-NOW (all_clients or one client_id).
message InputSendModeRequest {
bool write = 1;
InputSendMode mode = 2;
uint32 client_id = 3;
bool all_clients = 4;
}
message InputSendModeResponse {
bool success = 1;
InputSendMode mode = 2;
uint32 client_id = 3;
uint32 slaves_updated = 4;
}
/** Host → master: read LiPo ADC voltages (master local and/or slaves via ESP-NOW). */ /** Host → master: read LiPo ADC voltages (master local and/or slaves via ESP-NOW). */
message BatteryStatusRequest { message BatteryStatusRequest {
/** 0 = master only; >0 = one slave; ignored when all_clients */ /** 0 = master only; >0 = one slave; ignored when all_clients */