merge integration
# Conflicts: # cue-ios/CueIOS/AppState.swift # cue-ios/CueIOS/Models/Protocol.swift # cue/src/app.rs # cue/src/protocol.rs # main/protocol.h # main/wifi_transport.c
This commit is contained in:
commit
5f550f031a
|
|
@ -323,6 +323,9 @@ final class AppState {
|
||||||
phSlope = Double(slope)
|
phSlope = Double(slope)
|
||||||
phOffset = Double(offset)
|
phOffset = Double(offset)
|
||||||
status = String(format: "pH cal: slope=%.4f offset=%.4f", slope, offset)
|
status = String(format: "pH cal: slope=%.4f offset=%.4f", slope, offset)
|
||||||
|
|
||||||
|
case .keepalive:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ let RSP_REFS_DONE: UInt8 = 0x22
|
||||||
let RSP_REF_STATUS: UInt8 = 0x23
|
let RSP_REF_STATUS: UInt8 = 0x23
|
||||||
let RSP_CL_FACTOR: UInt8 = 0x24
|
let RSP_CL_FACTOR: UInt8 = 0x24
|
||||||
let RSP_PH_CAL: UInt8 = 0x25
|
let RSP_PH_CAL: UInt8 = 0x25
|
||||||
|
let RSP_KEEPALIVE: UInt8 = 0x50
|
||||||
|
|
||||||
// Cue -> ESP32
|
// Cue -> ESP32
|
||||||
let CMD_SET_SWEEP: UInt8 = 0x10
|
let CMD_SET_SWEEP: UInt8 = 0x10
|
||||||
|
|
@ -131,6 +132,7 @@ enum EisMessage {
|
||||||
case cellK(Float)
|
case cellK(Float)
|
||||||
case clFactor(Float)
|
case clFactor(Float)
|
||||||
case phCal(slope: Float, offset: Float)
|
case phCal(slope: Float, offset: Float)
|
||||||
|
case keepalive
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Response parser
|
// MARK: - Response parser
|
||||||
|
|
@ -271,6 +273,9 @@ func parseSysex(_ data: [UInt8]) -> EisMessage? {
|
||||||
offset: decodeFloat(p, at: 5)
|
offset: decodeFloat(p, at: 5)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
case RSP_KEEPALIVE:
|
||||||
|
return .keepalive
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -866,6 +866,7 @@ impl App {
|
||||||
self.ph_offset = Some(offset);
|
self.ph_offset = Some(offset);
|
||||||
self.status = format!("pH cal: slope={:.4} offset={:.4}", slope, offset);
|
self.status = format!("pH cal: slope={:.4} offset={:.4}", slope, offset);
|
||||||
}
|
}
|
||||||
|
EisMessage::Keepalive => {}
|
||||||
},
|
},
|
||||||
Message::TabSelected(t) => {
|
Message::TabSelected(t) => {
|
||||||
if t == Tab::Browse {
|
if t == Tab::Browse {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ pub const RSP_CELL_K: u8 = 0x11;
|
||||||
pub const RSP_REF_STATUS: u8 = 0x23;
|
pub const RSP_REF_STATUS: u8 = 0x23;
|
||||||
pub const RSP_CL_FACTOR: u8 = 0x24;
|
pub const RSP_CL_FACTOR: u8 = 0x24;
|
||||||
pub const RSP_PH_CAL: u8 = 0x25;
|
pub const RSP_PH_CAL: u8 = 0x25;
|
||||||
|
pub const RSP_KEEPALIVE: u8 = 0x50;
|
||||||
|
|
||||||
/* Cue → ESP32 */
|
/* Cue → ESP32 */
|
||||||
pub const CMD_SET_SWEEP: u8 = 0x10;
|
pub const CMD_SET_SWEEP: u8 = 0x10;
|
||||||
|
|
@ -266,6 +267,7 @@ pub enum EisMessage {
|
||||||
CellK(f32),
|
CellK(f32),
|
||||||
ClFactor(f32),
|
ClFactor(f32),
|
||||||
PhCal { slope: f32, offset: f32 },
|
PhCal { slope: f32, offset: f32 },
|
||||||
|
Keepalive,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_u16(data: &[u8]) -> u16 {
|
fn decode_u16(data: &[u8]) -> u16 {
|
||||||
|
|
@ -432,6 +434,7 @@ pub fn parse_sysex(data: &[u8]) -> Option<EisMessage> {
|
||||||
offset: decode_float(&p[5..10]),
|
offset: decode_float(&p[5..10]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
RSP_KEEPALIVE => Some(EisMessage::Keepalive),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
main/echem.c
38
main/echem.c
|
|
@ -327,7 +327,14 @@ int echem_clean(float v_mv, float duration_s)
|
||||||
AD5940_LPDAC0WriteS(code, VZERO_CODE);
|
AD5940_LPDAC0WriteS(code, VZERO_CODE);
|
||||||
|
|
||||||
printf("Clean: %.0f mV for %.0f s\n", v_mv, duration_s);
|
printf("Clean: %.0f mV for %.0f s\n", v_mv, duration_s);
|
||||||
vTaskDelay(pdMS_TO_TICKS((uint32_t)(duration_s * 1000.0f)));
|
|
||||||
|
uint32_t remain_ms = (uint32_t)(duration_s * 1000.0f);
|
||||||
|
while (remain_ms > 0) {
|
||||||
|
uint32_t chunk = remain_ms > 3000 ? 3000 : remain_ms;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(chunk));
|
||||||
|
remain_ms -= chunk;
|
||||||
|
if (remain_ms > 0) send_keepalive();
|
||||||
|
}
|
||||||
|
|
||||||
echem_shutdown_lp();
|
echem_shutdown_lp();
|
||||||
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
||||||
|
|
@ -497,7 +504,15 @@ static uint32_t sample_phase(float v_mv, float t_dep_ms, float t_meas_ms,
|
||||||
AD5940_LPDAC0WriteS(mv_to_vbias_code(v_mv), VZERO_CODE);
|
AD5940_LPDAC0WriteS(mv_to_vbias_code(v_mv), VZERO_CODE);
|
||||||
|
|
||||||
/* settling — no samples recorded */
|
/* settling — no samples recorded */
|
||||||
vTaskDelay(pdMS_TO_TICKS((uint32_t)t_dep_ms));
|
{
|
||||||
|
uint32_t remain_ms = (uint32_t)t_dep_ms;
|
||||||
|
while (remain_ms > 0) {
|
||||||
|
uint32_t chunk = remain_ms > 3000 ? 3000 : remain_ms;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(chunk));
|
||||||
|
remain_ms -= chunk;
|
||||||
|
if (remain_ms > 0) send_keepalive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* measurement — sample at ~50ms intervals */
|
/* measurement — sample at ~50ms intervals */
|
||||||
uint32_t n_samples = (uint32_t)(t_meas_ms / 50.0f + 0.5f);
|
uint32_t n_samples = (uint32_t)(t_meas_ms / 50.0f + 0.5f);
|
||||||
|
|
@ -541,7 +556,15 @@ int echem_chlorine(const ClConfig *cfg, ClPoint *out, uint32_t max_points,
|
||||||
|
|
||||||
printf("Cl: conditioning at %.0f mV for %.0f ms\n", cfg->v_cond, cfg->t_cond_ms);
|
printf("Cl: conditioning at %.0f mV for %.0f ms\n", cfg->v_cond, cfg->t_cond_ms);
|
||||||
AD5940_LPDAC0WriteS(mv_to_vbias_code(cfg->v_cond), VZERO_CODE);
|
AD5940_LPDAC0WriteS(mv_to_vbias_code(cfg->v_cond), VZERO_CODE);
|
||||||
vTaskDelay(pdMS_TO_TICKS((uint32_t)cfg->t_cond_ms));
|
{
|
||||||
|
uint32_t remain_ms = (uint32_t)cfg->t_cond_ms;
|
||||||
|
while (remain_ms > 0) {
|
||||||
|
uint32_t chunk = remain_ms > 3000 ? 3000 : remain_ms;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(chunk));
|
||||||
|
remain_ms -= chunk;
|
||||||
|
if (remain_ms > 0) send_keepalive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
printf("Cl: free chlorine at %.0f mV\n", cfg->v_free);
|
printf("Cl: free chlorine at %.0f mV\n", cfg->v_free);
|
||||||
idx = sample_phase(cfg->v_free, cfg->t_dep_ms, cfg->t_meas_ms,
|
idx = sample_phase(cfg->v_free, cfg->t_dep_ms, cfg->t_meas_ms,
|
||||||
|
|
@ -579,7 +602,14 @@ int echem_ph_ocp(const PhConfig *cfg, PhResult *result)
|
||||||
AD5940_ADCBaseCfgS(&adc);
|
AD5940_ADCBaseCfgS(&adc);
|
||||||
|
|
||||||
printf("pH: stabilizing %0.f s\n", cfg->stabilize_s);
|
printf("pH: stabilizing %0.f s\n", cfg->stabilize_s);
|
||||||
vTaskDelay(pdMS_TO_TICKS((uint32_t)(cfg->stabilize_s * 1000.0f)));
|
|
||||||
|
uint32_t remain_ms = (uint32_t)(cfg->stabilize_s * 1000.0f);
|
||||||
|
while (remain_ms > 0) {
|
||||||
|
uint32_t chunk = remain_ms > 3000 ? 3000 : remain_ms;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(chunk));
|
||||||
|
remain_ms -= chunk;
|
||||||
|
if (remain_ms > 0) send_keepalive();
|
||||||
|
}
|
||||||
|
|
||||||
/* average N readings of V(SE0) and V(RE0) */
|
/* average N readings of V(SE0) and V(RE0) */
|
||||||
#define PH_AVG_N 10
|
#define PH_AVG_N 10
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,14 @@ static int send_sysex(const uint8_t *sysex, uint16_t len)
|
||||||
return wifi_send_sysex(sysex, len);
|
return wifi_send_sysex(sysex, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- outbound: keepalive ---- */
|
||||||
|
|
||||||
|
int send_keepalive(void)
|
||||||
|
{
|
||||||
|
uint8_t sx[] = { 0xF0, 0x7D, RSP_KEEPALIVE, 0xF7 };
|
||||||
|
return send_sysex(sx, sizeof(sx));
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- outbound: EIS ---- */
|
/* ---- outbound: EIS ---- */
|
||||||
|
|
||||||
int send_sweep_start(uint32_t num_points, float freq_start, float freq_stop)
|
int send_sweep_start(uint32_t num_points, float freq_start, float freq_stop)
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@
|
||||||
#define RSP_REF_STATUS 0x23
|
#define RSP_REF_STATUS 0x23
|
||||||
#define RSP_CL_FACTOR 0x24
|
#define RSP_CL_FACTOR 0x24
|
||||||
#define RSP_PH_CAL 0x25
|
#define RSP_PH_CAL 0x25
|
||||||
|
#define RSP_KEEPALIVE 0x50
|
||||||
|
|
||||||
/* Session sync responses (0x4x) */
|
/* Session sync responses (0x4x) */
|
||||||
#define RSP_SESSION_CREATED 0x40
|
#define RSP_SESSION_CREATED 0x40
|
||||||
|
|
@ -152,6 +153,9 @@ int send_ref_lp_range(uint8_t mode, uint8_t low_idx, uint8_t high_idx);
|
||||||
int send_refs_done(void);
|
int send_refs_done(void);
|
||||||
int send_ref_status(uint8_t has_refs);
|
int send_ref_status(uint8_t has_refs);
|
||||||
|
|
||||||
|
/* keepalive (sent during long blocking ops) */
|
||||||
|
int send_keepalive(void);
|
||||||
|
|
||||||
/* session management */
|
/* session management */
|
||||||
const Session *session_get_all(uint8_t *count);
|
const Session *session_get_all(uint8_t *count);
|
||||||
uint8_t session_get_current(void);
|
uint8_t session_get_current(void);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@
|
||||||
|
|
||||||
#define UDP_PORT 5941
|
#define UDP_PORT 5941
|
||||||
#define UDP_BUF_SIZE 128
|
#define UDP_BUF_SIZE 128
|
||||||
#define MAX_UDP_CLIENTS 10
|
#define UDP_CLIENTS_MAX 16
|
||||||
|
#define CLIENT_TIMEOUT_MS 30000
|
||||||
|
|
||||||
static int udp_sock = -1;
|
static int udp_sock = -1;
|
||||||
static esp_netif_t *ap_netif;
|
static esp_netif_t *ap_netif;
|
||||||
|
|
||||||
|
|
@ -26,7 +28,7 @@ static struct {
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
uint8_t mac[6];
|
uint8_t mac[6];
|
||||||
bool active;
|
bool active;
|
||||||
} clients[MAX_UDP_CLIENTS];
|
} clients[UDP_CLIENTS_MAX];
|
||||||
|
|
||||||
static int client_count;
|
static int client_count;
|
||||||
|
|
||||||
|
|
@ -38,7 +40,7 @@ static void client_touch(const struct sockaddr_in *addr)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_count < MAX_UDP_CLIENTS) {
|
if (client_count < UDP_CLIENTS_MAX) {
|
||||||
clients[client_count].addr = *addr;
|
clients[client_count].addr = *addr;
|
||||||
clients[client_count].active = true;
|
clients[client_count].active = true;
|
||||||
memset(clients[client_count].mac, 0, 6);
|
memset(clients[client_count].mac, 0, 6);
|
||||||
|
|
@ -56,7 +58,7 @@ static void client_touch(const struct sockaddr_in *addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
client_count++;
|
client_count++;
|
||||||
printf("UDP: client added (%d/%d)\n", client_count, MAX_UDP_CLIENTS);
|
printf("UDP: client added (%d)\n", client_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,7 +67,7 @@ static void client_remove_by_mac(const uint8_t *mac)
|
||||||
for (int i = 0; i < client_count; ) {
|
for (int i = 0; i < client_count; ) {
|
||||||
if (memcmp(clients[i].mac, mac, 6) == 0) {
|
if (memcmp(clients[i].mac, mac, 6) == 0) {
|
||||||
clients[i] = clients[--client_count];
|
clients[i] = clients[--client_count];
|
||||||
printf("UDP: client removed by MAC (%d/%d)\n", client_count, MAX_UDP_CLIENTS);
|
printf("UDP: client removed by MAC (%d)\n", client_count);
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue