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

834 lines
22 KiB
C++

#include <avr/eeprom.h>
//#include "utils.h"
#include "EEPROM.h"
enum {standard_cfg, argon_cfg, multihe_cfg, jtext_cfg};
#define config_cfg_text "std|argon|multiHe|jtext"
PAR_LONG codeA = 0;
PAR_LONG codeB = 0;
PAR_BYTE codeAbad = 1;
PAR_BYTE codeBbad = 1;
PAR_TEXT configDevice[16] = "none";
PAR_LONG configVersion = 1;
PAR_BYTE configSpecial = 0;
PAR_BYTE configDeviceState = 0;
enum {config_local, config_remote, config_loading, config_by_code, config_by_touch};
#define config_state_text "local|remote|loading|by_code|by_touch"
char configItem[64] = "";
char configTitle[16] = "init";
ulong loadingUntil = 0; // show loading screen until ...
#if (RAMTEST)
PAR_LONG freeRam = 0;
#endif
word caladr = 0; // address for storing level calibration
enum {config_noflow, config_flow, config_non2, config_n2, config_nohe, config_select};
// coding resistor values.
// the values are choosen in a manner to distinguish between different values
// with a tolerance dependent on the measurement precision
// the effectively used values are between 220 and 47k, because these values are
// sold in an assortment (distrelec 728001)
enum {R_MIN, R_100, R_220, R_330, R_470, R_680, R_820,
R_1k, R_1k2, R_1k5, R_1k8, R_2k2, R_2k7, R_3k3, R_3k9, R_4k7, R_5k6, R_6k8, R_8k2,
R_10k, R_12k, R_15k, R_18k, R_22k, R_27k, R_33k, R_39k, R_47k, R_56k, R_68k, R_82k,
/* R_100k, R_120k, R_150k, */ R_MAX, R_INSTABLE};
static const long res_list[] PROGMEM = {0, 100, 220, 330, 470, 680, 820,
1000, 1200, 1500, 1800, 2200, 2700, 3300, 3900, 4700, 5600, 6800, 8200,
10000, 12000, 15000, 18000, 22000, 27000, 33000, 39000, 47000, 56000, 68000, 82000,
/* 100000,120000, 150000, */ 999999, 888888};
enum {HE = 1, N2 = 2, NV = 4, N2V = 8, HEV = 16, VALVEUNIT = 32};
enum {AMI20= 1, AMI38, AMI52, OXF38, OXF40, OXF52};
enum {AMI_heCur = 75, AMI_heWiRes = 460, OXF_heCur = 130, OXF_heWiRes = 166};
enum {EEPROM_version = 8, configMaxDev = 64};
static struct deviceTable_s {
char name[8];
byte rescode[2];
byte opt;
byte heCur; // current in mA
int heWiRes; // wire resistivity Ohm/m
int heEmpty; // warm length at 0 %
int heFull; // warm length at 100 %
} deviceTable[configMaxDev];
const long code_max = 10 * 1024L; // 10 times max. adc
const long code_res = 20000; // reference resistor
byte configNdevices = 0;
byte configItemIndex = 0;
byte configDevIdx = 0;
byte devno0 = 255;
enum {device_other = 250};
struct config_stat_s {
byte stableCode;
byte code;
word v0;
word v1;
word v2;
word ltcnt;
word eqcnt;
word gtcnt;
word cnt;
int8_t pos; // -1: decrease code, 1: increase code
};
void ConfigPars() {
ParFixed("cvs", configVersion, 0);
ParText("csf", configSoft, false);
ParText("cid", configIdn, false);
ParFixed("cda", codeA, 0);
ParFixed("cdb", codeB, 0);
ParEnum("cba", codeAbad, F("stable|instable"));
ParEnum("cbb", codeBbad, F("stable|instable"));
#if (RAMTEST)
ParFixed("cfr", freeRam, 0);
#endif
if (ParEnum("csp", configSpecial, F(config_cfg_text)) == par_command) {
ConfigSave();
setup();
return;
}
if (ParText("cdv", configDevice, true) == par_command) {
if (configSpecial == 0) {
if (configDevice[0] == '*' && configDevice[1] == 0) {
devno0 = 255; // force code detection
} else {
textcopy(configTitle, sizeof configTitle, configDevice);
if (screen != m_instr) {
screen = m_loading;
loadingUntil = now + 5000;
}
}
}
}
if (ParEnum("cds", configDeviceState, F(config_state_text)) == par_command) {
if (configDeviceState == config_remote) {
if (screen != m_instr) screen = m_home;
textcopy(configTitle, sizeof configTitle, configDevice);
}
}
if (ParText("c", configItem, false) == par_command) {
ConfigItem();
}
}
void ConfigDispShort(const char *text) {
DispText2(configTitle, text);
DispLine();
}
void ConfigDisp() {
if (configSpecial == standard_cfg) {
DispValue("A: ", p_codeA, p_codeAbad);
DispValue("B: ", p_codeB, p_codeBbad);
DispValue("n.v.:", p_flowAvailable, 0);
DispValue("LHe: ", p_heAvailable, 0);
DispValue("LN2:", p_n2Available, 0);
#if (RAMTEST)
DispValue("mem", p_freeRam, 0);
#endif
DispButton(2, m_menu, m_device, "device");
} else if (extension == valve_extension) {
DispText2("valve_ext", configSoft);
} else if (extension == he_extension) {
DispText2("he_ext", configSoft);
}
DispButton(0, m_menu, m_menu, "menu");
DispButton(1, m_menu, m_home, "home");
}
void ConfigLoadingDisp() {
ConfigDispShort("load");
if (time_ge(now, loadingUntil)) {
screen = m_home;
}
}
void ConfigInit() {
long value;
byte l;
byte eeversion = EEPROM.read(0);
eeSetAdr(1);
if (eeversion > EEPROM_version || eeversion < 5) {
configNdevices = 1;
textcopy(deviceTable[0].name, sizeof (deviceTable[0].name), "none");
deviceTable[0].opt = 0;
deviceTable[0].rescode[0] = R_MAX;
deviceTable[0].rescode[1] = R_MAX;
Serial.print("new EEPROM version:");
Serial.println(EEPROM_version);
ConfigSave();
}
EESTORE(value, ee_read);
ParSet(configVersion, value);
extension = no_extension;
if (eeversion == 5) {
ParSet(configSpecial, standard_cfg);
} else {
EESTORE(configSpecial, ee_read);
l = textcopyuntil((char *)configIdn, sizeof configIdn, configIdn, '.'); // find dot
if (l < sizeof configIdn - 2) {
#if PARCHECK == 0
ARRAY(configIdn,l) = '.'; // exception of PARCHECK, no complain about usage outside ParSet
#endif
l++;
l = textcopy((char *)configIdn + l, sizeof configIdn - l, ParFmt(p_configSpecial));
}
}
if (configSpecial != 0) {
configNdevices = 0;
ConfigSpecial(ee_read);
textcopy(configTitle, sizeof configTitle, configDevice);
Serial.print("load ");
Serial.print(EEPROM_version, DEC);
Serial.print(" ");
Serial.println(configTitle);
return;
}
EESTORE(configNdevices, ee_read);
Serial.print("load ");
Serial.print(EEPROM_version, DEC);
Serial.print(".");
Serial.println(configVersion);
eeStore((void *)&deviceTable, (sizeof deviceTable[0]) * configNdevices, ee_read);
EESTORE(heExtAvailable, ee_read);
if (heExtAvailable != 1 || EEPROM_version < 7) {
ParSet(heExtAvailable, 0);
}
if (EEPROM_version > 7) {
InstrStore(ee_read);
}
}
boolean ConfigCrit(word good, word total) {
// statistics: assume that more than 50 % of the samples are good in average
// the probability of a false positive should be less than 1e-10
// formula: m > n * (1 - p) + sqrt(- 2 * n * ln(1e-10) * p * (1-p)), m = good, n = total, p = 0.5
// or (2 * m - n)^2 > 2 * n * 23.026
ulong i;
if (good <= total / 2) return false; // avoid underflow
i = 2 * good - total; // 2 * (m - n * (1 - p))
if (i * i > 46L * total) { // 46 ~= 2^2 * 2 * -ln(1e-10) * 0.5 * 0.5
//Serial.print("C"); Serial.print(good); Serial.print("/"); Serial.println(total);
return true;
}
return false;
}
void ConfigStat(struct config_stat_s *s, int8_t pos, byte weight) {
byte j;
s->pos = 0;
if (s->cnt > 10000) {
/*
Serial.print("lt "); Serial.print(s->ltcnt);
Serial.print(" eq "); Serial.print(s->eqcnt);
Serial.print(" gt "); Serial.print(s->gtcnt);
Serial.print(" CODE "); Serial.println(s->code);
*/
s->cnt = 0;
s->ltcnt = 0;
s->eqcnt = 0;
s->gtcnt = 0;
s->stableCode = R_INSTABLE;
}
s->cnt += weight;
if (pos < 0) {
s->ltcnt += weight;
if (ConfigCrit(s->ltcnt / 10, s->cnt / 10)) {
/*
Serial.print("LT "); Serial.print(s->ltcnt);
Serial.print(" eq "); Serial.print(s->eqcnt);
Serial.print(" gt "); Serial.print(s->gtcnt);
Serial.print(" code "); Serial.println(s->code);
*/
s->ltcnt = 0;
s->eqcnt = weight;
s->gtcnt = 0;
s->cnt = weight;
if (s->code == 1) {
s->stableCode = 0;
} else {
s->stableCode = R_INSTABLE;
s->pos = -1;
}
}
} else if (pos > 0) {
s->gtcnt += weight;
if (ConfigCrit(s->gtcnt / 10, s->cnt / 10)) {
/*
Serial.print("lt "); Serial.print(s->ltcnt);
Serial.print(" eq "); Serial.print(s->eqcnt);
Serial.print(" GT "); Serial.print(s->gtcnt);
Serial.print(" code "); Serial.println(s->code);
*/
s->ltcnt = 0;
s->eqcnt = weight;
s->gtcnt = 0;
s->cnt = weight;
if (s->code == R_MAX - 1) {
s->stableCode = R_MAX;
} else {
s->stableCode = R_INSTABLE;
s->pos = 1;
}
}
} else {
s->eqcnt += weight;
if (ConfigCrit(s->eqcnt / 10, s->cnt / 10)) {
if (s->code != s->stableCode) {
s->stableCode = s->code;
/*
Serial.print("lt "); Serial.print(s->ltcnt);
Serial.print(" EQ "); Serial.print(s->eqcnt);
Serial.print(" gt "); Serial.print(s->gtcnt);
Serial.print(" code "); Serial.println(s->code);
*/
}
s->ltcnt = 0;
s->eqcnt = weight;
s->gtcnt = 0;
s->cnt = weight;
}
}
}
long ConfigResFromCode(byte code) {
const long *rp = res_list;
return pgm_read_dword(&rp[code]);
}
word ConfigAdcFromCode(byte code) {
long r = ConfigResFromCode(code);
if (r < 2000000000 / code_max) { // avoid overflow
return code_max * r / (r + code_res);
} else { // might not be needed
return r / ((r + code_res) / code_max);
}
}
long ConfigResFromAdc(word adc) {
if (adc == 0) return 0;
return code_res * (long)adc / (1024L - adc);
}
byte ConfigGetCode(byte chan, word adc) {
byte frac;
byte w;
static struct config_stat_s config_stat[2] = {{R_INSTABLE,1,0,1,2},{R_INSTABLE,1,0,1,2}};
int code;
int8_t pos;
struct config_stat_s *s;
s = &ARRAY(config_stat, chan);
adc *= 10;
if (adc < s->v1) {
pos = -1;
if (adc <= s->v0) {
frac = 0;
} else {
frac = 10 * (adc - s->v1) / (s->v1 - s->v0);
}
} else {
pos = 0;
if (adc >= s->v2) {
frac = 10;
} else {
frac = 10 * (adc - s->v2) / (s->v2 - s->v1);
}
}
if (frac < 3) {
ConfigStat(s, pos, 10);
} else if (frac > 7) {
ConfigStat(s, pos + 1, 10);
} else {
w = frac * 2 - 5;
ConfigStat(s, pos, 10 - w);
ConfigStat(s, pos + 1, w);
}
if (s->pos == 0) return s->stableCode;
code = s->code;
if (s->pos > 0) { // code is certainly higher
while (code < R_MAX - 1 && adc > ConfigAdcFromCode(code)) {
code ++;
FastHandler();
}
} else {
while (code > 1 && adc < ConfigAdcFromCode(code)) {
code --;
FastHandler();
}
}
s->v0 = ConfigAdcFromCode(code - 1);
s->v1 = ConfigAdcFromCode(code);
s->v2 = ConfigAdcFromCode(code + 1);
s->code = code;
return s->stableCode;
}
void ConfigDevice(byte devno, byte state) {
struct deviceTable_s *d;
byte av;
if (devno >= configMaxDev) {
devno = 0;
}
d = &ARRAY(deviceTable, devno);
if (d->name[0] == 0) {
ParSetText(configDevice, "unknown");
} else {
ParSetText(configDevice, d->name);
}
if (online) {
ParSet(configDeviceState, state);
if (configDeviceState == config_by_touch) {
screen = m_loading;
loadingUntil = now + 5000;
}
return;
}
textcopy(configTitle, sizeof configTitle, configDevice);
ParSet(configDeviceState, config_local);
ParSet(flowAvailable, (d->opt & NV) != 0);
ParSet(vuAvailable, (d->opt & VALVEUNIT) != 0);
ParSet(motorAvailable, flowAvailable);
if (d->opt & N2) {
av = 1;
} else if (d->opt & N2V) {
av = 2;
} else {
av = 0;
}
ParSet(n2Available, av);
if (d->opt & HE) {
av = 1;
} else if (d->opt & HEV) {
av = 2;
} else {
av = 0;
}
HeConfig(av, d->heCur, d->heWiRes, d->heEmpty, d->heFull);
if (screen == m_loading) {
screen = m_home;
}
}
void ConfigHandler() {
byte ca, cb;
byte devno1;
byte i, m;
struct deviceTable_s *d;
char name[16];
boolean changed;
#if (RAMTEST)
if (freeRam == 1) return;
ParSet(freeRam, RamFreeSize());
if (freeRam < 16) {
freeRam = 1;
DispError("stack", "overflow");
}
#endif
if (devno0 == 255 && devno0 == 0) { // make config faster when nothing else is running
m = 100;
} else {
m = 10;
}
for (i = 0; i < m; i++) {
ca = ConfigGetCode(0, aRead(a_codea));
if (ca == R_INSTABLE) {
ParSet(codeAbad, 1);
} else {
ParSet(codeAbad, 0);
ParSet(codeA, ConfigResFromCode(ca));
}
cb = ConfigGetCode(1, aRead(a_codeb));
if (cb == R_INSTABLE) {
ParSet(codeBbad, 1);
} else {
ParSet(codeBbad, 0);
ParSet(codeB, ConfigResFromCode(cb));
}
}
d = 0;
for (devno1 = 0; devno1 < configNdevices; devno1++) {
FastHandler();
d = &ARRAY(deviceTable,devno1);
if (d->rescode[0] == ca && d->rescode[1] == cb) {
break;
}
}
if (devno1 != devno0 && devno1 < configNdevices) {
devno0 = devno1;
if (d) {
ConfigDevice(devno0, config_by_code);
} else {
ParSetText(configDevice, "other");
screen = m_home;
}
//DispTriggerRewrite();
}
}
int8_t ConfigItemText(char *text, byte maxlen) {
int8_t len;
if (configItemIndex != 0) {
if (ARRAY(configItem,configItemIndex) != ',') return -1; // bad syntax
configItemIndex++;
}
len = textcopyuntil(text, maxlen, &ARRAY(configItem,configItemIndex), ',');
if (len == maxlen) return -1; // too long
configItemIndex += len;
return len;
}
int8_t ConfigItemNum(long *result, byte dig) {
byte len;
if (configItemIndex != 0) {
if (ARRAY(configItem,configItemIndex) != ',') return -1; // bad syntax
configItemIndex++;
}
*result = text2fix(&ARRAY(configItem,configItemIndex), dig, &len);
configItemIndex += len;
return len;
}
byte ConfigFindItem(char *name) {
byte devno;
for (devno = 0; devno < configNdevices; devno++) {
if (strcmp(ARRAY(deviceTable,devno).name, name) == 0) {
break;
}
}
return devno;
}
void ConfigTrimNum(char *buffer) {
// trims away training zeros and decimal points
char *p = 0;
while (*buffer != 0) {
if (p) { // we have already passed decimal point
if (*buffer != '0') { // we need this digit
p = buffer + 1;
}
} else if (*buffer == '.') {
p = buffer;
}
buffer++;
}
if (p) {
*p = 0;
}
}
void ConfigItem() {
struct deviceTable_s *d;
long value, r;
byte len, c, i, j, devno;
char buffer[8];
boolean addhecur;
if (configNdevices >= configMaxDev) {
SerialC.write('?');
Serial.print(F("config table full"));
return;
}
if (configItem[0] == '-') { // it's an instrument name list
// ignore obsolete item
return;
}
configItemIndex = 0;
d = &ARRAY(deviceTable,configNdevices);
if (ConfigItemText(d->name, sizeof d->name) < 0) {
SerialC.write('?');
Serial.print(F("device name too long\n"));
return;
}
if (ARRAY(configItem,configItemIndex) == 0) { // end of data
switch (d->name[0]) {
case 'c': configNdevices = 0; return;
case 's': ConfigSave(); return;
case 'b': configDevIdx = 0; return;
}
if (configDevIdx >= configNdevices) {
configItem[0] = 0;
configDevIdx = 0;
return;
}
d = &ARRAY(deviceTable,configDevIdx);
configDevIdx++;
// configItem is reconstructed in a standardized way
textcopy(configItem, sizeof configItem, d->name);
textappend(configItem, sizeof configItem, ",");
if (d->rescode[0] != R_MAX) {
fix2str(ConfigResFromCode(d->rescode[0]), 3, buffer, sizeof buffer);
ConfigTrimNum(buffer);
textappend(configItem, sizeof configItem, buffer);
}
textappend(configItem, sizeof configItem, ",");
if (d->rescode[1] != R_MAX) {
fix2str(ConfigResFromCode(d->rescode[1]), 3, buffer, sizeof buffer);
ConfigTrimNum(buffer);
textappend(configItem, sizeof configItem, buffer);
}
textappend(configItem, sizeof configItem, ",");
if (d->opt & N2) {
textappend(configItem, sizeof configItem, "n");
} else if (d->opt & N2V) {
textappend(configItem, sizeof configItem, "N");
}
if (d->opt & NV) textappend(configItem, sizeof configItem, "f");
if (d->opt & VALVEUNIT) textappend(configItem, sizeof configItem, "v");
if (d->opt & HE) {
if (d->heCur == AMI_heCur && d->heWiRes == AMI_heWiRes) {
textappend(configItem, sizeof configItem, "a");
addhecur = false;
} else if (d->heCur == OXF_heCur && d->heWiRes == OXF_heWiRes) {
textappend(configItem, sizeof configItem, "o");
addhecur = false;
} else {
textappend(configItem, sizeof configItem, "h");
addhecur = true;
}
textappend(configItem, sizeof configItem, ",");
fix2str(d->heEmpty, 0, buffer, sizeof buffer);
ConfigTrimNum(buffer);
textappend(configItem, sizeof configItem, buffer);
if (d->heFull != 0 || addhecur) {
textappend(configItem, sizeof configItem, ",");
fix2str(d->heFull, 0, buffer, sizeof buffer);
ConfigTrimNum(buffer);
textappend(configItem, sizeof configItem, buffer);
if (addhecur) {
textappend(configItem, sizeof configItem, ",");
fix2str(d->heCur, 0, buffer, sizeof buffer);
textappend(configItem, sizeof configItem, buffer);
fix2str(d->heWiRes, 0, buffer, sizeof buffer);
textappend(configItem, sizeof configItem, buffer);
}
}
} else if (d->opt & HEV) {
textappend(configItem, sizeof configItem, "H");
}
return;
}
for (j =0; j < 2; j++) {
len = ConfigItemNum(&r, 3);
if (len < 0) {
SerialC.write('?');
Serial.print(F("missing codes\n"));
return;
}
if (len == 0) {
d->rescode[j] = R_MAX;
} else {
for (c = 0; c < R_INSTABLE; c++) {
if (ConfigResFromCode(c) == r) {
d->rescode[j] = c;
break;
}
}
if (c == R_INSTABLE) {
SerialC.write('?');
Serial.print(F("unknown resistor code "));
Serial.println(r);
return;
}
}
}
if (ConfigItemText(buffer, sizeof buffer) < 0) {
SerialC.write('?');
Serial.print(F("missing flags\n"));
return;
}
d->heCur = 0;
d->opt = 0;
for (i = 0; i < sizeof buffer && buffer[i] != 0; i++) {
switch (buffer[i]) {
case 'n': d->opt |= N2; break;
case 'N': d->opt |= N2V; break;
case 'H': d->opt |= HEV; break;
case 'f': d->opt |= NV; break;
case 'h': d->opt |= HE; d->heCur = 1; d->heWiRes = 1; break;
case 'a': d->opt |= HE; d->heCur = AMI_heCur; d->heWiRes = AMI_heWiRes; break;
case 'o': d->opt |= HE; d->heCur = OXF_heCur; d->heWiRes = OXF_heWiRes; break;
case 'v': d->opt |= VALVEUNIT; break;
default: Serial.print(F("bad flags\n")); return;
}
}
if (d->heCur != 0) {
d->heFull = 0;
if (ConfigItemNum(&value, 0) < 0) {
SerialC.write('?');
Serial.print(F("missing He level res.\n"));
return;
}
d->heEmpty = value;
if (ConfigItemNum(&value, 0) >= 0) {
d->heFull = value;
if (ConfigItemNum(&value, 0) > 0) {
d->heCur = value;
if (ConfigItemNum(&value, 0) > 0) {
d->heWiRes = value;
}
}
}
}
if (d->heCur == 1) {
SerialC.write('?');
Serial.print(F("missing He level current\n"));
return;
}
if (d->heWiRes == 1) {
SerialC.write('?');
Serial.print(F("missing He wire resistivity\n"));
return;
}
devno = ConfigFindItem(d->name);
if (devno == configNdevices) {
configNdevices++;
// Serial.println("added");
} else {
ARRAY(deviceTable,devno) = *d;
// Serial.println("replaced");
}
return;
}
void ConfigSave() {
long value = configVersion;
EEPROM.write(0, EEPROM_version);
eeSetAdr(1);
EESTORE(value, ee_write);
EESTORE(configSpecial, ee_write);
if (configSpecial != 0) {
Serial.print("savespecial ");
Serial.print(EEPROM_version, DEC);
Serial.print(".");
Serial.println(configSpecial, DEC);
ConfigSpecial(ee_write);
} else {
EESTORE(configNdevices, ee_write);
Serial.print("save ");
Serial.print(EEPROM_version, DEC);
Serial.print(".");
Serial.println(configVersion);
eeStore((void *)&deviceTable, (sizeof deviceTable[0]) * configNdevices, ee_write);
EESTORE(heExtAvailable, ee_write);
if (EEPROM_version > 7) {
InstrStore(ee_write);
}
}
}
void ConfigCmd(byte cmd) {
switch (cmd) {
case config_noflow:
case config_flow:
ParSet(flowAvailable, cmd - config_noflow);
ParSet(motorAvailable, flowAvailable);
WriteDisplay();
//DispRedrawMenu(0);
break;
case config_non2:
case config_n2:
ParSet(n2Available, cmd - config_non2);
WriteDisplay();
// DispRedrawMenu(0);
break;
case config_nohe:
ParSet(heAvailable, false);
//DispTriggerRewrite();
WriteDisplay();
break;
}
}
void ConfigDeviceDisp() {
byte pos, devno, base;
base = DispPage() * 12;
if (base >= configNdevices) {
DispGotoPage(0);
DispClrAll();
base = 0;
}
for (pos = 13, devno = base; pos > 0 && devno < configNdevices; pos -= 2, devno++) {
if (pos == 1) pos = 12; // next column
DispButton(pos, m_device, devno, ARRAY(deviceTable,devno).name);
}
DispButton(0, m_device, device_other, "other");
DispButton(1, m_menu, m_home, "home");
}
void ConfigDeviceCmd(byte cmd) {
if (cmd < configNdevices && cmd != device_other) {
ConfigDevice(cmd, config_by_touch);
screen = m_home;
return;
}
DispClrAll();
DispNextPage(99);
}
void ConfigSpecial(EeMode mode) {
switch (configSpecial) {
case argon_cfg:
if (mode == ee_read) {
extension = valve_extension;
ParSet(auxAvailable, 1);
ParSetText(configDevice, "argon");
Serial.print("load");
} else {
Serial.print("save");
}
Serial.println(" argon");
EESTORE(auxDigits, mode);
EESTORE(auxScale, mode);
EESTORE(auxLabel, mode);
EESTORE(n2Available, mode);
Serial.print("nav="); Serial.print(n2Available);
break;
case multihe_cfg:
if (mode == ee_read) {
extension = he_extension;
ParSet(heAvailable, 1);
ParSet(heExtAvailable, 6);
ParSetText(configDevice, "LHe vess");
}
HeStore(mode);
break;
case jtext_cfg:
if (mode == ee_read) {
extension = valve_extension;
ParSetText(configDevice, "jtext");
Serial.print("load");
ParSet(n2Available, 1);
ParSet(motorAvailable, 1);
motorEndSwitch = 1;
} else {
Serial.print("save");
}
Serial.println(" jtext");
break;
}
}