Files
powerpods/main/client_registry.h
T
skrueckenandCursor 7f28a48a10 Add runtime UV protection toggle and treat near-zero sense as external power.
Per-pod NVS enable (default off) via UART/ESP-NOW/dashboard, and skip UV latch when LiPo ADC reads ~0 mV so USB/external supply still boots.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-01 22:51:17 +01:00

175 lines
7.2 KiB
C

#ifndef CLIENT_REGISTRY_H
#define CLIENT_REGISTRY_H
#include "board_input.h"
#include "bosch456.h"
#include "esp_err.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define CLIENT_REGISTRY_MAX 16
#define CLIENT_MAC_LEN 6
typedef struct {
uint32_t id;
bool available;
bool used;
uint8_t mac[CLIENT_MAC_LEN];
/** Milliseconds since boot when last packet was received from this client. */
uint32_t last_ping_at;
/** Milliseconds since boot when last heartbeat / SLAVE_INFO was accepted. */
uint32_t last_success_ping_at;
uint32_t version;
/** Accel deadzone in raw LSB per axis (master copy for ESP-NOW config). */
uint32_t accel_deadzone;
/** Tap detection config (master copy for ESP-NOW / UART read). */
bma456_tap_config_t tap_config;
/** Latest accel from slave ESP-NOW stream (master only). */
bool accel_valid;
int16_t accel_x;
int16_t accel_y;
int16_t accel_z;
uint32_t accel_updated_at;
/** Host-enabled ESP-NOW accel stream to master. */
bool accel_stream_enabled;
/** Host-enabled ESP-NOW tap notify flags. */
bool tap_notify_single;
bool tap_notify_double;
bool tap_notify_triple;
/** Latest tap from slave ESP-NOW (master only, short-lived cache). */
bool tap_valid;
uint32_t tap_kind;
uint32_t tap_updated_at;
/** Latest LiPo ADC from slave ESP-NOW battery report (~30 s). */
bool lipo1_valid;
bool lipo2_valid;
uint32_t lipo1_mv;
uint32_t lipo2_mv;
uint32_t battery_updated_at;
/** Master cache: software UV protection enabled on slave (default false). */
bool uv_protection_enabled;
/** 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
/** Tap events older than this are discarded (matches accel stream interval). */
#define CLIENT_REGISTRY_TAP_MAX_AGE_MS 10u
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);
/** Ms elapsed since timestamp; 0 if timestamp is 0. */
uint32_t client_registry_ms_since(uint32_t timestamp);
/** Register or refresh a client; updates both ping timestamps. */
esp_err_t client_registry_upsert(const uint8_t mac[CLIENT_MAC_LEN], uint32_t id,
uint32_t version, bool available, bool used,
bool *out_is_new);
/**
* Record a successful heartbeat (or initial slave info).
* Sets available=true and updates last_success_ping_at (and last_ping_at).
* If client was inactive, sets *out_reactivated=true.
*/
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);
/**
* 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);
const client_info_t *client_registry_at(size_t index);
const client_info_t *client_registry_find_by_mac(const uint8_t mac[CLIENT_MAC_LEN]);
const client_info_t *client_registry_find_by_id(uint32_t id);
esp_err_t client_registry_set_accel_deadzone(uint32_t client_id,
uint32_t deadzone);
esp_err_t client_registry_get_accel_deadzone(uint32_t client_id,
uint32_t *deadzone_out);
/** Push deadzone to all active registry entries; returns count updated. */
size_t client_registry_set_accel_deadzone_all(uint32_t deadzone);
esp_err_t client_registry_set_tap_config(uint32_t client_id,
const bma456_tap_config_t *cfg);
esp_err_t client_registry_get_tap_config(uint32_t client_id,
bma456_tap_config_t *cfg_out);
size_t client_registry_set_tap_config_all(const bma456_tap_config_t *cfg);
esp_err_t client_registry_set_uv_protection(uint32_t client_id, bool enabled);
esp_err_t client_registry_get_uv_protection(uint32_t client_id,
bool *enabled_out);
size_t client_registry_set_uv_protection_all(bool enabled);
/** Store latest accel sample from a slave (matched by sender MAC). */
esp_err_t client_registry_update_accel(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t slave_id, int16_t x, int16_t y,
int16_t z);
esp_err_t client_registry_set_accel_stream(uint32_t client_id, bool enabled);
esp_err_t client_registry_get_accel_stream(uint32_t client_id, bool *enabled_out);
size_t client_registry_set_accel_stream_all(bool enabled);
esp_err_t client_registry_set_tap_notify(uint32_t client_id, bool single,
bool double_tap, bool triple);
esp_err_t client_registry_get_tap_notify(uint32_t client_id, bool *single_out,
bool *double_tap_out,
bool *triple_out);
size_t client_registry_set_tap_notify_all(bool single, bool double_tap,
bool triple);
/** Store tap event from slave (matched by sender MAC). kind: 1=single, 2=double, 3=triple. */
esp_err_t client_registry_update_tap(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t slave_id, uint32_t kind);
/** Drop cached tap if older than CLIENT_REGISTRY_TAP_MAX_AGE_MS. */
void client_registry_expire_tap(client_info_t *info);
/** Clear cached tap after UART snapshot or expiry. */
void client_registry_clear_tap(client_info_t *info);
/**
* If client has a fresh tap (age <= CLIENT_REGISTRY_TAP_MAX_AGE_MS), copy it out
* and clear the cache. Returns true when an event was returned.
*/
bool client_registry_take_tap(uint32_t client_id, uint32_t *kind_out,
uint32_t *age_ms_out);
/** Master local LiPo (client_id 0 in UART battery responses). */
void client_registry_set_master_battery(const board_lipo_reading_t *reading);
bool client_registry_get_master_battery(board_lipo_reading_t *reading_out,
uint32_t *age_ms_out);
/** Store latest battery report from a slave (matched by sender MAC). */
esp_err_t client_registry_update_battery(const uint8_t mac[CLIENT_MAC_LEN],
uint32_t slave_id, bool lipo1_valid,
uint32_t lipo1_mv, bool lipo2_valid,
uint32_t lipo2_mv);
#endif