ec probe calibration

This commit is contained in:
interfisch 2024-04-23 23:39:18 +02:00
parent 798ef496e1
commit f0678485be
8 changed files with 602 additions and 61 deletions

View File

@ -2,11 +2,13 @@
#define _EC_H_ #define _EC_H_
#include <Arduino.h> #include <Arduino.h>
/*
mqttValueTiming timing_ec_adc; mqttValueTiming timing_ec_adc;
mqttValueTiming timing_ec_calibadc; mqttValueTiming timing_ec_calibadc;
mqttValueTiming timing_ec_adcadjusted; mqttValueTiming timing_ec_adcadjusted;
mqttValueTiming timing_ec_ec; mqttValueTiming timing_ec_ec;
mqttValueTiming timing_ec_sc; mqttValueTiming timing_ec_sc;
*/
@ -17,6 +19,7 @@ bool ec_flag_measurement_available=false;
#define EC_PIN_RELAY_PROBE 27 #define EC_PIN_RELAY_PROBE 27
#define EC_PIN_RELAY_PROBESELECT 26
//#define EC_PIN_ADC 4 //#define EC_PIN_ADC 4
@ -26,7 +29,8 @@ bool ec_flag_measurement_available=false;
#define EC_RESOLUTION 8 #define EC_RESOLUTION 8
#define EC_FREQUENCY 5000 #define EC_FREQUENCY 5000
#define EC_CALIB_ARRAY_SIZE 128 //#define EC_CALIB_ARRAY_SIZE 128
#define EC_CALIB_ARRAY_SIZE 32 //temporarily changed
uint16_t ec_calib_array[EC_CALIB_ARRAY_SIZE]; uint16_t ec_calib_array[EC_CALIB_ARRAY_SIZE];
uint16_t ec_calib_array_pos=0; uint16_t ec_calib_array_pos=0;
#define EC_CALIB_READ_INTERVAL 250 //interval of reading adc value inside a measurement #define EC_CALIB_READ_INTERVAL 250 //interval of reading adc value inside a measurement
@ -48,13 +52,22 @@ unsigned long ec_last_change_relay=0; //millis of last relay change
enum ECState{IDLE,MEASURE}; enum ECState{IDLE,MEASURE};
uint8_t probeselect=0;
const uint8_t num_probes=2;
ECState ecstate=IDLE; ECState ecstate=IDLE;
float ec_adc;
float ec_adc_adjusted; //adjusted for reference resistor
float ec_calib_adc; float ec_calib_adc;
float ec; //ec value after adjustment for reference (at current temperature)
float ec25; //ec value but temperature adjusted for 25 degC float ec_adc_A;
float ec_adc_adjusted_A; //adjusted for reference resistor
float ec_A; //ec value after adjustment for reference (at current temperature)
float ec25_A; //ec value but temperature adjusted for 25 degC
float ec_adc_B;
float ec_adc_adjusted_B; //adjusted for reference resistor
float ec_B; //ec value after adjustment for reference (at current temperature)
float ec25_B; //ec value but temperature adjusted for 25 degC
float ec_tempadjust_alpa=0.02; float ec_tempadjust_alpa=0.02;
float ec_reference_adc=6016.88; //adc reference value for the calibration resistor measurement. float ec_reference_adc=6016.88; //adc reference value for the calibration resistor measurement.
@ -66,21 +79,27 @@ float ec_reference_adc=6016.88; //adc reference value for the calibration resist
//float ec_calibration_polynom[]={1033.928052655456,-3.8909104921922895,0.005627541436014758,-4.103988840997024e-06,1.7231981870816133e-09,-4.433707707721975e-13,7.203892111369395e-17,-7.406549810844244e-21,4.667420606439905e-25,-1.6439457516812463e-29,2.477292190335455e-34}; //20220505 //float ec_calibration_polynom[]={1033.928052655456,-3.8909104921922895,0.005627541436014758,-4.103988840997024e-06,1.7231981870816133e-09,-4.433707707721975e-13,7.203892111369395e-17,-7.406549810844244e-21,4.667420606439905e-25,-1.6439457516812463e-29,2.477292190335455e-34}; //20220505
//float ec_calibration_polynom[]={-323.68589929771457,0.5836096440900665,-0.000279737392438965,5.98673062873e-08,-5.4460235093798435e-12,1.8535134644431135e-16}; //20230509 //float ec_calibration_polynom[]={-323.68589929771457,0.5836096440900665,-0.000279737392438965,5.98673062873e-08,-5.4460235093798435e-12,1.8535134644431135e-16}; //20230509
//float ec_calibration_polynom[]={212.6826331524675,-0.6043878865263305,0.000571551634082491,-1.827897106718841e-07,2.682337041246909e-11,-1.8368511021965982e-15,4.8269168538877025e-20}; //20230509 manuell //float ec_calibration_polynom[]={212.6826331524675,-0.6043878865263305,0.000571551634082491,-1.827897106718841e-07,2.682337041246909e-11,-1.8368511021965982e-15,4.8269168538877025e-20}; //20230509 manuell
float ec_calibration_polynom[]={-202.42892340068445,0.3775267988688599,-0.0001603031198536654,3.398964768344757e-08,-3.0277753798278316e-12,1.0137804308289433e-16}; //20230620, graphite electrodes float ec_calibration_polynom_A[]={18.953002079376887,-0.057529180570340156,7.44612274805891e-05,-8.847448650204697e-09,5.388914121653647e-13}; //20240423, graphite electrodes
float ec_calibration_linearize_below_adc=4000; //use linear approximation below this adc value. 0=disable float ec_calibration_linearize_below_adc_A=2000; //use linear approximation below this adc value. 0=disable
float ec_calibration_linear_lowADC=746; //x0 float ec_calibration_linear_lowADC_A=728; //x0
float ec_calibration_linear_lowEC=0; //y0 float ec_calibration_linear_lowEC_A=0; //y0
float ec_calibration_polynom_B[]={40.838922264426685,-0.08846039894456156,7.671298605466989e-05,-9.161423581523883e-09,5.529701600660142e-13}; //20240423, graphite electrodes
float ec_calibration_linearize_below_adc_B=2000; //use linear approximation below this adc value. 0=disable
float ec_calibration_linear_lowADC_B=728; //x0
float ec_calibration_linear_lowEC_B=0; //y0
bool ec_measurementReady(); bool ec_measurementReady();
void ec_startMeasurement(); void ec_startMeasurement();
void ec_setRange(uint8_t range); void ec_setRange(uint8_t range);
void ec_connectProbe(bool); void ec_connectProbe(bool, uint8_t);
void ec_releaseRelay(); void ec_releaseRelay();
float ec_getECfromADC(float adc); float ec_getECfromADC(float adc, float ec_calibration_polynom[], size_t len_ec_calibration_polynom, float ec_calibration_linearize_below_adc, float ec_calibration_linear_lowADC, float ec_calibration_linear_lowEC);
float ec_calculateEC25(float pEC,float pTemp); float ec_calculateEC25(float pEC,float pTemp);
void ec_setup() { void ec_setup() {
/*
timing_ec_adc.minchange=0.0; timing_ec_adc.minchange=0.0;
timing_ec_adc.maxchange=250; timing_ec_adc.maxchange=250;
timing_ec_adc.mintime=10*000; timing_ec_adc.mintime=10*000;
@ -105,13 +124,14 @@ void ec_setup() {
timing_ec_sc.maxchange=50; timing_ec_sc.maxchange=50;
timing_ec_sc.mintime=10*000; timing_ec_sc.mintime=10*000;
timing_ec_sc.maxtime=60*60*1000; timing_ec_sc.maxtime=60*60*1000;
*/
ledcSetup(EC_PWM_CH, EC_FREQUENCY, EC_RESOLUTION); ledcSetup(EC_PWM_CH, EC_FREQUENCY, EC_RESOLUTION);
ledcAttachPin(EC_PIN_FREQ, EC_PWM_CH); ledcAttachPin(EC_PIN_FREQ, EC_PWM_CH);
ledcWrite(EC_PWM_CH, 127); //50% duty cycle ledcWrite(EC_PWM_CH, 127); //50% duty cycle
pinMode(EC_PIN_RELAY_PROBE,OUTPUT); //LOW=Calibration/idle, HIGH=Probe connected pinMode(EC_PIN_RELAY_PROBE,OUTPUT); //LOW=Calibration/idle, HIGH=Probe connected
pinMode(EC_PIN_RELAY_PROBESELECT,OUTPUT); //LOW=Probe A, HIGH=Probe B
ec_releaseRelay(); ec_releaseRelay();
} }
@ -125,10 +145,12 @@ void ec_loop(unsigned long loopmillis) {
case IDLE: case IDLE:
if (loopmillis>last_measurement_ec+EC_MEASUREMENT_INTERVAL || force_ec_measurement) { //start measurement if idle if (loopmillis>last_measurement_ec+EC_MEASUREMENT_INTERVAL || force_ec_measurement) { //start measurement if idle
//Serial.println("DEBUG: Start measurement");
last_measurement_ec=loopmillis; last_measurement_ec=loopmillis;
force_ec_measurement=false; force_ec_measurement=false;
ec_startMeasurement(); ec_startMeasurement();
ec_connectProbe(true); ec_connectProbe(true,0); //Probe A
ecstate=MEASURE; ecstate=MEASURE;
} }
@ -136,23 +158,58 @@ void ec_loop(unsigned long loopmillis) {
case MEASURE: case MEASURE:
if (ec_measurementReady()) { if (ec_measurementReady()) {
//Serial.println("DEBUG: Measurement Ready");
float ec_adc;
float ec_adc_adjusted;
float ec;
float ec25;
ec_releaseRelay(); ec_releaseRelay();
ec_adc=getMean(ec_array,EC_ARRAY_SIZE); ec_adc=getMean(ec_array,EC_ARRAY_SIZE);
if (isValueArrayOK(ec_calib_array,EC_CALIB_ARRAY_SIZE,EC_ADC_UNAVAILABLE)){ if (isValueArrayOK(ec_calib_array,EC_CALIB_ARRAY_SIZE,EC_ADC_UNAVAILABLE)){
ec_calib_adc=getMean(ec_calib_array,EC_CALIB_ARRAY_SIZE); ec_calib_adc=getMean(ec_calib_array,EC_CALIB_ARRAY_SIZE);
ec_adc_adjusted=mapf(ec_adc,0,ec_calib_adc,0,ec_reference_adc); ec_adc_adjusted=mapf(ec_adc,0,ec_calib_adc,0,ec_reference_adc);
ec=ec_getECfromADC(ec_adc_adjusted); if (probeselect==0) {
ec=ec_getECfromADC(ec_adc_adjusted, ec_calibration_polynom_A, sizeof(ec_calibration_polynom_A), ec_calibration_linearize_below_adc_A, ec_calibration_linear_lowADC_A, ec_calibration_linear_lowEC_A);
}else if (probeselect==1) {
ec=ec_getECfromADC(ec_adc_adjusted, ec_calibration_polynom_B, sizeof(ec_calibration_polynom_B), ec_calibration_linearize_below_adc_B, ec_calibration_linear_lowADC_B, ec_calibration_linear_lowEC_B);
}
ec25=ec_calculateEC25(ec,tempC_reservoir); ec25=ec_calculateEC25(ec,tempC_reservoir);
//Serial.println("DEBUG: EC OK");
}else{ }else{
ec_calib_adc=EC_ADC_UNAVAILABLE; ec_calib_adc=EC_ADC_UNAVAILABLE;
ec_adc_adjusted=EC_ADC_UNAVAILABLE; ec_adc_adjusted=EC_ADC_UNAVAILABLE;
ec=EC_UNAVAILABLE; ec=EC_UNAVAILABLE;
ec25=EC_UNAVAILABLE; ec25=EC_UNAVAILABLE;
//Serial.println("DEBUG: EC unavailable");
} }
ec_flag_measurement_available=true; if (probeselect==0) {
//Serial.println("DEBUG: Assigning to A");
ec_adc_A=ec_adc;
ec_adc_adjusted_A=ec_adc_adjusted;
ec_A=ec;
ec25_A=ec25;
last_measurement_ec=loopmillis;
force_ec_measurement=false;
ec_startMeasurement();
probeselect=1; //Select Probe B
ec_connectProbe(true,probeselect); //Probe B
ecstate=MEASURE;
}else if(probeselect==1) {
//Serial.println("DEBUG: Assigning to B");
ec_adc_B=ec_adc;
ec_adc_adjusted_B=ec_adc_adjusted;
ec_B=ec;
ec25_B=ec25;
probeselect=0; //Reset to Probe A
ec_flag_measurement_available=true;
ecstate=IDLE;
}
ecstate=IDLE;
} }
break; break;
@ -209,21 +266,36 @@ bool ec_measurementReady(){
} }
void ec_connectProbe(bool relay) { void ec_connectProbe(bool relay, uint8_t probeselect) {
bool val=digitalRead(EC_PIN_RELAY_PROBE); bool val=digitalRead(EC_PIN_RELAY_PROBE);
bool valsel=digitalRead(EC_PIN_RELAY_PROBESELECT);
if (val!=relay) { //write only if different if (val!=relay) { //write only if different
digitalWrite(EC_PIN_RELAY_PROBE,relay); digitalWrite(EC_PIN_RELAY_PROBE,relay);
//Serial.print("DEBUG: Set Relay to "); Serial.println(relay);
ec_last_change_relay=millis();
}
if (valsel!=probeselect) { //write only if different
if (probeselect==0) {
digitalWrite(EC_PIN_RELAY_PROBESELECT,false);
//Serial.println("DEBUG: Selected Probe A");
}else if (probeselect==1) {
digitalWrite(EC_PIN_RELAY_PROBESELECT,true);
//Serial.println("DEBUG: Selected Probe B");
}
ec_last_change_relay=millis(); ec_last_change_relay=millis();
} }
} }
void ec_releaseRelay() { void ec_releaseRelay() {
digitalWrite(EC_PIN_RELAY_PROBE,LOW); digitalWrite(EC_PIN_RELAY_PROBE,LOW);
digitalWrite(EC_PIN_RELAY_PROBESELECT,LOW);
//Serial.println("DEBUG: Released Relays");
ec_last_change_relay=millis(); ec_last_change_relay=millis();
} }
float ec_getECfromADC(float adc) { float ec_getECfromADC(float adc, float ec_calibration_polynom[], size_t len_ec_calibration_polynom, float ec_calibration_linearize_below_adc, float ec_calibration_linear_lowADC, float ec_calibration_linear_lowEC) {
uint8_t polynom_order=sizeof(ec_calibration_polynom) / sizeof(ec_calibration_polynom[0]); //uint8_t polynom_order=sizeof(ec_calibration_polynom) / sizeof(ec_calibration_polynom[0]);
uint8_t polynom_order=len_ec_calibration_polynom / sizeof(ec_calibration_polynom[0]);
double _ec=0; double _ec=0;
if (adc>=ec_calibration_linearize_below_adc) { //adc is in range where polynomial approximation fits well if (adc>=ec_calibration_linearize_below_adc) { //adc is in range where polynomial approximation fits well
for (uint8_t i=0;i<polynom_order;i++) { for (uint8_t i=0;i<polynom_order;i++) {

View File

@ -0,0 +1,312 @@
#ifndef _EC_H_
#define _EC_H_
#include <Arduino.h>
#define EC_PIN_RELAY_PROBE 27
#define EC_PIN_RELAY_CALIBRATION 26
#define EC_PIN_RELAY_RANGE 25
#define EC_PIN_ADC 4
#define EC_PIN_FREQ 5
#define EC_PWM_CH 0
#define EC_RESOLUTION 8
#define EC_FREQUENCY 5000
#define EC_ARRAY_SIZE 128
uint16_t ec_array_rangeLow[EC_ARRAY_SIZE];
uint16_t ec_array_rangeHigh[EC_ARRAY_SIZE];
uint16_t ec_array_pos=EC_ARRAY_SIZE*2;
#define EC_MEASUREMENT_INTERVAL 10000 //complete filtered measurement every x ms
//One filtered measurement takes EC_READ_INTERVAL*EC_ARRAY_SIZE*2
#define EC_READ_INTERVAL 5 //interval of reading adc value inside a measurement
float ec_calib_rangeLow_Rlow=0; //adc value for low value resistor on low resistor value range
float ec_calib_rangeLow_Rhigh=0; //adc value for high value resistor on low resistor value range
float ec_calib_rangeHigh_Rlow=0; //adc value for low value resistor on high resistor value range
float ec_calib_rangeHigh_Rhigh=0; //adc value for high value resistor on high resistor value range
const float ec_calibresistor_low=990; //value of low value calibration resistor. Low is Relay NO
const float ec_calibresistor_high=9943; //value of high value calibration resistor. HIGH is Relay NC
unsigned long ec_last_calibration=0; //millis of last calibration
#define EC_CALIBRATION_VALID_TIME 120000 //time in ms a calibration is valid for
#define EC_RELAY_SWITCH_SETTLETIME 500 //time until voltage of ec circuit has settled
unsigned long ec_last_change_relay=0; //millis of last relay change
enum ECState{IDLE,CALIBRATELOW,CALIBRATEHIGH,MEASURE};
ECState ecstate=CALIBRATELOW;
bool ec_measurementReady();
void ec_startMeasurement();
void ec_setRange(bool);
void ec_connectProbe(bool);
void ec_setCalibration(bool calib);
void ec_releaseRelay();
void ec_startCalibration();
void ec_checkIfSettleTimeOK();
float ec_getResistance(float adc,float caliblow,float resistorlow,float calibhigh,float resistorhigh);
void ec_setup() {
pinMode(EC_PIN_ADC,INPUT);
ledcSetup(EC_PWM_CH, EC_FREQUENCY, EC_RESOLUTION);
ledcAttachPin(EC_PIN_FREQ, EC_PWM_CH);
ledcWrite(EC_PWM_CH, 127); //50% duty cycle
pinMode(EC_PIN_RELAY_PROBE,OUTPUT); //LOW=Calibration/idle, HIGH=Probe connected
pinMode(EC_PIN_RELAY_CALIBRATION,OUTPUT); //LOW=NC Calibration Resistor, HIGH=NO Calib. Res.
pinMode(EC_PIN_RELAY_RANGE,OUTPUT); //LOW=NC Range Resistor, HIGH=NO Range Resistor
ec_releaseRelay();
ec_startCalibration();
}
void ec_loop(unsigned long loopmillis) {
static unsigned long last_measurement_ec=0;
static unsigned long last_read_ec=0;
switch (ecstate) {
case IDLE:
if (loopmillis>ec_last_calibration+EC_CALIBRATION_VALID_TIME) { //calibration needed
ec_last_calibration=loopmillis;
ecstate=CALIBRATELOW;
ec_startCalibration();
}
if (loopmillis>last_measurement_ec+EC_MEASUREMENT_INTERVAL && ecstate==IDLE) { //start measurement if idle
last_measurement_ec=loopmillis;
ec_startMeasurement();
ec_connectProbe(true);
ecstate=MEASURE;
Serial.println("EC Take Measurement");
}
break;
case CALIBRATELOW:
if (ec_measurementReady()) {
//Serial.println("EC CALIBRATELOW measurement ready");
//save measurement
ec_calib_rangeLow_Rlow=getMean(ec_array_rangeLow,EC_ARRAY_SIZE);
ec_calib_rangeHigh_Rlow=getMean(ec_array_rangeHigh,EC_ARRAY_SIZE);
//ec_checkIfSettleTimeOK();
//Switch to High calibration
ecstate=CALIBRATEHIGH;
ec_setCalibration(HIGH);
ec_setRange(LOW);
ec_startMeasurement();
//Serial.println("EC Start calibration high");
}
break;
case CALIBRATEHIGH:
if (ec_measurementReady()) {
//Serial.println("EC CALIBRATEHIGH measurement ready");
//save measurement
ec_calib_rangeLow_Rhigh=getMean(ec_array_rangeLow,EC_ARRAY_SIZE);
ec_calib_rangeHigh_Rhigh=getMean(ec_array_rangeHigh,EC_ARRAY_SIZE);
//ec_checkIfSettleTimeOK();
//Serial.println("EC Release Relay");
ec_releaseRelay();
ecstate=IDLE;
/*
Serial.println("EC Calibration done");
Serial.print("ec_calib_rangeLow_Rlow="); Serial.println(ec_calib_rangeLow_Rlow);
Serial.print("ec_calib_rangeHigh_Rlow="); Serial.println(ec_calib_rangeHigh_Rlow);
Serial.print("ec_calib_rangeLow_Rhigh="); Serial.println(ec_calib_rangeLow_Rhigh);
Serial.print("ec_calib_rangeHigh_Rhigh="); Serial.println(ec_calib_rangeHigh_Rhigh);
*/
Serial.println("EC Calibration Result: ");
Serial.print(ec_calib_rangeLow_Rlow);
Serial.print(", "); Serial.print(ec_calib_rangeHigh_Rlow);
Serial.print(", "); Serial.print(ec_calib_rangeLow_Rhigh);
Serial.print(", "); Serial.println(ec_calib_rangeHigh_Rhigh);
}
break;
case MEASURE:
if (ec_measurementReady()) {
ec_releaseRelay();
float adc_rangelow=getMean(ec_array_rangeLow,EC_ARRAY_SIZE);
float adc_rangehigh=getMean(ec_array_rangeHigh,EC_ARRAY_SIZE);
Serial.println();
float resistance_rangelow=ec_getResistance(adc_rangelow,ec_calib_rangeLow_Rlow,ec_calibresistor_low,ec_calib_rangeLow_Rhigh,ec_calibresistor_high);
Serial.print("Range Low: ADC="); Serial.print(adc_rangelow); Serial.print(", resistance="); Serial.println(resistance_rangelow);
Serial.println();
float resistance_rangehigh=ec_getResistance(adc_rangehigh,ec_calib_rangeHigh_Rlow,ec_calibresistor_low,ec_calib_rangeHigh_Rhigh,ec_calibresistor_high);
Serial.print("Range High: ADC="); Serial.print(adc_rangehigh); Serial.print(", resistance="); Serial.println(resistance_rangehigh);
ecstate=IDLE;
}
break;
}
if (loopmillis>last_read_ec+EC_READ_INTERVAL && ec_array_pos/2<EC_ARRAY_SIZE) { //take reading into array if measurement running
last_read_ec=loopmillis;
//flag_print= ec_array_pos==EC_ARRAY_SIZE;
//ec_array_pos%=EC_ARRAY_SIZE;
if (ec_array_pos<EC_ARRAY_SIZE){ //low range
ec_setRange(LOW);
}else{ //high range
ec_setRange(HIGH);
}
if (loopmillis>ec_last_change_relay+EC_RELAY_SWITCH_SETTLETIME) { //values have settled
uint16_t value=analogRead(EC_PIN_ADC);
if (ec_array_pos<EC_ARRAY_SIZE){ //low range
ec_array_rangeLow[ec_array_pos%EC_ARRAY_SIZE]=value;
}else{ //high range
ec_array_rangeHigh[ec_array_pos%EC_ARRAY_SIZE]=value;
}
/*
if (ec_array_pos==0) {
Serial.println(""); Serial.print("Lowrange:");
}
if (ec_array_pos==EC_ARRAY_SIZE) {
Serial.println(""); Serial.print("Highrange:");
}
Serial.print(value); Serial.print(" ");
if (ec_array_pos==EC_ARRAY_SIZE*2-1) {
Serial.println("");
}
*/
ec_array_pos++;
}
}
}
void ec_startCalibration() {
//Switch to Low calibration
ec_setCalibration(LOW);
ec_setRange(LOW);
ec_startMeasurement();
Serial.println("EC Started Calibration");
}
void ec_startMeasurement() {
ec_array_pos=0;
}
bool ec_measurementReady(){
if (ec_array_pos>=EC_ARRAY_SIZE*2) { //reached end of both arrays
return true;
}else{
return false;
}
}
void ec_setRange(bool range) {
//range low means low resistor value -> NO -> relay High
bool val=digitalRead(EC_PIN_RELAY_RANGE);
if (val!=!range) { //write only if different
digitalWrite(EC_PIN_RELAY_RANGE,!range);
ec_last_change_relay=millis();
}
}
void ec_connectProbe(bool relay) {
bool val=digitalRead(EC_PIN_RELAY_PROBE);
if (val!=relay) { //write only if different
digitalWrite(EC_PIN_RELAY_PROBE,relay);
ec_last_change_relay=millis();
}
}
void ec_setCalibration(bool calib) {
//calib low means low resistor value -> NO -> relay high
ec_connectProbe(false);
bool val=digitalRead(EC_PIN_RELAY_CALIBRATION);
if (val!=!calib) { //write only if different
digitalWrite(EC_PIN_RELAY_CALIBRATION,!calib);
ec_last_change_relay=millis();
}
}
void ec_releaseRelay() {
digitalWrite(EC_PIN_RELAY_PROBE,LOW);
digitalWrite(EC_PIN_RELAY_CALIBRATION,LOW);
digitalWrite(EC_PIN_RELAY_RANGE,LOW);
ec_last_change_relay=millis();
}
void ec_checkIfSettleTimeOK() {
/*
Serial.print("ec_array_rangeLow[0]="); Serial.println(ec_array_rangeLow[0]);
Serial.print("rangeLow min="); Serial.println(getMin(ec_array_rangeLow,EC_ARRAY_SIZE));
Serial.print("rangeLow max="); Serial.println(getMax(ec_array_rangeLow,EC_ARRAY_SIZE));
*/
if (ec_array_rangeLow[0]<=getMin(ec_array_rangeLow,EC_ARRAY_SIZE) || ec_array_rangeLow[0]>=getMax(ec_array_rangeLow,EC_ARRAY_SIZE)){
//is first value the highest or lowest?
Serial.println("Warning: EC_RELAY_SWITCH_SETTLETIME might be too low! (ec_calib_rangeLow_Rlow)");
}
/*
Serial.print("ec_array_rangeHigh[0]="); Serial.println(ec_array_rangeHigh[0]);
Serial.print("rangeHigh min="); Serial.println(getMin(ec_array_rangeHigh,EC_ARRAY_SIZE));
Serial.print("rangeHigh max="); Serial.println(getMax(ec_array_rangeHigh,EC_ARRAY_SIZE));
*/
if (ec_array_rangeHigh[0]<=getMin(ec_array_rangeHigh,EC_ARRAY_SIZE) || ec_array_rangeHigh[0]>=getMax(ec_array_rangeHigh,EC_ARRAY_SIZE)){
//is first value the highest or lowest?
Serial.println("Warning: EC_RELAY_SWITCH_SETTLETIME might be too low! (ec_array_rangeHigh)");
}
}
float ec_getResistance(float adc,float caliblow,float resistorlow,float calibhigh,float resistorhigh)
{
//adc = adc reading to calculate resistance for
//caliblow = adc value from calibration. Low resistance
//resistorlow = actual resistor value. Low resistance
//calibhjgh = adc value from calibration. High resistance
//resistorhigh = actual resistor value. High resistance
//y=mx+a;
//resistorlow=m*caliblow+a;
//resistorhigh=m*calibhigh+a;
//linear interpolation interpolate
double m=(resistorhigh-resistorlow)/(calibhigh-caliblow);
float a=resistorlow-m*caliblow;
Serial.print("m="); Serial.println(m);
Serial.print("a="); Serial.println(a);
return m*adc+a;
}
#endif

View File

@ -140,6 +140,7 @@ void temperature_loop(unsigned long loopmillis) {
} }
} }
/* temporarily disabled
tempC_air = sensors.getTempC(thermometerAir); tempC_air = sensors.getTempC(thermometerAir);
if (tempC_air == DEVICE_DISCONNECTED_C) if (tempC_air == DEVICE_DISCONNECTED_C)
{ {
@ -152,6 +153,7 @@ void temperature_loop(unsigned long loopmillis) {
tempCmean_air=DEVICE_DISCONNECTED_C; tempCmean_air=DEVICE_DISCONNECTED_C;
} }
} }
*/
tempC_case = sensors.getTempC(thermometerCase); tempC_case = sensors.getTempC(thermometerCase);
if (tempC_case == DEVICE_DISCONNECTED_C) if (tempC_case == DEVICE_DISCONNECTED_C)

View File

@ -0,0 +1,99 @@
#ifndef _WATERLEVEL_H_
#define _WATERLEVEL_H_
#include <HCSR04.h>
#define HCSR04_PIN_ECHO 17
#define HCSR04_PIN_TRIGGER 16
#define HCSR04_TIMEOUT 5000 //default is 100000 (uS)
#define READINTERVAL_HCSR04 200
#define WATERLEVELMEAN_SIZE 32
#define WATERLEVELMEAN_FILTER_CUTOFF 8 //max value is around WATERLEVELMEAN_SIZE/2
float waterlevelMean_array[WATERLEVELMEAN_SIZE];
uint16_t waterlevelMean_array_pos=0;
#define WATERLEVEL_UNAVAILABLE -1
float waterlevel=WATERLEVEL_UNAVAILABLE; //distance from floor to water surface [mm]
float watervolume=WATERLEVEL_UNAVAILABLE; //calculated Volume in Reservoir
uint16_t waterlevel_failcounter=0;
#define WATERLEVEL_MAXFAILS 15 //maximum counter value
#define WATERLEVEL_FAILTHRESHOLD 10 //if failcounter is greater or equal this value waterlevel will not be valid
//Calibration
float waterlevel_calib_offset_measured=0; //Sollwert
float waterlevel_calib_offset_sensor=178.67; //Istwert
float waterlevel_calib_reservoirArea=27*36.5; //area in cm^2
float waterlevel_heightToVolume(float distance);
void waterlevel_setup() {
//HCSR04.begin(HCSR04_PIN_TRIGGER, HCSR04_PIN_ECHO);
HCSR04.begin(HCSR04_PIN_TRIGGER, HCSR04_PIN_ECHO,HCSR04_TIMEOUT, HCSR04.eUltraSonicUnlock_t::unlockSkip);
for (uint16_t i=0;i<WATERLEVELMEAN_SIZE;i++) {
waterlevelMean_array[i]=-1; //-1 is also timeout value
}
}
void waterlevel_loop(unsigned long loopmillis) {
static unsigned long last_read_hcsr04;
if (loopmillis>=last_read_hcsr04+READINTERVAL_HCSR04) {
last_read_hcsr04=loopmillis;
float temperature=20.0;
if (tempCmean_air!=DEVICE_DISCONNECTED_C) { //sensor ok
temperature=tempCmean_air;
}
double* distances = HCSR04.measureDistanceMm(temperature);
double distance=distances[0];
//Serial.print("Distance reading:"); Serial.println(distance);
if (distance!=WATERLEVEL_UNAVAILABLE) { //successful
waterlevelMean_array[waterlevelMean_array_pos]=distance;
waterlevelMean_array_pos++;
waterlevelMean_array_pos%=WATERLEVELMEAN_SIZE;
if (waterlevel_failcounter>0) { //reduce failcounter if sucessfull
waterlevel_failcounter--;
}
}else{
if (waterlevel_failcounter<WATERLEVEL_MAXFAILS) {
waterlevel_failcounter++;
}
}
if (isValueArrayOKf(waterlevelMean_array,WATERLEVELMEAN_SIZE,WATERLEVEL_UNAVAILABLE)){
//float _distance=getFilteredf(waterlevelMean_array,WATERLEVELMEAN_SIZE,WATERLEVELMEAN_FILTER_CUTOFF);
float _distance=getMaxf(waterlevelMean_array,WATERLEVELMEAN_SIZE);
//Invert distance and offset
waterlevel=distance-(waterlevel_calib_offset_sensor+waterlevel_calib_offset_measured);
watervolume=waterlevel_heightToVolume(_distance);
//float _meanWaterlevel=getMeanf(waterlevelMean,WATERLEVELMEAN_SIZE);
//Serial.print("\t Dist="); Serial.print(_filteredWaterlevel); Serial.print("mm"); Serial.print("(+- "); Serial.print((getMaxf(waterlevelMean,WATERLEVELMEAN_SIZE)-getMinf(waterlevelMean,WATERLEVELMEAN_SIZE))/2.0); Serial.print(")"); Serial.print(" [mean="); Serial.print(_meanWaterlevel); Serial.print("]");
}
if (waterlevel_failcounter>=WATERLEVEL_FAILTHRESHOLD) { //too many failed readings
waterlevel=WATERLEVEL_UNAVAILABLE;
watervolume=WATERLEVEL_UNAVAILABLE;
/*if (debug) {
Serial.print("Waterlevel Failcounter="); Serial.println(waterlevel_failcounter);
}*/
}
}
}
float waterlevel_heightToVolume(float distance){
return waterlevel_calib_reservoirArea/100 * distance/100; //area[cm^2] in dm^2 * height in dm = dm^3= L
}
#endif

View File

@ -1,6 +1,6 @@
const char ssid[] = ""; const char ssid[] = "fischnetziot";
const char pass[] = ""; const char pass[] = "LiMkJ1sVcEAU68MPEgJ";
const char mqtt_host[] = "10.0.0.1"; const char mqtt_host[] = "10.0.0.1";
const char client_id[] = "hydroponic"; const char client_id[] = "hydroponic";

View File

@ -1,3 +1,5 @@
#Use venv
import numpy as np import numpy as np
from scipy.optimize import curve_fit from scipy.optimize import curve_fit
@ -8,32 +10,36 @@ import matplotlib.pyplot as plt
from pandas import * from pandas import *
# reading CSV file # reading CSV file
data = read_csv("20230620_NaCl_raw.csv") data = read_csv("20240423_EC_Calibration.csv")
# converting column data to list # converting column data to list
#solutionAdded = data['solutionAdded'].tolist() #in ml solutionAdded = data['solutionAdded'].tolist() #in ml
tempReservoir = data['tempReservoir'].tolist() #in C tempReservoir = data['tempReservoir'].tolist() #in C
adc = data['ECadcAdjusted'].tolist() #adc reading adc = data['ECadcAdjusted_B'].tolist() #adc reading
solutionConcentration=5924.8 #mg/L NaCl #solutionConcentration=5924.8 #mg/L NaCl
startWaterAmount=0.3 #L solutionConcentration=5690 #mg/L NaCl
startWaterAmount=300 #mL (same unit as solutionAded)
ppmToECfactor=1/0.46 ppmToECfactor=1/0.46
#concentration = [x*solutionConcentration/(startWaterAmount+x) for x in solutionAdded] concentration = [x*solutionConcentration/(startWaterAmount+x) for x in solutionAdded]
#ECcalculated = [x*ppmToECfactor for x in concentration] #uS/cm ECcalculated = [x*ppmToECfactor for x in concentration] #uS/cm
ECmeasured = data['ecMeasured'].tolist() #in C #ECmeasured = data['ecMeasured'].tolist() #in C
#print(concentration) print("Concentration")
#print(ECcalculated) print(concentration)
print("")
print("ECcalculated")
print(ECcalculated)
x = adc x = adc
#y = ECcalculated y = ECcalculated
y = ECmeasured #y = ECmeasured

4
messung/requirements.txt Normal file
View File

@ -0,0 +1,4 @@
numpy==1.22
scipy
matplotlib
pandas

View File

@ -4,9 +4,13 @@
#include "wifi_functions.h" #include "wifi_functions.h"
bool debug=true; //print Serial information bool debug=false; //print Serial information
bool mqtt=true; bool mqtt=false;
bool eccalibrationoutput=false; //serial output for ec calibration bool eccalibrationoutput=true; //serial output for ec calibration
/* Write to file with:
sudo stty -F /dev/ttyUSB0 115200
cat /dev/ttyUSB0 | tee received.txt
*/
@ -75,8 +79,8 @@ void setup() {
Serial.println("Setup EC"); Serial.println("Setup EC");
ec_setup(); ec_setup();
Serial.println("Setup Waterlevel"); //Serial.println("Setup Waterlevel");
waterlevel_setup(); //waterlevel_setup(); //temporarily disabled
Serial.println("Setup Temperature"); Serial.println("Setup Temperature");
temperature_setup(); temperature_setup();
@ -116,7 +120,7 @@ void loop() {
temperature_loop(loopmillis); temperature_loop(loopmillis);
waterlevel_loop(loopmillis); //waterlevel_loop(loopmillis);
flow_loop(loopmillis); flow_loop(loopmillis);
@ -132,11 +136,23 @@ void loop() {
delay(100); delay(100);
} }
if (eccalibrationoutput && !digitalRead(PIN_BUTTON) && !getReading) { if (eccalibrationoutput && !digitalRead(PIN_BUTTON) && !getReading) {
getReading=true; if (!isValueArrayOK(ec_calib_array,EC_CALIB_ARRAY_SIZE,EC_ADC_UNAVAILABLE)) {
force_ec_measurement=true; for (uint8_t blink=0;blink<5;blink++) {
ec_flag_measurement_available=false; digitalWrite(PIN_LED,HIGH);
digitalWrite(PIN_LED,HIGH); delay(100);
digitalWrite(PIN_LED,LOW);
delay(100);
}
}else{
getReading=true;
force_ec_measurement=true;
ec_flag_measurement_available=false;
digitalWrite(PIN_LED,HIGH);
}
} }
@ -148,8 +164,10 @@ void loop() {
Serial.print(loopmillis); Serial.print(","); Serial.print(loopmillis); Serial.print(",");
Serial.print(tempCmean_reservoir); Serial.print(","); Serial.print(tempCmean_reservoir); Serial.print(",");
Serial.print(ec_calib_adc); Serial.print(","); Serial.print(ec_calib_adc); Serial.print(",");
Serial.print(ec_adc); Serial.print(","); Serial.print(ec_adc_A); Serial.print(",");
Serial.print(ec_adc_adjusted); Serial.print(ec_adc_adjusted_A); Serial.print(",");
Serial.print(ec_adc_B); Serial.print(",");
Serial.print(ec_adc_adjusted_B);
Serial.println(); Serial.println();
} }
@ -164,7 +182,7 @@ void loop() {
if (sm_mean1==SM_DISCONNECTED || sm_mean2==SM_DISCONNECTED) { if (sm_mean1==SM_DISCONNECTED || sm_mean2==SM_DISCONNECTED) {
valueError=true; valueError=true;
} }
if (ec==EC_UNAVAILABLE){ if (ec_A==EC_UNAVAILABLE || ec_B==EC_UNAVAILABLE){
valueError=true; valueError=true;
} }
@ -213,14 +231,25 @@ void loop() {
Serial.print("Flow = "); Serial.print(flow); Serial.print("Flow = "); Serial.print(flow);
Serial.println(); Serial.println();
Serial.print("EC ec_calib_adc,ec_adc,ec_adc_adjusted = "); Serial.print("EC ec_calib_adc,ec_adc_A,ec_adc_adjusted_A = ");
Serial.print(ec_calib_adc); Serial.print(","); Serial.print(ec_calib_adc); Serial.print(",");
Serial.print(ec_adc); Serial.print(","); Serial.print(ec_adc_A); Serial.print(",");
Serial.print(ec_adc_adjusted); Serial.print(ec_adc_adjusted_A);
Serial.println(); Serial.println();
Serial.print("EC ec,ec25 = "); Serial.print("EC ec_A,ec25_A = ");
Serial.print(ec); Serial.print(","); Serial.print(ec_A); Serial.print(",");
Serial.print(ec25); Serial.print(ec25_A);
Serial.println();
Serial.print("EC ec_calib_adc,ec_adc_B,ec_adc_adjusted_B = ");
Serial.print(ec_calib_adc); Serial.print(",");
Serial.print(ec_adc_B); Serial.print(",");
Serial.print(ec_adc_adjusted_B);
Serial.println();
Serial.print("EC ec_B,ec25_B = ");
Serial.print(ec_B); Serial.print(",");
Serial.print(ec25_B);
Serial.println(); Serial.println();
Serial.print("Waterlevel,Volume = "); Serial.print("Waterlevel,Volume = ");
@ -273,18 +302,35 @@ void loop() {
if (ec_flag_measurement_available){ if (ec_flag_measurement_available){
ec_flag_measurement_available=false; ec_flag_measurement_available=false;
if (ec_adc!=0) {
publishValue("ec/adc",ec_adc,0);
}
if (ec_calib_adc!=0) { if (ec_calib_adc!=0) {
publishValue("ec/eccalibadc",ec_calib_adc,0); publishValue("ec/eccalibadc",ec_calib_adc,0);
} }
if (ec_adc_adjusted!=0) {
publishValue("ec/adcadjusted",ec_adc_adjusted,0); //Probe A
if (ec_adc_A!=0) {
publishValue("ecA/adc",ec_adc_A,0);
} }
if (ec!=EC_UNAVAILABLE){
publishValue("ec/ec",ec,0); if (ec_adc_adjusted_A!=0) {
publishValue("ec/sc",ec25,0); publishValue("ecA/adcadjusted",ec_adc_adjusted_A,0);
}
if (ec_A!=EC_UNAVAILABLE){
publishValue("ecA/ec",ec_A,0);
publishValue("ecA/sc",ec25_A,0);
}
//Probe B
if (ec_adc_B!=0) {
publishValue("ecB/adc",ec_adc_B,0);
}
if (ec_adc_adjusted_B!=0) {
publishValue("ecB/adcadjusted",ec_adc_adjusted_B,0);
}
if (ec_B!=EC_UNAVAILABLE){
publishValue("ecB/ec",ec_B,0);
publishValue("ecB/sc",ec25_B,0);
} }
} }