version ccu4_70a

This commit is contained in:
2023-09-11 18:04:35 +02:00
parent 6290047b70
commit 9e86be8fec
14 changed files with 2105 additions and 661 deletions
+2
View File
@@ -14,6 +14,8 @@ void AuxPars() {
void AuxDisp() {
DispText(auxLabel);
DispBigValue(p_aux, 0);
DispButton(0, m_menu * 16 + m_commands, "menu");
DispButton(1, m_menu * 16 + m_home, "home");
}
View File
+576
View File
@@ -0,0 +1,576 @@
#include "utils.h"
#include "DispSerial.h"
#include "SerialWrapper.h"
#include "dipdisplay.h"
#include "motor.h"
#include "parser.h"
#define ARRAY_CHECK
#include "par.h"
#define RAMTEST 0
#define SERIALCOMMON 0
PAR_TEXT configSoft[16] = "7.0";
long now; // global variable, updated by FastHandler
byte inside;
boolean displayAvailable;
boolean online = 0;
byte dispEco = 5; // default eco brightness: 5 %
// parameter list
enum {
p_none, p_flow, p_flowAvailable, p_flowSensState, p_motorState, p_motorAutoState,
p_motorAvailable, p_motorPulse, p_motorOpenTime, p_motorCurrent, p_motorFreeCurrent,
p_motorTorqueConst, p_motorTorque, p_motorTorqueLimit, p_motorFlowTarget,
p_motorOverrunConst, p_motorOverrun, p_motorRead, p_motorWiggle,
p_heLevel, p_heRaw, p_heAvailable, p_heDrive, p_heCurrent, p_heVolt, p_heHigh, p_heLow,
p_heWiRes, p_heEmpty, p_heFull, p_heFastTime, p_heSlowTime, p_heSlope, p_heFirst,
p_heValve, p_heSensState, p_heAuto, p_heMeas, p_heFast, p_heCommand, /* p_heBooster, */
p_heIncreaseTolerance, p_heFastTimeout,
p_n2Available, p_n2upper, p_n2lower, p_n2threshold, p_n2tubecooldelay,
p_n2filltimeout, p_n2fault, p_n2auto, p_n2valve, p_n2command, p_boostdelay,
p_codeA, p_codeB, p_codeAbad, p_codeBbad, p_freeRam,
p_timC, p_timN, p_timH, p_timF, p_timM, p_timV, p_timQ,
p_configDevice, p_configDeviceState, p_configSoft, p_configIdn, p_configSpecial,
p_configSave, p_configVersion, p_configItem, p_configInstr,
p_auxAvailable, p_aux, p_auxScale, p_auxDigits, p_auxLabel,
p_heFeedback, p_n2Feedback, p_vuAvailable, p_heExtAvailable, p_heExtChannel,
p_nBase
};
enum {p_vu = p_nBase - 1, p_vuCmd = p_vu + 12}; // valve unit base (+ 24 parameters)
enum {p_heExtWiRes = p_nBase, p_heExtEmpty, p_heExtFull, p_heExtLevel, p_heExtState = p_heExtLevel + 6}; // he ext base (+ 15 parameters)
enum {p_nPar = p_nBase + 24}; // max. parameter of all extensions
enum {no_extension, valve_extension, he_extension};
byte extension = valve_extension;
char *menu_text = "menu|home|configuration";
char *menu_text1_flow = "needle valve";
char *menu_text2_n2 = "LN2";
char *menu_text3_he = "LHe level";
enum {m_none, m_menu, m_home, m_device, m_instr, m_loading, m_commands, m_config, m_nv, m_n2, m_he, m_ext};
enum {stop_all};
enum {N_hdl = 8};
byte screen = m_home; // screen shown on start
PAR_TEXT configIdn[16] = "CCU4";
PAR_BYTE n2Available = 0;
PAR_BYTE heAvailable = 0;
PAR_BYTE flowAvailable = 0;
PAR_BYTE motorAvailable = 0; // was 1, why?
PAR_BYTE vuAvailable = 0;
PAR_BYTE heExtAvailable = 0; // 0: no addition He Level channel, 1: 2nd He Channel, 6: 6 additional channels with He Extension
PAR_BYTE auxAvailable = 0;
PAR_LONG timC = 0;
PAR_LONG timN = 0;
PAR_LONG timH = 0;
PAR_LONG timF = 0;
PAR_LONG timM = 0;
PAR_LONG timV = 0;
PAR_LONG timQ = 0;
// parameters for Summary command:
int sumidx = 0; // actual position in parameter list
int sumsup = 0; // index of next supplementary parameters to write
enum {
io_lev_enable = 2,
io_lev_current = 3,
io_motor_enable = 4,
io_motor_current = 5,
io_motor_open = 6,
io_motor_close = 7,
io_n2_valve = 8,
io_he_valve = 9,
io_motor2_open = 10,
io_motor2_close = 11,
io_lev_boost = 12,
io_valve_boost = 13,
io_disp_input = 20,
io_he_select = 21,
io_ext1 = 22,
io_ext2 = 30,
io_ext3 = 38,
io_ext4 = 46
};
enum {
a_lev_current = 0,
a_lev_vplus = 1,
a_levb_vplus = 2,
a_lev_vminus = 3,
a_codea = 4,
a_codeb = 5,
a_n2_lower = 6,
a_n2_upper = 7,
a_flow_prec = 8,
a_flow_raw = 9,
a_motor_volt = 10,
a_motor_current = 11,
a_lhe_feedback = 12,
a_ln2_feedback = 13,
a_aux_current = 14,
a_levb_vminus = 15
};
#if (SERIALCOMMON)
#define Serial2 Serial
#endif
DispSerial SerialD(&Serial3);
SerialWrapper SerialC(&Serial2);
void setup() {
analogReference(EXTERNAL);
#if (RAMTEST)
RamFill();
#endif
Serial.begin(9600);
Serial.print("\n---------\n");
Serial3.begin(9600);
#if (!SERIALCOMMON)
Serial2.begin(9600);
#endif
SerialC.handler = HardHandler;
SerialD.handler = HardHandler;
ParSetAllChanged();
ConfigInit();
ValveInit();
HeInit();
FlowInit();
switch (extension) {
case valve_extension: VuInit(); break;
case he_extension: break;
}
displayAvailable = DispInit(ParHandler, m_home); // initialize Display and tell that it should call ParHandler between every line written on the display
WriteDisplay();
}
void Pars() {
FlowPars();
HePars();
N2Pars();
ValvePars();
ConfigPars();
if (auxAvailable) AuxPars();
ParFixed("tc", timC, 1);
ParFixed("tn", timN, 1);
ParFixed("th", timH, 1);
ParFixed("tf", timF, 1);
ParFixed("tm", timM, 1);
ParFixed("tv", timV, 1);
ParFixed("tq", timQ, 0);
switch (extension) {
case valve_extension: VuPars(); break;
}
}
void HomeDisp() {
boolean run = false;
boolean big = false;
ConfigDispShort(configSoft);
DispFullScreenTouch(m_menu * 16); // indicate full screen touch
if (HeShown()) {
run |= HeDispShort(&big);
DispLine();
}
if (flowAvailable || motorAvailable) {
run |= FlowDispShort(!big);
DispLine();
}
if (auxAvailable) {
AuxDisp();
DispLine();
}
if (N2Shown()) {
run |= N2DispShort();
DispLine();
}
switch (extension) {
case valve_extension: VuDispShort(); DispLine(); break;
}
if (run) {
DispButton(1, stop_all, "stop");
} else if (!online) {
DispButton(1, m_menu * 16, ":offline"); // button code as on full screen touch
}
DispButton(0, m_menu * 16 + m_commands, "menu");
}
void CmdDisp() {
N2Buttons(7);
MotorButtons(12);
HeButtons(13);
switch (extension) {
case valve_extension: VuButtons(6); break;
}
DispButton(2, m_menu * 16 + m_config, "config");
DispButton(1, m_menu * 16 + m_home, "home");
}
void WriteDisplay() {
DispBegin(screen);
/*
DispMenu(0, 1, menu_text);
if (flowAvailable || motorAvailable) {
DispAppendToMenu(menu_text1_flow);
}
if (N2Shown()) {
DispAppendToMenu(menu_text2_n2);
}
if (HeShown()) {
DispAppendToMenu(menu_text3_he);
}
DispEndMenu();
*/
switch (screen) {
case m_home:
if (extension == he_extension) {
HeDispExt();
DispEnd();
return;
}
HomeDisp();
break;
case m_loading:
ConfigLoadingDisp();
break;
case m_commands:
CmdDisp();
break;
case m_config:
ConfigDispShort("cfg");
ConfigDisp();
break;
case m_device:
ConfigDeviceDisp();
break;
case m_instr:
ConfigInstrDisp();
break;
case m_he:
ConfigDispShort("LHe");
HeDisp();
break;
case m_n2:
ConfigDispShort("LN2");
N2Disp();
break;
case m_nv:
ConfigDispShort("n.v.");
FlowDisp();
break;
case m_ext:
switch (extension) {
case valve_extension:
ConfigDispShort("valves");
VuDisp();
break;
}
break;
}
// DispButton(0, m_menu * 16 + m_commands, "menu");
// DispButton(1, m_menu * 16 + m_home, "home");
DispEnd();
}
void DisplayHandler() {
int button;
byte menu, code;
static ulong last=0;
static ulong lastW=0;
//if (!expired(&last, 30)) return;
DispGet(menu, code);
switch (menu) {
case disp_wake: // wake up
screen = m_home;
if (heExtAvailable <= 1) HeSetFast();
break;
case disp_none: // nothing touched
break;
case disp_sleep: // sleep
screen = m_home;
break;
case m_menu:
if (code == 0) {
screen = m_commands;
} else {
screen = code;
}
break;
case m_home:
if (code == stop_all) {
HeCmd(stop_all);
N2Cmd(stop_all);
MotorCmd(stop_all);
}
break;
case m_config:
ConfigCmd(code);
break;
case m_device:
ConfigDeviceCmd(code);
break;
case m_instr:
ConfigInstrCmd(code);
break;
case m_nv:
MotorCmd(code);
break;
case m_n2:
N2Cmd(code);
break;
case m_he:
HeCmd(code);
break;
case m_ext:
if (extension == valve_extension) VuCmd(code);
break;
}
ParHandler();
if (!expired(&lastW, 100)) return;
WriteDisplay();
}
boolean spc;
name_t name;
void OutResult(boolean ok) {
char *value;
long s;
if (name != 0) {
if (spc) {
SerialC.write(' ');
} else {
spc = true;
}
s = micros();
value = ParFmtByName(name);
SerialC.print(c40(name));
if (value == 0 || !ok) {
SerialC.write('?');
} else {
SerialC.write('=');
SerialC.print(value);
}
name = 0;
}
}
byte List(byte nr, name_t name, const char *formatted, const char *astext, byte unchanged) {
SerialC.print(c40(name));
SerialC.print('\t');
if (formatted) {
SerialC.print(formatted);
}
if (astext) {
SerialC.print('\t');
if (astext[0] == '~') { // remove blink control character
astext++;
}
SerialC.print(astext);
}
SerialC.print('\n');
return 0;
}
byte Summary(byte nr, name_t name, const char *formatted, const char *astext, byte unchanged) {
char str[8];
if (!formatted) return 0;
sumidx++;
if (unchanged && sumsup / 4 != sumidx / 4) return 0; // write unchanged only if within sumsup ... sumsup + 3
c40toString(name, str, sizeof str);
SerialC.print(str);
SerialC.print('=');
SerialC.print(formatted);
SerialC.print(' ');
return 1;
}
void ShowSummary() {
sumidx = 0;
ParScan(Summary); // show changed values plus the parameters within sumsup ... sumsup + 3
sumsup += 4;
if (sumsup > sumidx) {
sumsup = 0;
}
}
void ParHandler() {
char *txt;
ParserType type;
char sym;
static ulong lastQuery = 0;
if (bitRead(inside, 0)) {
Serial.print("already inside ParHandler\n");
return;
}
bitSet(inside,0);
HardHandler();
type = Parser();
if (type == parser_none) {
if (online && time_ge(now, lastQuery + 5000)) {
online = false;
}
} else {
lastQuery = now;
online = true;
switch (type) {
case parser_value:
//Serial.println("number");
if (name == 0) {
SerialC.print('?');
}
OutResult(ParCommand(name));
break;
// case parser_colon:
// name = ParserIdent();
// ParSetHidden(name);
// name = 0;
// break;
case parser_ident:
OutResult(true);
name = ParserIdent();
break;
case parser_symbol:
sym = ParserSymbol();
if (sym == ':') {
ParHide(name);
name = 0;
break;
}
OutResult(true);
if (sym == '*') {
ParScan(List);
} else if (sym == '?') {
ShowSummary();
} else if (sym == '!') {
DispDebug(true);
} else if (sym == '$') {
#if (RAMTEST)
RamTest();
#else
SerialC.print(F("ramtest disabled\n"));
#endif
} else {
SerialC.print(sym);
}
spc = false;
break;
case parser_error:
SerialC.print('?');
break;
}
}
bitClear(inside,0);
}
int aRead(byte pin) {
int result;
result = analogRead(pin);
FastHandler();
return result;
}
void FastHandler() {
static long old = 0;
now = millis();
if (now == old) return;
if (now - old > timQ && old != 0) {
//Serial.println(old);
//Serial.println(now);
ParSet(timQ, now - old);
}
/*
if ((ulong) now / 1000 != (ulong) old / 1000) {
//Serial.print(now, DEC); Serial.println("tick");
}
*/
old = now;
MotorFastHandler();
ValveFastHandler();
}
void HardHandler() {
static byte hdl=0;
static ulong callcnt = 0;
static long times[N_hdl] = {0};
static long cnt[N_hdl] = {0};
static long last = 0;
byte i;
long t;
long m;
if (bitRead(inside, 1)) {
Serial.print("already inside HardHandler\n");
return;
}
bitSet(inside, 1);
FastHandler();
callcnt++;
if (time_ge(now, callcnt)) {
bitClear(inside, 1);
return; // make sure that this function does not consume more than 1 ms IN AVERAGE (for serial connections)
}
if (time_ge(now, callcnt + 1000)) {
Serial.println(now);
Serial.println(callcnt);
Serial.print("HardHandler is not called often enough\n");
callcnt = now - 1000;
}
t = micros();
switch (hdl) {
case 0: ConfigHandler(); break;
case 1: N2Handler(); break;
case 2: HeHandler(); break;
case 3: FlowHandler(); break;
case 4: MotorHandler(); break;
case 5: ValveHandler(); break;
case 6: AuxHandler(); break;
case 7: if (extension == valve_extension) VuHandler(); break;
}
t = micros() - t;
times[hdl] += t;
cnt[hdl] ++;
if (cnt[hdl] >= 10) {
m = times[hdl] * 0.001;
switch (hdl) {
case 0: ParSet(timC, m); break;
case 1: ParSet(timN, m); break;
case 2: ParSet(timH, m); break;
case 3: ParSet(timF, m); break;
case 4: ParSet(timM, m); break;
case 5: ParSet(timV, m); break;
}
cnt[hdl] = 0;
times[hdl] = 0;
}
hdl++;
if (hdl >= N_hdl) {
hdl = 0;
}
bitClear(inside, 1);
}
void loop() {
ParHandler();
if (displayAvailable) DisplayHandler();
}
+81 -7
View File
@@ -9,6 +9,7 @@ PAR_LONG codeB = 0;
PAR_BYTE codeAbad = 1;
PAR_BYTE codeBbad = 1;
PAR_TEXT configDevice[16] = "none";
PAR_TEXT configInstr[8] = "offline";
PAR_LONG configVersion = 1;
PAR_BYTE configSpecial = 0;
PAR_BYTE configDeviceState = 0;
@@ -54,6 +55,10 @@ static struct deviceTable_s {
int heFull; // warm length at 100 %
} deviceTable[configMaxDev];
char configInstrList[255]; // at limit, as index must be byte
byte configInstrLen = 0;
byte configInstrIdx = 0;
const long code_max = 10 * 1024L; // 10 times max. adc
const long code_res = 20000; // reference resistor
@@ -108,6 +113,7 @@ void ConfigPars() {
}
}
ParFixed("cvs", configVersion, 0);
ParText("cin", configInstr, true);
if (ParText("c", configItem, false) == par_command) {
ConfigItem();
}
@@ -122,18 +128,21 @@ void ConfigDisp() {
if (configSpecial == standard_cfg) {
DispValue("A: ", p_codeA, p_codeAbad);
DispValue("B: ", p_codeB, p_codeBbad);
DispCheckbox(config_flow, config_noflow, p_flowAvailable, "n.v.active");
DispValue("LHe level: ", p_heAvailable, 0);
DispCheckbox(config_n2, config_non2, p_n2Available, "N2 active");
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 * 16 + m_device, "device");
DispButton(3, m_menu * 16 + m_instr, "instr");
} else if (extension == valve_extension) {
DispText2("valve_ext", configSoft);
} else if (extension == he_extension) {
DispText2("he_ext", configSoft);
}
DispButton(0, m_menu * 16 + m_commands, "menu");
DispButton(1, m_menu * 16 + m_home, "home");
}
void ConfigLoadingDisp() {
@@ -186,6 +195,7 @@ void ConfigInit() {
if (heExtAvailable != 1 || EEPROM_version < 7) {
ParSet(heExtAvailable, 0);
}
eeStoreText(configInstrList, sizeof configInstrList, ee_read);
}
boolean ConfigCrit(word good, word total) {
@@ -520,6 +530,17 @@ void ConfigItem() {
Serial.print(F("config table full"));
return;
}
if (configItem[0] == '-') { // it's an instrument name list
if (configInstrLen < sizeof configInstrList - 2) {
if (configInstrLen != 0) {
configInstrList[configInstrLen] = ',';
configInstrLen++;
}
configInstrLen += textcopy(configInstrList + configInstrLen, sizeof configInstrList - configInstrLen, configItem + 1);
}
Serial.println(configInstrList);
return;
}
configItemIndex = 0;
d = &deviceTable[configNdevices];
if (ConfigItemText(d->name, sizeof d->name) < 0) {
@@ -529,9 +550,9 @@ void ConfigItem() {
}
if (configItem[configItemIndex] == 0) { // end of data
switch (d->name[0]) {
case 'c': configNdevices = 0; return;
case 'c': configNdevices = 0; configInstrLen = 0; configInstrList[0] = 0; return;
case 's': ConfigSave(); return;
case 'b': configDevIdx = 0; return;
case 'b': configDevIdx = 0; configInstrIdx = 0; return;
}
if (configDevIdx >= configNdevices) {
configItem[0] = 0;
@@ -540,6 +561,7 @@ void ConfigItem() {
}
d = &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) {
@@ -613,7 +635,8 @@ void ConfigItem() {
}
if (c == R_INSTABLE) {
SerialC.write('?');
Serial.print(F("unknown resistor code\n"));
Serial.print(F("unknown resistor code "));
Serial.println(r);
return;
}
}
@@ -698,6 +721,7 @@ void ConfigSave() {
Serial.println(configVersion);
eeStore((void *)&deviceTable, (sizeof deviceTable[0]) * configNdevices, ee_write);
EESTORE(heExtAvailable, heExtAvailable);
eeStoreText(configInstrList, sizeof configInstrList, ee_write);
}
}
@@ -737,6 +761,7 @@ void ConfigDeviceDisp() {
DispButton(pos, m_device * 16 + (devno - configDevIdx), deviceTable[devno].name);
}
DispButton(0, m_device * 16 + 15, "other");
DispButton(1, m_menu * 16 + m_home, "home");
}
void ConfigDeviceCmd(byte cmd) {
@@ -747,10 +772,59 @@ void ConfigDeviceCmd(byte cmd) {
ConfigDevice(devno, config_by_touch);
return;
}
DispTriggerRewrite();
//DispTriggerRewrite();
configDevIdx += 12;
}
void ConfigInstrDisp() {
byte pos, i, idx = 0;
char name[8];
for (i = 0; i < sizeof configInstrList; i++) {
if (i >= configInstrLen) {
configInstrIdx = 0;
idx = 0;
i = 0;
break;
}
if (idx == configInstrIdx) break;
if (configInstrList[i] == ',') idx++;
}
for (pos = 13, idx = configInstrIdx; pos > 0; pos -= 2, idx++) {
if (pos == 1) pos = 12; // next column
i += textcopyuntil(name, sizeof name, configInstrList + i, ',');
if (configInstrList[i] == ',') i++;
if (name[0] != 0) {
DispButton(pos, m_instr * 16 + (idx - configInstrIdx), name);
}
}
DispButton(1, m_menu * 16 + m_home, "home");
DispButton(0, m_instr * 16 + 15, "other");
}
void ConfigInstrCmd(byte cmd) {
byte idx;
int i;
char name[8];
idx = configInstrIdx + cmd;
if (cmd == 15) {
configInstrIdx += 12;
return;
}
for (i = 0; i < sizeof configInstrList; i++) {
if (idx == 0) break;
if (configInstrList[i] == 0) {
configInstrIdx += 12;
return;
}
if (configInstrList[i] == ',') idx--;
}
textcopyuntil(name, sizeof name, configInstrList + i, ',');
ParSetText(configInstr, name);
return;
}
void ConfigSpecial(EeMode mode) {
switch (configSpecial) {
case argon_cfg:
+430 -497
View File
File diff suppressed because it is too large Load Diff
+783
View File
@@ -0,0 +1,783 @@
//#include "utils.h"
#define DISPDEBUG 0
enum {disp_maxbuttons = 14};
struct {
boolean commdebug;
boolean rewrite; // true during a rewrite
boolean forced; // true when display for next element is forced
boolean rewriteLater; // delayed rewrite
//boolean finishMenu; // finish the menu out of DispMenu
ulong lastAct; // time of last action
boolean bright;
boolean wakemeup;
//boolean freeze;
byte actMenu; // actual screen
byte textPos;
byte butLim;
byte button[disp_maxbuttons]; // code for button
byte fullCode; // code when full screen was touched
byte more;
word buttonmask;
void (*handler)();
} disp = {0};
void DispSend(const char *data) {
if (data) SerialD.print(data);
SerialD.write((byte)0);
}
boolean DispInit(void (*handler)(), byte homescreen) {
long start;
disp.commdebug = false;
disp.handler = handler;
SerialD.commdebug = disp.commdebug;
// should we accept that there might be no display? - this would end in an infinite loop:
start = millis();
do {
DispPutStr("#DO1,"); // turn Display
if (millis() - start > 3000) { // no display
return 0;
}
} while (SerialD.send() < 0); // check until success as the display needs time to get running
//delay(1000);
DispPutStr("#TA,#DL,#YZ1,#QZ3,#YH100,"); // disable terminal and clear Display, dimmer fast, blink 0.3 s, full light
DispPutStr("#ZV1,"); // drawing mode 'set'
//DispSend("\033NT\002"); // touch menu mode
while (SerialD.read() >= 0); // clear old requests
disp.wakemeup = false;
disp.bright = true;
disp.actMenu = homescreen;
disp.lastAct = start;
disp.rewriteLater = true;
if (DISPDEBUG) {
Serial.println(F("D init"));
}
return 1;
}
void DispPut(byte chr) {
SerialD.write(chr);
}
void DispPutStr(const char *str) {
SerialD.print(str);
}
void DispPutData(byte *data, byte len) {
for (; len>0; data++,len--) {
SerialD.write(*data);
}
}
// this might not be needed
void DispTriggerRewrite() {
disp.rewriteLater = true;
}
boolean DispShowThis(byte nr) {
return /*!disp.freeze && */ (ParChanged(nr) || disp.rewrite);
}
void DispButtonArea(byte nr, byte coord[4], byte clr[4]) {
byte x1, x2, y1, y2;
x1 = ((nr + 1) % 2) * 33;
y1 = 129 - (nr / 2 + 1) * 18;
x2 = x1 + 30;
y2 = y1 + 16;
coord[0] = x1;
coord[1] = y1;
coord[2] = x2;
coord[3] = y2;
x2+=2;
if (y1 > 0) y1--;
if (x2 > 63) x2 = 63;
if (clr) {
clr[0] = x1;
clr[1] = y1;
clr[2] = x2;
clr[3] = y2;
}
}
void DispClrButton(byte nr, byte newCode) {
byte code = ARRAY(disp.button, nr);
byte i;
if (newCode != 0) {
// clear other buttons with the same code
for (i = 0; i < disp_maxbuttons; i++) {
if (ARRAY(disp.button, i) == newCode) {
if (DISPDEBUG) {
Serial.print("D clr button i");
Serial.print(i);
Serial.print(" cod = ");
Serial.println(newCode);
}
DispPutStr("\033AL"); DispPut(newCode); DispPut(1);
DispSend(0);
ARRAY(disp.button, i) = 0;
}
}
}
if (code != 0) {
// clear button at position nr
if (DISPDEBUG) {
Serial.print("D clr button at nr ");
Serial.print(nr);
Serial.print(" cod = ");
Serial.println(code);
}
DispPutStr("\033AL"); DispPut(code); DispPut(1);
DispSend(0);
}
SerialD.send();
ARRAY(disp.button, nr) = newCode;
}
void DispBegin(byte menu) {
byte coord[4], clr[4];
byte nr;
disp.textPos = 0;
disp.buttonmask = 0;
disp.rewrite = false;
//bitSet(disp.buttonmask, 0); // mark menu button as used
//if (disp.freeze) return;
if (disp.actMenu != menu) {
disp.actMenu = menu;
disp.more = 0;
} else if (!disp.rewriteLater) {
return;
}
disp.rewrite = true;
disp.rewriteLater = false;
ParSetAllChangedDisp();
if (DISPDEBUG) {
Serial.println(F("D begin ---"));
}
DispSend("#AL0,1,"); // clear all touch buttons
for (nr = 0; nr < disp_maxbuttons; nr++) {
disp.button[nr] = 0;
}
SerialD.send();
}
void DispFullScreenTouch(byte code) {
if (disp.rewrite /* && !disp.freeze */) {
DispSend("#AH0,0,63,127,"); // activate general touch area
SerialD.send();
}
disp.fullCode = code;
}
byte DispMore() {
return disp.more;
}
byte DispSetMore(byte value) {
disp.more = value;
}
/*
void DispMenu(byte nr, byte code, char *menutitle) {
byte coord[4], clr[4];
byte touchCode, oldCode, newCode;
bitSet(disp.buttonmask, nr); // mark button as used
if (disp.freeze) return;
if (code >= 16 || nr == 0) {
touchCode = code;
} else {
touchCode = disp.actMenu * 16 + code;
}
newCode = disp_menuoff + nr;
oldCode = ARRAY(disp.button, nr);
if (oldCode == newCode && !disp.rewrite) return;
DispClrButton(nr, newCode);
DispButtonArea(nr, coord, clr);
//DispPutStr("\033RL"); DispPutData(clr, 4); // clear rect
// show menu button and define popup menu
// button font geneva10, frame thin
DispPutStr("#AF4,#AE1,\033AM"); DispPutData(coord, 4);
DispPut(0); DispPut(disp_menuoff+nr); DispPut(touchCode); DispPutStr("OC"); DispPutStr(menutitle);
if (DISPDEBUG) {
Serial.print(F("D menu\n"));
}
disp.finishMenu = true;
return;
}
void DispRedrawMenu(byte nr) {
ARRAY(disp.button, nr) = 0;
}
void DispAppendToMenu(char *text) {
if (disp.finishMenu) {
disp.finishMenu = false;
DispPut('|');
DispPutStr(text);
disp.finishMenu = true;
}
}
void DispEndMenu() {
if (disp.finishMenu) {
DispSend(0);
disp.finishMenu = false;
SerialD.send();
}
}
*/
void DispButton(byte nr, byte code, char *text) {
byte x, y, w;
byte coord[4], clr[4];
byte touchCode, oldCode;
disp.handler();
bitSet(disp.buttonmask, nr); // mark button as used
//if (disp.freeze) return;
if (code >= 16) {
touchCode = code;
} else {
touchCode = disp.actMenu * 16 + code;
}
oldCode = ARRAY(disp.button, nr);
if (oldCode == touchCode && !disp.rewrite && !disp.forced) return;
disp.forced = false;
DispClrButton(nr, touchCode);
DispButtonArea(nr, coord, clr);
if (text[0] == '*') { // top button for command screen
DispPutStr("#AE1,"); // frame thin
text++;
} else if (text[0] == '-') { // appended button for command screen
DispPutStr("#AE1,"); // frame thin
text++;
coord[1] -= 2;
} else if (text[0] == ':') { // no frame button
DispPutStr("#AE0,"); // no frame
text++;
} else { // normal button
DispPutStr("#AE1,"); // frame thin
}
DispPutStr("\033RL"); DispPutData(clr, 4); // clear rect
// AF4 button font Geneva10
if (DISPDEBUG) {
Serial.print(F("D len "));
Serial.print(DispTextLength(text));
Serial.println(text);
}
if (DispTextLength(text) < 30) {
DispPutStr("#AF4,");
} else {
DispPutStr("#AF1,");
}
// AT create button
DispPutStr("\033AT"); DispPutData(coord, 4);
DispPut(touchCode); DispPut(0); DispPut('C'); DispSend(text);
SerialD.send();
if (DISPDEBUG) {
Serial.print(F("D button "));
Serial.println(text);
}
}
void DispLine() {
if (disp.textPos > 110) return; // do not draw line in menu button area
if (disp.rewrite /* && !disp.freeze */) {
DispPutStr("\033GD"); DispPut(0); DispPut(disp.textPos - 1); DispPut(63); DispPut(disp.textPos - 1); // draw line
SerialD.send();
}
disp.textPos+=1;
}
void DispEnd() {
byte nr;
int8_t lastbut;
byte coord[4], clr[4];
//if (disp.freeze) return;
lastbut = -1;
for (nr = 0; nr < disp_maxbuttons; nr++) {
if (bitRead(disp.buttonmask, nr)) {
lastbut = nr;
} else if (disp.button[nr]) {
DispClrButton(nr, 0);
bitSet(disp.buttonmask, nr);
}
}
if (!disp.rewrite) return;
if (lastbut % 2 == 0) lastbut++;
if (DISPDEBUG) {
Serial.print("lastbut");
Serial.println(lastbut);
}
for (nr = 0; nr <= lastbut; nr++) {
if (!bitRead(disp.buttonmask, nr)) {
DispButtonArea(nr, coord, clr);
//Serial.print("clear "); Serial.print(clr[1], DEC); Serial.print(" "); Serial.println(clr[3], DEC);
DispPutStr("\033RL"); DispPutData(clr, 4); // clear rect
}
}
// clear gap between text and buttons
if (lastbut < 0) {
clr[1] = 127;
} else {
DispButtonArea(lastbut, coord, clr);
}
if (disp.textPos > 0) disp.textPos--;
if (DISPDEBUG) {
Serial.print("textpos");
Serial.println(disp.textPos);
Serial.print("clrbut");
Serial.println(clr[1]);
}
if (disp.textPos < clr[1]) {
if (DISPDEBUG) {
Serial.print("gap "); Serial.print(disp.textPos, DEC); Serial.print(" "); Serial.println(clr[1], DEC);
}
DispPutStr("\033RL"); DispPut(0); DispPut(disp.textPos); DispPut(63); DispPut(clr[1]-1); // clear rect
}
SerialD.send(); // complete send
if (DISPDEBUG) {
Serial.println(F("D end ---"));
}
}
void DispRewrite() {
disp.rewrite = true;
}
void DispRewriteNext() {
disp.forced = true;
}
void DispTextArg(byte fmt, const char *head, const char *text) {
byte xpos, h, align, x;
int i;
boolean zoom = false;
int len, maxlen;
char txt[2];
disp.handler();
if (DISPDEBUG) {
Serial.print(F("D text "));
Serial.print(fmt, DEC);
Serial.print("|");
if (head) Serial.print(head);
Serial.println(text);
}
len = DispTextLength(text);
maxlen = 127;
switch (fmt) {
case fmt_none:
return;
case fmt_lsmall:
DispPutStr("#ZF4,"); align='L'; xpos = 0; h = 11; break;
case fmt_ckbox:
DispPutStr("#ZF4,"); align='L'; xpos = 18; h = 11; break;
case fmt_rsmall:
//sameline = true;
DispPutStr("#ZF4,"); align='R'; xpos = 63; h = 11; break;
case fmt_medium:
DispPutStr("#ZF4,");
align='R'; xpos = 63; h = 26; break;
case fmt_big:
if (len >= 30) {
DispPutStr("#ZF4,#ZZ1,2,"); zoom = true;
} else {
DispPutStr("#ZF6,"); head = 0;
}
align='R'; xpos = 63; h = 27;
break;
}
// clear text line
DispPutStr("\033RL"); DispPut(0); DispPut(disp.textPos - (disp.textPos > 0)); DispPut(63); DispPut(disp.textPos + h - 2); // clear rect
if (head && head[0]) {
DispPutStr("\033Z");
if (align == 'R') {
DispPut('L'); DispPut(0); DispPut(disp.textPos);
DispSend(head);
maxlen -= DispTextLength(head);
if (fmt == fmt_medium && len < 30) DispPutStr("#ZF6,"); // use Big for number
DispPutStr("\033Z"); DispPut(align); DispPut(xpos); DispPut(disp.textPos);
} else {
DispPut(align); DispPut(xpos); DispPut(disp.textPos);
DispPutStr(head);
maxlen -= DispTextLength(head);
}
} else {
DispPutStr("\033Z"); DispPut(align); DispPut(xpos); DispPut(disp.textPos);
}
if (len > maxlen) {
txt[0] = text[0];
txt[1] = 0;
len = DispTextLength(txt);
for (i = 0; len <= maxlen && text[i] != 0; i++) {
DispPut(text[i]);
txt[0] = text[i+1];
len += DispTextLength(txt);
}
DispSend("");
} else {
if (text[0] == 0) {
DispSend(" ");
} else {
DispSend(text);
}
}
disp.textPos += h;
if (zoom) {
DispSend("#ZZ1,1,");
}
SerialD.send();
}
void DispSkip(byte fmt) {
switch (fmt) {
case fmt_none:
return;
case fmt_medium:
disp.textPos += 26; break;
case fmt_big:
disp.textPos += 27; break;
case fmt_lsmall:
case fmt_rsmall:
case fmt_ckbox:
default:
disp.textPos += 11; break;
}
}
void DispText(const char *text) {
if (disp.rewrite || disp.forced /*&& !disp.freeze*/) {
disp.forced = false;
DispTextArg(fmt_lsmall, 0, text);
} else {
DispSkip(fmt_lsmall);
}
}
void DispText2(const char *head, const char *text) {
if (disp.rewrite || disp.forced) {
disp.forced = false;
DispTextArg(fmt_rsmall, head, text);
} else {
DispSkip(fmt_rsmall);
}
}
int DispTextLength(const char *text) {
// caclulate text length for Font 4 (GENEVA10)
// the table contains the pure width, we have to add one pixel for the gap
static byte len[96] = {5,2,4,6,5,8,7,2,3,3,6,5,3,4,2,4,
5,5,5,5,5,5,5,5,5,5,3,3,4,5,4,5,
7,6,5,5,5,4,4,5,5,2,5,5,4,7,5,5,
5,5,5,5,5,5,5,7,5,5,4,3,5,3,3,5,
2,4,4,4,4,4,3,4,4,2,3,4,2,7,4,4,
4,4,4,4,3,4,5,7,5,5,4,3,1,3,5,6};
int length = 0;
char ch;
while ((ch = *text) != 0) {
if (ch < 32 || ch >= 128) {
length += 5;
} else {
length += len[ch-32] + 1;
}
text++;
}
return length;
}
boolean DispState(char *head, byte nr) {
char text[16];
if (DispShowThis(nr)) {
DispTextArg(fmt_lsmall, head, ParFmt(nr));
return true;
}
DispSkip(fmt_lsmall);
return false;
}
boolean DispValueArg(byte fmt, char *head, byte nr, byte stateNr) {
byte state, maxlen;
char text[16];
//if (!disp.freeze) {
if (stateNr) {
state = ParGet(stateNr);
} else {
state = 0;
}
if (state > 0) {
if (DispShowThis(stateNr)) {
DispTextArg(fmt, head, ParFmt(stateNr));
return true;
}
} else {
if (DispShowThis(nr)) {
if (fmt == fmt_big) {
head = 0;
maxlen = 4;
} else if (fmt == fmt_medium) {
maxlen = 4;
} else {
maxlen = 0; // no max length
}
DispTextArg(fmt, head, ParFmt(nr, maxlen));
return true;
}
}
//}
DispSkip(fmt);
return false;
}
boolean DispBigValue(byte nr, byte stateNr) {
return DispValueArg(fmt_big, 0, nr, stateNr);
}
boolean DispMediumValue(char *head, byte nr, byte stateNr) {
return DispValueArg(fmt_medium, head, nr, stateNr);
}
boolean DispValue(char *head, byte nr, byte stateNr) {
return DispValueArg(fmt_rsmall, head, nr, stateNr);
}
boolean DispCheckbox(byte on, byte off, byte nr, char *text) {
byte value = ParGet(nr);
byte code;
static byte oldPos = 1;
if (disp.textPos == oldPos) { // make a gap between two succesive checkboxes
disp.textPos += 4;
} else {
oldPos = disp.textPos;
}
if (!DispShowThis(nr)) {
DispSkip(fmt_ckbox);
oldPos = disp.textPos;
return false;
}
if (DISPDEBUG) {
Serial.print(F("D checkbox "));
Serial.println(text);
}
if (disp.textPos > oldPos) { // clear gap
DispPutStr("\033RL"); DispPut(0); DispPut(oldPos - 1); DispPut(63); DispPut(disp.textPos - 1);
}
DispTextArg(fmt_ckbox, 0, text);
// button font 4x6, no button frame
DispPutStr("#AF4,#AE0,\033AT"); DispPut(0); DispPut(disp.textPos-14); DispPut(63); DispPut(disp.textPos-1);
if (value) {
code = off;
} else {
code = on;
}
DispPut(0); DispPut(disp.actMenu * 16 + code); DispPut('L');
if (value) {
DispSend("[x]");
} else {
DispSend("[ ]");
}
SerialD.send();
oldPos = disp.textPos;
return true;
}
void DispDebugGet(char ch, int8_t i) {
static boolean off = true;
if (!DISPDEBUG) return;
if (off) {
Serial.print(F("D get"));
off = false;
}
if (ch == 0) {
Serial.print('>');
Serial.println(i, DEC);
off = true;
return;
}
Serial.print(' ');
if (ch == '#') {
Serial.print(i, DEC);
} else {
Serial.print(ch);
}
}
int DispGetEvent(byte *code, byte maxlen) {
int ch, len, arg;
do {
ch = SerialD.read();
if (ch < 0) return ch;
} while (ch != 27); // skip chars until esc
ch = SerialD.read();
if (ch < 0) return ch;
len = SerialD.read();
if (len < 0) return len;
while (len > 0) {
len--;
arg = SerialD.read();
if (arg < 0) return arg;
if (maxlen > 0) {
*code = arg;
code++;
maxlen--;
}
}
while (maxlen > 0) {
*code = 0;
code++;
maxlen--;
}
return ch;
}
void DispGet(byte &menu, byte &code) {
// return menu and code
// menu might have the special values disp_none, disp_wake, disp_sleep
int ch;
int result = 0;
byte len;
//boolean menuOn=false;
byte arg;
long s = now;
if (digitalRead(io_disp_input) != LOW) {
if (disp.bright && time_ge(now, disp.lastAct + 3600000)) { // 3600000 = 1 h
if (dispEco > 99) dispEco = 99;
DispPutStr("#YH"); DispPut('0' + dispEco / 10); DispPut('0' + dispEco % 10); DispPut(','); // display light dark
SerialD.send();
disp.bright = false;
disp.wakemeup = false;
//DispTriggerRewrite();
menu = disp_sleep;
code = 0;
} else {
menu = disp_none;
code = 0;
}
return;
}
while ((ch = DispGetEvent(&arg, 1)) >= 0) {
disp.wakemeup = true;
switch (ch) {
case 'A':
//case 'N': // menu response
DispDebugGet('#', ch);
if (arg > 0) {
result = arg;
//disp.freeze = false;
}
break;
case 'H':
DispDebugGet('.', 0);
// if full screen touch is activated, tapping anywhere on screen triggers command screen
if (arg == 1) { // touch (not drag or release)
if (disp.bright) { // when dark: only wake up
result = disp.fullCode;
}
}
break;
/*
case 'T':
menuOn = true;
DispDebugGet('m', 0);
disp.freeze = true;
break;
*/
}
}
/*
if (menuOn) {
DispSend("\033NT\002"); SerialD.send();
result = 0;
}
*/
if (result != 0) {
disp.lastAct = now;
}
if (!disp.bright && disp.wakemeup) { // there was any event
disp.wakemeup = false;
DispSend("#YH100"); // full light
SerialD.send();
//DispTriggerRewrite(); // refresh display
//disp.freeze = false;
disp.bright = true;
disp.lastAct = now;
if (result == 0) {
result = 16 * disp_wake; // wake up
}
}
if (result != 0) {
DispDebugGet(0, result);
}
/*
if (disp.commdebug) {
Serial.println("<.");
if (result == 0) {
while (Serial.read() < 0);
}
}
*/
if (result == 0) {
menu = disp_none;
code = 0;
} else {
menu = result / 16;
code = result % 16;
}
if (DISPDEBUG && menu != disp_none) {
Serial.print("mnu ");
Serial.print(menu, DEC);
Serial.print("cod ");
Serial.println(code, DEC);
}
return;
}
/*
int DispActMenu() {
return disp.actMenu;
}
*/
void DispError(char *head, char *text) {
Serial.print("\nERROR: ");
Serial.print(head);
Serial.print(' ');
Serial.println(text);
disp.textPos = 0;
DispTriggerRewrite();
DispBegin(0);
DispSend("#DL,");
DispTextArg(fmt_lsmall, 0, "ERROR:");
DispTextArg(fmt_lsmall, head, text);
delay(999999999);
while (1);
}
void DispDebug(boolean on) {
disp.commdebug = on;
SerialD.commdebug = on;
}
+72 -78
View File
@@ -27,7 +27,10 @@ PAR_BYTE heFast = 1; // 0: slow, 1: fast
PAR_BYTE heAuto = 1; // 0: off, 1: auto fill
PAR_BYTE heMeas = 0; // 0: idle, 1: measuring, 2:checking
PAR_LONG heExtLevel[6] = {0};
PAR_BYTE heExtState[6] = {2,2,2,2,2,2};
PAR_BYTE heExtState[6] = {he_disabled, he_disabled, he_disabled, he_disabled, he_disabled, he_disabled}; // disabled
PAR_LONG heExtWiRes = 500;
PAR_LONG heExtEmpty = 200;
PAR_LONG heExtFull = 0;
PAR_LONG heExtChannel = 7;
long heMinLev[7] = {0,0,0,0,0,0,0};
long heMinLevel;
@@ -45,6 +48,9 @@ const long heMeasTol = 20; // mm
ulong heLastIncrease = -240000;
boolean checking = false;
boolean started = false;
float volt_to_mm = 300;
long empty_10um = 20000;
float mm_to_percent = 0.5;
void HePars() {
byte changed;
@@ -79,23 +85,28 @@ void HePars() {
if (changed) {
heLastIncrease = now;
}
ParEnum("hav", heAvailable, F("no|yes"));
ParEnum("hav", heAvailable, F("no|yes|ext"));
//ParEnum("hbe", heBooster, F("disable|enable"));
if (ParEnum("hcd", heCommand, he_cmd_text) == par_command) {
HeRemoteCmd(heCommand);
}
ParEnum("hea", heExtAvailable, F("0")); // use enum 0 for pure byte
if (heExtAvailable > 0) {
byte n, m = heExtAvailable;
if (m > 6) {
m = 6;
}
ParFixed("hch", heExtChannel, 0);
for (n = 0; n < m; n++) {
ParNrFixed(p_nHeExt + n, addNumToC40(C("h"), n), heExtLevel[n], 2);
ParNrEnum(p_nHeExt + n + 6, addNumToC40(C("hs"), n), heExtState[n], he_sens_text);
if (heExtAvailable > 6) {
ParSet(heExtAvailable, 1);
}
changed |= (ParFixed("hwr0", heExtWiRes, 0) == par_command);
changed |= (ParFixed("hem0", heExtEmpty, 0) == par_command);
changed |= (ParFixed("hfu0", heExtFull, 0) == par_command);
if (changed) {
for (byte n = 0; n < 6; n++) {
if (heExtState[n] != he_disabled) {
ParArraySet(heExtState, n, he_not_yet);
}
}
}
ParFixed("hch", heExtChannel, 0);
ParFixedArray("h", heExtLevel, 0, heExtAvailable - 1, 2);
ParEnumArray("hs", heExtState, 0, heExtAvailable - 1, he_sens_text);
}
void HeInit() {
@@ -121,21 +132,21 @@ void HeInit() {
byte HeValueNr(byte channel) {
if (channel >= 0 && channel < 6) {
return p_nHeExt + channel;
return p_heExtLevel + channel;
}
return p_heLevel;
}
byte HeStateNr(byte channel) {
if (channel >= 0 && channel < 6) {
return p_nHeExt + channel + 6;
return p_heExtState + channel;
}
return p_heSensState;
}
void HeSetLev(byte channel, long value) {
if (channel >= 0 && channel < 6) {
ParNrSet(p_nHeExt + channel, heExtLevel[channel], value);
ParArraySet(heExtLevel, channel, value);
} else if (channel == 6) {
ParSet(heLevel, value);
}
@@ -143,7 +154,7 @@ void HeSetLev(byte channel, long value) {
void HeSetState(byte channel, byte value) {
if (channel >= 0 && channel < 6) {
ParNrSet(p_nHeExt + channel + 6, heExtState[channel], value);
ParArraySet(heExtState, channel, value);
} else if (channel == 6) {
ParSet(heSensState, value);
}
@@ -186,7 +197,7 @@ void HeDispExt() {
static byte lastState[7]; // 0: disabled, 1: message, 2: value
static byte shown[7] = {2,2,2,2,2,0,0}; // 0: off, 1: small, 2: big
byte sh;
boolean dirty, cutatend;
boolean cutatend;
byte st;
DispFullScreenTouch(m_menu * 16); // indicate full screen touch
@@ -216,7 +227,6 @@ void HeDispExt() {
}
}
}
dirty = false;
cutatend = true;
while (channel > 0) {
channel--;
@@ -227,7 +237,6 @@ void HeDispExt() {
if (shown[channel] == 0) {
used++;
need--;
dirty = true;
shown[channel] = 1;
}
} else if (shown[channel] == 0) {
@@ -236,7 +245,6 @@ void HeDispExt() {
} else {
used++;
need--;
dirty = true;
shown[channel] = 1;
}
} else if (used + need + want > 10) {
@@ -245,17 +253,14 @@ void HeDispExt() {
if (shown[channel] == 2 && used + need + want == 11) {
used -= 1;
shown[channel] = 1;
dirty = true;
} else {
used -= shown[channel];
shown[channel] = 0;
dirty = true;
}
}
} else {
if (shown[channel] == 2) {
used--;
dirty = true;
shown[channel] = 1;
}
}
@@ -275,11 +280,9 @@ void HeDispExt() {
if (shown[channel] == 2) {
shown[channel] = 1;
used--;
dirty = true;
}
}
}
if (dirty) DispRewrite();
for (channel = 0; channel < 7; channel++) {
head[0] = '0' + channel;
if (shown[channel] == 1 && used < 10 && HeGetState(channel) == he_sens_ok) {
@@ -308,7 +311,6 @@ boolean HeDispShort(boolean *big) {
}
if (showLevel != *big) {
showLevel = *big;
DispRewrite();
}
return heValve == valve_on;
}
@@ -381,6 +383,8 @@ void HeDisp() {
DispButton(4, codebase + he_fill, "fill");
}
}
DispButton(0, m_menu * 16 + m_commands, "menu");
DispButton(1, m_menu * 16 + m_home, "home");
}
void HeEndMeas() {
@@ -417,9 +421,16 @@ void HeEndMeas() {
void HeSelect() {
byte pin;
if (heExtChannel == 6) {
volt_to_mm = 1e7 / (heWiRes * heCurrent); // result is in 1e-5 m (+5), input in 1e-2 V (-2), heWiRes in Ohm/m, heCurrent in 1e-4 A (+4)
mm_to_percent = 100.0 / (heEmpty - heFull);
empty_10um = 100 * heEmpty;
digitalWrite(io_he_select, LOW);
} else {
volt_to_mm = 1e7 / (heExtWiRes * heCurrent);
mm_to_percent = 100.0 / (heExtEmpty - heExtFull);
empty_10um = 100 * heExtEmpty;
digitalWrite(io_he_select, HIGH);
}
if (heExtAvailable > 1) {
@@ -453,6 +464,8 @@ void HeHandler() {
static long heDriveCurrent = 0;
static boolean heWarmCheck = false;
ulong tim;
const float sum_to_current = 1235. / 1024 / 100 / 5 / 0.1; // 1235 mV / 1024 adcsteps / 100 sum_counts / 5 Ohm / 0.1 mA
const float sum_to_volt = 1235. / 1024 / 200 * 105.62 / 5.62 / 10; // 1235 mV / 1024 adcsteps / 200 sum_counts * (105.62 kOhm / 5.62 kOhm) / 10 mV;
if (heAvailable != 1) {
return;
@@ -525,7 +538,8 @@ void HeHandler() {
cnt += 20;
if (cnt < 100) return;
long cur = sumc * (1235L * 10 / 5) / cnt / 1024; // 1235 mV, 10 (heCurrent is in 0.1 mA), / 5 Ohm
// cnt == 100
long cur = sumc * sum_to_current;
if (HEDEBUG) { Serial.print(cur); Serial.print(",");}
if (cur > 3) { // there is a current
if (HEDEBUG) Serial.print("sens yes");
@@ -589,7 +603,9 @@ void HeHandler() {
cnt += 20;
if (cnt < 100) return;
ParSet(heCurrent, sumc * (1235L * 10 / 5) / cnt / 1024); // 1235 mV, 10 (heCurrent is in 0.1 mA), / 5 Ohm
// cnt == 100
ParSet(heCurrent, sumc * sum_to_current);
if (time_ge(now, start + 500)) { // after 0.5 sec
if (heCurrent < heDriveCurrent * 5) { // measured current less than 50 % of drive current
heIntState = he_no_sens;
@@ -605,16 +621,16 @@ void HeHandler() {
Serial.println(sump);
Serial.println(summ);
}
sump = sump * 103 / 48; // 221 + 5.62 kOhm instead of 100 + 5.62 kOhm
summ = summ * 86 / 171; // 47.5 + 5.62 instead of 105.62 kOhm
sump = sump * ((221 + 5.62) / 105.62); // 221 + 5.62 kOhm instead of 100 + 5.62 kOhm
summ = summ * ((47.5 + 5.62) / 105.62); // 47.5 + 5.62 instead of 105.62 kOhm
}
ParSet(heVolt, (sump - summ) * 2321 / cnt / (2 * 1024)); // 2321 = 1235 mV * (100 kOhm + 5.62 kOhm) / 5.62 kOhm / 10 mV
heVplus = sump * 2321 / cnt / (2 * 1024);
ParSet(heVolt, (sump - summ) * sum_to_volt);
heVplus = sump * sum_to_volt;
if (HEDEBUG && 0) {
Serial.print("(");
Serial.print(sump * 2321 / cnt / (2 * 1024));
Serial.print(sump * sum_to_volt);
Serial.print("-");
Serial.print(summ * 2321 / cnt / (2 * 1024));
Serial.print(summ * sum_to_volt);
Serial.print("/");
Serial.print(heCurrent);
Serial.print(")");
@@ -630,7 +646,7 @@ void HeHandler() {
sump = 0;
summ = 0;
cnt = 0;
lev = heVolt * 100000 / (heWiRes * heCurrent / 100); // lev is in 1e-5 m (+5), heVolt in 1e-2 V (-2), heWiRes in Ohm/m, heCurrent in 1e-4 A (+4)
lev = heVolt * volt_to_mm;
if (HEDEBUG) {
//Serial.print("("); Serial.print(heVolt); Serial.print(" "); Serial.print(heWiRes); Serial.print(" "); Serial.print(heCurrent); Serial.print(")");
//Serial.print(lev, DEC); Serial.print(" ");
@@ -686,7 +702,7 @@ void HeHandler() {
}
}
lev /= n; // calculate the mean of up to 5 values
heIntLevel = (heEmpty * 100 - lev) * 100 / (heEmpty - heFull); // heEmpty is in mm, lev is in 0.01 mm, result is in 0.01 %
heIntLevel = (empty_10um - lev) * mm_to_percent; // lev is in 10 um = 0.01 mm, result is in 0.01 %
ParSet(heRaw, -lev); // negative raw value: while filling, value increases
if (HEDEBUG) {
Serial.print("r="); Serial.print(heRaw, DEC); Serial.print(" lev="); Serial.print(heIntLevel, DEC); Serial.print(' '); Serial.println(heMinLevel, DEC);
@@ -787,7 +803,6 @@ void HeCmd(byte cmd) {
break;
case he_more:
DispSetMore(!DispMore());
DispTriggerRewrite();
break;
}
}
@@ -808,52 +823,31 @@ void HeValveState(boolean ok) {
boolean HeStateDisp(char *title) {
char *text;
boolean showState;
static byte lastMeas = 2;
showState = DispShowThis(p_heValve) | DispShowThis(p_heFast) | DispShowThis(p_heAuto);
if (!showState) {
if ((heMeas == 1) != lastMeas) {
showState = true;
}
}
if (showState) {
lastMeas = (heMeas == 1);
if (heValve == valve_on) {
if (heMeas) {
text = "~fill meas";
} else {
text = "~filling";
}
} else if (heAuto && heValve == valve_off) {
if (heMeas == 1) {
text = "watc.meas";
} else {
text = "watching";
}
} else if (heAvailable == 2) {
text = "fill off";
} else if (heFast) {
if (heMeas == 1) {
text = "fast meas";
// } else if (heMeas == 2) {
// text = "fast chck";
} else {
text = "fast";
}
} else if (heMeas == 1) {
text = "meas";
// } else if (heMeas == 2) {
// text = "check";
if (heValve == valve_on) {
if (heMeas) {
text = "~fill meas";
} else {
text = "slow";
text = "~filling";
}
DispTextArg(fmt_lsmall, title, text);
return true;
} else {
DispSkip(fmt_lsmall);
return false;
} else if (heAuto && heValve == valve_off) {
if (heMeas == 1) {
text = "watc.meas";
} else {
text = "watching";
}
} else if (heAvailable == 2) {
text = "fill off";
} else if (heFast) {
if (heMeas == 1) {
text = "fast meas";
} else {
text = "fast";
}
} else if (heMeas == 1) {
text = "meas";
}
DispTextRow(fmt_lsmall, title, text);
}
void HeStore(EeMode mode) {
+6 -8
View File
@@ -129,6 +129,8 @@ void FlowDisp() {
DispButton(5, motor_setauto, "auto");
}
}
DispButton(0, m_menu * 16 + m_commands, "menu");
DispButton(1, m_menu * 16 + m_home, "home");
}
void FlowHandler() {
@@ -560,15 +562,11 @@ void MotorCmd(byte cmd) {
void FlowStateDisp() {
char *text;
if (DispShowThis(p_motorState) | DispShowThis(p_motorAutoState)) {
if (motorState == motor_idle && motorAutoState != motor_offline) {
text = ParFmt(p_motorAutoState);
} else {
text = ParFmt(p_motorState);
}
DispTextArg(fmt_lsmall, "n.v. ", text);
if (motorState == motor_idle && motorAutoState != motor_offline) {
text = ParFmt(p_motorAutoState);
} else {
DispSkip(fmt_lsmall);
text = ParFmt(p_motorState);
}
DispTextRow(fmt_lsmall, "n.v. ", text);
}
+20 -22
View File
@@ -22,7 +22,7 @@ long n2start;
long n2StartThreshold = 0; // threshold to be reached within n2tubecooldelay after start
void N2Pars() {
ParEnum("nav", n2Available, F("no|yes"));
ParEnum("nav", n2Available, F("no|yes|ext"));
ParFixed("nu", n2upper, 2);
ParFixed("nl", n2lower, 2);
ParFixed("nth", n2threshold, 2);
@@ -84,6 +84,8 @@ void N2Disp() {
}
DispButton(2, codebase + n2_fill, "fill");
}
DispButton(0, m_menu * 16 + m_commands, "menu");
DispButton(1, m_menu * 16 + m_home, "home");
}
const long n2_fact = 1024L * 5000 / 1235; // 5 V (excit. voltage), 1.235 V (analog reference)
@@ -254,31 +256,27 @@ void N2ValveState(boolean ok) {
void N2StateDisp(char *title) {
char *text;
if (DispShowThis(p_n2valve) | DispShowThis(p_n2auto) | DispShowThis(p_n2fault)) {
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";
if (n2valve == valve_on) {
if (n2fault == n2_no_sensor) {
text = "~fill no s.";
} else {
text = "off";
text = "~filling";
}
DispTextArg(fmt_lsmall, title, text);
} 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 {
DispSkip(fmt_lsmall);
text = "off";
}
DispTextRow(fmt_lsmall, title, text);
}
+27 -7
View File
@@ -1,4 +1,4 @@
enum {par_none, par_get, par_command, par_fmtdisp, par_fmt, par_scan, par_done, par_readonly, par_disable};
enum {par_none, par_get, par_command, par_fmtdisp, par_fmt, par_scan, par_hide, par_done, par_readonly};
#define PARCHECK 0
// PARCHECK 1: check usage of parameters, does not create useful code!
@@ -9,27 +9,46 @@ enum {par_none, par_get, par_command, par_fmtdisp, par_fmt, par_scan, par_done,
#define PAR_BYTE const signed char
#define PAR_TEXT const char
#define ParFixed(name, variable, dig) (&variable - (unsigned long *)0)
#define ParNrFixed(nr, name, variable, dig) (&variable - (unsigned long *)0)
//#define ParNrFixed(nr, name, variable, dig) (&variable - (unsigned long *)0)
#define ParText(name, variable, update) (variable - (char *)0)
#define ParSetText(variable, value) (variable - (const char *)0)
#define ParEnum(name, variable, list) (&variable - (signed char *)0)
#define ParNrEnum(nr, name, variable, list) (&variable - (signed char *)0)
//#define ParNrEnum(nr, name, variable, list) (&variable - (signed char *)0)
#define ParFixedArray(name, variable, par_first, par_last, dig) (variable - (unsigned long *)0)
#define ParEnumArray(name, variable, par_first, par_last, dig) (variable - (signed char *)0)
#define ParEnumArrayCmd(name, variable, par_first, par_last, dig, cmd) (variable - (signed char *)0)
#else
// Arrays starting at index 1 have to be declared with one additional value at zero
#define PAR_LONG long
#define PAR_BYTE byte
#define PAR_TEXT char
#define ParFixed(name, variable, dig) ParFixed_(p_##variable, C(name), &variable, dig)
#define ParNrFixed(nr, nameC40, variable, dig) ParFixed_(nr, nameC40, &(variable), dig)
//#define ParNrFixed(nr, nameC40, variable, dig) ParFixed_(nr, nameC40, &(variable), dig)
#define ParText(name, variable, update) ParText_(p_##variable, C(name), variable, sizeof(variable), update)
#define ParSetText(variable, value) ParSetText_(p_##variable, variable, sizeof variable, value)
#define ParEnum(name, variable, list) ParEnum_(p_##variable, C(name), &variable, list)
#define ParNrEnum(nr, nameC40, variable, list) ParEnum_(nr, nameC40, &(variable), list)
//#define ParNrEnum(nr, nameC40, variable, list) ParEnum_(nr, nameC40, &(variable), list)
#define ParFixedArray(name, variable, par_first, par_last, dig) \
for (byte par_nr_n = par_first; par_nr_n <= par_last; par_nr_n++) { \
ParFixed_(p_##variable + par_nr_n, addNumToC40(C(name), par_nr_n), variable + par_nr_n, dig); \
}
#define ParEnumArray(name, variable, par_first, par_last, list) \
for (byte par_nr_n = par_first; par_nr_n < par_last; par_nr_n++) { \
ParEnum_(p_##variable + par_nr_n, addNumToC40(C(name), par_nr_n), variable + par_nr_n, list); \
}
#define ParEnumArrayCmd(name, variable, par_first, par_last, list, cmd) \
for (byte par_nr_n = par_first; par_nr_n < par_last; par_nr_n++) { \
if (ParEnum_(p_##variable + par_nr_n, addNumToC40(C(name), par_nr_n), variable + par_nr_n, list) == par_command) { \
cmd(par_nr_n); \
}; \
}
#endif
enum {par_serial_unchanged, par_display_unchanged, par_disabled};
enum {par_serial_unchanged, par_display_unchanged, par_hidden};
// byte is not defined here, use uint8_t
@@ -47,5 +66,6 @@ typedef struct {
} ParAction;
#define ParSet(variable, value) ParSet_(p_##variable, (void *)&variable, sizeof variable, value)
#define ParNrSet(nr, variable, value) ParSet_(nr, (void *)&(variable), sizeof variable, value)
#define ParArraySet(variable, index, value) ParSet_(p_##variable + index, (void *)(variable + index), sizeof (*variable), value)
//#define ParNrSet(nr, variable, value) ParSet_(nr, (void *)&(variable), sizeof variable, value)
+40 -13
View File
@@ -37,10 +37,6 @@ void ParSetAllChangedDisp() {
ParClrAll(par_display_unchanged);
}
void ParSetAllEnabled() {
ParClrAll(par_disabled);
}
boolean ParChanged(byte nr) {
if (bitRead(flags[nr], par_display_unchanged)) return false;
bitSet(flags[nr], par_display_unchanged);
@@ -101,7 +97,7 @@ long ParGet(byte nr) {
number[0] = (nr / 100) % 10 + '0';
number[1] = (nr / 10) % 10 + '0';
number[2] = nr % 10 + '0';
number[4] = 0;
number[3] = 0;
DispError(number, " undefined");
}
return act.value;
@@ -119,6 +115,17 @@ boolean ParCommand(name_t name) {
return act.action == par_done;
}
void ParHide(name_t name) {
ParAction *oldact = action;
ParAction act;
action = &act;
act.action = par_hide;
act.name = name;
Pars();
action = oldact;
}
char *ParFmtByName(name_t name) {
ParAction *oldact = action;
ParAction act;
@@ -177,20 +184,26 @@ byte ParFixed_(byte nr, name_t name, long *ptr, byte dig) {
byte result = action->action;
byte len;
if (bitRead(flags[nr], par_disabled)) return par_none;
FastHandler();
switch (action->action) {
case par_done: return par_none;
case par_disable: bitSet(flags[nr], par_disabled); return par_none;
case par_hide:
if (name != action->name) return par_none;
//Serial.print("hide "); Serial.println(c40(name));
bitSet(flags[nr], par_hidden);
action->action = par_done;
break;
case par_scan:
if (bitRead(flags[nr], par_hidden)) return par_none;
fix2str(*ptr, dig, ctx.scanform, sizeof ctx.scanform);
if (ctx.scan(nr, name, ctx.scanform, 0, bitRead(flags[nr], par_serial_unchanged))) {
bitSet(flags[nr], par_serial_unchanged);
}
return par_scan;
break;
case par_command:
if (name != action->name) return par_none;
if (ctx.readonly) {
action->action = par_none;
return par_readonly;
}
value = ParserFixed(dig, &len);
@@ -213,6 +226,8 @@ byte ParFixed_(byte nr, name_t name, long *ptr, byte dig) {
break;
case par_fmt:
if (name != action->name) return par_none;
//Serial.print("show "); Serial.println(c40(name));
bitClear(flags[nr], par_hidden);
value = *ptr;
fix2str(value, dig, ctx.formatted, sizeof ctx.formatted);
action->action = par_done;
@@ -238,22 +253,27 @@ byte ParEnum_(byte nr, name_t name, byte *ptr, flash_str list) {
long value;
byte len;
if (bitRead(flags[nr], par_disabled)) return par_none;
FastHandler();
switch (action->action) {
case par_done: return par_none;
case par_disable: bitSet(flags[nr], par_disabled); return par_none;
case par_hide:
if (name != action->name) return par_none;
bitSet(flags[nr], par_hidden);
action->action = par_done;
break;
case par_scan:
if (bitRead(flags[nr], par_hidden)) return par_none;
enum2str(*ptr, list, ctx.scantext, sizeof ctx.scantext);
fix2str(*ptr, 0, ctx.scanform, sizeof ctx.scanform);
if (ctx.scan(nr, name, ctx.scanform, ctx.scantext, bitRead(flags[nr], par_serial_unchanged))) {
bitSet(flags[nr], par_serial_unchanged);
//if(PARDEBUG) { Serial.print('('); Serial.print(c40(name)); Serial.print(":"); Serial.print(nr); Serial.print(')'); };
}
return true;
break;
case par_command:
if (name != action->name) return par_none;
if (ctx.readonly) {
action->action = par_none;
return par_readonly;
}
value = ParserFixed(0, &len);
@@ -273,6 +293,7 @@ byte ParEnum_(byte nr, name_t name, byte *ptr, flash_str list) {
break;
case par_fmt:
if (name != action->name) return par_none;
bitClear(flags[nr], par_hidden);
fix2str(*ptr, 0, ctx.formatted, sizeof ctx.formatted);
action->action = par_done;
break;
@@ -293,12 +314,16 @@ byte ParText_(byte nr, name_t name, char *ptr, byte siz, boolean update) {
byte l;
char ch, *p;
if (bitRead(flags[nr], par_disabled)) return par_none;
FastHandler();
switch (action->action) {
case par_done: return par_none;
case par_disable: bitSet(flags[nr], par_disabled); return par_none;
case par_hide:
if (name != action->name) return par_none;
bitSet(flags[nr], par_hidden);
action->action = par_done;
break;
case par_scan:
if (bitRead(flags[nr], par_hidden)) return par_none;
if (update) {
textcopy(ctx.scanform, sizeof ctx.scanform, ptr);
for (p = ctx.scanform; *p != 0; p++) {
@@ -315,6 +340,7 @@ byte ParText_(byte nr, name_t name, char *ptr, byte siz, boolean update) {
case par_command:
if (name != action->name) return par_none;
if (ctx.readonly) {
action->action = par_none;
return par_readonly;
}
ParSetText_(nr, ptr, siz, ParserText());
@@ -328,6 +354,7 @@ byte ParText_(byte nr, name_t name, char *ptr, byte siz, boolean update) {
break;
case par_fmt:
if (name != action->name) return par_none;
bitClear(flags[nr], par_hidden);
textcopy(ctx.formatted, sizeof(ctx.formatted), ptr);
for (l = 0; l < sizeof(ctx.formatted); l++) {
if (ctx.formatted[l] == 0) break;
+4 -3
View File
@@ -1,4 +1,4 @@
enum {parser_idle, parser_val, parser_id, parser_sym, parser_txt};
enum {parser_idle, parser_val, parser_id, parser_sym, parser_spec};
enum {parser_num_pos, parser_num_neg, parser_num_error};
@@ -53,9 +53,10 @@ ParserType Parser() {
*/
ch = SerialC.read();
} else {
if (parser.ident >= 64000) {
if (parser.ident >= 64000*64000) {
result = parser_error;
} else {
// Serial.print("parsed "); Serial.print(parser.ident); Serial.println(c40(parser.ident));
result = parser_ident;
}
if (ch == '=') {
@@ -97,7 +98,7 @@ long ParserFixed(byte dig, byte *len) {
return text2fix(parser.value, dig, len);
}
word ParserIdent() {
ulong ParserIdent() {
return parser.ident;
}
+46 -3
View File
@@ -79,6 +79,21 @@ byte textappend(char *dst, byte maxlen, const char *src) {
return len + textcopyuntil(dst + len, maxlen - len, src, 0);
}
byte textequal(char *a, char *b) {
if (a == b) return 1;
if (a == 0) {
if (*b == 0) return 1;
return 0;
}
if (b == 0) {
if (*a == 0) return 1;
return 0;
}
for (; *a == *b; a++, b++) {
if (*a == 0) return 1;
}
return 0;
}
size_t array_out_of_bounds(const char *func, const char *arrayname, size_t index) {
Serial.println();
@@ -193,17 +208,45 @@ void eeSetAdr(word adr) {
void eeStore(const void *data, word length, byte mode) {
byte *p;
for (p = (byte *)data; length > 0; length--, p++, eeAdr++) {
if (mode == ee_write) {
if (mode == ee_write) {
for (p = (byte *)data; length > 0; length--, p++, eeAdr++) {
//Serial.print(eeAdr); Serial.print(":"); if(*p > ' ' && *p <= 'z') Serial.print((char)*p); Serial.println(*p);
EEPROM.write(eeAdr, *p);
} else {
}
} else {
for (p = (byte *)data; length > 0; length--, p++, eeAdr++) {
*p = EEPROM.read(eeAdr);
//Serial.print(eeAdr); Serial.print(":"); if(*p > ' ' && *p <= 'z') Serial.print((char)*p); Serial.println(*p);
}
}
}
void eeStoreText(char *p, word length, byte mode) {
char ch;
char *fini = p + length;
if (mode == ee_write) {
while (p < fini) {
ch = *p;
EEPROM.write(eeAdr, ch);
eeAdr++;
if (ch == 0) break;
p++;
}
} else {
while (p < fini) {
ch = EEPROM.read(eeAdr);
eeAdr++;
if (ch == -1) { // empty EEPROM location
ch = 0;
}
*p = ch;
if (ch == 0) break;
p++;
//Serial.print(eeAdr); Serial.print(":"); if(*p > ' ' && *p <= 'z') Serial.print((char)*p); Serial.println(*p);
}
}
}
long text2fix(const char *str, byte dig, byte *len) {
boolean neg;
long res = 0;
+18 -23
View File
@@ -11,21 +11,20 @@ void VuInit() {
pin = VuPin(v);
pinMode(pin, INPUT_PULLUP); // sense/boost pin
pinMode(pin + 1, INPUT); // hold pin
ParNrSet(p_vu0 + v, vu[v], no_valve);
ParArraySet(vu, v, no_valve);
}
}
void VuParCmd(byte v) {
switch (vuCmd[v]) {
case valve_on: VuSet(v, valve_boost); break;
case valve_off: VuSet(v, valve_off); break;
}
}
void VuPars() {
byte v;
for (v = 1; v <= 12; v++) {
ParNrEnum(p_vu0 + v, addNumToC40(C("v"), v), vu[v], valve_text);
if (ParNrEnum(p_vuCmd0 + v, addNumToC40(C("vc"), v), vuCmd[v], valve_text) == par_command) {
switch (vuCmd[v]) {
case valve_on: VuSet(v, valve_boost); break;
case valve_off: VuSet(v, valve_off); break;
}
}
}
ParEnumArray("v", vu, 1, 12, valve_text);
ParEnumArrayCmd("vc", vuCmd, 1, 12, valve_text, VuParCmd);
}
void VuSet(byte v, byte valveState) {
@@ -54,7 +53,8 @@ void VuSet(byte v, byte valveState) {
default:
if (VUDEBUG) { Serial.print(v); Serial.println(" ELSE"); };
}
ParNrSet(p_vu0 + v, vu[v], valveState);
ParArraySet(vu, v, valveState);
//ParNrSet(p_vu0 + v, vu[v], valveState);
}
byte VuPin(byte v) {
@@ -103,18 +103,12 @@ void VuDispShort() {
if (va == 0) {
va = v;
} else {
if (DispShowThis(p_vu0 + va) || DispShowThis(p_vu0 + v)) {
DispRewriteNext();
}
DispText2(VuText(res1, va), VuText(res2, v));
va = 0;
}
}
}
if (va != 0) {
if (DispShowThis(p_vu0 + va)) {
DispRewriteNext();
}
DispText2(VuText(res1, va), "");
}
}
@@ -131,13 +125,12 @@ void VuDisp() {
//VuDispShort();
for (v = 1; v <= 12; v++) {
if (vu[v] != no_valve) {
if (DispShowThis(p_vu0 + v)) {
DispRewriteNext();
}
DispButton(pos, m_ext * 16 + v, VuText(res1, v));
pos++;
}
}
DispButton(0, m_menu * 16 + m_commands, "menu");
DispButton(1, m_menu * 16 + m_home, "home");
}
void VuHandler() {
@@ -176,10 +169,12 @@ void VuHandler() {
void VuCmd(byte v) {
if (vu[v] == valve_off) {
ParNrSet(p_vuCmd0 + v, vuCmd[v], valve_on);
//ParNrSet(p_vuCmd0 + v, vuCmd[v], valve_on);
ParArraySet(vuCmd, v, valve_on);
VuSet(v, valve_boost);
} else if (vu[v] == valve_on || vu[v] == valve_boost) {
ParNrSet(p_vuCmd0 + v, vuCmd[v], valve_off);
//ParNrSet(p_vuCmd0 + v, vuCmd[v], valve_off);
ParArraySet(vuCmd, v, valve_off);
VuSet(v, valve_off);
}
}