Files
2023-09-11 18:04:36 +02:00

600 lines
17 KiB
Arduino

#define MOTDEBUG 0
#define MOTCHKDEBUG 0
//--- needle valve flow control
PAR_LONG flow = 0;
PAR_BYTE flowSensState = 0;
PAR_BYTE motorState = 0;
PAR_LONG motorPulse = 0;
PAR_LONG motorOpenTime = 60;
PAR_LONG motorCurrent = 50;
PAR_LONG motorFreeCurrent = 120;
//PAR_LONG motorOverrunConst = 25000; // obsolete
PAR_LONG motorTorqueConst = 1100;
PAR_LONG motorTorqueLimit = 200;
PAR_LONG motorTorque = 0;
//PAR_LONG motorOverrun = 0; // obsolete
PAR_LONG motorMeasuredPulse = 0;
PAR_LONG motorRead = 0;
PAR_LONG motorFlowTarget = 0;
PAR_LONG motorWiggle = 0;
PAR_LONG motorSpeed = 0;
#define motor_text F("idle|~opening|~closing|opened|closed|no motor")
enum {motor_idle, motor_opening, motor_closing, motor_opened, motor_closed, motor_unplugged};
PAR_BYTE motorAutoState = 0;
#define motor_auto_text F("fixed|contrl.|autom.|offline")
enum {motor_fixed, motor_ctrl, motor_auto, motor_offline};
// commands
enum {motor_stop=0, motor_open, motor_close, motor_setfixed, motor_setctrl, motor_setauto}; // commands
ulong phaseStart = 0;
long mvolt = 0;
int8_t motorDir = 0;
byte motorCheckCurrent = 0;
MotorState pstate = pulse_off;
long flowUnfiltered = 0;
long pulseLength = 0;
byte lastAuto = motor_auto;
long motorPulseSum = 0;
ulong motorSpeedCalcTime = 0;
void FlowInit() {
pinMode(io_motor_current, OUTPUT);
analogWrite(io_motor_current, 0);
pinMode(io_motor_open, OUTPUT);
pinMode(io_motor_close, OUTPUT);
// switch on current: input pullup high
digitalWrite(io_motor_enable, HIGH);
pinMode(io_motor_enable, INPUT);
}
void FlowPars() {
ParFixed("f", flow, 2);
ParEnum("fav", flowAvailable, F("no|yes"));
ParEnum("fs", flowSensState, F("ok|no sensor"));
ParEnum("fm", motorState, motor_text);
ParEnum("fa", motorAutoState, motor_auto_text);
ParEnum("mav", motorAvailable, F("no|yes"));
if (ParFixed("mp", motorPulse, 3) == par_command) {
MotorSync();
MotorStart();
}
ParFixed("mot", motorOpenTime, 0);
ParFixed("mc", motorCurrent, 0);
//ParFixed("mfc", motorFreeCurrent, 0); // obsolete
ParFixed("mtc", motorTorqueConst, 0);
ParFixed("mtl", motorTorqueLimit, 3);
ParFixed("mt", motorTorque, 3);
ParFixed("mft", motorFlowTarget, 2);
//ParFixed("moc", motorOverrunConst, 0); // obsolete
//ParFixed("mo", motorOverrun, 3); // obsolete
ParFixed("mcr", motorRead, 1);
ParFixed("mw", motorWiggle, 3);
ParFixed("mmp", motorMeasuredPulse, 3); // measured pulse length until target reached
ParFixed("msp", motorSpeed, 3);
}
boolean FlowDispShort(boolean big) {
if (flowAvailable) {
if (big) {
DispText("flow/mbar");
DispBigValue(p_flow, p_flowSensState); // flow value
} else {
DispMediumValue("flow", p_flow, p_flowSensState); // flow value
//DispValue("flow", p_flow, p_flowSensState);
}
FlowStateDisp();
} else {
DispState("m.v. ", p_motorState);
}
return motorState == motor_opening || motorState == motor_closing;
}
boolean MotorDispShort() {
DispTextRow(fmt_lsmall, "m.v. ", ParFmt(p_motorState));
}
void MotorButtons(byte top) {
byte codebase = m_nv * 16;
if (!flowAvailable && !motorAvailable) return;
if (flowAvailable) {
DispButton(top, m_menu, m_nv, "*n.v.");
} else {
DispButton(top, m_menu, m_nv, "*m.v.");
}
if (motorState != motor_unplugged) {
if (motorAutoState == motor_ctrl) {
DispButton(top - 2, m_nv, motor_setfixed, "-fixed");
DispButton(top - 4, m_nv, motor_setauto, "-auto");
} else if (motorAutoState == motor_auto) {
DispButton(top - 2, m_nv, motor_setfixed, "-fixed");
DispButton(top - 4, m_nv, motor_setctrl, "-contrl");
} else {
if (abs(motorPulse) > 300 && (motorState == motor_opening || motorState == motor_closing)) {
DispButton(top - 2, m_nv, motor_stop, "-stop");
} else {
DispButton(top - 2, m_nv, motor_open, "-open");
DispButton(top - 4, m_nv, motor_close, "-close");
}
}
}
}
void FlowDisp() {
byte codebase = m_nv * 16;
if (flowAvailable) {
DispText("flow/mbar");
DispBigValue(p_flow, p_flowSensState); // flow value
DispState("n.v. ", p_motorState);
DispState("mode: ", p_motorAutoState);
} else {
DispState("m.v. ", p_motorState);
}
if (motorState != motor_unplugged) {
DispButton(2, m_nv, motor_open, "open");
DispButton(3, m_nv, motor_close, "close");
if (motorAutoState == motor_auto || motorAutoState == motor_ctrl) {
DispButton(4, m_nv, motor_setfixed, "fixed");
} else if (abs(motorPulse) > 300 && (motorState == motor_opening || motorState == motor_closing)) {
DispButton(4, m_nv, motor_stop, "stop");
}
if (motorAutoState == motor_offline) {
// no button here
} else if (motorAutoState == motor_auto || (motorAutoState != motor_ctrl && lastAuto == motor_ctrl)) {
DispButton(5, m_nv, motor_setctrl, "contrl");
} else {
DispButton(5, m_nv, motor_setauto, "auto");
}
}
DispButton(0, m_menu, m_menu, "menu");
DispButton(1, m_menu, m_home, "home");
}
void FlowHandler() {
static ulong last;
long raw;
long rsum = 0;
long psum = 0;
int rcnt = 0;
int pcnt = 0;
int pmax = 0;
long dif;
long maxdif, md2;
static long sum=0;
static long cnt=0;
static const long fact = 10;
static ulong lastUpdate = 0;
if (!flowAvailable) return;
rsum = 0;
psum = 0;
rcnt = 0;
pcnt = 0;
pmax = 0;
while (pcnt + rcnt < 50) {
if (pcnt < pmax / 2) {
psum += aRead(a_flow_prec) * 19L; // 19 = 47.5 Ohm / 2.5 Ohm (2.5 Ohm = reference R)
pcnt++;
} else {
rsum += aRead(a_flow_raw) * 99L; // 99 = 247.5 Ohm / 2.5 Ohm
rcnt++;
}
// calculate the weight for the precise range meas. (0...100)
if (pcnt > rcnt) {
pmax = 1000 - psum / pcnt / 19;
} else {
pmax = 1000 - rsum / rcnt / 19;
}
if (pmax < 0) {
pmax = 0;
} else if (pmax > 100) {
pmax = 100;
}
}
if (rcnt == 0) {
pmax = 100;
}
if (pcnt == 0) {
pmax = 0;
flowUnfiltered = 0;
} else {
flowUnfiltered = psum * pmax / pcnt;
}
if (rcnt > 0) {
flowUnfiltered += rsum * (100 - pmax) / rcnt;
}
// flowUnfiltered is in 0.01 mbar
// 250 = 100 (weight) / 1235 mV * 1024 / 2.5 Ohm * 47.5 Ohm * 247.5 Ohm / 250 mbar * 16 mA * (0.01 mbar)
// 6150 = 4 mA / 16 mA * 250 mbar / (0.01 mbar) - 1.0 mbar (margin)
flowUnfiltered = flowUnfiltered / 250 - 6150;
sum += flowUnfiltered;
cnt++;
if (cnt > 1000 || expired(&lastUpdate, 250)) {
//if (cnt > 1000 || expired(&lastUpdate, 500)) {
ParSet(flow, sum / cnt);
if (flow < -5000) {
ParSet(flowSensState, 1);
} else if (flow > -4000) {
ParSet(flowSensState, 0);
}
sum = 0;
cnt = 0;
}
}
void MotorSetCurrent(int8_t sign, MotorCurrentType ct) {
long c = 0;
/*
long cf;
if (ct == zero_current) {
c = 0;
} else {
cf = motorFreeCurrent + motorFreeCurrent / 14;
if (ct == freeClose_current) {
c = cf;
} else {
c = motorCurrent + motorCurrent / 14;
if (c > cf * 2 / 3) {
c = cf * 2 / 3;
}
if (ct == freeOpen_current) {
c = (c + cf) / 2;
}
}
}
*/
if (ct != zero_current) {
if (motorCurrent > 100) {
ParSet(motorCurrent, 100); // this is the permanent limit of the switch MAX314
}
c = motorCurrent + motorCurrent / 14;
}
analogWrite(io_motor_current, c);
if (sign) {
// enable current
pinMode(io_motor_enable, INPUT);
digitalWrite(io_motor_enable, HIGH);
if (sign < 0) {
if (MOTDEBUG) {
Serial.print(c, DEC);
Serial.print(" motor-\n");
}
digitalWrite(io_motor_open, HIGH);
digitalWrite(io_motor_close, LOW);
motorDir = -1;
} else {
if (MOTDEBUG) {
Serial.print(c, DEC);
Serial.print(" motor+\n");
}
digitalWrite(io_motor_open, LOW);
digitalWrite(io_motor_close, HIGH);
motorDir = 1;
}
} else {
if (MOTDEBUG) {
Serial.print(c, DEC);
Serial.print(" motor0\n");
}
// disable current
digitalWrite(io_motor_enable, LOW);
pinMode(io_motor_enable, OUTPUT);
motorDir = 0;
}
}
void MotorSetState(MotorState p) {
if (MOTCHKDEBUG) {
Serial.print(pstate); Serial.print(" -> "); Serial.println(p);
}
pstate = p;
phaseStart = now;
}
void MotorStart() {
FastHandler();
if (motorPulse == 0) {
pulseLength = 0;
return;
}
pulseLength = abs(motorPulse);
if (pstate == pulse_running) {
motorPulseSum += (now - phaseStart) * motorDir;
if ((motorDir > 0) == (motorPulse > 0)) {
// do not interrupt a running pulse, but count new pulse from now
phaseStart = now;
return;
}
}
if (pstate != pulse_before) {
MotorSetState(pulse_before);
MotorSetCurrent(0, std_current); // set current output, but with disabled output (for stabilizing pwm filter)
}
}
void MotorFastHandler() {
if (pstate == pulse_running) {
if (time_ge(now, phaseStart + pulseLength)) {
motorPulseSum += pulseLength * motorDir;
if (MOTDEBUG) {
Serial.print(pulseLength);
Serial.print(" pulseEND\n");
}
//MotorSetCurrent(motorDir, zero_current); // stop motor
MotorSetCurrent(0, zero_current); // stop motor (0: short circuit)
MotorSetState(pulse_after);
ParSet(motorState, motor_idle);
}
}
}
void MotorSync() {
// synchronize motorSpeed sum interval
if (time_ge(now, motorSpeedCalcTime + 1500)) return;
motorSpeedCalcTime = now - 750;
}
void MotorHandler() {
long current, volt;
long drive;
byte i;
static byte off_cnt = 0;
long p;
if (!motorAvailable) return;
if (motorAutoState == motor_ctrl || motorAutoState == motor_auto) {
lastAuto = motorAutoState;
}
if (!online) {
if (motorAutoState != motor_offline) {
ParSet(motorAutoState, motor_offline);
}
}
current = aRead(a_motor_current) + aRead(a_motor_current);
volt = 0;
for (i = 0; i < 4; i++) {
volt += aRead(a_motor_volt);
}
current += aRead(a_motor_current) + aRead(a_motor_current);
if (volt > 3000 && current <= 4) {
if (motorState != motor_unplugged) {
if (off_cnt > 20) {
// switch off
ParSet(motorPulse, 0);
ParSet(motorState, motor_unplugged);
ParSet(motorAutoState, motor_fixed);
if (MOTCHKDEBUG) {
Serial.print("NO MOT\n");
}
} else {
off_cnt++;
}
}
} else if (off_cnt > 0) {
off_cnt--;
}
switch (pstate) {
case pulse_before:
if (motorFlowTarget > 0 && motorDir > 0 && flowUnfiltered > motorFlowTarget) {
ParSet(motorState, motor_idle);
ParSet(motorFlowTarget, 0);
//ParSet(motorOverrun, 0);
MotorSetState(pulse_off);
break;
}
if (time_ge(now, phaseStart + 200)) {
if (motorPulse > 0) {
MotorSetCurrent(1, std_current);
ParSet(motorState, motor_opening);
} else {
MotorSetCurrent(-1, std_current);
ParSet(motorState, motor_closing);
}
mvolt = 0;
MotorSetState(pulse_running);
ParSet(motorMeasuredPulse, 0); // reset measured open pulse
if (MOTDEBUG) {
Serial.println("CLOSING PULSE");
}
}
break;
case pulse_running:
if (motorFlowTarget > 0 && motorDir > 0 && flowUnfiltered > motorFlowTarget) {
ParSet(motorMeasuredPulse, motorDir * (now - phaseStart));
motorPulseSum += motorMeasuredPulse;
MotorSetCurrent(0, zero_current); // stop motor (0: short circuit)
ParSet(motorState, motor_idle);
// this feature has to be choosen before every pulse
ParSet(motorFlowTarget, 0);
MotorSetState(pulse_after);
break;
}
if (time_ge(now, phaseStart + 250)) { // check torque
if (mvolt == 0) {
ParSet(motorTorque, current * motorTorqueConst / volt);
} else { // averaged torque
ParSet(motorTorque, (motorTorque * 4 + current * motorTorqueConst / volt) / 5);
}
mvolt = volt / 4;
if (MOTDEBUG) {
Serial.print(now - phaseStart);
Serial.print("ms ");
Serial.print(volt * 19 / 3); // 1235 mV * (20 kOhm / 1kOhm + 1) / 4 / 1024
Serial.print("mV ");
Serial.print(current * 7 / 130); // 1235 mV / 5.6 Ohm / 4 times / 1024
Serial.print("mA ");
Serial.print(motorTorque);
Serial.println("tq");
}
if (motorEndSwitch && motorState != motor_closing) {
if (aRead(a_aux_current) > 10) {
ParSet(motorState, motor_opened);
}
}
if (motorTorque > motorTorqueLimit) {
if (motorDir > 0) {
if (MOTDEBUG) {
Serial.println("OPENED");
}
ParSet(motorState, motor_opened);
} else {
if (MOTDEBUG) {
Serial.println("CLOSED");
}
ParSet(motorState, motor_closed);
}
}
}
if (motorState != motor_opening && motorState != motor_closing) {
if (MOTDEBUG) {
Serial.print(motorState);
Serial.print(" dir");
Serial.print(motorDir);
Serial.println(" Stop");
}
MotorSetCurrent(motorDir, zero_current); // stop motor
motorPulseSum += motorDir * (now - phaseStart);
MotorSetState(pulse_after);
}
break;
case pulse_after:
if (motorWiggle != 0) {
MotorSetState(pulse_running);
pulseLength = abs(motorWiggle);
if (motorPulse > 0) {
MotorSetCurrent(-1, std_current);
ParSet(motorState, motor_closing);
ParSet(motorPulse, -pulseLength);
} else {
MotorSetCurrent(1, std_current);
ParSet(motorState, motor_opening);
ParSet(motorPulse, pulseLength);
}
ParSet(motorWiggle, 0);
break;
}
MotorSetCurrent(-1, zero_current);
MotorSetState(pulse_off);
mvolt = 0;
/*
if (time_ge(now, phaseStart + 1000)) { // stop anyway after 1000 msec
MotorSetState(pulse_off);
mvolt = 0;
} else if (volt < 100) {
if (mvolt > volt + 50) {
ParSet(motorOverrun, (motorOverrunConst - volt * motorOverrunConst / mvolt) / (now - phaseStart));
}
MotorSetState(pulse_off);
mvolt = 0;
}
*/
break;
case pulse_off:
if (time_ge(now, phaseStart + 3000)) {
MotorSetState(pulse_check0);
MotorSetCurrent(0, zero_current);
}
if (motorState == motor_opening || motorState == motor_closing) {
ParSet(motorState, motor_idle);
}
break;
case pulse_check0:
if (time_ge(now, phaseStart + 250)) {
MotorSetState(pulse_check);
MotorSetCurrent(1, zero_current);
motorCheckCurrent = 0;
}
break;
case pulse_check:
if (MOTCHKDEBUG) {
Serial.print("volt "); Serial.print(volt); Serial.print(" current "); Serial.print(current); Serial.print(" drive "); Serial.println(motorCheckCurrent);
}
if (volt > 500) { // voltage high - current low: no motor
if (current <= 4) {
if (motorState != motor_unplugged) {
ParSet(motorState, motor_unplugged);
ParSet(motorAutoState, motor_fixed);
ParSet(motorPulse, 0);
if (MOTCHKDEBUG) {
Serial.print("NO MOT\n");
}
}
analogWrite(io_motor_current, 0);
}
} else if (current > 4) {
if (MOTCHKDEBUG) {
Serial.print("MOT is there\n");
}
if (motorState == motor_unplugged) {
ParSet(motorState, motor_idle);
}
} else if (motorCheckCurrent < 20) {
motorCheckCurrent++;
analogWrite(io_motor_current, motorCheckCurrent);
break;
}
MotorSetCurrent(0, zero_current);
MotorSetState(pulse_off);
}
if (time_ge(now, motorSpeedCalcTime + 5000)) {
motorSpeedCalcTime = now;
if (pstate == pulse_running) {
p = motorDir * (now - phaseStart);
} else {
p = 0;
}
ParSet(motorSpeed, motorPulseSum + p);
motorPulseSum = - p;
}
}
void MotorCmd(byte cmd) {
switch (cmd) {
case motor_setfixed:
case motor_stop:
ParSet(motorAutoState, motor_fixed);
if (motorState == motor_opening || motorState == motor_closing) {
ParSet(motorState, motor_idle);
if (MOTDEBUG) {
Serial.print("stopped\n");
}
ParSet(motorPulse, 0);
}
break;
case motor_open:
if (motorState != motor_unplugged) {
ParSet(motorPulse, 1000 * motorOpenTime);
MotorStart();
}
break;
case motor_close:
if (motorState != motor_unplugged) {
ParSet(motorPulse, -1000 * motorOpenTime);
MotorStart();
}
break;
case motor_setctrl:
ParSet(motorAutoState, motor_ctrl);
break;
case motor_setauto:
ParSet(motorAutoState, motor_auto);
break;
}
}
void FlowStateDisp() {
const char *text;
if (motorState == motor_idle && motorAutoState != motor_offline) {
text = ParFmt(p_motorAutoState);
} else {
text = ParFmt(p_motorState);
}
DispTextRow(fmt_lsmall, "n.v. ", text);
}