Files
CCU4-firmware/n2.ino
2023-09-11 18:04:36 +02:00

281 lines
7.2 KiB
C++

#include "valve.h"
#define N2DEBUG 0
//--- Liquid Nitrogen
PAR_LONG n2upper = 0;
PAR_LONG n2lower = 0;
PAR_LONG n2threshold = 9000;
PAR_LONG n2tubecooldelay = 300;
PAR_LONG n2filltimeout = 1800;
PAR_BYTE n2fault = 0;
#define n2_sens_text F(" |no sensor|sns.short|sns.upside dn|sns. warm|empty")
enum {n2_sens_ok, n2_no_sensor, n2_sensor_short, n2_upside_down, n2_sensor_warm, n2_empty};
PAR_BYTE n2valve = 0;
PAR_BYTE n2auto = 1; // 0: off, 1: auto fill
PAR_BYTE n2command = 0;
#define n2_cmd_text F("stop|fill|off|watch")
enum {n2_stop=0, n2_fill, n2_off, n2_watch}; // commands
long n2start;
long n2StartThreshold = 0; // threshold to be reached within n2tubecooldelay after start
void N2Pars() {
ParEnum("nav", n2Available, F("no|yes|ext"));
ParFixed("nu", n2upper, 2);
ParFixed("nl", n2lower, 2);
ParFixed("nth", n2threshold, 2);
ParFixed("ntc", n2tubecooldelay, 0);
ParFixed("ntm", n2filltimeout, 0);
ParEnum("ns", n2fault, n2_sens_text);
ParEnum("na", n2auto, F("off|auto"));
ParEnum("nv", n2valve, valve_text);
if (ParEnum("nc", n2command, n2_cmd_text) == par_command) {
N2RemoteCmd(n2command);
}
}
boolean N2Shown() {
return n2Available; // || n2valve != no_valve;
}
boolean N2DispShort() {
N2StateDisp("LN2 ");
return n2valve == valve_on;
}
void N2Buttons(byte top) {
byte codebase = m_n2 * 16;
if (!n2Available) return;
DispButton(top, m_menu, m_n2, "*LN2");
if (n2valve == valve_on) {
DispButton(top - 2, m_n2, n2_watch, "-watch");
} else if (n2valve != no_valve) {
if (n2auto) {
DispButton(top - 2, m_n2, n2_off, "-off");
} else {
DispButton(top - 2, m_n2, n2_watch, "-watch");
}
DispButton(top - 4, m_n2, n2_fill, "-fill");
}
}
void N2Disp() {
byte codebase = m_n2 * 16;
if (n2Available == 1) {
DispText("N2 upper/K");
DispValue(0, p_n2upper, 0); // upper n2 sensor
DispText("N2 lower/K");
DispValue(0, p_n2lower, 0); // lower n2 sensor
}
N2StateDisp("");
//DispState(0, p_n2fault); // sensor state
//DispState(0, p_n2valve); // valve state
if (n2valve == valve_on) {
DispButton(3, m_n2, n2_stop, "watch");
} else if (n2valve != no_valve) {
if (n2auto) {
DispButton(3, m_n2, n2_off, "off");
} else {
DispButton(3, m_n2, n2_watch, "watch");
}
DispButton(2, m_n2, n2_fill, "fill");
}
DispButton(0, m_menu, m_menu, "menu");
DispButton(1, m_menu, m_home, "home");
}
const long n2_fact = 1024L * 5000 / 1235; // 5 V (excit. voltage), 1.235 V (analog reference)
const long n2_res = 6200; // 62 Ohm
void N2Handler() {
static long sumu = 0, suml = 0;
static int cnt = 0;
static ulong last;
const int8_t cond_off = -3; // -number of n2upper values below threshold
const int8_t cond_on = 20; // number of n2lower values above threshold
static int8_t cond = 0;
long t;
byte state;
if (n2Available != 1) return;
sumu += aRead(a_n2_upper);
suml += aRead(a_n2_lower);
cnt++;
if (expired(&last, 500) || cnt >= 400) {
state = n2_sens_ok;
if (sumu >= 1023L * cnt) {
state = n2_sensor_short;
ParSet(n2upper, 0);
} else {
if (sumu == 0) {
sumu = 1;
}
t = (n2_res * n2_fact * 25L / (sumu * 100 / cnt) - n2_res / 4) + 2680; // rough formula for pt1000: R / Ohm * 0.25 K + 26.8 K
if (t > 99990) {
t = 99990;
}
ParSet(n2upper, t);
}
if (suml >= 1023L * cnt) {
state = n2_sensor_short;
ParSet(n2lower, 0);
} else {
if (suml == 0) {
suml = 1;
}
t = (n2_res * n2_fact * 25L / (suml * 100 / cnt) - n2_res / 4) + 2680; // rough formula for pt1000: R / Ohm * 0.25 K + 26.8 K
if (t > 99990) {
t = 99990;
}
ParSet(n2lower, t);
}
cnt = 0;
sumu = 0;
suml = 0;
if (state == n2_sens_ok) {
if (n2lower == 99990 || n2upper == 99990) {
state = n2_no_sensor;
} else if (n2lower > n2upper + n2upper / 10) {
state = n2_upside_down;
} else if (n2lower > 20000) {
state = n2_sensor_warm;
// ParSet(n2auto, false);
}
}
ParSet(n2fault, state);
if (n2upper < n2threshold) { // condition for stopping
if (cond > cond_off) cond--;
} else if (n2lower > n2threshold) { // condition for fill start
if (cond < cond_on) cond++;
} else {
cond = 0;
}
if (n2valve == valve_on) {
if (cond <= cond_off) { // n2upper was a couple of times below threshold
N2StopFill();
cond = 0;
}
if (time_ge(now, n2start + n2filltimeout * 1000)) {
Serial.print("filltmo");
N2Cmd(n2_stop);
ParSet(n2valve, valve_timeout);
Serial.println(n2valve);
} else if (time_ge(now, n2start + n2tubecooldelay * 1000)) {
if (n2lower > n2StartThreshold) {
Serial.print("startmo");
N2Cmd(n2_stop);
ParSet(n2valve, valve_timeout1);
Serial.println(n2valve);
} else if (n2StartThreshold > n2threshold) { // still cooling lower sensor
N2SetStart();
}
}
} else if (cond >= cond_on) { // n2lower was a couple of times above threshold
if (n2valve == valve_off && n2auto && (n2fault == n2_sens_ok || n2fault == n2_empty)) {
N2Cmd(n2_fill);
cond = 0;
} else if (n2fault == n2_sens_ok) {
ParSet(n2fault, n2_empty);
}
}
}
}
void N2SetStart() {
n2start = now;
n2StartThreshold = n2lower - 500;
if (n2StartThreshold < n2threshold) {
n2StartThreshold = n2threshold;
}
}
void N2RemoteCmd(byte cmd) {
if (n2Available == 1 && n2fault >= n2_no_sensor && n2fault <= n2_upside_down) {
if (cmd == n2_fill || cmd == n2_watch) return;
}
N2Cmd(cmd);
}
void N2StopFill() {
ValveSet(io_n2_valve, LOW);
if (n2valve == valve_on) {
ParSet(n2valve, valve_off);
}
}
void N2Cmd(byte cmd) {
if (n2valve >= valve_timeout) {
N2StopFill();
}
switch (cmd) {
case n2_stop:
N2StopFill();
break;
case n2_watch:
N2StopFill();
ParSet(n2auto, true);
break;
case n2_fill:
ParSet(n2auto, true);
if (N2DEBUG) Serial.println("fill");
if (n2valve != no_valve && (n2Available != 1 || n2upper > n2threshold)) {
if (n2valve != valve_on) {
ValveSet(io_n2_valve, HIGH);
}
ParSet(n2valve, valve_on);
if (N2DEBUG) Serial.println("valve on");
N2SetStart();
} else if (N2DEBUG) {
Serial.println(n2valve);
Serial.println(n2upper);
Serial.println(n2threshold);
}
break;
case n2_off:
N2StopFill();
ParSet(n2auto, false);
break;
}
}
void N2ValveState(boolean ok) {
if (ok) {
if (n2valve == no_valve) {
ParSet(n2valve, valve_off);
N2StopFill();
}
} else {
N2StopFill();
ParSet(n2valve, no_valve);
}
}
void N2StateDisp(const char *title) {
const char *text;
if (n2valve == valve_on) {
if (n2fault == n2_no_sensor) {
text = "~fill no s.";
} else {
text = "~filling";
}
} else if (n2valve == valve_timeout) {
text = "timeout";
} else if (n2valve == valve_timeout1) {
text = "tmoStart";
} else if (n2fault) {
text = ParFmt(p_n2fault);
} else if (n2auto && n2valve == valve_off) {
text = "watching";
} else if (n2valve == no_valve) {
text = "no valve";
} else {
text = "off";
}
DispTextRow(fmt_lsmall, title, text);
}