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;