From 5b051cfa2066e1beb6a820f0ba9ce6eacceda7d1 Mon Sep 17 00:00:00 2001 From: jess Date: Thu, 2 Apr 2026 19:31:10 -0700 Subject: [PATCH] firmware: add pH calibration NVS, protocol, and dispatch --- main/eis.c | 36 ++++++++++++++++++++++++++++++++++-- main/eis.h | 5 +++++ main/eis4.c | 11 +++++++++++ main/protocol.c | 13 +++++++++++++ main/protocol.h | 7 +++++++ main/wifi_transport.c | 6 ++++++ 6 files changed, 76 insertions(+), 2 deletions(-) diff --git a/main/eis.c b/main/eis.c index 833e67a..78b7d5d 100644 --- a/main/eis.c +++ b/main/eis.c @@ -26,6 +26,8 @@ static struct { /* cell constant K (cm⁻¹), cached from NVS */ static float cell_k_cached; static float cl_factor_cached; +static float ph_slope_cached; +static float ph_offset_cached; /* open-circuit calibration data */ static struct { @@ -594,8 +596,10 @@ int eis_has_open_cal(void) return ocal.valid; } -#define NVS_CELLK_KEY "cell_k" -#define NVS_CLFACTOR_KEY "cl_factor" +#define NVS_CELLK_KEY "cell_k" +#define NVS_CLFACTOR_KEY "cl_factor" +#define NVS_PH_SLOPE_KEY "ph_slope" +#define NVS_PH_OFFSET_KEY "ph_offset" void eis_set_cell_k(float k) { @@ -646,3 +650,31 @@ void eis_load_cl_factor(void) cl_factor_cached = 0.0f; nvs_close(h); } + +void eis_set_ph_cal(float slope, float offset) +{ + ph_slope_cached = slope; + ph_offset_cached = offset; + nvs_handle_t h; + if (nvs_open(NVS_OCAL_NS, NVS_READWRITE, &h) != ESP_OK) return; + nvs_set_blob(h, NVS_PH_SLOPE_KEY, &slope, sizeof(slope)); + nvs_set_blob(h, NVS_PH_OFFSET_KEY, &offset, sizeof(offset)); + nvs_commit(h); + nvs_close(h); +} + +float eis_get_ph_slope(void) { return ph_slope_cached; } +float eis_get_ph_offset(void) { return ph_offset_cached; } + +void eis_load_ph_cal(void) +{ + nvs_handle_t h; + if (nvs_open(NVS_OCAL_NS, NVS_READONLY, &h) != ESP_OK) return; + size_t len = sizeof(ph_slope_cached); + if (nvs_get_blob(h, NVS_PH_SLOPE_KEY, &ph_slope_cached, &len) != ESP_OK || len != sizeof(ph_slope_cached)) + ph_slope_cached = 0.0f; + len = sizeof(ph_offset_cached); + if (nvs_get_blob(h, NVS_PH_OFFSET_KEY, &ph_offset_cached, &len) != ESP_OK || len != sizeof(ph_offset_cached)) + ph_offset_cached = 0.0f; + nvs_close(h); +} diff --git a/main/eis.h b/main/eis.h index 8fbb4d3..76c4bb2 100644 --- a/main/eis.h +++ b/main/eis.h @@ -71,4 +71,9 @@ void eis_set_cl_factor(float f); float eis_get_cl_factor(void); void eis_load_cl_factor(void); +void eis_set_ph_cal(float slope, float offset); +float eis_get_ph_slope(void); +float eis_get_ph_offset(void); +void eis_load_ph_cal(void); + #endif diff --git a/main/eis4.c b/main/eis4.c index dace30e..a502119 100644 --- a/main/eis4.c +++ b/main/eis4.c @@ -57,6 +57,7 @@ void app_main(void) eis_load_open_cal(); eis_load_cell_k(); eis_load_cl_factor(); + eis_load_ph_cal(); temp_init(); esp_netif_init(); @@ -225,6 +226,16 @@ void app_main(void) send_cl_factor(eis_get_cl_factor()); break; + case CMD_SET_PH_CAL: + eis_set_ph_cal(cmd.ph_cal.slope, cmd.ph_cal.offset); + send_ph_cal(cmd.ph_cal.slope, cmd.ph_cal.offset); + printf("pH cal set: slope=%.4f offset=%.4f\n", cmd.ph_cal.slope, cmd.ph_cal.offset); + break; + + case CMD_GET_PH_CAL: + send_ph_cal(eis_get_ph_slope(), eis_get_ph_offset()); + break; + case CMD_START_CL: { ClConfig cl_cfg; cl_cfg.v_cond = cmd.cl.v_cond; diff --git a/main/protocol.c b/main/protocol.c index 504a6ed..bf81691 100644 --- a/main/protocol.c +++ b/main/protocol.c @@ -303,6 +303,19 @@ int send_cl_end(void) return send_sysex(sx, sizeof(sx)); } +/* ---- outbound: pH calibration ---- */ + +int send_ph_cal(float slope, float offset) +{ + uint8_t sx[16]; + uint16_t p = 0; + sx[p++] = 0xF0; sx[p++] = 0x7D; sx[p++] = RSP_PH_CAL; + encode_float(slope, &sx[p]); p += 5; + encode_float(offset, &sx[p]); p += 5; + sx[p++] = 0xF7; + return send_sysex(sx, p); +} + /* ---- outbound: pH ---- */ int send_ph_result(float v_ocp_mv, float ph, float temp_c) diff --git a/main/protocol.h b/main/protocol.h index 8ab5709..59308f7 100644 --- a/main/protocol.h +++ b/main/protocol.h @@ -28,6 +28,8 @@ #define CMD_CLEAR_REFS 0x32 #define CMD_SET_CL_FACTOR 0x33 #define CMD_GET_CL_FACTOR 0x34 +#define CMD_SET_PH_CAL 0x35 +#define CMD_GET_PH_CAL 0x36 /* Session sync commands (0x4x) */ #define CMD_SESSION_CREATE 0x40 @@ -59,6 +61,7 @@ #define RSP_REFS_DONE 0x22 #define RSP_REF_STATUS 0x23 #define RSP_CL_FACTOR 0x24 +#define RSP_PH_CAL 0x25 /* Session sync responses (0x4x) */ #define RSP_SESSION_CREATED 0x40 @@ -85,6 +88,7 @@ typedef struct { struct { float v_mv; float duration_s; } clean; float cell_k; float cl_factor; + struct { float slope; float offset; } ph_cal; struct { uint8_t name_len; char name[MAX_SESSION_NAME]; } session_create; struct { uint8_t id; } session_switch; struct { uint8_t id; uint8_t name_len; char name[MAX_SESSION_NAME]; } session_rename; @@ -139,6 +143,9 @@ int send_cell_k(float k); /* outbound: chlorine factor */ int send_cl_factor(float f); +/* outbound: pH calibration */ +int send_ph_cal(float slope, float offset); + /* outbound: reference collection */ int send_ref_frame(uint8_t mode, uint8_t rtia_idx); int send_ref_lp_range(uint8_t mode, uint8_t low_idx, uint8_t high_idx); diff --git a/main/wifi_transport.c b/main/wifi_transport.c index 1d05ba4..3bb56e3 100644 --- a/main/wifi_transport.c +++ b/main/wifi_transport.c @@ -170,6 +170,11 @@ static void parse_udp_sysex(const uint8_t *data, uint16_t len) if (len < 8) return; cmd.cl_factor = decode_float(&data[3]); break; + case CMD_SET_PH_CAL: + if (len < 13) return; + cmd.ph_cal.slope = decode_float(&data[3]); + cmd.ph_cal.offset = decode_float(&data[8]); + break; case CMD_SESSION_CREATE: if (len < 5) return; cmd.session_create.name_len = data[3] & 0x7F; @@ -197,6 +202,7 @@ static void parse_udp_sysex(const uint8_t *data, uint16_t len) case CMD_GET_TEMP: case CMD_GET_CELL_K: case CMD_GET_CL_FACTOR: + case CMD_GET_PH_CAL: case CMD_START_REFS: case CMD_GET_REFS: case CMD_CLEAR_REFS: