diff --git a/cue/assets/cue.icns b/cue/assets/cue.icns index 146e75a..9a084d7 100644 Binary files a/cue/assets/cue.icns and b/cue/assets/cue.icns differ diff --git a/cue/assets/cue.svg b/cue/assets/cue.svg index e96efcb..64ea3a6 100644 --- a/cue/assets/cue.svg +++ b/cue/assets/cue.svg @@ -1,1212 +1,3972 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/cue/assets/cue2.svg b/cue/assets/cue2.svg deleted file mode 100644 index 64ea3a6..0000000 --- a/cue/assets/cue2.svg +++ /dev/null @@ -1,3972 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cue/assets/cue22.svg b/cue/assets/cue22.svg new file mode 100644 index 0000000..e96efcb --- /dev/null +++ b/cue/assets/cue22.svg @@ -0,0 +1,1212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cue/build.sh b/cue/build.sh index f178c0d..670339a 100755 --- a/cue/build.sh +++ b/cue/build.sh @@ -20,7 +20,12 @@ render_svg() { if command -v rsvg-convert >/dev/null 2>&1; then rsvg-convert -w "$size" -h "$size" "$svg" -o "$out" elif command -v magick >/dev/null 2>&1; then - magick "$svg" -background none -resize "${size}x${size}" "$out" + # Compute DPI so SVG viewport rasterizes at target pixel size. + # Default 72 DPI renders the 83.25-unit viewport at 83px; + # scaling DPI proportionally renders at exact target dimensions. + local dpi=$(( (size * 72 + 82) / 83 )) + magick -background none -density "$dpi" "$svg" \ + -resize "${size}x${size}" "PNG32:$out" else echo "Error: need rsvg-convert (librsvg) or magick (ImageMagick 7)" exit 1 @@ -32,7 +37,7 @@ case "$(uname -s)" in echo "==> Generating .icns" ICONSET=$(mktemp -d)/cue.iconset mkdir -p "$ICONSET" - for size in 16 32 64 128 256 512; do + for size in 16 32 128 256 512; do render_svg "$SVG" "$size" "$ICONSET/icon_${size}x${size}.png" double=$((size * 2)) render_svg "$SVG" "$double" "$ICONSET/icon_${size}x${size}@2x.png" diff --git a/cue/src/app.rs b/cue/src/app.rs index e6b924a..a810ead 100644 --- a/cue/src/app.rs +++ b/cue/src/app.rs @@ -708,27 +708,27 @@ impl App { Tab::Eis => row![ column![ text("Start Hz").size(12), - text_input("1000", &self.freq_start).on_input(Message::FreqStartChanged).width(100), + text_input("1000", &self.freq_start).on_input(Message::FreqStartChanged).width(90), ].spacing(2), column![ text("Stop Hz").size(12), - text_input("200000", &self.freq_stop).on_input(Message::FreqStopChanged).width(100), + text_input("200000", &self.freq_stop).on_input(Message::FreqStopChanged).width(90), ].spacing(2), column![ text("PPD").size(12), - text_input("10", &self.ppd).on_input(Message::PpdChanged).width(60), + text_input("10", &self.ppd).on_input(Message::PpdChanged).width(50), ].spacing(2), column![ text("RTIA").size(12), - pick_list(Rtia::ALL, Some(self.rtia), Message::RtiaSelected), + pick_list(Rtia::ALL, Some(self.rtia), Message::RtiaSelected).width(110), ].spacing(2), column![ text("RCAL").size(12), - pick_list(Rcal::ALL, Some(self.rcal), Message::RcalSelected), + pick_list(Rcal::ALL, Some(self.rcal), Message::RcalSelected).width(160), ].spacing(2), column![ text("Electrodes").size(12), - pick_list(Electrode::ALL, Some(self.electrode), Message::ElectrodeSelected), + pick_list(Electrode::ALL, Some(self.electrode), Message::ElectrodeSelected).width(170), ].spacing(2), button(text("Apply").size(13)) .style(style_apply()) @@ -739,7 +739,7 @@ impl App { .padding([6, 20]) .on_press(Message::StartSweep), ] - .spacing(10) + .spacing(8) .align_y(iced::Alignment::End) .into(), @@ -758,7 +758,7 @@ impl App { ].spacing(2), column![ text("RTIA").size(12), - pick_list(LpRtia::ALL, Some(self.lsv_rtia), Message::LsvRtiaSelected), + pick_list(LpRtia::ALL, Some(self.lsv_rtia), Message::LsvRtiaSelected).width(Length::Shrink), ].spacing(2), button(text("Start LSV").size(13)) .style(style_action()) @@ -784,7 +784,7 @@ impl App { ].spacing(2), column![ text("RTIA").size(12), - pick_list(LpRtia::ALL, Some(self.amp_rtia), Message::AmpRtiaSelected), + pick_list(LpRtia::ALL, Some(self.amp_rtia), Message::AmpRtiaSelected).width(Length::Shrink), ].spacing(2), if self.amp_running { button(text("Stop").size(13)) @@ -829,7 +829,7 @@ impl App { ].spacing(2), column![ text("RTIA").size(12), - pick_list(LpRtia::ALL, Some(self.cl_rtia), Message::ClRtiaSelected), + pick_list(LpRtia::ALL, Some(self.cl_rtia), Message::ClRtiaSelected).width(Length::Shrink), ].spacing(2), button(text("Measure").size(13)) .style(style_action()) diff --git a/main/echem.c b/main/echem.c index b9d0d5c..de29646 100644 --- a/main/echem.c +++ b/main/echem.c @@ -161,6 +161,10 @@ static float read_current_ua(float rtia_ohms) uint32_t raw = AD5940_ReadAfeResult(AFERESULT_SINC2); int32_t code = (raw & (1UL << 15)) ? (int32_t)(raw | 0xFFFF0000UL) : (int32_t)raw; + /* clamp near ADC saturation to prevent sign-flip wrap artifact */ + if (code > 32700) code = 32700; + if (code < -32700) code = -32700; + /* I = V_tia / RTIA, V_tia = code * Vref / (PGA * 32768) */ float v_tia = (float)code * 1.82f / (1.5f * 32768.0f); float i_a = v_tia / rtia_ohms; @@ -245,6 +249,34 @@ static float read_voltage_mv(uint32_t muxp) return (float)code * 1820.0f / (1.5f * 32768.0f); } +static void echem_shutdown_lp(void) +{ + LPLoopCfg_Type lp; + AD5940_StructInit(&lp, sizeof(lp)); + lp.LpDacCfg.LpdacSel = LPDAC0; + lp.LpDacCfg.LpDacSrc = LPDACSRC_MMR; + lp.LpDacCfg.LpDacVzeroMux = LPDACVZERO_6BIT; + lp.LpDacCfg.LpDacVbiasMux = LPDACVBIAS_12BIT; + lp.LpDacCfg.LpDacSW = 0; + lp.LpDacCfg.LpDacRef = LPDACREF_2P5; + lp.LpDacCfg.PowerEn = bFALSE; + lp.LpDacCfg.DataRst = bFALSE; + lp.LpDacCfg.DacData6Bit = 0; + lp.LpDacCfg.DacData12Bit = 0; + lp.LpAmpCfg.LpAmpSel = LPAMP0; + lp.LpAmpCfg.LpAmpPwrMod = LPAMPPWR_NORM; + lp.LpAmpCfg.LpPaPwrEn = bFALSE; + lp.LpAmpCfg.LpTiaPwrEn = bFALSE; + lp.LpAmpCfg.LpTiaRf = LPTIARF_OPEN; + lp.LpAmpCfg.LpTiaRload = LPTIARLOAD_SHORT; + lp.LpAmpCfg.LpTiaRtia = LPTIARTIA_OPEN; + lp.LpAmpCfg.LpTiaSW = 0; + AD5940_LPLoopCfgS(&lp); + + SWMatrixCfg_Type sw = { SWD_OPEN, SWP_OPEN, SWN_OPEN, SWT_OPEN }; + AD5940_SWMatrixCfgS(&sw); +} + /* ---- public ---- */ void echem_default_lsv(LSVConfig *cfg) @@ -265,7 +297,33 @@ void echem_default_amp(AmpConfig *cfg) cfg->lp_rtia = LP_RTIA_10K; } -int echem_lsv(const LSVConfig *cfg, LSVPoint *out, uint32_t max_points) +static void lsv_calc_step(const LSVConfig *cfg, uint32_t max_points, + uint32_t *n_out, float *step_out) +{ + float v_range = cfg->v_stop - cfg->v_start; + uint32_t n_lsb = (uint32_t)(fabsf(v_range / VBIAS_LSB) + 0.5f); + uint32_t n_steps = n_lsb; + uint32_t step_mult = 1; + if (n_steps > max_points) { + step_mult = (n_lsb + max_points - 1) / max_points; + n_steps = (n_lsb + step_mult - 1) / step_mult; + } + if (n_steps < 2) n_steps = 2; + *n_out = n_steps; + *step_out = (v_range > 0) ? VBIAS_LSB * step_mult : -VBIAS_LSB * step_mult; +} + +uint32_t echem_lsv_calc_steps(const LSVConfig *cfg, uint32_t max_points) +{ + float v_range = cfg->v_stop - cfg->v_start; + if (fabsf(v_range) < 0.001f) return 0; + uint32_t n; + float step; + lsv_calc_step(cfg, max_points, &n, &step); + return n; +} + +int echem_lsv(const LSVConfig *cfg, LSVPoint *out, uint32_t max_points, lsv_point_cb_t cb) { if (cfg->lp_rtia >= LP_RTIA_COUNT) return 0; float rtia = lp_rtia_ohms[cfg->lp_rtia]; @@ -281,17 +339,9 @@ int echem_lsv(const LSVConfig *cfg, LSVPoint *out, uint32_t max_points) float v_range = cfg->v_stop - cfg->v_start; if (fabsf(v_range) < 0.001f) return 0; - /* compute steps to always cover full range within max_points */ - uint32_t n_lsb = (uint32_t)(fabsf(v_range / VBIAS_LSB) + 0.5f); - uint32_t n_steps = n_lsb; - uint32_t step_mult = 1; - if (n_steps > max_points) { - step_mult = (n_lsb + max_points - 1) / max_points; - n_steps = (n_lsb + step_mult - 1) / step_mult; - } - if (n_steps < 2) n_steps = 2; - - float step = (v_range > 0) ? VBIAS_LSB * step_mult : -VBIAS_LSB * step_mult; + uint32_t n_steps; + float step; + lsv_calc_step(cfg, max_points, &n_steps, &step); float delay_ms = fabsf(step / cfg->scan_rate) * 1000.0f; if (delay_ms < 1.0f) delay_ms = 1.0f; @@ -312,13 +362,15 @@ int echem_lsv(const LSVConfig *cfg, LSVPoint *out, uint32_t max_points) out[i].i_ua = i_ua; printf("%10.1f %10.3f\n", v_mv, i_ua); + if (cb) cb((uint16_t)i, v_mv, i_ua); } + echem_shutdown_lp(); AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); return (int)n_steps; } -int echem_amp(const AmpConfig *cfg, AmpPoint *out, uint32_t max_points) +int echem_amp(const AmpConfig *cfg, AmpPoint *out, uint32_t max_points, amp_point_cb_t cb) { if (cfg->lp_rtia >= LP_RTIA_COUNT) return 0; float rtia = lp_rtia_ohms[cfg->lp_rtia]; @@ -347,7 +399,6 @@ int echem_amp(const AmpConfig *cfg, AmpPoint *out, uint32_t max_points) uint32_t count = 0; for (uint32_t i = 0; i < max_samples; i++) { - /* check for stop command (non-blocking) */ BleCommand cmd; if (ble_recv_command(&cmd, 0) == 0 && cmd.type == CMD_STOP_AMP) break; @@ -360,10 +411,12 @@ int echem_amp(const AmpConfig *cfg, AmpPoint *out, uint32_t max_points) count++; printf("%10.1f %10.3f\n", t_ms, i_ua); + if (cb) cb((uint16_t)i, t_ms, i_ua); vTaskDelay(interval); } + echem_shutdown_lp(); AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); return (int)count; } @@ -383,7 +436,7 @@ void echem_default_cl(ClConfig *cfg) static uint32_t sample_phase(float v_mv, float t_dep_ms, float t_meas_ms, uint8_t phase, float rtia_ohms, ClPoint *out, uint32_t idx, uint32_t max_points, - TickType_t t0, float *avg_out) + TickType_t t0, float *avg_out, cl_point_cb_t cb) { AD5940_LPDAC0WriteS(mv_to_vbias_code(v_mv), VZERO_CODE); @@ -405,6 +458,8 @@ static uint32_t sample_phase(float v_mv, float t_dep_ms, float t_meas_ms, out[idx].t_ms = t_ms; out[idx].i_ua = i_ua; out[idx].phase = phase; + + if (cb) cb((uint16_t)idx, t_ms, i_ua, phase); idx++; sum += i_ua; @@ -417,7 +472,8 @@ static uint32_t sample_phase(float v_mv, float t_dep_ms, float t_meas_ms, return idx; } -int echem_chlorine(const ClConfig *cfg, ClPoint *out, uint32_t max_points, ClResult *result) +int echem_chlorine(const ClConfig *cfg, ClPoint *out, uint32_t max_points, + ClResult *result, cl_point_cb_t cb) { if (cfg->lp_rtia >= LP_RTIA_COUNT) return 0; float rtia = lp_rtia_ohms[cfg->lp_rtia]; @@ -434,15 +490,16 @@ int echem_chlorine(const ClConfig *cfg, ClPoint *out, uint32_t max_points, ClRes 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, CL_PHASE_FREE, rtia, out, idx, max_points, t0, - &result->i_free_ua); + &result->i_free_ua, cb); printf("Cl: total chlorine at %.0f mV\n", cfg->v_total); idx = sample_phase(cfg->v_total, cfg->t_dep_ms, cfg->t_meas_ms, CL_PHASE_TOTAL, rtia, out, idx, max_points, t0, - &result->i_total_ua); + &result->i_total_ua, cb); printf("Cl: free=%.3f uA, total=%.3f uA\n", result->i_free_ua, result->i_total_ua); + echem_shutdown_lp(); AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); return (int)idx; } @@ -490,6 +547,7 @@ int echem_ph_ocp(const PhConfig *cfg, PhResult *result) printf("pH: SE0=%.1f mV, RE0=%.1f mV, OCP=%.1f mV, pH=%.2f\n", v_se0, v_re0, ocp, result->ph); + echem_shutdown_lp(); AD5940_AFECtrlS(AFECTRL_ALL, bFALSE); return 0; } diff --git a/main/echem.h b/main/echem.h index 5cab4e9..09c06a0 100644 --- a/main/echem.h +++ b/main/echem.h @@ -80,14 +80,19 @@ typedef struct { float temp_c; /* temperature used */ } PhResult; +typedef int (*lsv_point_cb_t)(uint16_t idx, float v_mv, float i_ua); +typedef int (*amp_point_cb_t)(uint16_t idx, float t_ms, float i_ua); +typedef int (*cl_point_cb_t)(uint16_t idx, float t_ms, float i_ua, uint8_t phase); + void echem_default_lsv(LSVConfig *cfg); void echem_default_amp(AmpConfig *cfg); void echem_default_cl(ClConfig *cfg); void echem_default_ph(PhConfig *cfg); -int echem_lsv(const LSVConfig *cfg, LSVPoint *out, uint32_t max_points); -int echem_amp(const AmpConfig *cfg, AmpPoint *out, uint32_t max_points); -int echem_chlorine(const ClConfig *cfg, ClPoint *out, uint32_t max_points, ClResult *result); +uint32_t echem_lsv_calc_steps(const LSVConfig *cfg, uint32_t max_points); +int echem_lsv(const LSVConfig *cfg, LSVPoint *out, uint32_t max_points, lsv_point_cb_t cb); +int echem_amp(const AmpConfig *cfg, AmpPoint *out, uint32_t max_points, amp_point_cb_t cb); +int echem_chlorine(const ClConfig *cfg, ClPoint *out, uint32_t max_points, ClResult *result, cl_point_cb_t cb); int echem_ph_ocp(const PhConfig *cfg, PhResult *result); #endif diff --git a/main/eis.c b/main/eis.c index 2f28b9b..d93da6e 100644 --- a/main/eis.c +++ b/main/eis.c @@ -410,7 +410,7 @@ int eis_measure_point(float freq_hz, EISPoint *out) return 0; } -int eis_sweep(EISPoint *out, uint32_t max_points) +int eis_sweep(EISPoint *out, uint32_t max_points, eis_point_cb_t cb) { uint32_t n = eis_calc_num_points(&ctx.cfg); if (n > max_points) n = max_points; @@ -435,6 +435,7 @@ int eis_sweep(EISPoint *out, uint32_t max_points) printf("%10.1f %12.2f %10.2f %12.2f %12.2f %6.2f%%\n", out[0].freq_hz, out[0].mag_ohms, out[0].phase_deg, out[0].z_real, out[0].z_imag, out[0].pct_err); + if (cb) cb(0, &out[0]); for (uint32_t i = 1; i < n; i++) { float freq; @@ -443,6 +444,7 @@ int eis_sweep(EISPoint *out, uint32_t max_points) printf("%10.1f %12.2f %10.2f %12.2f %12.2f %6.2f%%\n", out[i].freq_hz, out[i].mag_ohms, out[i].phase_deg, out[i].z_real, out[i].z_imag, out[i].pct_err); + if (cb) cb((uint16_t)i, &out[i]); } /* guard: throwaway at stop frequency to cap the sweep cleanly */ diff --git a/main/eis.h b/main/eis.h index b23a5d6..c43b27a 100644 --- a/main/eis.h +++ b/main/eis.h @@ -49,11 +49,13 @@ typedef struct { float pct_err; } EISPoint; +typedef int (*eis_point_cb_t)(uint16_t idx, const EISPoint *pt); + void eis_default_config(EISConfig *cfg); void eis_init(const EISConfig *cfg); void eis_reconfigure(const EISConfig *cfg); int eis_measure_point(float freq_hz, EISPoint *out); -int eis_sweep(EISPoint *out, uint32_t max_points); +int eis_sweep(EISPoint *out, uint32_t max_points, eis_point_cb_t cb); uint32_t eis_calc_num_points(const EISConfig *cfg); #endif diff --git a/main/eis4.c b/main/eis4.c index 4b5b7d9..815a7b6 100644 --- a/main/eis4.c +++ b/main/eis4.c @@ -22,14 +22,9 @@ static void do_sweep(void) eis_init(&cfg); uint32_t n = eis_calc_num_points(&cfg); - int got = eis_sweep(results, n); + ble_send_sweep_start(n, cfg.freq_start_hz, cfg.freq_stop_hz); + int got = eis_sweep(results, n, ble_send_eis_point); printf("Sweep complete: %d points\n", got); - - ble_send_sweep_start(got, cfg.freq_start_hz, cfg.freq_stop_hz); - for (int i = 0; i < got; i++) { - ble_send_eis_point(i, &results[i]); - vTaskDelay(pdMS_TO_TICKS(10)); - } ble_send_sweep_end(); } @@ -124,14 +119,10 @@ void app_main(void) printf("LSV: %.0f-%.0f mV, %.0f mV/s, rtia=%u\n", lsv_cfg.v_start, lsv_cfg.v_stop, lsv_cfg.scan_rate, lsv_cfg.lp_rtia); - int got = echem_lsv(&lsv_cfg, lsv_results, ECHEM_MAX_POINTS); + uint32_t n = echem_lsv_calc_steps(&lsv_cfg, ECHEM_MAX_POINTS); + ble_send_lsv_start(n, lsv_cfg.v_start, lsv_cfg.v_stop); + int got = echem_lsv(&lsv_cfg, lsv_results, ECHEM_MAX_POINTS, ble_send_lsv_point); printf("LSV complete: %d points\n", got); - - ble_send_lsv_start(got, lsv_cfg.v_start, lsv_cfg.v_stop); - for (int i = 0; i < got; i++) { - ble_send_lsv_point(i, lsv_results[i].v_mv, lsv_results[i].i_ua); - vTaskDelay(pdMS_TO_TICKS(10)); - } ble_send_lsv_end(); break; } @@ -146,14 +137,8 @@ void app_main(void) amp_cfg.v_hold, amp_cfg.interval_ms, amp_cfg.duration_s); ble_send_amp_start(amp_cfg.v_hold); - - int got = echem_amp(&_cfg, amp_results, ECHEM_MAX_POINTS); + int got = echem_amp(&_cfg, amp_results, ECHEM_MAX_POINTS, ble_send_amp_point); printf("Amp complete: %d points\n", got); - - for (int i = 0; i < got; i++) { - ble_send_amp_point(i, amp_results[i].t_ms, amp_results[i].i_ua); - vTaskDelay(pdMS_TO_TICKS(10)); - } ble_send_amp_end(); break; } @@ -187,16 +172,14 @@ void app_main(void) cl_cfg.t_meas_ms = cmd.cl.t_meas_ms; cl_cfg.lp_rtia = cmd.cl.lp_rtia; + uint32_t n_per = (uint32_t)(cl_cfg.t_meas_ms / 50.0f + 0.5f); + if (n_per < 2) n_per = 2; + ble_send_cl_start(2 * n_per); ClResult cl_result; - int got = echem_chlorine(&cl_cfg, cl_results, ECHEM_MAX_POINTS, &cl_result); + int got = echem_chlorine(&cl_cfg, cl_results, ECHEM_MAX_POINTS, + &cl_result, ble_send_cl_point); printf("Cl complete: %d points, free=%.3f uA, total=%.3f uA\n", got, cl_result.i_free_ua, cl_result.i_total_ua); - - ble_send_cl_start(got); - for (int i = 0; i < got; i++) { - ble_send_cl_point(i, cl_results[i].t_ms, cl_results[i].i_ua, cl_results[i].phase); - vTaskDelay(pdMS_TO_TICKS(10)); - } ble_send_cl_result(cl_result.i_free_ua, cl_result.i_total_ua); ble_send_cl_end(); break;