nah it really does work pretty good now, the hard part is working out
the electrode arrays themsevles now
This commit is contained in:
parent
a3749af7b5
commit
8130f17b44
Binary file not shown.
5180
cue/assets/cue.svg
5180
cue/assets/cue.svg
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 433 KiB |
3972
cue/assets/cue2.svg
3972
cue/assets/cue2.svg
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 433 KiB |
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 255 KiB |
|
|
@ -20,7 +20,12 @@ render_svg() {
|
||||||
if command -v rsvg-convert >/dev/null 2>&1; then
|
if command -v rsvg-convert >/dev/null 2>&1; then
|
||||||
rsvg-convert -w "$size" -h "$size" "$svg" -o "$out"
|
rsvg-convert -w "$size" -h "$size" "$svg" -o "$out"
|
||||||
elif command -v magick >/dev/null 2>&1; then
|
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
|
else
|
||||||
echo "Error: need rsvg-convert (librsvg) or magick (ImageMagick 7)"
|
echo "Error: need rsvg-convert (librsvg) or magick (ImageMagick 7)"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -32,7 +37,7 @@ case "$(uname -s)" in
|
||||||
echo "==> Generating .icns"
|
echo "==> Generating .icns"
|
||||||
ICONSET=$(mktemp -d)/cue.iconset
|
ICONSET=$(mktemp -d)/cue.iconset
|
||||||
mkdir -p "$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"
|
render_svg "$SVG" "$size" "$ICONSET/icon_${size}x${size}.png"
|
||||||
double=$((size * 2))
|
double=$((size * 2))
|
||||||
render_svg "$SVG" "$double" "$ICONSET/icon_${size}x${size}@2x.png"
|
render_svg "$SVG" "$double" "$ICONSET/icon_${size}x${size}@2x.png"
|
||||||
|
|
|
||||||
|
|
@ -708,27 +708,27 @@ impl App {
|
||||||
Tab::Eis => row![
|
Tab::Eis => row![
|
||||||
column![
|
column![
|
||||||
text("Start Hz").size(12),
|
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),
|
].spacing(2),
|
||||||
column![
|
column![
|
||||||
text("Stop Hz").size(12),
|
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),
|
].spacing(2),
|
||||||
column![
|
column![
|
||||||
text("PPD").size(12),
|
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),
|
].spacing(2),
|
||||||
column![
|
column![
|
||||||
text("RTIA").size(12),
|
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),
|
].spacing(2),
|
||||||
column![
|
column![
|
||||||
text("RCAL").size(12),
|
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),
|
].spacing(2),
|
||||||
column![
|
column![
|
||||||
text("Electrodes").size(12),
|
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),
|
].spacing(2),
|
||||||
button(text("Apply").size(13))
|
button(text("Apply").size(13))
|
||||||
.style(style_apply())
|
.style(style_apply())
|
||||||
|
|
@ -739,7 +739,7 @@ impl App {
|
||||||
.padding([6, 20])
|
.padding([6, 20])
|
||||||
.on_press(Message::StartSweep),
|
.on_press(Message::StartSweep),
|
||||||
]
|
]
|
||||||
.spacing(10)
|
.spacing(8)
|
||||||
.align_y(iced::Alignment::End)
|
.align_y(iced::Alignment::End)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
||||||
|
|
@ -758,7 +758,7 @@ impl App {
|
||||||
].spacing(2),
|
].spacing(2),
|
||||||
column![
|
column![
|
||||||
text("RTIA").size(12),
|
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),
|
].spacing(2),
|
||||||
button(text("Start LSV").size(13))
|
button(text("Start LSV").size(13))
|
||||||
.style(style_action())
|
.style(style_action())
|
||||||
|
|
@ -784,7 +784,7 @@ impl App {
|
||||||
].spacing(2),
|
].spacing(2),
|
||||||
column![
|
column![
|
||||||
text("RTIA").size(12),
|
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),
|
].spacing(2),
|
||||||
if self.amp_running {
|
if self.amp_running {
|
||||||
button(text("Stop").size(13))
|
button(text("Stop").size(13))
|
||||||
|
|
@ -829,7 +829,7 @@ impl App {
|
||||||
].spacing(2),
|
].spacing(2),
|
||||||
column![
|
column![
|
||||||
text("RTIA").size(12),
|
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),
|
].spacing(2),
|
||||||
button(text("Measure").size(13))
|
button(text("Measure").size(13))
|
||||||
.style(style_action())
|
.style(style_action())
|
||||||
|
|
|
||||||
94
main/echem.c
94
main/echem.c
|
|
@ -161,6 +161,10 @@ static float read_current_ua(float rtia_ohms)
|
||||||
uint32_t raw = AD5940_ReadAfeResult(AFERESULT_SINC2);
|
uint32_t raw = AD5940_ReadAfeResult(AFERESULT_SINC2);
|
||||||
int32_t code = (raw & (1UL << 15)) ? (int32_t)(raw | 0xFFFF0000UL) : (int32_t)raw;
|
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) */
|
/* I = V_tia / RTIA, V_tia = code * Vref / (PGA * 32768) */
|
||||||
float v_tia = (float)code * 1.82f / (1.5f * 32768.0f);
|
float v_tia = (float)code * 1.82f / (1.5f * 32768.0f);
|
||||||
float i_a = v_tia / rtia_ohms;
|
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);
|
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 ---- */
|
/* ---- public ---- */
|
||||||
|
|
||||||
void echem_default_lsv(LSVConfig *cfg)
|
void echem_default_lsv(LSVConfig *cfg)
|
||||||
|
|
@ -265,7 +297,33 @@ void echem_default_amp(AmpConfig *cfg)
|
||||||
cfg->lp_rtia = LP_RTIA_10K;
|
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;
|
if (cfg->lp_rtia >= LP_RTIA_COUNT) return 0;
|
||||||
float rtia = lp_rtia_ohms[cfg->lp_rtia];
|
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;
|
float v_range = cfg->v_stop - cfg->v_start;
|
||||||
if (fabsf(v_range) < 0.001f) return 0;
|
if (fabsf(v_range) < 0.001f) return 0;
|
||||||
|
|
||||||
/* compute steps to always cover full range within max_points */
|
uint32_t n_steps;
|
||||||
uint32_t n_lsb = (uint32_t)(fabsf(v_range / VBIAS_LSB) + 0.5f);
|
float step;
|
||||||
uint32_t n_steps = n_lsb;
|
lsv_calc_step(cfg, max_points, &n_steps, &step);
|
||||||
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;
|
|
||||||
|
|
||||||
float delay_ms = fabsf(step / cfg->scan_rate) * 1000.0f;
|
float delay_ms = fabsf(step / cfg->scan_rate) * 1000.0f;
|
||||||
if (delay_ms < 1.0f) delay_ms = 1.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;
|
out[i].i_ua = i_ua;
|
||||||
|
|
||||||
printf("%10.1f %10.3f\n", v_mv, 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);
|
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
||||||
return (int)n_steps;
|
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;
|
if (cfg->lp_rtia >= LP_RTIA_COUNT) return 0;
|
||||||
float rtia = lp_rtia_ohms[cfg->lp_rtia];
|
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;
|
uint32_t count = 0;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < max_samples; i++) {
|
for (uint32_t i = 0; i < max_samples; i++) {
|
||||||
/* check for stop command (non-blocking) */
|
|
||||||
BleCommand cmd;
|
BleCommand cmd;
|
||||||
if (ble_recv_command(&cmd, 0) == 0 && cmd.type == CMD_STOP_AMP)
|
if (ble_recv_command(&cmd, 0) == 0 && cmd.type == CMD_STOP_AMP)
|
||||||
break;
|
break;
|
||||||
|
|
@ -360,10 +411,12 @@ int echem_amp(const AmpConfig *cfg, AmpPoint *out, uint32_t max_points)
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
printf("%10.1f %10.3f\n", t_ms, i_ua);
|
printf("%10.1f %10.3f\n", t_ms, i_ua);
|
||||||
|
if (cb) cb((uint16_t)i, t_ms, i_ua);
|
||||||
|
|
||||||
vTaskDelay(interval);
|
vTaskDelay(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
echem_shutdown_lp();
|
||||||
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
||||||
return (int)count;
|
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,
|
static uint32_t sample_phase(float v_mv, float t_dep_ms, float t_meas_ms,
|
||||||
uint8_t phase, float rtia_ohms,
|
uint8_t phase, float rtia_ohms,
|
||||||
ClPoint *out, uint32_t idx, uint32_t max_points,
|
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);
|
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].t_ms = t_ms;
|
||||||
out[idx].i_ua = i_ua;
|
out[idx].i_ua = i_ua;
|
||||||
out[idx].phase = phase;
|
out[idx].phase = phase;
|
||||||
|
|
||||||
|
if (cb) cb((uint16_t)idx, t_ms, i_ua, phase);
|
||||||
idx++;
|
idx++;
|
||||||
|
|
||||||
sum += i_ua;
|
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;
|
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;
|
if (cfg->lp_rtia >= LP_RTIA_COUNT) return 0;
|
||||||
float rtia = lp_rtia_ohms[cfg->lp_rtia];
|
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);
|
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,
|
||||||
CL_PHASE_FREE, rtia, out, idx, max_points, t0,
|
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);
|
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,
|
idx = sample_phase(cfg->v_total, cfg->t_dep_ms, cfg->t_meas_ms,
|
||||||
CL_PHASE_TOTAL, rtia, out, idx, max_points, t0,
|
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);
|
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);
|
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
||||||
return (int)idx;
|
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",
|
printf("pH: SE0=%.1f mV, RE0=%.1f mV, OCP=%.1f mV, pH=%.2f\n",
|
||||||
v_se0, v_re0, ocp, result->ph);
|
v_se0, v_re0, ocp, result->ph);
|
||||||
|
|
||||||
|
echem_shutdown_lp();
|
||||||
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
AD5940_AFECtrlS(AFECTRL_ALL, bFALSE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
main/echem.h
11
main/echem.h
|
|
@ -80,14 +80,19 @@ typedef struct {
|
||||||
float temp_c; /* temperature used */
|
float temp_c; /* temperature used */
|
||||||
} PhResult;
|
} 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_lsv(LSVConfig *cfg);
|
||||||
void echem_default_amp(AmpConfig *cfg);
|
void echem_default_amp(AmpConfig *cfg);
|
||||||
void echem_default_cl(ClConfig *cfg);
|
void echem_default_cl(ClConfig *cfg);
|
||||||
void echem_default_ph(PhConfig *cfg);
|
void echem_default_ph(PhConfig *cfg);
|
||||||
|
|
||||||
int echem_lsv(const LSVConfig *cfg, LSVPoint *out, uint32_t max_points);
|
uint32_t echem_lsv_calc_steps(const LSVConfig *cfg, uint32_t max_points);
|
||||||
int echem_amp(const AmpConfig *cfg, AmpPoint *out, uint32_t max_points);
|
int echem_lsv(const LSVConfig *cfg, LSVPoint *out, uint32_t max_points, lsv_point_cb_t cb);
|
||||||
int echem_chlorine(const ClConfig *cfg, ClPoint *out, uint32_t max_points, ClResult *result);
|
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);
|
int echem_ph_ocp(const PhConfig *cfg, PhResult *result);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -410,7 +410,7 @@ int eis_measure_point(float freq_hz, EISPoint *out)
|
||||||
return 0;
|
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);
|
uint32_t n = eis_calc_num_points(&ctx.cfg);
|
||||||
if (n > max_points) n = max_points;
|
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",
|
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].freq_hz, out[0].mag_ohms, out[0].phase_deg,
|
||||||
out[0].z_real, out[0].z_imag, out[0].pct_err);
|
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++) {
|
for (uint32_t i = 1; i < n; i++) {
|
||||||
float freq;
|
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",
|
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].freq_hz, out[i].mag_ohms, out[i].phase_deg,
|
||||||
out[i].z_real, out[i].z_imag, out[i].pct_err);
|
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 */
|
/* guard: throwaway at stop frequency to cap the sweep cleanly */
|
||||||
|
|
|
||||||
|
|
@ -49,11 +49,13 @@ typedef struct {
|
||||||
float pct_err;
|
float pct_err;
|
||||||
} EISPoint;
|
} EISPoint;
|
||||||
|
|
||||||
|
typedef int (*eis_point_cb_t)(uint16_t idx, const EISPoint *pt);
|
||||||
|
|
||||||
void eis_default_config(EISConfig *cfg);
|
void eis_default_config(EISConfig *cfg);
|
||||||
void eis_init(const EISConfig *cfg);
|
void eis_init(const EISConfig *cfg);
|
||||||
void eis_reconfigure(const EISConfig *cfg);
|
void eis_reconfigure(const EISConfig *cfg);
|
||||||
int eis_measure_point(float freq_hz, EISPoint *out);
|
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);
|
uint32_t eis_calc_num_points(const EISConfig *cfg);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
39
main/eis4.c
39
main/eis4.c
|
|
@ -22,14 +22,9 @@ static void do_sweep(void)
|
||||||
eis_init(&cfg);
|
eis_init(&cfg);
|
||||||
|
|
||||||
uint32_t n = eis_calc_num_points(&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);
|
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();
|
ble_send_sweep_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,14 +119,10 @@ void app_main(void)
|
||||||
printf("LSV: %.0f-%.0f mV, %.0f mV/s, rtia=%u\n",
|
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);
|
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);
|
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();
|
ble_send_lsv_end();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -146,14 +137,8 @@ void app_main(void)
|
||||||
amp_cfg.v_hold, amp_cfg.interval_ms, amp_cfg.duration_s);
|
amp_cfg.v_hold, amp_cfg.interval_ms, amp_cfg.duration_s);
|
||||||
|
|
||||||
ble_send_amp_start(amp_cfg.v_hold);
|
ble_send_amp_start(amp_cfg.v_hold);
|
||||||
|
int got = echem_amp(&_cfg, amp_results, ECHEM_MAX_POINTS, ble_send_amp_point);
|
||||||
int got = echem_amp(&_cfg, amp_results, ECHEM_MAX_POINTS);
|
|
||||||
printf("Amp complete: %d points\n", got);
|
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();
|
ble_send_amp_end();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -187,16 +172,14 @@ void app_main(void)
|
||||||
cl_cfg.t_meas_ms = cmd.cl.t_meas_ms;
|
cl_cfg.t_meas_ms = cmd.cl.t_meas_ms;
|
||||||
cl_cfg.lp_rtia = cmd.cl.lp_rtia;
|
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;
|
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",
|
printf("Cl complete: %d points, free=%.3f uA, total=%.3f uA\n",
|
||||||
got, cl_result.i_free_ua, cl_result.i_total_ua);
|
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_result(cl_result.i_free_ua, cl_result.i_total_ua);
|
||||||
ble_send_cl_end();
|
ble_send_cl_end();
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue