Assign sequential pod IDs from the master with NVS-stable bindings.
Replaces MAC-byte self-IDs with master allocation (1–255), slave preferred-ID reconnect, debug reset via UART/UI, and rate-limited ASSIGN to avoid ESP-NOW NO_MEM storms. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -64,6 +64,11 @@ type findMeAPIResponse struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type resetIdMapAPIResponse struct {
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type restartAPIRequest struct {
|
||||
ClientID uint32 `json:"client_id"`
|
||||
}
|
||||
@@ -139,6 +144,9 @@ func mountServeAPI(mux *http.ServeMux, link *managedSerial, hub *wsHub, streamCt
|
||||
}
|
||||
serveRestart(w, r, link)
|
||||
})
|
||||
mux.HandleFunc("POST /api/clients/reset-id-map", func(w http.ResponseWriter, r *http.Request) {
|
||||
serveResetClientIdMap(w, r, link)
|
||||
})
|
||||
mux.HandleFunc("/api/log-level", func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
@@ -361,6 +369,23 @@ func serveRestart(w http.ResponseWriter, r *http.Request, link *managedSerial) {
|
||||
writeJSON(w, http.StatusOK, restartAPIResponse{Success: true, ClientID: body.ClientID})
|
||||
}
|
||||
|
||||
func serveResetClientIdMap(w http.ResponseWriter, r *http.Request, link *managedSerial) {
|
||||
if !link.IsConnected() {
|
||||
writeJSON(w, http.StatusServiceUnavailable, resetIdMapAPIResponse{Error: "host not connected"})
|
||||
return
|
||||
}
|
||||
resp, err := link.resetClientIdMap()
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusServiceUnavailable, resetIdMapAPIResponse{Error: err.Error()})
|
||||
return
|
||||
}
|
||||
if !resp.GetSuccess() {
|
||||
writeJSON(w, http.StatusOK, resetIdMapAPIResponse{Success: false, Error: "rejected by device"})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, resetIdMapAPIResponse{Success: true})
|
||||
}
|
||||
|
||||
func serveFindMe(w http.ResponseWriter, r *http.Request, link *managedSerial) {
|
||||
var body findMeAPIRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
|
||||
@@ -84,13 +84,7 @@ func NewBench(cfg Config) (*Bench, error) {
|
||||
return nil, fmt.Errorf("config %q slave %q: %w", cfg.ID, s.ID, err)
|
||||
}
|
||||
s.MAC = mac
|
||||
if s.ClientID == nil {
|
||||
parts := strings.Split(mac, ":")
|
||||
var last byte
|
||||
fmt.Sscanf(parts[5], "%02x", &last)
|
||||
id := uint(last)
|
||||
s.ClientID = &id
|
||||
}
|
||||
/* client_id is master-assigned (sequential); omit until known from `gotool clients`. */
|
||||
b.slaveByID[s.ID] = s
|
||||
}
|
||||
return b, nil
|
||||
@@ -112,6 +106,9 @@ func (b *Bench) ResolveClientID(slaveID string) (uint32, error) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if s.ClientID == nil {
|
||||
return 0, fmt.Errorf("slave %q: client_id required (master-assigned; set in config after join)", slaveID)
|
||||
}
|
||||
return uint32(*s.ClientID), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package autotest
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNewBenchClientIDFromMAC(t *testing.T) {
|
||||
func TestNewBenchNoDefaultClientIDFromMAC(t *testing.T) {
|
||||
cfg := Config{
|
||||
ID: "t",
|
||||
Network: 1,
|
||||
@@ -20,8 +20,39 @@ func TestNewBenchClientIDFromMAC(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, _ := b.Slave("pod")
|
||||
if *s.ClientID != 16 {
|
||||
t.Fatalf("client_id=%d want 16", *s.ClientID)
|
||||
if s.ClientID != nil {
|
||||
t.Fatalf("client_id should be unset, got %d", *s.ClientID)
|
||||
}
|
||||
if _, err := b.ResolveClientID("pod"); err == nil {
|
||||
t.Fatal("ResolveClientID should fail without client_id")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBenchExplicitClientID(t *testing.T) {
|
||||
id := uint(3)
|
||||
cfg := Config{
|
||||
ID: "t",
|
||||
Network: 1,
|
||||
MasterMAC: "aa:bb:cc:dd:ee:ff",
|
||||
UART: UARTConfig{
|
||||
Master: "/dev/ttyUSB0",
|
||||
},
|
||||
Slaves: []SlaveNode{{
|
||||
ID: "pod",
|
||||
MAC: "50:78:7d:18:01:10",
|
||||
ClientID: &id,
|
||||
}},
|
||||
}
|
||||
b, err := NewBench(cfg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := b.ResolveClientID("pod")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != 3 {
|
||||
t.Fatalf("client_id=%d want 3", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ func checkClients(bench *Bench, step Step, client MasterClient) error {
|
||||
if found == nil {
|
||||
return fmt.Errorf("slave %q mac %s not in client list", name, want.MAC)
|
||||
}
|
||||
if uint32(*want.ClientID) != found.GetId() {
|
||||
if want.ClientID != nil && uint32(*want.ClientID) != found.GetId() {
|
||||
return fmt.Errorf("slave %q id=%d want client_id=%d", name, found.GetId(), *want.ClientID)
|
||||
}
|
||||
if e.Available != nil && found.GetAvailable() != *e.Available {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"powerpod/gotool/pb"
|
||||
)
|
||||
|
||||
func runClientsResetIds(sp *serialPort) error {
|
||||
resp, err := sp.resetClientIdMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !resp.GetSuccess() {
|
||||
return fmt.Errorf("client id map reset rejected")
|
||||
}
|
||||
fmt.Println("client id map cleared (master map + live registry; online slaves notified)")
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeClientIdMapResetPayload(payload []byte) (*pb.ClientIdMapResetResponse, error) {
|
||||
if len(payload) < 2 {
|
||||
return nil, fmt.Errorf("short client id map reset response")
|
||||
}
|
||||
if payload[0] != byte(pb.MessageType_CLIENT_ID_MAP_RESET) {
|
||||
return nil, fmt.Errorf("unexpected command id 0x%02x (want 0x%02x)",
|
||||
payload[0], byte(pb.MessageType_CLIENT_ID_MAP_RESET))
|
||||
}
|
||||
var respMsg pb.UartMessage
|
||||
if err := proto.Unmarshal(payload[1:], &respMsg); err != nil {
|
||||
return nil, fmt.Errorf("decode: %w", err)
|
||||
}
|
||||
r := respMsg.GetClientIdMapResetResponse()
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("missing client_id_map_reset_response")
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (s *serialPort) resetClientIdMap() (*pb.ClientIdMapResetResponse, error) {
|
||||
payload, err := s.exchange(byte(pb.MessageType_CLIENT_ID_MAP_RESET), "CLIENT_ID_MAP_RESET")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decodeClientIdMapResetPayload(payload)
|
||||
}
|
||||
|
||||
func (m *managedSerial) resetClientIdMap() (*pb.ClientIdMapResetResponse, error) {
|
||||
payload, err := m.exchange(byte(pb.MessageType_CLIENT_ID_MAP_RESET), "CLIENT_ID_MAP_RESET")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decodeClientIdMapResetPayload(payload)
|
||||
}
|
||||
+10
-2
@@ -16,7 +16,8 @@ func usage() {
|
||||
fmt.Fprintf(os.Stderr, " test uses uart.master from bench config when -port is omitted\n\n")
|
||||
fmt.Fprintf(os.Stderr, "commands:\n")
|
||||
fmt.Fprintf(os.Stderr, " version firmware version and git hash\n")
|
||||
fmt.Fprintf(os.Stderr, " clients registered ESP-NOW slaves on the master\n")
|
||||
fmt.Fprintf(os.Stderr, " clients registered ESP-NOW slaves on the master\n")
|
||||
fmt.Fprintf(os.Stderr, " clients-reset-ids clear master id map + live registry (notifies online slaves)\n")
|
||||
fmt.Fprintf(os.Stderr, " deadzone get/set accelerometer deadzone (LSB)\n")
|
||||
fmt.Fprintf(os.Stderr, " tap-config get/set BMA456 tap detection parameters\n")
|
||||
fmt.Fprintf(os.Stderr, " tap-notify get/set which tap kinds notify via ESP-NOW\n")
|
||||
@@ -72,7 +73,7 @@ func main() {
|
||||
bleName: *bleName,
|
||||
settle: time.Duration(*bleSettle * float64(time.Second)),
|
||||
}, flag.Args()[1:])
|
||||
case "version", "clients", "client-info", "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", "ota", "ota-progress", "ota_progress":
|
||||
if *useBLE && *portName != "" {
|
||||
fmt.Fprintf(os.Stderr, "use either -port or -ble, not both\n")
|
||||
os.Exit(2)
|
||||
@@ -112,6 +113,13 @@ func main() {
|
||||
runErr = runVersionHost(h)
|
||||
case "clients", "client-info":
|
||||
runErr = runClientsHost(h)
|
||||
case "clients-reset-ids", "clients_reset_ids":
|
||||
sp, ok := h.(*serialPort)
|
||||
if !ok {
|
||||
runErr = fmt.Errorf("%s over BLE not wired yet; use -port", cmd)
|
||||
break
|
||||
}
|
||||
runErr = runClientsResetIds(sp)
|
||||
case "cache-status", "cache_status":
|
||||
runErr = runCacheStatusHost(h)
|
||||
case "accel-stream", "accel_stream":
|
||||
|
||||
+210
-79
@@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc v7.36.0
|
||||
// protoc v7.36.1
|
||||
// source: uart_messages.proto
|
||||
|
||||
package pb
|
||||
@@ -54,6 +54,10 @@ const (
|
||||
MessageType_TAP_CONFIG MessageType = 32
|
||||
// * Master → host (BLE only): periodic pushed cache snapshot with ESP timestamp.
|
||||
MessageType_BLE_STREAM_TICK MessageType = 33
|
||||
// *
|
||||
// Host → device: clear master client-id NVS map + live registry (and notify
|
||||
// online slaves), or on a slave clear local master/id binding only.
|
||||
MessageType_CLIENT_ID_MAP_RESET MessageType = 34
|
||||
)
|
||||
|
||||
// Enum value maps for MessageType.
|
||||
@@ -84,6 +88,7 @@ var (
|
||||
31: "SET_LOG_LEVEL",
|
||||
32: "TAP_CONFIG",
|
||||
33: "BLE_STREAM_TICK",
|
||||
34: "CLIENT_ID_MAP_RESET",
|
||||
}
|
||||
MessageType_value = map[string]int32{
|
||||
"UNKNOWN": 0,
|
||||
@@ -111,6 +116,7 @@ var (
|
||||
"SET_LOG_LEVEL": 31,
|
||||
"TAP_CONFIG": 32,
|
||||
"BLE_STREAM_TICK": 33,
|
||||
"CLIENT_ID_MAP_RESET": 34,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -234,6 +240,8 @@ type UartMessage struct {
|
||||
// *UartMessage_TapConfigRequest
|
||||
// *UartMessage_TapConfigResponse
|
||||
// *UartMessage_BleStreamTick
|
||||
// *UartMessage_ClientIdMapResetRequest
|
||||
// *UartMessage_ClientIdMapResetResponse
|
||||
Payload isUartMessage_Payload `protobuf_oneof:"payload"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -607,6 +615,24 @@ func (x *UartMessage) GetBleStreamTick() *BleStreamTick {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UartMessage) GetClientIdMapResetRequest() *ClientIdMapResetRequest {
|
||||
if x != nil {
|
||||
if x, ok := x.Payload.(*UartMessage_ClientIdMapResetRequest); ok {
|
||||
return x.ClientIdMapResetRequest
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *UartMessage) GetClientIdMapResetResponse() *ClientIdMapResetResponse {
|
||||
if x != nil {
|
||||
if x, ok := x.Payload.(*UartMessage_ClientIdMapResetResponse); ok {
|
||||
return x.ClientIdMapResetResponse
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isUartMessage_Payload interface {
|
||||
isUartMessage_Payload()
|
||||
}
|
||||
@@ -755,6 +781,14 @@ type UartMessage_BleStreamTick struct {
|
||||
BleStreamTick *BleStreamTick `protobuf:"bytes,41,opt,name=ble_stream_tick,json=bleStreamTick,proto3,oneof"`
|
||||
}
|
||||
|
||||
type UartMessage_ClientIdMapResetRequest struct {
|
||||
ClientIdMapResetRequest *ClientIdMapResetRequest `protobuf:"bytes,42,opt,name=client_id_map_reset_request,json=clientIdMapResetRequest,proto3,oneof"`
|
||||
}
|
||||
|
||||
type UartMessage_ClientIdMapResetResponse struct {
|
||||
ClientIdMapResetResponse *ClientIdMapResetResponse `protobuf:"bytes,43,opt,name=client_id_map_reset_response,json=clientIdMapResetResponse,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*UartMessage_AckPayload) isUartMessage_Payload() {}
|
||||
|
||||
func (*UartMessage_EchoPayload) isUartMessage_Payload() {}
|
||||
@@ -827,6 +861,10 @@ func (*UartMessage_TapConfigResponse) isUartMessage_Payload() {}
|
||||
|
||||
func (*UartMessage_BleStreamTick) isUartMessage_Payload() {}
|
||||
|
||||
func (*UartMessage_ClientIdMapResetRequest) isUartMessage_Payload() {}
|
||||
|
||||
func (*UartMessage_ClientIdMapResetResponse) isUartMessage_Payload() {}
|
||||
|
||||
type Ack struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
@@ -3071,6 +3109,87 @@ func (x *RestartResponse) GetClientId() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// * Host → master or slave: clear persisted client-id assignment state.
|
||||
type ClientIdMapResetRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClientIdMapResetRequest) Reset() {
|
||||
*x = ClientIdMapResetRequest{}
|
||||
mi := &file_uart_messages_proto_msgTypes[36]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ClientIdMapResetRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientIdMapResetRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ClientIdMapResetRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[36]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientIdMapResetRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ClientIdMapResetRequest) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{36}
|
||||
}
|
||||
|
||||
type ClientIdMapResetResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ClientIdMapResetResponse) Reset() {
|
||||
*x = ClientIdMapResetResponse{}
|
||||
mi := &file_uart_messages_proto_msgTypes[37]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ClientIdMapResetResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientIdMapResetResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ClientIdMapResetResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[37]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientIdMapResetResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ClientIdMapResetResponse) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{37}
|
||||
}
|
||||
|
||||
func (x *ClientIdMapResetResponse) GetSuccess() bool {
|
||||
if x != nil {
|
||||
return x.Success
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// * Host → master: read/write global log level (esp_log_level_set("*", …)).
|
||||
type SetLogLevelRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
@@ -3083,7 +3202,7 @@ type SetLogLevelRequest struct {
|
||||
|
||||
func (x *SetLogLevelRequest) Reset() {
|
||||
*x = SetLogLevelRequest{}
|
||||
mi := &file_uart_messages_proto_msgTypes[36]
|
||||
mi := &file_uart_messages_proto_msgTypes[38]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3095,7 +3214,7 @@ func (x *SetLogLevelRequest) String() string {
|
||||
func (*SetLogLevelRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SetLogLevelRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[36]
|
||||
mi := &file_uart_messages_proto_msgTypes[38]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3108,7 +3227,7 @@ func (x *SetLogLevelRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SetLogLevelRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SetLogLevelRequest) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{36}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{38}
|
||||
}
|
||||
|
||||
func (x *SetLogLevelRequest) GetWrite() bool {
|
||||
@@ -3135,7 +3254,7 @@ type SetLogLevelResponse struct {
|
||||
|
||||
func (x *SetLogLevelResponse) Reset() {
|
||||
*x = SetLogLevelResponse{}
|
||||
mi := &file_uart_messages_proto_msgTypes[37]
|
||||
mi := &file_uart_messages_proto_msgTypes[39]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3147,7 +3266,7 @@ func (x *SetLogLevelResponse) String() string {
|
||||
func (*SetLogLevelResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SetLogLevelResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[37]
|
||||
mi := &file_uart_messages_proto_msgTypes[39]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3160,7 +3279,7 @@ func (x *SetLogLevelResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SetLogLevelResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SetLogLevelResponse) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{37}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{39}
|
||||
}
|
||||
|
||||
func (x *SetLogLevelResponse) GetSuccess() bool {
|
||||
@@ -3199,7 +3318,7 @@ type TapConfigRequest struct {
|
||||
|
||||
func (x *TapConfigRequest) Reset() {
|
||||
*x = TapConfigRequest{}
|
||||
mi := &file_uart_messages_proto_msgTypes[38]
|
||||
mi := &file_uart_messages_proto_msgTypes[40]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3211,7 +3330,7 @@ func (x *TapConfigRequest) String() string {
|
||||
func (*TapConfigRequest) ProtoMessage() {}
|
||||
|
||||
func (x *TapConfigRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[38]
|
||||
mi := &file_uart_messages_proto_msgTypes[40]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3224,7 +3343,7 @@ func (x *TapConfigRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TapConfigRequest.ProtoReflect.Descriptor instead.
|
||||
func (*TapConfigRequest) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{38}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{40}
|
||||
}
|
||||
|
||||
func (x *TapConfigRequest) GetWrite() bool {
|
||||
@@ -3331,7 +3450,7 @@ type TapConfigResponse struct {
|
||||
|
||||
func (x *TapConfigResponse) Reset() {
|
||||
*x = TapConfigResponse{}
|
||||
mi := &file_uart_messages_proto_msgTypes[39]
|
||||
mi := &file_uart_messages_proto_msgTypes[41]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3343,7 +3462,7 @@ func (x *TapConfigResponse) String() string {
|
||||
func (*TapConfigResponse) ProtoMessage() {}
|
||||
|
||||
func (x *TapConfigResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[39]
|
||||
mi := &file_uart_messages_proto_msgTypes[41]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3356,7 +3475,7 @@ func (x *TapConfigResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use TapConfigResponse.ProtoReflect.Descriptor instead.
|
||||
func (*TapConfigResponse) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{39}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{41}
|
||||
}
|
||||
|
||||
func (x *TapConfigResponse) GetClientId() uint32 {
|
||||
@@ -3453,7 +3572,7 @@ type OtaStartPayload struct {
|
||||
|
||||
func (x *OtaStartPayload) Reset() {
|
||||
*x = OtaStartPayload{}
|
||||
mi := &file_uart_messages_proto_msgTypes[40]
|
||||
mi := &file_uart_messages_proto_msgTypes[42]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3465,7 +3584,7 @@ func (x *OtaStartPayload) String() string {
|
||||
func (*OtaStartPayload) ProtoMessage() {}
|
||||
|
||||
func (x *OtaStartPayload) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[40]
|
||||
mi := &file_uart_messages_proto_msgTypes[42]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3478,7 +3597,7 @@ func (x *OtaStartPayload) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use OtaStartPayload.ProtoReflect.Descriptor instead.
|
||||
func (*OtaStartPayload) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{40}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{42}
|
||||
}
|
||||
|
||||
func (x *OtaStartPayload) GetTotalSize() uint32 {
|
||||
@@ -3499,7 +3618,7 @@ type OtaPayload struct {
|
||||
|
||||
func (x *OtaPayload) Reset() {
|
||||
*x = OtaPayload{}
|
||||
mi := &file_uart_messages_proto_msgTypes[41]
|
||||
mi := &file_uart_messages_proto_msgTypes[43]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3511,7 +3630,7 @@ func (x *OtaPayload) String() string {
|
||||
func (*OtaPayload) ProtoMessage() {}
|
||||
|
||||
func (x *OtaPayload) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[41]
|
||||
mi := &file_uart_messages_proto_msgTypes[43]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3524,7 +3643,7 @@ func (x *OtaPayload) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use OtaPayload.ProtoReflect.Descriptor instead.
|
||||
func (*OtaPayload) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{41}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{43}
|
||||
}
|
||||
|
||||
func (x *OtaPayload) GetSeq() uint32 {
|
||||
@@ -3550,7 +3669,7 @@ type OtaEndPayload struct {
|
||||
|
||||
func (x *OtaEndPayload) Reset() {
|
||||
*x = OtaEndPayload{}
|
||||
mi := &file_uart_messages_proto_msgTypes[42]
|
||||
mi := &file_uart_messages_proto_msgTypes[44]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3562,7 +3681,7 @@ func (x *OtaEndPayload) String() string {
|
||||
func (*OtaEndPayload) ProtoMessage() {}
|
||||
|
||||
func (x *OtaEndPayload) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[42]
|
||||
mi := &file_uart_messages_proto_msgTypes[44]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3575,7 +3694,7 @@ func (x *OtaEndPayload) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use OtaEndPayload.ProtoReflect.Descriptor instead.
|
||||
func (*OtaEndPayload) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{42}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{44}
|
||||
}
|
||||
|
||||
// Device → host status (also used as ACK after each 4 KiB written).
|
||||
@@ -3592,7 +3711,7 @@ type OtaStatusPayload struct {
|
||||
|
||||
func (x *OtaStatusPayload) Reset() {
|
||||
*x = OtaStatusPayload{}
|
||||
mi := &file_uart_messages_proto_msgTypes[43]
|
||||
mi := &file_uart_messages_proto_msgTypes[45]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3604,7 +3723,7 @@ func (x *OtaStatusPayload) String() string {
|
||||
func (*OtaStatusPayload) ProtoMessage() {}
|
||||
|
||||
func (x *OtaStatusPayload) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[43]
|
||||
mi := &file_uart_messages_proto_msgTypes[45]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3617,7 +3736,7 @@ func (x *OtaStatusPayload) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use OtaStatusPayload.ProtoReflect.Descriptor instead.
|
||||
func (*OtaStatusPayload) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{43}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{45}
|
||||
}
|
||||
|
||||
func (x *OtaStatusPayload) GetStatus() uint32 {
|
||||
@@ -3658,7 +3777,7 @@ type OtaSlaveProgressRequest struct {
|
||||
|
||||
func (x *OtaSlaveProgressRequest) Reset() {
|
||||
*x = OtaSlaveProgressRequest{}
|
||||
mi := &file_uart_messages_proto_msgTypes[44]
|
||||
mi := &file_uart_messages_proto_msgTypes[46]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3670,7 +3789,7 @@ func (x *OtaSlaveProgressRequest) String() string {
|
||||
func (*OtaSlaveProgressRequest) ProtoMessage() {}
|
||||
|
||||
func (x *OtaSlaveProgressRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[44]
|
||||
mi := &file_uart_messages_proto_msgTypes[46]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3683,7 +3802,7 @@ func (x *OtaSlaveProgressRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use OtaSlaveProgressRequest.ProtoReflect.Descriptor instead.
|
||||
func (*OtaSlaveProgressRequest) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{44}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{46}
|
||||
}
|
||||
|
||||
func (x *OtaSlaveProgressRequest) GetClientId() uint32 {
|
||||
@@ -3707,7 +3826,7 @@ type OtaSlaveProgressEntry struct {
|
||||
|
||||
func (x *OtaSlaveProgressEntry) Reset() {
|
||||
*x = OtaSlaveProgressEntry{}
|
||||
mi := &file_uart_messages_proto_msgTypes[45]
|
||||
mi := &file_uart_messages_proto_msgTypes[47]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3719,7 +3838,7 @@ func (x *OtaSlaveProgressEntry) String() string {
|
||||
func (*OtaSlaveProgressEntry) ProtoMessage() {}
|
||||
|
||||
func (x *OtaSlaveProgressEntry) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[45]
|
||||
mi := &file_uart_messages_proto_msgTypes[47]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3732,7 +3851,7 @@ func (x *OtaSlaveProgressEntry) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use OtaSlaveProgressEntry.ProtoReflect.Descriptor instead.
|
||||
func (*OtaSlaveProgressEntry) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{45}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{47}
|
||||
}
|
||||
|
||||
func (x *OtaSlaveProgressEntry) GetClientId() uint32 {
|
||||
@@ -3783,7 +3902,7 @@ type OtaSlaveProgressResponse struct {
|
||||
|
||||
func (x *OtaSlaveProgressResponse) Reset() {
|
||||
*x = OtaSlaveProgressResponse{}
|
||||
mi := &file_uart_messages_proto_msgTypes[46]
|
||||
mi := &file_uart_messages_proto_msgTypes[48]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -3795,7 +3914,7 @@ func (x *OtaSlaveProgressResponse) String() string {
|
||||
func (*OtaSlaveProgressResponse) ProtoMessage() {}
|
||||
|
||||
func (x *OtaSlaveProgressResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_uart_messages_proto_msgTypes[46]
|
||||
mi := &file_uart_messages_proto_msgTypes[48]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -3808,7 +3927,7 @@ func (x *OtaSlaveProgressResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use OtaSlaveProgressResponse.ProtoReflect.Descriptor instead.
|
||||
func (*OtaSlaveProgressResponse) Descriptor() ([]byte, []int) {
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{46}
|
||||
return file_uart_messages_proto_rawDescGZIP(), []int{48}
|
||||
}
|
||||
|
||||
func (x *OtaSlaveProgressResponse) GetActive() bool {
|
||||
@@ -3850,7 +3969,7 @@ var File_uart_messages_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_uart_messages_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x13uart_messages.proto\x12\x04alox\x1a\fnanopb.proto\"\x92\x16\n" +
|
||||
"\x13uart_messages.proto\x12\x04alox\x1a\fnanopb.proto\"\xd3\x17\n" +
|
||||
"\vUartMessage\x12%\n" +
|
||||
"\x04type\x18\x01 \x01(\x0e2\x11.alox.MessageTypeR\x04type\x12,\n" +
|
||||
"\vack_payload\x18\x02 \x01(\v2\t.alox.AckH\x00R\n" +
|
||||
@@ -3892,7 +4011,9 @@ const file_uart_messages_proto_rawDesc = "" +
|
||||
"\x16set_log_level_response\x18& \x01(\v2\x19.alox.SetLogLevelResponseH\x00R\x13setLogLevelResponse\x12F\n" +
|
||||
"\x12tap_config_request\x18' \x01(\v2\x16.alox.TapConfigRequestH\x00R\x10tapConfigRequest\x12I\n" +
|
||||
"\x13tap_config_response\x18( \x01(\v2\x17.alox.TapConfigResponseH\x00R\x11tapConfigResponse\x12=\n" +
|
||||
"\x0fble_stream_tick\x18) \x01(\v2\x13.alox.BleStreamTickH\x00R\rbleStreamTickB\t\n" +
|
||||
"\x0fble_stream_tick\x18) \x01(\v2\x13.alox.BleStreamTickH\x00R\rbleStreamTick\x12]\n" +
|
||||
"\x1bclient_id_map_reset_request\x18* \x01(\v2\x1d.alox.ClientIdMapResetRequestH\x00R\x17clientIdMapResetRequest\x12`\n" +
|
||||
"\x1cclient_id_map_reset_response\x18+ \x01(\v2\x1e.alox.ClientIdMapResetResponseH\x00R\x18clientIdMapResetResponseB\t\n" +
|
||||
"\apayload\"\x05\n" +
|
||||
"\x03Ack\"!\n" +
|
||||
"\vEchoPayload\x12\x12\n" +
|
||||
@@ -4060,7 +4181,10 @@ const file_uart_messages_proto_rawDesc = "" +
|
||||
"\tclient_id\x18\x01 \x01(\rR\bclientId\"H\n" +
|
||||
"\x0fRestartResponse\x12\x18\n" +
|
||||
"\asuccess\x18\x01 \x01(\bR\asuccess\x12\x1b\n" +
|
||||
"\tclient_id\x18\x02 \x01(\rR\bclientId\"@\n" +
|
||||
"\tclient_id\x18\x02 \x01(\rR\bclientId\"\x19\n" +
|
||||
"\x17ClientIdMapResetRequest\"4\n" +
|
||||
"\x18ClientIdMapResetResponse\x12\x18\n" +
|
||||
"\asuccess\x18\x01 \x01(\bR\asuccess\"@\n" +
|
||||
"\x12SetLogLevelRequest\x12\x14\n" +
|
||||
"\x05write\x18\x01 \x01(\bR\x05write\x12\x14\n" +
|
||||
"\x05level\x18\x02 \x01(\rR\x05level\"E\n" +
|
||||
@@ -4128,7 +4252,7 @@ const file_uart_messages_proto_rawDesc = "" +
|
||||
"\x0faggregate_bytes\x18\x03 \x01(\rR\x0eaggregateBytes\x12\x1f\n" +
|
||||
"\vslave_count\x18\x04 \x01(\rR\n" +
|
||||
"slaveCount\x12:\n" +
|
||||
"\x06slaves\x18\x05 \x03(\v2\x1b.alox.OtaSlaveProgressEntryB\x05\x92?\x02\x10\x10R\x06slaves*\xbf\x03\n" +
|
||||
"\x06slaves\x18\x05 \x03(\v2\x1b.alox.OtaSlaveProgressEntryB\x05\x92?\x02\x10\x10R\x06slaves*\xd8\x03\n" +
|
||||
"\vMessageType\x12\v\n" +
|
||||
"\aUNKNOWN\x10\x00\x12\a\n" +
|
||||
"\x03ACK\x10\x01\x12\b\n" +
|
||||
@@ -4157,7 +4281,8 @@ const file_uart_messages_proto_rawDesc = "" +
|
||||
"\rSET_LOG_LEVEL\x10\x1f\x12\x0e\n" +
|
||||
"\n" +
|
||||
"TAP_CONFIG\x10 \x12\x13\n" +
|
||||
"\x0fBLE_STREAM_TICK\x10!\"\x04\b\x18\x10\x18\"\x04\b\x1c\x10\x1c*G\n" +
|
||||
"\x0fBLE_STREAM_TICK\x10!\x12\x17\n" +
|
||||
"\x13CLIENT_ID_MAP_RESET\x10\"\"\x04\b\x18\x10\x18\"\x04\b\x1c\x10\x1c*G\n" +
|
||||
"\aTapKind\x12\f\n" +
|
||||
"\bTAP_NONE\x10\x00\x12\x0e\n" +
|
||||
"\n" +
|
||||
@@ -4180,7 +4305,7 @@ func file_uart_messages_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_uart_messages_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
|
||||
var file_uart_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 47)
|
||||
var file_uart_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 49)
|
||||
var file_uart_messages_proto_goTypes = []any{
|
||||
(MessageType)(0), // 0: alox.MessageType
|
||||
(TapKind)(0), // 1: alox.TapKind
|
||||
@@ -4220,17 +4345,19 @@ var file_uart_messages_proto_goTypes = []any{
|
||||
(*EspNowFindMeResponse)(nil), // 35: alox.EspNowFindMeResponse
|
||||
(*RestartRequest)(nil), // 36: alox.RestartRequest
|
||||
(*RestartResponse)(nil), // 37: alox.RestartResponse
|
||||
(*SetLogLevelRequest)(nil), // 38: alox.SetLogLevelRequest
|
||||
(*SetLogLevelResponse)(nil), // 39: alox.SetLogLevelResponse
|
||||
(*TapConfigRequest)(nil), // 40: alox.TapConfigRequest
|
||||
(*TapConfigResponse)(nil), // 41: alox.TapConfigResponse
|
||||
(*OtaStartPayload)(nil), // 42: alox.OtaStartPayload
|
||||
(*OtaPayload)(nil), // 43: alox.OtaPayload
|
||||
(*OtaEndPayload)(nil), // 44: alox.OtaEndPayload
|
||||
(*OtaStatusPayload)(nil), // 45: alox.OtaStatusPayload
|
||||
(*OtaSlaveProgressRequest)(nil), // 46: alox.OtaSlaveProgressRequest
|
||||
(*OtaSlaveProgressEntry)(nil), // 47: alox.OtaSlaveProgressEntry
|
||||
(*OtaSlaveProgressResponse)(nil), // 48: alox.OtaSlaveProgressResponse
|
||||
(*ClientIdMapResetRequest)(nil), // 38: alox.ClientIdMapResetRequest
|
||||
(*ClientIdMapResetResponse)(nil), // 39: alox.ClientIdMapResetResponse
|
||||
(*SetLogLevelRequest)(nil), // 40: alox.SetLogLevelRequest
|
||||
(*SetLogLevelResponse)(nil), // 41: alox.SetLogLevelResponse
|
||||
(*TapConfigRequest)(nil), // 42: alox.TapConfigRequest
|
||||
(*TapConfigResponse)(nil), // 43: alox.TapConfigResponse
|
||||
(*OtaStartPayload)(nil), // 44: alox.OtaStartPayload
|
||||
(*OtaPayload)(nil), // 45: alox.OtaPayload
|
||||
(*OtaEndPayload)(nil), // 46: alox.OtaEndPayload
|
||||
(*OtaStatusPayload)(nil), // 47: alox.OtaStatusPayload
|
||||
(*OtaSlaveProgressRequest)(nil), // 48: alox.OtaSlaveProgressRequest
|
||||
(*OtaSlaveProgressEntry)(nil), // 49: alox.OtaSlaveProgressEntry
|
||||
(*OtaSlaveProgressResponse)(nil), // 50: alox.OtaSlaveProgressResponse
|
||||
}
|
||||
var file_uart_messages_proto_depIdxs = []int32{
|
||||
0, // 0: alox.UartMessage.type:type_name -> alox.MessageType
|
||||
@@ -4239,16 +4366,16 @@ var file_uart_messages_proto_depIdxs = []int32{
|
||||
5, // 3: alox.UartMessage.version_response:type_name -> alox.VersionResponse
|
||||
7, // 4: alox.UartMessage.client_info_response:type_name -> alox.ClientInfoResponse
|
||||
9, // 5: alox.UartMessage.client_input_response:type_name -> alox.ClientInputResponse
|
||||
42, // 6: alox.UartMessage.ota_start:type_name -> alox.OtaStartPayload
|
||||
43, // 7: alox.UartMessage.ota_payload:type_name -> alox.OtaPayload
|
||||
44, // 8: alox.UartMessage.ota_end:type_name -> alox.OtaEndPayload
|
||||
45, // 9: alox.UartMessage.ota_status:type_name -> alox.OtaStatusPayload
|
||||
44, // 6: alox.UartMessage.ota_start:type_name -> alox.OtaStartPayload
|
||||
45, // 7: alox.UartMessage.ota_payload:type_name -> alox.OtaPayload
|
||||
46, // 8: alox.UartMessage.ota_end:type_name -> alox.OtaEndPayload
|
||||
47, // 9: alox.UartMessage.ota_status:type_name -> alox.OtaStatusPayload
|
||||
10, // 10: alox.UartMessage.accel_deadzone_request:type_name -> alox.AccelDeadzoneRequest
|
||||
11, // 11: alox.UartMessage.accel_deadzone_response:type_name -> alox.AccelDeadzoneResponse
|
||||
28, // 12: alox.UartMessage.espnow_unicast_test_request:type_name -> alox.EspNowUnicastTestRequest
|
||||
29, // 13: alox.UartMessage.espnow_unicast_test_response:type_name -> alox.EspNowUnicastTestResponse
|
||||
46, // 14: alox.UartMessage.ota_slave_progress_request:type_name -> alox.OtaSlaveProgressRequest
|
||||
48, // 15: alox.UartMessage.ota_slave_progress_response:type_name -> alox.OtaSlaveProgressResponse
|
||||
48, // 14: alox.UartMessage.ota_slave_progress_request:type_name -> alox.OtaSlaveProgressRequest
|
||||
50, // 15: alox.UartMessage.ota_slave_progress_response:type_name -> alox.OtaSlaveProgressResponse
|
||||
32, // 16: alox.UartMessage.led_ring_progress_request:type_name -> alox.LedRingProgressRequest
|
||||
33, // 17: alox.UartMessage.led_ring_progress_response:type_name -> alox.LedRingProgressResponse
|
||||
34, // 18: alox.UartMessage.espnow_find_me_request:type_name -> alox.EspNowFindMeRequest
|
||||
@@ -4265,28 +4392,30 @@ var file_uart_messages_proto_depIdxs = []int32{
|
||||
26, // 29: alox.UartMessage.cache_status_response:type_name -> alox.CacheStatusResponse
|
||||
30, // 30: alox.UartMessage.espnow_echo_ping_request:type_name -> alox.EspNowEchoPingRequest
|
||||
31, // 31: alox.UartMessage.espnow_echo_ping_response:type_name -> alox.EspNowEchoPingResponse
|
||||
38, // 32: alox.UartMessage.set_log_level_request:type_name -> alox.SetLogLevelRequest
|
||||
39, // 33: alox.UartMessage.set_log_level_response:type_name -> alox.SetLogLevelResponse
|
||||
40, // 34: alox.UartMessage.tap_config_request:type_name -> alox.TapConfigRequest
|
||||
41, // 35: alox.UartMessage.tap_config_response:type_name -> alox.TapConfigResponse
|
||||
40, // 32: alox.UartMessage.set_log_level_request:type_name -> alox.SetLogLevelRequest
|
||||
41, // 33: alox.UartMessage.set_log_level_response:type_name -> alox.SetLogLevelResponse
|
||||
42, // 34: alox.UartMessage.tap_config_request:type_name -> alox.TapConfigRequest
|
||||
43, // 35: alox.UartMessage.tap_config_response:type_name -> alox.TapConfigResponse
|
||||
27, // 36: alox.UartMessage.ble_stream_tick:type_name -> alox.BleStreamTick
|
||||
6, // 37: alox.ClientInfoResponse.clients:type_name -> alox.ClientInfo
|
||||
8, // 38: alox.ClientInputResponse.clients:type_name -> alox.ClientInput
|
||||
15, // 39: alox.BatterySample.lipo1:type_name -> alox.LipoReading
|
||||
15, // 40: alox.BatterySample.lipo2:type_name -> alox.LipoReading
|
||||
16, // 41: alox.BatteryStatusResponse.samples:type_name -> alox.BatterySample
|
||||
1, // 42: alox.TapEvent.kind:type_name -> alox.TapKind
|
||||
1, // 43: alox.CacheClientTap.kind:type_name -> alox.TapKind
|
||||
23, // 44: alox.CacheClientStatus.accel:type_name -> alox.CacheClientAccel
|
||||
24, // 45: alox.CacheClientStatus.tap:type_name -> alox.CacheClientTap
|
||||
25, // 46: alox.CacheStatusResponse.clients:type_name -> alox.CacheClientStatus
|
||||
25, // 47: alox.BleStreamTick.clients:type_name -> alox.CacheClientStatus
|
||||
47, // 48: alox.OtaSlaveProgressResponse.slaves:type_name -> alox.OtaSlaveProgressEntry
|
||||
49, // [49:49] is the sub-list for method output_type
|
||||
49, // [49:49] is the sub-list for method input_type
|
||||
49, // [49:49] is the sub-list for extension type_name
|
||||
49, // [49:49] is the sub-list for extension extendee
|
||||
0, // [0:49] is the sub-list for field type_name
|
||||
38, // 37: alox.UartMessage.client_id_map_reset_request:type_name -> alox.ClientIdMapResetRequest
|
||||
39, // 38: alox.UartMessage.client_id_map_reset_response:type_name -> alox.ClientIdMapResetResponse
|
||||
6, // 39: alox.ClientInfoResponse.clients:type_name -> alox.ClientInfo
|
||||
8, // 40: alox.ClientInputResponse.clients:type_name -> alox.ClientInput
|
||||
15, // 41: alox.BatterySample.lipo1:type_name -> alox.LipoReading
|
||||
15, // 42: alox.BatterySample.lipo2:type_name -> alox.LipoReading
|
||||
16, // 43: alox.BatteryStatusResponse.samples:type_name -> alox.BatterySample
|
||||
1, // 44: alox.TapEvent.kind:type_name -> alox.TapKind
|
||||
1, // 45: alox.CacheClientTap.kind:type_name -> alox.TapKind
|
||||
23, // 46: alox.CacheClientStatus.accel:type_name -> alox.CacheClientAccel
|
||||
24, // 47: alox.CacheClientStatus.tap:type_name -> alox.CacheClientTap
|
||||
25, // 48: alox.CacheStatusResponse.clients:type_name -> alox.CacheClientStatus
|
||||
25, // 49: alox.BleStreamTick.clients:type_name -> alox.CacheClientStatus
|
||||
49, // 50: alox.OtaSlaveProgressResponse.slaves:type_name -> alox.OtaSlaveProgressEntry
|
||||
51, // [51:51] is the sub-list for method output_type
|
||||
51, // [51:51] is the sub-list for method input_type
|
||||
51, // [51:51] is the sub-list for extension type_name
|
||||
51, // [51:51] is the sub-list for extension extendee
|
||||
0, // [0:51] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_uart_messages_proto_init() }
|
||||
@@ -4331,6 +4460,8 @@ func file_uart_messages_proto_init() {
|
||||
(*UartMessage_TapConfigRequest)(nil),
|
||||
(*UartMessage_TapConfigResponse)(nil),
|
||||
(*UartMessage_BleStreamTick)(nil),
|
||||
(*UartMessage_ClientIdMapResetRequest)(nil),
|
||||
(*UartMessage_ClientIdMapResetResponse)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
@@ -4338,7 +4469,7 @@ func file_uart_messages_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_uart_messages_proto_rawDesc), len(file_uart_messages_proto_rawDesc)),
|
||||
NumEnums: 2,
|
||||
NumMessages: 47,
|
||||
NumMessages: 49,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
{
|
||||
"id": "pod-1",
|
||||
"mac": "50:78:7d:18:01:10",
|
||||
"client_id": 16,
|
||||
"client_id": 1,
|
||||
"console": "/dev/ttyACM1"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -540,6 +540,12 @@
|
||||
title="Master-Tap-Config an alle Slaves (ESP-NOW)">
|
||||
Tap-Cfg alle
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm"
|
||||
@click="resetClientIdMap()"
|
||||
:disabled="busy || !hostUp()"
|
||||
title="NVS-ID-Map + Live-Registry leeren; Online-Slaves verlieren Binding">
|
||||
ID-Map leeren
|
||||
</button>
|
||||
<span class="badge bg-secondary" x-text="(state.clients || []).length + ' registered'"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1957,6 +1963,26 @@
|
||||
this.busy = false;
|
||||
}
|
||||
},
|
||||
async resetClientIdMap() {
|
||||
if (!confirm('ID-Map leeren? Master vergisst alle MAC→ID-Zuordnungen; Online-Slaves verlieren ihr Binding.')) {
|
||||
return;
|
||||
}
|
||||
this.busy = true;
|
||||
try {
|
||||
const r = await fetch('/api/clients/reset-id-map', { method: 'POST' });
|
||||
const data = await r.json();
|
||||
if (!r.ok || !data.success) {
|
||||
this.flash(data.error || 'ID-Map leeren fehlgeschlagen', false);
|
||||
return;
|
||||
}
|
||||
this.state = { ...this.state, clients: [] };
|
||||
this.flash('ID-Map geleert — Slaves melden sich neu an', true);
|
||||
} catch (e) {
|
||||
this.flash(String(e), false);
|
||||
} finally {
|
||||
this.busy = false;
|
||||
}
|
||||
},
|
||||
async restart(clientId = 0) {
|
||||
this.busy = true;
|
||||
try {
|
||||
|
||||
@@ -29,6 +29,7 @@ idf_component_register(
|
||||
"cmd/cmd_espnow_echo_ping.c"
|
||||
"cmd/cmd_espnow_find_me.c"
|
||||
"cmd/cmd_restart.c"
|
||||
"cmd/cmd_client_id_map_reset.c"
|
||||
"pod_reboot.c"
|
||||
"cmd/cmd_led_ring.c"
|
||||
"cmd/cmd_battery.c"
|
||||
|
||||
+144
-18
@@ -1,4 +1,5 @@
|
||||
#include "client_registry.h"
|
||||
#include "pod_settings.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/idf_additions.h"
|
||||
@@ -13,12 +14,68 @@ typedef struct {
|
||||
} client_slot_t;
|
||||
|
||||
static client_slot_t s_clients[CLIENT_REGISTRY_MAX];
|
||||
static pod_id_map_t s_id_map;
|
||||
|
||||
static struct {
|
||||
board_lipo_reading_t reading;
|
||||
uint32_t updated_at;
|
||||
} s_master_battery;
|
||||
|
||||
static bool id_in_use(uint32_t id, const uint8_t *except_mac) {
|
||||
for (uint8_t i = 0; i < s_id_map.count; i++) {
|
||||
if (s_id_map.entries[i].id != id) {
|
||||
continue;
|
||||
}
|
||||
if (except_mac != NULL &&
|
||||
memcmp(s_id_map.entries[i].mac, except_mac, CLIENT_MAC_LEN) == 0) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int find_map_index(const uint8_t mac[CLIENT_MAC_LEN]) {
|
||||
for (uint8_t i = 0; i < s_id_map.count; i++) {
|
||||
if (memcmp(s_id_map.entries[i].mac, mac, CLIENT_MAC_LEN) == 0) {
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static esp_err_t persist_id_map(void) {
|
||||
return pod_settings_save_id_map(&s_id_map);
|
||||
}
|
||||
|
||||
static uint32_t alloc_next_free_id(void) {
|
||||
if (s_id_map.count >= POD_ID_MAP_MAX) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t start = s_id_map.next_id;
|
||||
if (start < POD_CLIENT_ID_MIN) {
|
||||
start = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
}
|
||||
for (uint32_t n = 0; n < POD_CLIENT_ID_MAX; n++) {
|
||||
uint32_t id = (uint32_t)start + n;
|
||||
if (id > POD_CLIENT_ID_MAX) {
|
||||
id = id - POD_CLIENT_ID_MAX;
|
||||
}
|
||||
if (id < POD_CLIENT_ID_MIN) {
|
||||
continue;
|
||||
}
|
||||
if (!id_in_use(id, NULL)) {
|
||||
uint32_t next = id + 1;
|
||||
if (next > POD_CLIENT_ID_MAX) {
|
||||
next = POD_CLIENT_ID_MIN;
|
||||
}
|
||||
s_id_map.next_id = (uint8_t)next;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t client_registry_now_ms(void) {
|
||||
return (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS);
|
||||
}
|
||||
@@ -95,7 +152,84 @@ static client_slot_t *alloc_slot(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void client_registry_init(void) { memset(s_clients, 0, sizeof(s_clients)); }
|
||||
void client_registry_init(void) {
|
||||
memset(s_clients, 0, sizeof(s_clients));
|
||||
memset(&s_id_map, 0, sizeof(s_id_map));
|
||||
s_id_map.next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
(void)pod_settings_load_id_map(&s_id_map);
|
||||
if (s_id_map.next_id < POD_CLIENT_ID_MIN) {
|
||||
s_id_map.next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t client_registry_assign_id(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t preferred_id, uint32_t *out_id) {
|
||||
if (mac == NULL || out_id == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
int idx = find_map_index(mac);
|
||||
if (idx >= 0) {
|
||||
*out_id = s_id_map.entries[idx].id;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint32_t id = 0;
|
||||
if (preferred_id >= POD_CLIENT_ID_MIN && preferred_id <= POD_CLIENT_ID_MAX &&
|
||||
!id_in_use(preferred_id, mac)) {
|
||||
id = preferred_id;
|
||||
} else {
|
||||
id = alloc_next_free_id();
|
||||
}
|
||||
if (id == 0) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (s_id_map.count >= POD_ID_MAP_MAX) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
memcpy(s_id_map.entries[s_id_map.count].mac, mac, CLIENT_MAC_LEN);
|
||||
s_id_map.entries[s_id_map.count].id = (uint8_t)id;
|
||||
s_id_map.count++;
|
||||
if (id >= s_id_map.next_id) {
|
||||
uint32_t next = id + 1;
|
||||
if (next > POD_CLIENT_ID_MAX) {
|
||||
next = POD_CLIENT_ID_MIN;
|
||||
}
|
||||
s_id_map.next_id = (uint8_t)next;
|
||||
}
|
||||
|
||||
esp_err_t err = persist_id_map();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "persist id_map failed after assign id=%lu: %s",
|
||||
(unsigned long)id, esp_err_to_name(err));
|
||||
}
|
||||
*out_id = id;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void client_registry_clear_id_map(void) {
|
||||
memset(s_clients, 0, sizeof(s_clients));
|
||||
memset(&s_id_map, 0, sizeof(s_id_map));
|
||||
s_id_map.next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
esp_err_t err = pod_settings_clear_id_map();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "clear id_map NVS failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
ESP_LOGI(TAG, "cleared id map and live registry");
|
||||
}
|
||||
|
||||
void client_registry_mark_assign_sent(const uint8_t mac[CLIENT_MAC_LEN]) {
|
||||
if (mac == NULL) {
|
||||
return;
|
||||
}
|
||||
client_slot_t *slot = find_slot(mac);
|
||||
if (slot == NULL) {
|
||||
return;
|
||||
}
|
||||
slot->info.last_assign_sent_at = now_ms();
|
||||
}
|
||||
|
||||
const client_info_t *
|
||||
client_registry_find_by_mac(const uint8_t mac[CLIENT_MAC_LEN]) {
|
||||
@@ -171,18 +305,19 @@ void client_registry_check_timeouts(uint32_t timeout_ms) {
|
||||
uint32_t now = now_ms();
|
||||
|
||||
for (size_t i = 0; i < CLIENT_REGISTRY_MAX; i++) {
|
||||
if (!s_clients[i].active || !s_clients[i].info.available) {
|
||||
if (!s_clients[i].active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t elapsed = now - s_clients[i].info.last_success_ping_at;
|
||||
if (elapsed > timeout_ms) {
|
||||
s_clients[i].info.available = false;
|
||||
char mac_str[18];
|
||||
mac_to_str(s_clients[i].info.mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGW(TAG, "client inactive id=%lu mac=%s (no heartbeat for %lu ms)",
|
||||
ESP_LOGW(TAG, "client dropped id=%lu mac=%s (no heartbeat for %lu ms)",
|
||||
(unsigned long)s_clients[i].info.id, mac_str,
|
||||
(unsigned long)elapsed);
|
||||
s_clients[i].active = false;
|
||||
memset(&s_clients[i].info, 0, sizeof(s_clients[i].info));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -552,24 +687,15 @@ esp_err_t client_registry_update_battery(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
|
||||
client_slot_t *slot = find_slot(mac);
|
||||
if (slot == NULL) {
|
||||
bool is_new = false;
|
||||
esp_err_t err = client_registry_upsert(mac, slave_id, 0, true, false, &is_new);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
slot = find_slot(mac);
|
||||
if (slot == NULL) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
ESP_LOGI(TAG, "battery auto-registered id=%lu (report before heartbeat)",
|
||||
(unsigned long)slave_id);
|
||||
/* Ids are assigned on presence only; ignore early battery reports. */
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (slot->info.id != slave_id) {
|
||||
ESP_LOGW(TAG, "battery id %lu → %lu for mac %02x:…:%02x",
|
||||
if (slave_id != 0 && slot->info.id != slave_id) {
|
||||
ESP_LOGW(TAG, "battery id mismatch slot=%lu report=%lu mac %02x:…:%02x",
|
||||
(unsigned long)slot->info.id, (unsigned long)slave_id, mac[0],
|
||||
mac[5]);
|
||||
slot->info.id = slave_id;
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
slot->info.lipo1_valid = lipo1_valid;
|
||||
|
||||
+23
-1
@@ -47,6 +47,8 @@ typedef struct {
|
||||
uint32_t lipo1_mv;
|
||||
uint32_t lipo2_mv;
|
||||
uint32_t battery_updated_at;
|
||||
/** Master: last ESPNOW_ASSIGN_ID send time (rate-limit resends). */
|
||||
uint32_t last_assign_sent_at;
|
||||
} client_info_t;
|
||||
|
||||
#define CLIENT_REGISTRY_DEFAULT_ACCEL_DEADZONE 100u
|
||||
@@ -55,6 +57,23 @@ typedef struct {
|
||||
|
||||
void client_registry_init(void);
|
||||
|
||||
/**
|
||||
* Assign a stable logical id for mac (1…255). Honors preferred if free or
|
||||
* already owned by mac; otherwise allocates the next free id. Persists the
|
||||
* master id map to NVS.
|
||||
*/
|
||||
esp_err_t client_registry_assign_id(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t preferred_id, uint32_t *out_id);
|
||||
|
||||
/**
|
||||
* Clear NVS id map, RAM map, and all live registry slots. Does not notify
|
||||
* slaves (caller sends CLEAR_ID_BINDING).
|
||||
*/
|
||||
void client_registry_clear_id_map(void);
|
||||
|
||||
/** Record that ASSIGN_ID was sent to this MAC (for resend rate limiting). */
|
||||
void client_registry_mark_assign_sent(const uint8_t mac[CLIENT_MAC_LEN]);
|
||||
|
||||
/** Milliseconds since boot (same clock as stored ping timestamps). */
|
||||
uint32_t client_registry_now_ms(void);
|
||||
|
||||
@@ -75,7 +94,10 @@ esp_err_t client_registry_heartbeat(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t id, uint32_t version, bool used,
|
||||
bool *out_is_new, bool *out_reactivated);
|
||||
|
||||
/** Mark clients inactive when last_success_ping_at is older than timeout_ms. */
|
||||
/**
|
||||
* Drop live registry slots whose last_success_ping_at is older than
|
||||
* timeout_ms (NVS id map unchanged). Offline clients disappear from CLIENT_INFO.
|
||||
*/
|
||||
void client_registry_check_timeouts(uint32_t timeout_ms);
|
||||
|
||||
size_t client_registry_count(void);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "cmd_client_id_map_reset.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_now_comm.h"
|
||||
#include "esp_now_core.h"
|
||||
#include "uart_cmd.h"
|
||||
|
||||
static const char *TAG = "[ID_MAP_RESET]";
|
||||
|
||||
static void reply(bool success) {
|
||||
alox_UartMessage response;
|
||||
uart_cmd_init_response(&response, alox_MessageType_CLIENT_ID_MAP_RESET,
|
||||
alox_UartMessage_client_id_map_reset_response_tag);
|
||||
response.payload.client_id_map_reset_response.success = success;
|
||||
uart_cmd_send(&response, TAG);
|
||||
}
|
||||
|
||||
static void handle_client_id_map_reset(const uint8_t *data, size_t len) {
|
||||
(void)data;
|
||||
(void)len;
|
||||
|
||||
esp_err_t err;
|
||||
if (esp_now_core_is_master()) {
|
||||
ESP_LOGI(TAG, "reset master id map + notify online slaves");
|
||||
err = esp_now_comm_reset_client_id_map();
|
||||
} else {
|
||||
ESP_LOGI(TAG, "clear local slave id binding");
|
||||
err = esp_now_comm_clear_local_id_binding();
|
||||
}
|
||||
reply(err == ESP_OK);
|
||||
}
|
||||
|
||||
void cmd_client_id_map_reset_register(void) {
|
||||
uart_cmd_register(alox_MessageType_CLIENT_ID_MAP_RESET,
|
||||
handle_client_id_map_reset);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void cmd_client_id_map_reset_register(void);
|
||||
@@ -55,6 +55,22 @@ esp_err_t esp_now_comm_send_led_ring(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
esp_err_t esp_now_comm_send_restart(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t client_id);
|
||||
|
||||
/** Master: tell slave its assigned logical client id. */
|
||||
esp_err_t esp_now_comm_send_assign_id(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t assigned_id);
|
||||
|
||||
/** Master: tell slave to forget persisted master/id binding. */
|
||||
esp_err_t esp_now_comm_send_clear_id_binding(const uint8_t mac[CLIENT_MAC_LEN]);
|
||||
|
||||
/**
|
||||
* Master: clear NVS id map + live registry and unicast CLEAR_ID_BINDING to
|
||||
* every currently registered live slave (snapshot before clear).
|
||||
*/
|
||||
esp_err_t esp_now_comm_reset_client_id_map(void);
|
||||
|
||||
/** Slave: clear local NVS master/id binding (UART reset on slave). */
|
||||
esp_err_t esp_now_comm_clear_local_id_binding(void);
|
||||
|
||||
/** Master → slave OTA (unicast). */
|
||||
esp_err_t esp_now_comm_send_ota_start(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t total_size);
|
||||
|
||||
+117
-7
@@ -22,6 +22,8 @@ static const uint8_t ESPNOW_BCAST[ESP_NOW_ETH_ALEN] = {0xff, 0xff, 0xff,
|
||||
#define ESPNOW_CLIENT_TIMEOUT_MS \
|
||||
(ESPNOW_HEARTBEAT_INTERVAL_MS * ESPNOW_HEARTBEAT_MISS_COUNT)
|
||||
#define ESPNOW_BATTERY_INTERVAL_MS 30000
|
||||
/** Min gap between ASSIGN_ID resends when slave has not yet echoed the id. */
|
||||
#define ESPNOW_ASSIGN_RESEND_MS 2000u
|
||||
|
||||
static const char *TAG = "[ESPNOW_M]";
|
||||
|
||||
@@ -168,6 +170,22 @@ static esp_err_t send_restart(const uint8_t *dest_mac, uint32_t client_id) {
|
||||
return esp_now_core_send(dest_mac, &msg);
|
||||
}
|
||||
|
||||
static esp_err_t send_assign_id(const uint8_t *dest_mac, uint32_t assigned_id) {
|
||||
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
||||
msg.type = alox_EspNowMessageType_ESPNOW_ASSIGN_ID;
|
||||
msg.which_payload = alox_EspNowMessage_assign_id_tag;
|
||||
msg.payload.assign_id.assigned_id = assigned_id;
|
||||
/* Fast path: avoid 8× NO_MEM retries blocking the shared send lock. */
|
||||
return esp_now_core_send_fast(dest_mac, &msg);
|
||||
}
|
||||
|
||||
static esp_err_t send_clear_id_binding(const uint8_t *dest_mac) {
|
||||
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
||||
msg.type = alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING;
|
||||
msg.which_payload = alox_EspNowMessage_clear_id_binding_tag;
|
||||
return esp_now_core_send_fast(dest_mac, &msg);
|
||||
}
|
||||
|
||||
static esp_err_t send_tap_notify(const uint8_t *dest_mac, uint32_t client_id,
|
||||
bool single, bool double_tap, bool triple) {
|
||||
alox_EspNowMessage msg = alox_EspNowMessage_init_zero;
|
||||
@@ -251,6 +269,70 @@ esp_err_t esp_now_comm_send_restart(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_now_comm_send_assign_id(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t assigned_id) {
|
||||
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_assign_id(mac, assigned_id);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "unicast ASSIGN_ID %lu to %s", (unsigned long)assigned_id,
|
||||
mac_str);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "unicast ASSIGN_ID to %s failed: %s", mac_str,
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_now_comm_send_clear_id_binding(const uint8_t mac[CLIENT_MAC_LEN]) {
|
||||
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_clear_id_binding(mac);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG, "unicast CLEAR_ID_BINDING to %s", mac_str);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "unicast CLEAR_ID_BINDING to %s failed: %s", mac_str,
|
||||
esp_err_to_name(err));
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_now_comm_reset_client_id_map(void) {
|
||||
if (!esp_now_core_is_master()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
uint8_t macs[CLIENT_REGISTRY_MAX][CLIENT_MAC_LEN];
|
||||
size_t n = client_registry_count();
|
||||
if (n > CLIENT_REGISTRY_MAX) {
|
||||
n = CLIENT_REGISTRY_MAX;
|
||||
}
|
||||
size_t copied = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
const client_info_t *c = client_registry_at(i);
|
||||
if (c == NULL) {
|
||||
continue;
|
||||
}
|
||||
memcpy(macs[copied], c->mac, CLIENT_MAC_LEN);
|
||||
copied++;
|
||||
}
|
||||
|
||||
client_registry_clear_id_map();
|
||||
|
||||
for (size_t i = 0; i < copied; i++) {
|
||||
(void)esp_now_comm_send_clear_id_binding(macs[i]);
|
||||
}
|
||||
ESP_LOGI(TAG, "client id map reset; notified %u slave(s)", (unsigned)copied);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_now_comm_send_find_me(const uint8_t mac[CLIENT_MAC_LEN],
|
||||
uint32_t client_id) {
|
||||
if (mac == NULL || !esp_now_core_is_master()) {
|
||||
@@ -495,25 +577,53 @@ static void handle_client_presence(const alox_EspNowSlavePresence *presence,
|
||||
|
||||
esp_now_core_ensure_peer(mac);
|
||||
|
||||
uint32_t assigned_id = 0;
|
||||
esp_err_t err =
|
||||
client_registry_assign_id(mac, presence->slave_id, &assigned_id);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "id assign failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_new = false;
|
||||
bool reactivated = false;
|
||||
esp_err_t err = client_registry_heartbeat(
|
||||
mac, presence->slave_id, presence->version, presence->used, &is_new,
|
||||
&reactivated);
|
||||
err = client_registry_heartbeat(mac, assigned_id, presence->version,
|
||||
presence->used, &is_new, &reactivated);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "client registry full");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send ASSIGN when new/rejoined, or when slave has not echoed the id yet.
|
||||
* Rate-limit mismatch resends so failed assigns do not flood ESP-NOW buffers
|
||||
* (ESP_ERR_ESPNOW_NO_MEM) and block other unicast traffic.
|
||||
*/
|
||||
const bool needs_assign =
|
||||
is_new || reactivated || presence->slave_id != assigned_id;
|
||||
if (needs_assign) {
|
||||
const client_info_t *client = client_registry_find_by_mac(mac);
|
||||
const bool due =
|
||||
is_new || reactivated || client == NULL ||
|
||||
client->last_assign_sent_at == 0 ||
|
||||
client_registry_ms_since(client->last_assign_sent_at) >=
|
||||
ESPNOW_ASSIGN_RESEND_MS;
|
||||
if (due) {
|
||||
if (esp_now_comm_send_assign_id(mac, assigned_id) == ESP_OK) {
|
||||
client_registry_mark_assign_sent(mac);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(mac, mac_str, sizeof(mac_str));
|
||||
if (is_new) {
|
||||
ESP_LOGI(TAG, "client registered id=%lu mac=%s ver=%lu",
|
||||
(unsigned long)presence->slave_id, mac_str,
|
||||
(unsigned long)presence->version);
|
||||
ESP_LOGI(TAG, "client registered id=%lu (preferred=%lu) mac=%s ver=%lu",
|
||||
(unsigned long)assigned_id, (unsigned long)presence->slave_id,
|
||||
mac_str, (unsigned long)presence->version);
|
||||
} else if (reactivated) {
|
||||
ESP_LOGI(TAG, "client reconnected id=%lu mac=%s",
|
||||
(unsigned long)presence->slave_id, mac_str);
|
||||
(unsigned long)assigned_id, mac_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+132
-36
@@ -37,6 +37,11 @@ static bool s_tap_notify_double;
|
||||
static bool s_tap_notify_triple;
|
||||
static uint8_t s_master_mac[ESP_NOW_ETH_ALEN];
|
||||
static uint32_t s_last_discover_ms;
|
||||
/** Logical id assigned by current/last master (0 = unbound). */
|
||||
static uint32_t s_assigned_id;
|
||||
/** Master MAC from NVS binding (may differ from current s_master_mac). */
|
||||
static uint8_t s_bound_master_mac[ESP_NOW_ETH_ALEN];
|
||||
static bool s_has_binding;
|
||||
|
||||
typedef enum {
|
||||
SLAVE_TX_SLAVE_INFO = 1,
|
||||
@@ -45,15 +50,91 @@ typedef enum {
|
||||
|
||||
static QueueHandle_t s_tx_queue;
|
||||
|
||||
static void queue_tx(slave_tx_op_t op);
|
||||
|
||||
static bool from_joined_master(const uint8_t *master_mac) {
|
||||
return s_joined && esp_now_core_mac_equal(master_mac, s_master_mac);
|
||||
}
|
||||
|
||||
static uint32_t my_client_id(void) { return s_assigned_id; }
|
||||
|
||||
/** True when command targets this slave (0 = broadcast / any). */
|
||||
static bool client_id_matches(uint32_t client_id) {
|
||||
if (client_id == 0) {
|
||||
return true;
|
||||
}
|
||||
return s_assigned_id != 0 && client_id == s_assigned_id;
|
||||
}
|
||||
|
||||
static uint32_t preferred_id_for_master(void) {
|
||||
if (!s_has_binding || s_assigned_id == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (!s_joined || !esp_now_core_mac_equal(s_master_mac, s_bound_master_mac)) {
|
||||
return 0;
|
||||
}
|
||||
return s_assigned_id;
|
||||
}
|
||||
|
||||
static void load_id_binding(void) {
|
||||
uint8_t mac[ESP_NOW_ETH_ALEN];
|
||||
uint32_t id = 0;
|
||||
s_has_binding = pod_settings_load_id_binding(mac, &id);
|
||||
if (s_has_binding) {
|
||||
memcpy(s_bound_master_mac, mac, ESP_NOW_ETH_ALEN);
|
||||
s_assigned_id = id;
|
||||
ESP_LOGI(TAG, "loaded id binding id=%lu master=%02x:…:%02x",
|
||||
(unsigned long)id, mac[0], mac[5]);
|
||||
} else {
|
||||
memset(s_bound_master_mac, 0, sizeof(s_bound_master_mac));
|
||||
s_assigned_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_assigned_id(uint32_t assigned_id) {
|
||||
if (assigned_id < POD_CLIENT_ID_MIN || assigned_id > POD_CLIENT_ID_MAX) {
|
||||
ESP_LOGW(TAG, "ignore invalid assigned_id=%lu", (unsigned long)assigned_id);
|
||||
return;
|
||||
}
|
||||
const bool same =
|
||||
s_has_binding && s_assigned_id == assigned_id &&
|
||||
esp_now_core_mac_equal(s_bound_master_mac, s_master_mac);
|
||||
s_assigned_id = assigned_id;
|
||||
memcpy(s_bound_master_mac, s_master_mac, ESP_NOW_ETH_ALEN);
|
||||
s_has_binding = true;
|
||||
if (same) {
|
||||
return;
|
||||
}
|
||||
if (pod_settings_save_id_binding(s_master_mac, assigned_id) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "assigned id=%lu but NVS save failed",
|
||||
(unsigned long)assigned_id);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "assigned id=%lu (persisted)", (unsigned long)assigned_id);
|
||||
}
|
||||
queue_tx(SLAVE_TX_BATTERY);
|
||||
}
|
||||
|
||||
static void clear_id_binding_local(void) {
|
||||
s_assigned_id = 0;
|
||||
s_has_binding = false;
|
||||
memset(s_bound_master_mac, 0, sizeof(s_bound_master_mac));
|
||||
(void)pod_settings_clear_id_binding();
|
||||
ESP_LOGI(TAG, "cleared local id binding");
|
||||
}
|
||||
|
||||
esp_err_t esp_now_comm_clear_local_id_binding(void) {
|
||||
if (esp_now_core_is_master()) {
|
||||
return ESP_OK;
|
||||
}
|
||||
clear_id_binding_local();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void fill_presence(alox_EspNowSlavePresence *presence) {
|
||||
const uint8_t *own = esp_now_core_own_mac();
|
||||
presence->network = esp_now_core_network();
|
||||
presence->version = POWERPOD_FW_VERSION;
|
||||
presence->slave_id = own[5];
|
||||
presence->slave_id = preferred_id_for_master();
|
||||
presence->available = true;
|
||||
presence->used = false;
|
||||
esp_now_proto_setup_presence_encode(presence, own);
|
||||
@@ -139,7 +220,7 @@ static void queue_tx(slave_tx_op_t op) {
|
||||
}
|
||||
|
||||
static void send_battery_to_master(void) {
|
||||
if (!s_joined) {
|
||||
if (!s_joined || s_assigned_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,7 +228,7 @@ static void send_battery_to_master(void) {
|
||||
board_input_read_lipo(&reading);
|
||||
|
||||
alox_EspNowBatteryReport report = alox_EspNowBatteryReport_init_zero;
|
||||
report.client_id = esp_now_core_own_mac()[5];
|
||||
report.client_id = s_assigned_id;
|
||||
report.lipo1_valid = reading.lipo1_valid;
|
||||
report.lipo2_valid = reading.lipo2_valid;
|
||||
report.lipo1_mv = reading.lipo1_mv;
|
||||
@@ -261,10 +342,7 @@ static void handle_echo_ping(const uint8_t *master_mac,
|
||||
|
||||
static void handle_restart(const uint8_t *master_mac,
|
||||
const alox_EspNowRestart *req) {
|
||||
const uint8_t *own = esp_now_core_own_mac();
|
||||
uint32_t my_id = own[5];
|
||||
|
||||
if (req->client_id != 0 && req->client_id != my_id) {
|
||||
if (!client_id_matches(req->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -273,14 +351,14 @@ static void handle_restart(const uint8_t *master_mac,
|
||||
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "RESTART from master %s (id=%lu)", mac_str, (unsigned long)my_id);
|
||||
ESP_LOGI(TAG, "RESTART from master %s (id=%lu)", mac_str,
|
||||
(unsigned long)my_client_id());
|
||||
pod_schedule_restart();
|
||||
}
|
||||
|
||||
static void handle_battery_query(const uint8_t *master_mac,
|
||||
const alox_EspNowBatteryQuery *query) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (query->client_id != 0 && query->client_id != my_id) {
|
||||
if (!client_id_matches(query->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -291,8 +369,7 @@ static void handle_battery_query(const uint8_t *master_mac,
|
||||
|
||||
static void handle_led_ring(const uint8_t *master_mac,
|
||||
const alox_EspNowLedRing *msg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (msg->client_id != 0 && msg->client_id != my_id) {
|
||||
if (!client_id_matches(msg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -313,13 +390,12 @@ static void handle_led_ring(const uint8_t *master_mac,
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "LED_RING mode %lu from master %s (id=%lu)",
|
||||
(unsigned long)req.mode, mac_str, (unsigned long)my_id);
|
||||
(unsigned long)req.mode, mac_str, (unsigned long)my_client_id());
|
||||
cmd_led_ring_apply(&req);
|
||||
}
|
||||
|
||||
static void handle_find_me(const uint8_t *master_mac, const alox_EspNowFindMe *req) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (req->client_id != 0 && req->client_id != my_id) {
|
||||
if (!client_id_matches(req->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -327,14 +403,14 @@ static void handle_find_me(const uint8_t *master_mac, const alox_EspNowFindMe *r
|
||||
}
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "FIND_ME from master %s (id=%lu)", mac_str, (unsigned long)my_id);
|
||||
ESP_LOGI(TAG, "FIND_ME from master %s (id=%lu)", mac_str,
|
||||
(unsigned long)my_client_id());
|
||||
led_ring_find_me();
|
||||
}
|
||||
|
||||
static void handle_accel_stream(const uint8_t *master_mac,
|
||||
const alox_EspNowAccelStream *cfg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (cfg->client_id != 0 && cfg->client_id != my_id) {
|
||||
if (!client_id_matches(cfg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -344,13 +420,12 @@ static void handle_accel_stream(const uint8_t *master_mac,
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "accel stream %s from master %s (id=%lu)",
|
||||
cfg->enable ? "on" : "off", mac_str, (unsigned long)my_id);
|
||||
cfg->enable ? "on" : "off", mac_str, (unsigned long)my_client_id());
|
||||
}
|
||||
|
||||
static void handle_tap_notify(const uint8_t *master_mac,
|
||||
const alox_EspNowTapNotify *cfg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (cfg->client_id != 0 && cfg->client_id != my_id) {
|
||||
if (!client_id_matches(cfg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -364,13 +439,12 @@ static void handle_tap_notify(const uint8_t *master_mac,
|
||||
ESP_LOGI(TAG,
|
||||
"tap notify single=%d double=%d triple=%d from master %s (id=%lu)",
|
||||
cfg->single, cfg->double_tap, cfg->triple, mac_str,
|
||||
(unsigned long)my_id);
|
||||
(unsigned long)my_client_id());
|
||||
}
|
||||
|
||||
static void handle_accel_deadzone(const uint8_t *master_mac,
|
||||
const alox_EspNowAccelDeadzone *cfg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (cfg->client_id != 0 && cfg->client_id != my_id) {
|
||||
if (!client_id_matches(cfg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -380,7 +454,7 @@ static void handle_accel_deadzone(const uint8_t *master_mac,
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG,
|
||||
"accel deadzone from master %s: %lu LSB id=%lu (sensor %s)", mac_str,
|
||||
(unsigned long)cfg->deadzone, (unsigned long)my_id,
|
||||
(unsigned long)cfg->deadzone, (unsigned long)my_client_id(),
|
||||
bma456_is_ready() ? "ok" : "not installed");
|
||||
bma456_set_accel_deadzone(cfg->deadzone);
|
||||
if (pod_settings_save_accel_deadzone(cfg->deadzone) != ESP_OK) {
|
||||
@@ -391,8 +465,7 @@ static void handle_accel_deadzone(const uint8_t *master_mac,
|
||||
|
||||
static void handle_tap_config(const uint8_t *master_mac,
|
||||
const alox_EspNowTapConfig *msg) {
|
||||
uint32_t my_id = esp_now_core_own_mac()[5];
|
||||
if (msg->client_id != 0 && msg->client_id != my_id) {
|
||||
if (!client_id_matches(msg->client_id)) {
|
||||
return;
|
||||
}
|
||||
if (s_joined && !esp_now_core_mac_equal(master_mac, s_master_mac)) {
|
||||
@@ -415,7 +488,7 @@ static void handle_tap_config(const uint8_t *master_mac,
|
||||
esp_now_core_mac_to_str(master_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG,
|
||||
"tap config from master %s: sens=%u id=%lu (sensor %s)", mac_str,
|
||||
(unsigned)cfg.tap_sens_thres, (unsigned long)my_id,
|
||||
(unsigned)cfg.tap_sens_thres, (unsigned long)my_client_id(),
|
||||
bma456_is_ready() ? "ok" : "not installed");
|
||||
|
||||
if (!bma456_tap_config_valid(&cfg)) {
|
||||
@@ -431,6 +504,21 @@ static void handle_tap_config(const uint8_t *master_mac,
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_assign_id(const uint8_t *master_mac,
|
||||
const alox_EspNowAssignId *msg) {
|
||||
if (!from_joined_master(master_mac)) {
|
||||
return;
|
||||
}
|
||||
apply_assigned_id(msg->assigned_id);
|
||||
}
|
||||
|
||||
static void handle_clear_id_binding(const uint8_t *master_mac) {
|
||||
if (!from_joined_master(master_mac)) {
|
||||
return;
|
||||
}
|
||||
clear_id_binding_local();
|
||||
}
|
||||
|
||||
static void handle_discover(const uint8_t *sender_mac,
|
||||
const alox_EspNowDiscover *discover) {
|
||||
if (discover->network != esp_now_core_network()) {
|
||||
@@ -464,11 +552,12 @@ static void handle_discover(const uint8_t *sender_mac,
|
||||
|
||||
char mac_str[18];
|
||||
esp_now_core_mac_to_str(sender_mac, mac_str, sizeof(mac_str));
|
||||
ESP_LOGI(TAG, "joined network %u, master %s", (unsigned)discover->network,
|
||||
mac_str);
|
||||
ESP_LOGI(TAG, "joined network %u, master %s (preferred_id=%lu)",
|
||||
(unsigned)discover->network, mac_str,
|
||||
(unsigned long)preferred_id_for_master());
|
||||
|
||||
queue_tx(SLAVE_TX_SLAVE_INFO);
|
||||
queue_tx(SLAVE_TX_BATTERY);
|
||||
/* Battery after ASSIGN_ID so reports carry the definitive client_id. */
|
||||
}
|
||||
|
||||
static void check_master_timeout(void) {
|
||||
@@ -489,14 +578,14 @@ static void check_master_timeout(void) {
|
||||
|
||||
static void accel_stream_task(void *param) {
|
||||
(void)param;
|
||||
const uint8_t *own = esp_now_core_own_mac();
|
||||
|
||||
ESP_LOGI(TAG, "accel stream task (interval %u ms)",
|
||||
(unsigned)ESPNOW_ACCEL_INTERVAL_MS);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(ESPNOW_ACCEL_INTERVAL_MS));
|
||||
if (!s_joined || !s_accel_stream_enabled || !bma456_is_ready()) {
|
||||
if (!s_joined || s_assigned_id == 0 || !s_accel_stream_enabled ||
|
||||
!bma456_is_ready()) {
|
||||
continue;
|
||||
}
|
||||
int16_t x = 0;
|
||||
@@ -505,13 +594,13 @@ static void accel_stream_task(void *param) {
|
||||
if (bma456_read_accel(&x, &y, &z) != ESP_OK) {
|
||||
continue;
|
||||
}
|
||||
(void)send_accel_sample(s_master_mac, own[5], x, y, z);
|
||||
(void)send_accel_sample(s_master_mac, s_assigned_id, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_bma456_tap(bma456_tap_kind_t kind, void *ctx) {
|
||||
(void)ctx;
|
||||
if (!s_joined) {
|
||||
if (!s_joined || s_assigned_id == 0) {
|
||||
return;
|
||||
}
|
||||
bool enabled = false;
|
||||
@@ -531,7 +620,7 @@ static void on_bma456_tap(bma456_tap_kind_t kind, void *ctx) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
(void)send_tap_event(s_master_mac, esp_now_core_own_mac()[5], (uint32_t)kind);
|
||||
(void)send_tap_event(s_master_mac, s_assigned_id, (uint32_t)kind);
|
||||
}
|
||||
|
||||
static void heartbeat_task(void *param) {
|
||||
@@ -654,6 +743,12 @@ void esp_now_slave_on_recv(const esp_now_recv_info_t *info, const uint8_t *data,
|
||||
handle_restart(info->src_addr, &msg.payload.restart);
|
||||
}
|
||||
break;
|
||||
case alox_EspNowMessage_assign_id_tag:
|
||||
handle_assign_id(info->src_addr, &msg.payload.assign_id);
|
||||
break;
|
||||
case alox_EspNowMessage_clear_id_binding_tag:
|
||||
handle_clear_id_binding(info->src_addr);
|
||||
break;
|
||||
case alox_EspNowMessage_ota_start_tag:
|
||||
if (from_joined_master(info->src_addr)) {
|
||||
ota_espnow_slave_on_start(info->src_addr, &msg.payload.ota_start);
|
||||
@@ -668,6 +763,7 @@ void esp_now_slave_on_recv(const esp_now_recv_info_t *info, const uint8_t *data,
|
||||
|
||||
esp_err_t esp_now_slave_start(void) {
|
||||
reset_join();
|
||||
load_id_binding();
|
||||
|
||||
s_tx_queue = xQueueCreate(4, sizeof(slave_tx_op_t));
|
||||
if (s_tx_queue == NULL) {
|
||||
|
||||
@@ -10,9 +10,31 @@ static const char *NS = "powerpod";
|
||||
static const char *KEY_ACCEL_DZ = "accel_dz";
|
||||
static const char *KEY_UV_LATCH = "uv_latch";
|
||||
static const char *KEY_TAP_CFG = "tap_cfg";
|
||||
static const char *KEY_ID_MAP = "id_map";
|
||||
static const char *KEY_ID_BIND = "id_bind";
|
||||
|
||||
#define ACCEL_DEADZONE_MAX 4095u
|
||||
#define TAP_CFG_NVS_VERSION 1u
|
||||
#define ID_MAP_NVS_VERSION 1u
|
||||
#define ID_BIND_NVS_VERSION 1u
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t mac[POD_ID_MAC_LEN];
|
||||
uint8_t id;
|
||||
} id_map_entry_nvs_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t version;
|
||||
uint8_t next_id;
|
||||
uint8_t count;
|
||||
id_map_entry_nvs_t entries[POD_ID_MAP_MAX];
|
||||
} id_map_nvs_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint8_t version;
|
||||
uint8_t master_mac[POD_ID_MAC_LEN];
|
||||
uint8_t assigned_id;
|
||||
} id_bind_nvs_t;
|
||||
|
||||
/** Packed layout for NVS blob (stable across rebuilds). */
|
||||
typedef struct __attribute__((packed)) {
|
||||
@@ -302,3 +324,232 @@ esp_err_t pod_settings_set_uv_latched(bool latched) {
|
||||
esp_err_t pod_settings_clear_uv_latched(void) {
|
||||
return pod_settings_set_uv_latched(false);
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_load_id_map(pod_id_map_t *out) {
|
||||
if (out == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
if (!s_nvs_ready) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NS, NVS_READONLY, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
id_map_nvs_t blob;
|
||||
memset(&blob, 0, sizeof(blob));
|
||||
size_t len = sizeof(blob);
|
||||
err = nvs_get_blob(handle, KEY_ID_MAP, &blob, &len);
|
||||
nvs_close(handle);
|
||||
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (err != ESP_OK || len < 3 || blob.version != ID_MAP_NVS_VERSION) {
|
||||
ESP_LOGW(TAG, "read id_map failed: %s (len=%u)", esp_err_to_name(err),
|
||||
(unsigned)len);
|
||||
return ESP_OK;
|
||||
}
|
||||
if (blob.count > POD_ID_MAP_MAX) {
|
||||
ESP_LOGW(TAG, "id_map count %u invalid", (unsigned)blob.count);
|
||||
return ESP_OK;
|
||||
}
|
||||
if (blob.next_id < POD_CLIENT_ID_MIN) {
|
||||
blob.next_id = (uint8_t)POD_CLIENT_ID_MIN;
|
||||
}
|
||||
|
||||
out->next_id = blob.next_id;
|
||||
out->count = blob.count;
|
||||
for (uint8_t i = 0; i < blob.count; i++) {
|
||||
memcpy(out->entries[i].mac, blob.entries[i].mac, POD_ID_MAC_LEN);
|
||||
out->entries[i].id = blob.entries[i].id;
|
||||
}
|
||||
ESP_LOGI(TAG, "loaded id_map count=%u next_id=%u", (unsigned)out->count,
|
||||
(unsigned)out->next_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_save_id_map(const pod_id_map_t *map) {
|
||||
if (map == NULL || map->count > POD_ID_MAP_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (map->next_id < POD_CLIENT_ID_MIN) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t err = ensure_nvs();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
id_map_nvs_t blob;
|
||||
memset(&blob, 0, sizeof(blob));
|
||||
blob.version = ID_MAP_NVS_VERSION;
|
||||
blob.next_id = map->next_id;
|
||||
blob.count = map->count;
|
||||
for (uint8_t i = 0; i < map->count; i++) {
|
||||
memcpy(blob.entries[i].mac, map->entries[i].mac, POD_ID_MAC_LEN);
|
||||
blob.entries[i].id = map->entries[i].id;
|
||||
}
|
||||
|
||||
nvs_handle_t handle;
|
||||
err = nvs_open(NS, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "nvs_open failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = nvs_set_blob(handle, KEY_ID_MAP, &blob, sizeof(blob));
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "save id_map failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "saved id_map count=%u next_id=%u", (unsigned)map->count,
|
||||
(unsigned)map->next_id);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_clear_id_map(void) {
|
||||
esp_err_t err = ensure_nvs();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
nvs_handle_t handle;
|
||||
err = nvs_open(NS, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "nvs_open failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = nvs_erase_key(handle, KEY_ID_MAP);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
err = ESP_OK;
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "clear id_map failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "cleared id_map");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool pod_settings_load_id_binding(uint8_t master_mac[POD_ID_MAC_LEN],
|
||||
uint32_t *assigned_id_out) {
|
||||
if (master_mac == NULL || assigned_id_out == NULL) {
|
||||
return false;
|
||||
}
|
||||
memset(master_mac, 0, POD_ID_MAC_LEN);
|
||||
*assigned_id_out = 0;
|
||||
if (!s_nvs_ready) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nvs_handle_t handle;
|
||||
esp_err_t err = nvs_open(NS, NVS_READONLY, &handle);
|
||||
if (err != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
id_bind_nvs_t blob = {0};
|
||||
size_t len = sizeof(blob);
|
||||
err = nvs_get_blob(handle, KEY_ID_BIND, &blob, &len);
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK || len != sizeof(blob) ||
|
||||
blob.version != ID_BIND_NVS_VERSION) {
|
||||
return false;
|
||||
}
|
||||
if (blob.assigned_id < POD_CLIENT_ID_MIN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(master_mac, blob.master_mac, POD_ID_MAC_LEN);
|
||||
*assigned_id_out = blob.assigned_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_save_id_binding(const uint8_t master_mac[POD_ID_MAC_LEN],
|
||||
uint32_t assigned_id) {
|
||||
if (master_mac == NULL || assigned_id < POD_CLIENT_ID_MIN ||
|
||||
assigned_id > POD_CLIENT_ID_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t err = ensure_nvs();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
id_bind_nvs_t blob = {
|
||||
.version = ID_BIND_NVS_VERSION,
|
||||
.assigned_id = (uint8_t)assigned_id,
|
||||
};
|
||||
memcpy(blob.master_mac, master_mac, POD_ID_MAC_LEN);
|
||||
|
||||
nvs_handle_t handle;
|
||||
err = nvs_open(NS, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "nvs_open failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = nvs_set_blob(handle, KEY_ID_BIND, &blob, sizeof(blob));
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "save id_bind failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "saved id_bind id=%lu master=%02x:…:%02x",
|
||||
(unsigned long)assigned_id, master_mac[0], master_mac[5]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t pod_settings_clear_id_binding(void) {
|
||||
esp_err_t err = ensure_nvs();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
nvs_handle_t handle;
|
||||
err = nvs_open(NS, NVS_READWRITE, &handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "nvs_open failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
err = nvs_erase_key(handle, KEY_ID_BIND);
|
||||
if (err == ESP_ERR_NVS_NOT_FOUND) {
|
||||
err = ESP_OK;
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
err = nvs_commit(handle);
|
||||
}
|
||||
nvs_close(handle);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "clear id_bind failed: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
ESP_LOGI(TAG, "cleared id_bind");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -35,4 +35,36 @@ bool pod_settings_is_uv_latched(void);
|
||||
esp_err_t pod_settings_set_uv_latched(bool latched);
|
||||
esp_err_t pod_settings_clear_uv_latched(void);
|
||||
|
||||
/** Logical slave ids: 1…POD_CLIENT_ID_MAX (0 reserved for local master). */
|
||||
#define POD_CLIENT_ID_MIN 1u
|
||||
#define POD_CLIENT_ID_MAX 255u
|
||||
#define POD_ID_MAP_MAX 255u
|
||||
#define POD_ID_MAC_LEN 6u
|
||||
|
||||
typedef struct {
|
||||
uint8_t mac[POD_ID_MAC_LEN];
|
||||
uint8_t id;
|
||||
} pod_id_map_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t next_id; /**< Next candidate when allocating (1…255). */
|
||||
uint8_t count;
|
||||
pod_id_map_entry_t entries[POD_ID_MAP_MAX];
|
||||
} pod_id_map_t;
|
||||
|
||||
/** Master: load MAC→id map from NVS (zeros *out if unset). */
|
||||
esp_err_t pod_settings_load_id_map(pod_id_map_t *out);
|
||||
esp_err_t pod_settings_save_id_map(const pod_id_map_t *map);
|
||||
esp_err_t pod_settings_clear_id_map(void);
|
||||
|
||||
/**
|
||||
* Slave: load persisted {master_mac, assigned_id}.
|
||||
* Returns true when a valid binding was loaded.
|
||||
*/
|
||||
bool pod_settings_load_id_binding(uint8_t master_mac[POD_ID_MAC_LEN],
|
||||
uint32_t *assigned_id_out);
|
||||
esp_err_t pod_settings_save_id_binding(const uint8_t master_mac[POD_ID_MAC_LEN],
|
||||
uint32_t assigned_id);
|
||||
esp_err_t pod_settings_clear_id_binding(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "cmd_espnow_echo_ping.h"
|
||||
#include "cmd_espnow_find_me.h"
|
||||
#include "cmd_restart.h"
|
||||
#include "cmd_client_id_map_reset.h"
|
||||
#include "cmd_client_info.h"
|
||||
#include "cmd_version.h"
|
||||
#include "cmd_ota.h"
|
||||
@@ -226,6 +227,7 @@ void app_main(void) {
|
||||
cmd_espnow_echo_ping_register();
|
||||
cmd_espnow_find_me_register();
|
||||
cmd_restart_register();
|
||||
cmd_client_id_map_reset_register();
|
||||
cmd_led_ring_register();
|
||||
cmd_battery_register();
|
||||
cmd_ota_register();
|
||||
|
||||
@@ -27,6 +27,12 @@ PB_BIND(alox_EspNowDiscover, alox_EspNowDiscover, AUTO)
|
||||
PB_BIND(alox_EspNowSlavePresence, alox_EspNowSlavePresence, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_EspNowAssignId, alox_EspNowAssignId, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_EspNowClearIdBinding, alox_EspNowClearIdBinding, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_EspNowAccelDeadzone, alox_EspNowAccelDeadzone, AUTO)
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,11 @@ typedef enum _alox_EspNowMessageType {
|
||||
alox_EspNowMessageType_ESPNOW_TAP_EVENT = 18,
|
||||
alox_EspNowMessageType_ESPNOW_ECHO_PING = 19,
|
||||
alox_EspNowMessageType_ESPNOW_ECHO_PONG = 20,
|
||||
alox_EspNowMessageType_ESPNOW_SET_TAP_CONFIG = 21
|
||||
alox_EspNowMessageType_ESPNOW_SET_TAP_CONFIG = 21,
|
||||
/* * Master → slave: assigned logical client id after join/presence. */
|
||||
alox_EspNowMessageType_ESPNOW_ASSIGN_ID = 22,
|
||||
/* * Master → slave: forget persisted master/id binding (debug reset). */
|
||||
alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING = 23
|
||||
} alox_EspNowMessageType;
|
||||
|
||||
/* Struct definitions */
|
||||
@@ -74,11 +78,22 @@ typedef struct _alox_EspNowSlavePresence {
|
||||
uint32_t network;
|
||||
pb_callback_t mac;
|
||||
uint32_t version;
|
||||
/* * Preferred id from NVS for this master (0 = none / unbound). */
|
||||
uint32_t slave_id;
|
||||
bool available;
|
||||
bool used;
|
||||
} alox_EspNowSlavePresence;
|
||||
|
||||
/* * Master → slave: definitive logical client id for this session/master. */
|
||||
typedef struct _alox_EspNowAssignId {
|
||||
uint32_t assigned_id;
|
||||
} alox_EspNowAssignId;
|
||||
|
||||
/* * Master → slave: clear persisted {master_mac, assigned_id} binding. */
|
||||
typedef struct _alox_EspNowClearIdBinding {
|
||||
char dummy_field;
|
||||
} alox_EspNowClearIdBinding;
|
||||
|
||||
typedef struct _alox_EspNowAccelDeadzone {
|
||||
uint32_t deadzone;
|
||||
uint32_t client_id; /* 0 = all slaves; otherwise only matching slave_id applies */
|
||||
@@ -204,6 +219,8 @@ typedef struct _alox_EspNowMessage {
|
||||
alox_EspNowEchoPing echo_ping;
|
||||
alox_EspNowEchoPong echo_pong;
|
||||
alox_EspNowTapConfig tap_config;
|
||||
alox_EspNowAssignId assign_id;
|
||||
alox_EspNowClearIdBinding clear_id_binding;
|
||||
} payload;
|
||||
} alox_EspNowMessage;
|
||||
|
||||
@@ -214,8 +231,10 @@ extern "C" {
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _alox_EspNowMessageType_MIN alox_EspNowMessageType_ESPNOW_UNKNOWN
|
||||
#define _alox_EspNowMessageType_MAX alox_EspNowMessageType_ESPNOW_SET_TAP_CONFIG
|
||||
#define _alox_EspNowMessageType_ARRAYSIZE ((alox_EspNowMessageType)(alox_EspNowMessageType_ESPNOW_SET_TAP_CONFIG+1))
|
||||
#define _alox_EspNowMessageType_MAX alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING
|
||||
#define _alox_EspNowMessageType_ARRAYSIZE ((alox_EspNowMessageType)(alox_EspNowMessageType_ESPNOW_CLEAR_ID_BINDING+1))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -248,6 +267,8 @@ extern "C" {
|
||||
#define alox_EspNowRestart_init_default {0}
|
||||
#define alox_EspNowDiscover_init_default {0, 0}
|
||||
#define alox_EspNowSlavePresence_init_default {0, {{NULL}, NULL}, 0, 0, 0, 0}
|
||||
#define alox_EspNowAssignId_init_default {0}
|
||||
#define alox_EspNowClearIdBinding_init_default {0}
|
||||
#define alox_EspNowAccelDeadzone_init_default {0, 0}
|
||||
#define alox_EspNowAccelStream_init_default {0, 0}
|
||||
#define alox_EspNowAccelSample_init_default {0, 0, 0, 0}
|
||||
@@ -269,6 +290,8 @@ extern "C" {
|
||||
#define alox_EspNowRestart_init_zero {0}
|
||||
#define alox_EspNowDiscover_init_zero {0, 0}
|
||||
#define alox_EspNowSlavePresence_init_zero {0, {{NULL}, NULL}, 0, 0, 0, 0}
|
||||
#define alox_EspNowAssignId_init_zero {0}
|
||||
#define alox_EspNowClearIdBinding_init_zero {0}
|
||||
#define alox_EspNowAccelDeadzone_init_zero {0, 0}
|
||||
#define alox_EspNowAccelStream_init_zero {0, 0}
|
||||
#define alox_EspNowAccelSample_init_zero {0, 0, 0, 0}
|
||||
@@ -300,6 +323,7 @@ extern "C" {
|
||||
#define alox_EspNowSlavePresence_slave_id_tag 4
|
||||
#define alox_EspNowSlavePresence_available_tag 5
|
||||
#define alox_EspNowSlavePresence_used_tag 6
|
||||
#define alox_EspNowAssignId_assigned_id_tag 1
|
||||
#define alox_EspNowAccelDeadzone_deadzone_tag 1
|
||||
#define alox_EspNowAccelDeadzone_client_id_tag 2
|
||||
#define alox_EspNowAccelStream_enable_tag 1
|
||||
@@ -368,6 +392,8 @@ extern "C" {
|
||||
#define alox_EspNowMessage_echo_ping_tag 20
|
||||
#define alox_EspNowMessage_echo_pong_tag 21
|
||||
#define alox_EspNowMessage_tap_config_tag 22
|
||||
#define alox_EspNowMessage_assign_id_tag 23
|
||||
#define alox_EspNowMessage_clear_id_binding_tag 24
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define alox_EspNowUnicastTest_FIELDLIST(X, a) \
|
||||
@@ -413,6 +439,16 @@ X(a, STATIC, SINGULAR, BOOL, used, 6)
|
||||
#define alox_EspNowSlavePresence_CALLBACK pb_default_field_callback
|
||||
#define alox_EspNowSlavePresence_DEFAULT NULL
|
||||
|
||||
#define alox_EspNowAssignId_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, assigned_id, 1)
|
||||
#define alox_EspNowAssignId_CALLBACK NULL
|
||||
#define alox_EspNowAssignId_DEFAULT NULL
|
||||
|
||||
#define alox_EspNowClearIdBinding_FIELDLIST(X, a) \
|
||||
|
||||
#define alox_EspNowClearIdBinding_CALLBACK NULL
|
||||
#define alox_EspNowClearIdBinding_DEFAULT NULL
|
||||
|
||||
#define alox_EspNowAccelDeadzone_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, deadzone, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, client_id, 2)
|
||||
@@ -534,7 +570,9 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,tap_notify,payload.tap_notify), 18)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,tap_event,payload.tap_event), 19) \
|
||||
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,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,clear_id_binding,payload.clear_id_binding), 24)
|
||||
#define alox_EspNowMessage_CALLBACK NULL
|
||||
#define alox_EspNowMessage_DEFAULT NULL
|
||||
#define alox_EspNowMessage_payload_discover_MSGTYPE alox_EspNowDiscover
|
||||
@@ -558,6 +596,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config,payload.tap_config), 22)
|
||||
#define alox_EspNowMessage_payload_echo_ping_MSGTYPE alox_EspNowEchoPing
|
||||
#define alox_EspNowMessage_payload_echo_pong_MSGTYPE alox_EspNowEchoPong
|
||||
#define alox_EspNowMessage_payload_tap_config_MSGTYPE alox_EspNowTapConfig
|
||||
#define alox_EspNowMessage_payload_assign_id_MSGTYPE alox_EspNowAssignId
|
||||
#define alox_EspNowMessage_payload_clear_id_binding_MSGTYPE alox_EspNowClearIdBinding
|
||||
|
||||
extern const pb_msgdesc_t alox_EspNowUnicastTest_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowEchoPing_msg;
|
||||
@@ -566,6 +606,8 @@ extern const pb_msgdesc_t alox_EspNowFindMe_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowRestart_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowDiscover_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowSlavePresence_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowAssignId_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowClearIdBinding_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowAccelDeadzone_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowAccelStream_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowAccelSample_msg;
|
||||
@@ -589,6 +631,8 @@ extern const pb_msgdesc_t alox_EspNowMessage_msg;
|
||||
#define alox_EspNowRestart_fields &alox_EspNowRestart_msg
|
||||
#define alox_EspNowDiscover_fields &alox_EspNowDiscover_msg
|
||||
#define alox_EspNowSlavePresence_fields &alox_EspNowSlavePresence_msg
|
||||
#define alox_EspNowAssignId_fields &alox_EspNowAssignId_msg
|
||||
#define alox_EspNowClearIdBinding_fields &alox_EspNowClearIdBinding_msg
|
||||
#define alox_EspNowAccelDeadzone_fields &alox_EspNowAccelDeadzone_msg
|
||||
#define alox_EspNowAccelStream_fields &alox_EspNowAccelStream_msg
|
||||
#define alox_EspNowAccelSample_fields &alox_EspNowAccelSample_msg
|
||||
@@ -611,8 +655,10 @@ extern const pb_msgdesc_t alox_EspNowMessage_msg;
|
||||
#define alox_EspNowAccelDeadzone_size 12
|
||||
#define alox_EspNowAccelSample_size 24
|
||||
#define alox_EspNowAccelStream_size 8
|
||||
#define alox_EspNowAssignId_size 6
|
||||
#define alox_EspNowBatteryQuery_size 6
|
||||
#define alox_EspNowBatteryReport_size 22
|
||||
#define alox_EspNowClearIdBinding_size 0
|
||||
#define alox_EspNowDiscover_size 8
|
||||
#define alox_EspNowEchoPing_size 22
|
||||
#define alox_EspNowEchoPong_size 22
|
||||
|
||||
@@ -27,6 +27,10 @@ enum EspNowMessageType {
|
||||
ESPNOW_ECHO_PING = 19;
|
||||
ESPNOW_ECHO_PONG = 20;
|
||||
ESPNOW_SET_TAP_CONFIG = 21;
|
||||
/** Master → slave: assigned logical client id after join/presence. */
|
||||
ESPNOW_ASSIGN_ID = 22;
|
||||
/** Master → slave: forget persisted master/id binding (debug reset). */
|
||||
ESPNOW_CLEAR_ID_BINDING = 23;
|
||||
}
|
||||
|
||||
message EspNowUnicastTest {
|
||||
@@ -67,11 +71,20 @@ message EspNowSlavePresence {
|
||||
uint32 network = 1;
|
||||
bytes mac = 2;
|
||||
uint32 version = 3;
|
||||
/** Preferred id from NVS for this master (0 = none / unbound). */
|
||||
uint32 slave_id = 4;
|
||||
bool available = 5;
|
||||
bool used = 6;
|
||||
}
|
||||
|
||||
/** Master → slave: definitive logical client id for this session/master. */
|
||||
message EspNowAssignId {
|
||||
uint32 assigned_id = 1;
|
||||
}
|
||||
|
||||
/** Master → slave: clear persisted {master_mac, assigned_id} binding. */
|
||||
message EspNowClearIdBinding {}
|
||||
|
||||
message EspNowAccelDeadzone {
|
||||
uint32 deadzone = 1;
|
||||
uint32 client_id = 2; // 0 = all slaves; otherwise only matching slave_id applies
|
||||
@@ -193,5 +206,7 @@ message EspNowMessage {
|
||||
EspNowEchoPing echo_ping = 20;
|
||||
EspNowEchoPong echo_pong = 21;
|
||||
EspNowTapConfig tap_config = 22;
|
||||
EspNowAssignId assign_id = 23;
|
||||
EspNowClearIdBinding clear_id_binding = 24;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,12 @@ PB_BIND(alox_RestartRequest, alox_RestartRequest, AUTO)
|
||||
PB_BIND(alox_RestartResponse, alox_RestartResponse, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_ClientIdMapResetRequest, alox_ClientIdMapResetRequest, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_ClientIdMapResetResponse, alox_ClientIdMapResetResponse, AUTO)
|
||||
|
||||
|
||||
PB_BIND(alox_SetLogLevelRequest, alox_SetLogLevelRequest, AUTO)
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,11 @@ typedef enum _alox_MessageType {
|
||||
/* * Host → master: get/set BMA456H multitap detection parameters. */
|
||||
alox_MessageType_TAP_CONFIG = 32,
|
||||
/* * Master → host (BLE only): periodic pushed cache snapshot with ESP timestamp. */
|
||||
alox_MessageType_BLE_STREAM_TICK = 33
|
||||
alox_MessageType_BLE_STREAM_TICK = 33,
|
||||
/* *
|
||||
Host → device: clear master client-id NVS map + live registry (and notify
|
||||
online slaves), or on a slave clear local master/id binding only. */
|
||||
alox_MessageType_CLIENT_ID_MAP_RESET = 34
|
||||
} alox_MessageType;
|
||||
|
||||
typedef enum _alox_TapKind {
|
||||
@@ -323,6 +327,15 @@ typedef struct _alox_RestartResponse {
|
||||
uint32_t client_id;
|
||||
} alox_RestartResponse;
|
||||
|
||||
/* * Host → master or slave: clear persisted client-id assignment state. */
|
||||
typedef struct _alox_ClientIdMapResetRequest {
|
||||
char dummy_field;
|
||||
} alox_ClientIdMapResetRequest;
|
||||
|
||||
typedef struct _alox_ClientIdMapResetResponse {
|
||||
bool success;
|
||||
} alox_ClientIdMapResetResponse;
|
||||
|
||||
/* * Host → master: read/write global log level (esp_log_level_set("*", …)). */
|
||||
typedef struct _alox_SetLogLevelRequest {
|
||||
bool write;
|
||||
@@ -456,6 +469,8 @@ typedef struct _alox_UartMessage {
|
||||
alox_TapConfigRequest tap_config_request;
|
||||
alox_TapConfigResponse tap_config_response;
|
||||
alox_BleStreamTick ble_stream_tick;
|
||||
alox_ClientIdMapResetRequest client_id_map_reset_request;
|
||||
alox_ClientIdMapResetResponse client_id_map_reset_response;
|
||||
} payload;
|
||||
} alox_UartMessage;
|
||||
|
||||
@@ -466,8 +481,8 @@ extern "C" {
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _alox_MessageType_MIN alox_MessageType_UNKNOWN
|
||||
#define _alox_MessageType_MAX alox_MessageType_BLE_STREAM_TICK
|
||||
#define _alox_MessageType_ARRAYSIZE ((alox_MessageType)(alox_MessageType_BLE_STREAM_TICK+1))
|
||||
#define _alox_MessageType_MAX alox_MessageType_CLIENT_ID_MAP_RESET
|
||||
#define _alox_MessageType_ARRAYSIZE ((alox_MessageType)(alox_MessageType_CLIENT_ID_MAP_RESET+1))
|
||||
|
||||
#define _alox_TapKind_MIN alox_TapKind_TAP_NONE
|
||||
#define _alox_TapKind_MAX alox_TapKind_TAP_TRIPLE
|
||||
@@ -521,6 +536,8 @@ extern "C" {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -561,6 +578,8 @@ extern "C" {
|
||||
#define alox_EspNowFindMeResponse_init_default {0, 0}
|
||||
#define alox_RestartRequest_init_default {0}
|
||||
#define alox_RestartResponse_init_default {0, 0}
|
||||
#define alox_ClientIdMapResetRequest_init_default {0}
|
||||
#define alox_ClientIdMapResetResponse_init_default {0}
|
||||
#define alox_SetLogLevelRequest_init_default {0, 0}
|
||||
#define alox_SetLogLevelResponse_init_default {0, 0}
|
||||
#define alox_TapConfigRequest_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
@@ -608,6 +627,8 @@ extern "C" {
|
||||
#define alox_EspNowFindMeResponse_init_zero {0, 0}
|
||||
#define alox_RestartRequest_init_zero {0}
|
||||
#define alox_RestartResponse_init_zero {0, 0}
|
||||
#define alox_ClientIdMapResetRequest_init_zero {0}
|
||||
#define alox_ClientIdMapResetResponse_init_zero {0}
|
||||
#define alox_SetLogLevelRequest_init_zero {0, 0}
|
||||
#define alox_SetLogLevelResponse_init_zero {0, 0}
|
||||
#define alox_TapConfigRequest_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
@@ -739,6 +760,7 @@ extern "C" {
|
||||
#define alox_RestartRequest_client_id_tag 1
|
||||
#define alox_RestartResponse_success_tag 1
|
||||
#define alox_RestartResponse_client_id_tag 2
|
||||
#define alox_ClientIdMapResetResponse_success_tag 1
|
||||
#define alox_SetLogLevelRequest_write_tag 1
|
||||
#define alox_SetLogLevelRequest_level_tag 2
|
||||
#define alox_SetLogLevelResponse_success_tag 1
|
||||
@@ -822,6 +844,8 @@ extern "C" {
|
||||
#define alox_UartMessage_tap_config_request_tag 39
|
||||
#define alox_UartMessage_tap_config_response_tag 40
|
||||
#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_response_tag 43
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define alox_UartMessage_FIELDLIST(X, a) \
|
||||
@@ -861,7 +885,9 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,set_log_level_request,payload.set_lo
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,set_log_level_response,payload.set_log_level_response), 38) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,tap_config_request,payload.tap_config_request), 39) \
|
||||
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_response,payload.client_id_map_reset_response), 43)
|
||||
#define alox_UartMessage_CALLBACK NULL
|
||||
#define alox_UartMessage_DEFAULT NULL
|
||||
#define alox_UartMessage_payload_ack_payload_MSGTYPE alox_Ack
|
||||
@@ -900,6 +926,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,ble_stream_tick,payload.ble_stream_t
|
||||
#define alox_UartMessage_payload_tap_config_request_MSGTYPE alox_TapConfigRequest
|
||||
#define alox_UartMessage_payload_tap_config_response_MSGTYPE alox_TapConfigResponse
|
||||
#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_response_MSGTYPE alox_ClientIdMapResetResponse
|
||||
|
||||
#define alox_Ack_FIELDLIST(X, a) \
|
||||
|
||||
@@ -1170,6 +1198,16 @@ X(a, STATIC, SINGULAR, UINT32, client_id, 2)
|
||||
#define alox_RestartResponse_CALLBACK NULL
|
||||
#define alox_RestartResponse_DEFAULT NULL
|
||||
|
||||
#define alox_ClientIdMapResetRequest_FIELDLIST(X, a) \
|
||||
|
||||
#define alox_ClientIdMapResetRequest_CALLBACK NULL
|
||||
#define alox_ClientIdMapResetRequest_DEFAULT NULL
|
||||
|
||||
#define alox_ClientIdMapResetResponse_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BOOL, success, 1)
|
||||
#define alox_ClientIdMapResetResponse_CALLBACK NULL
|
||||
#define alox_ClientIdMapResetResponse_DEFAULT NULL
|
||||
|
||||
#define alox_SetLogLevelRequest_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BOOL, write, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, level, 2)
|
||||
@@ -1298,6 +1336,8 @@ extern const pb_msgdesc_t alox_EspNowFindMeRequest_msg;
|
||||
extern const pb_msgdesc_t alox_EspNowFindMeResponse_msg;
|
||||
extern const pb_msgdesc_t alox_RestartRequest_msg;
|
||||
extern const pb_msgdesc_t alox_RestartResponse_msg;
|
||||
extern const pb_msgdesc_t alox_ClientIdMapResetRequest_msg;
|
||||
extern const pb_msgdesc_t alox_ClientIdMapResetResponse_msg;
|
||||
extern const pb_msgdesc_t alox_SetLogLevelRequest_msg;
|
||||
extern const pb_msgdesc_t alox_SetLogLevelResponse_msg;
|
||||
extern const pb_msgdesc_t alox_TapConfigRequest_msg;
|
||||
@@ -1347,6 +1387,8 @@ extern const pb_msgdesc_t alox_OtaSlaveProgressResponse_msg;
|
||||
#define alox_EspNowFindMeResponse_fields &alox_EspNowFindMeResponse_msg
|
||||
#define alox_RestartRequest_fields &alox_RestartRequest_msg
|
||||
#define alox_RestartResponse_fields &alox_RestartResponse_msg
|
||||
#define alox_ClientIdMapResetRequest_fields &alox_ClientIdMapResetRequest_msg
|
||||
#define alox_ClientIdMapResetResponse_fields &alox_ClientIdMapResetResponse_msg
|
||||
#define alox_SetLogLevelRequest_fields &alox_SetLogLevelRequest_msg
|
||||
#define alox_SetLogLevelResponse_fields &alox_SetLogLevelResponse_msg
|
||||
#define alox_TapConfigRequest_fields &alox_TapConfigRequest_msg
|
||||
@@ -1382,6 +1424,8 @@ extern const pb_msgdesc_t alox_OtaSlaveProgressResponse_msg;
|
||||
#define alox_CacheClientTap_size 8
|
||||
#define alox_CacheStatusRequest_size 0
|
||||
#define alox_CacheStatusResponse_size 736
|
||||
#define alox_ClientIdMapResetRequest_size 0
|
||||
#define alox_ClientIdMapResetResponse_size 2
|
||||
#define alox_ClientInput_size 22
|
||||
#define alox_EspNowEchoPingRequest_size 17
|
||||
#define alox_EspNowEchoPingResponse_size 25
|
||||
|
||||
@@ -37,6 +37,11 @@ enum MessageType {
|
||||
TAP_CONFIG = 32;
|
||||
/** Master → host (BLE only): periodic pushed cache snapshot with ESP timestamp. */
|
||||
BLE_STREAM_TICK = 33;
|
||||
/**
|
||||
* Host → device: clear master client-id NVS map + live registry (and notify
|
||||
* online slaves), or on a slave clear local master/id binding only.
|
||||
*/
|
||||
CLIENT_ID_MAP_RESET = 34;
|
||||
}
|
||||
|
||||
message UartMessage {
|
||||
@@ -78,6 +83,8 @@ message UartMessage {
|
||||
TapConfigRequest tap_config_request = 39;
|
||||
TapConfigResponse tap_config_response = 40;
|
||||
BleStreamTick ble_stream_tick = 41;
|
||||
ClientIdMapResetRequest client_id_map_reset_request = 42;
|
||||
ClientIdMapResetResponse client_id_map_reset_response = 43;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +356,13 @@ message RestartResponse {
|
||||
uint32 client_id = 2;
|
||||
}
|
||||
|
||||
/** Host → master or slave: clear persisted client-id assignment state. */
|
||||
message ClientIdMapResetRequest {}
|
||||
|
||||
message ClientIdMapResetResponse {
|
||||
bool success = 1;
|
||||
}
|
||||
|
||||
/** Host → master: read/write global log level (esp_log_level_set("*", …)). */
|
||||
message SetLogLevelRequest {
|
||||
bool write = 1;
|
||||
|
||||
Reference in New Issue
Block a user