624 lines
No EOL
16 KiB
C++
624 lines
No EOL
16 KiB
C++
#include <Arduino.h>
|
|
#include <Wire.h>
|
|
|
|
|
|
// ESP32-S3 Devkit M-1 Left USB-C: Program, Right USB-C Serial
|
|
|
|
bool valueError=false;
|
|
|
|
unsigned long last_check=0;
|
|
|
|
|
|
#include "wifi_functions.h"
|
|
|
|
bool debug=false; //print Serial information
|
|
bool mqtt=true;
|
|
bool eccalibrationoutput=false; //serial output for ec calibration
|
|
/* Write to file with:
|
|
sudo stty -F /dev/ttyUSB0 115200
|
|
cat /dev/ttyUSB0 | tee received.txt
|
|
|
|
falls nicht geht, vorher einmal kurz per screen verbinden
|
|
*/
|
|
|
|
bool valuesStabilized=false; //gets set true when values are stable (avaeraging arrays filled)
|
|
|
|
|
|
|
|
#include "helpfunctions.h"
|
|
#ifdef EC_CALIBRATION_POLYNOM
|
|
#include "ADS1X15.h"
|
|
#endif
|
|
|
|
|
|
|
|
// ######## Temperature
|
|
#ifdef ONE_WIRE_BUS_PIN
|
|
#include "temperature.h"
|
|
#endif
|
|
|
|
// ######## Water Level
|
|
#ifdef RES_AREA
|
|
#include "waterlevel.h"
|
|
#endif
|
|
|
|
// ######## EC
|
|
#ifdef EC_CALIBRATION_POLYNOM
|
|
ADS1115 ADS(0x48);
|
|
bool adsenabled=true;
|
|
#include "ec.h"
|
|
#endif
|
|
|
|
|
|
// PUMP
|
|
#if defined(VALVECOUNT) || defined(RELAISCOUNT)
|
|
#include "pump.h"
|
|
#endif
|
|
|
|
|
|
// ######## Flow Rate
|
|
#ifdef FLOW_PIN
|
|
#include "flow.h"
|
|
#endif
|
|
|
|
// ######## Soilmoisture
|
|
//#include "soilmoisture.h"
|
|
|
|
|
|
#ifdef PIN_NEOPIXEL
|
|
#include <Adafruit_NeoPixel.h>
|
|
Adafruit_NeoPixel pixels(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
|
|
#endif
|
|
|
|
|
|
|
|
void setup() {
|
|
#ifdef PIN_BUTTON
|
|
pinMode(PIN_BUTTON,INPUT_PULLUP);
|
|
#endif
|
|
#ifdef PIN_LED
|
|
pinMode(PIN_LED,OUTPUT);
|
|
digitalWrite(PIN_LED,LOW);
|
|
#endif
|
|
|
|
|
|
#if defined(VALVECOUNT) || defined(RELAISCOUNT)
|
|
pump_setup();
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(NEOPIXEL_POWER)
|
|
// If this board has a power control pin, we must set it to output and high
|
|
// in order to enable the NeoPixels. We put this in an #if defined so it can
|
|
// be reused for other boards without compilation errors
|
|
pinMode(NEOPIXEL_POWER, OUTPUT);
|
|
digitalWrite(NEOPIXEL_POWER, HIGH);
|
|
#endif
|
|
#ifdef PIN_NEOPIXEL
|
|
pixels.begin();
|
|
pixels.setBrightness(100); // not so bright
|
|
pixels.clear();
|
|
|
|
//Flash colors for debug
|
|
pixels.setPixelColor(0, pixels.Color(255, 0,0));
|
|
pixels.show();
|
|
delay(250);
|
|
pixels.setPixelColor(0, pixels.Color(0, 255,0));
|
|
pixels.show();
|
|
delay(250);
|
|
pixels.setPixelColor(0, pixels.Color(0, 0,255));
|
|
pixels.show();
|
|
delay(250);
|
|
pixels.clear();
|
|
pixels.show();
|
|
|
|
#endif
|
|
|
|
|
|
Serial.begin(115200);
|
|
if (mqtt) {
|
|
WiFi.begin(ssid, pass);
|
|
client.begin(mqtt_host, net);
|
|
client.onMessage(messageReceived);
|
|
connect();
|
|
}
|
|
|
|
#if defined(VALVECOUNT) || defined(RELAISCOUNT)
|
|
pump_setup();
|
|
srOutputOff();
|
|
#endif
|
|
|
|
|
|
#ifdef PIN_SDA
|
|
Wire.begin(PIN_SDA,PIN_SCL);
|
|
Serial.print("I2C Clock Speed=");
|
|
Serial.println(Wire.getClock());
|
|
i2cscan();
|
|
#endif
|
|
|
|
#ifdef RES_AREA
|
|
Serial.println("Setup Waterlevel");
|
|
waterlevel_setup();
|
|
#endif
|
|
|
|
|
|
//init ADS1115
|
|
#ifdef EC_CALIBRATION_POLYNOM
|
|
if (!ADS.begin()) {
|
|
Serial.println("Error:"); delay(2000); Serial.println("ADS1115 Init Error!");
|
|
if (mqtt){
|
|
publishInfo("error/general","ADS1115 Init Error");
|
|
}
|
|
adsenabled=false;
|
|
}
|
|
if (adsenabled){
|
|
ADS.setGain(0);
|
|
}
|
|
|
|
Serial.println("Setup EC");
|
|
ec_setup();
|
|
#endif
|
|
|
|
|
|
#ifdef ONE_WIRE_BUS_PIN
|
|
Serial.println("Setup Temperature");
|
|
temperature_setup();
|
|
#endif
|
|
|
|
#ifdef FLOW_PIN
|
|
Serial.println("Setup Flow");
|
|
flow_setup();
|
|
#endif
|
|
|
|
|
|
/*
|
|
Serial.println("Setup Soilmoisture");
|
|
sm_setup();
|
|
*/
|
|
|
|
Serial.println("Finished Setup");
|
|
delay(200);
|
|
|
|
#ifdef EC_CALIBRATION_POLYNOM
|
|
//Test adc to ec function output
|
|
if (eccalibrationoutput) {
|
|
Serial.println();
|
|
Serial.print("adc"); Serial.print(","); Serial.print("ec"); Serial.println();
|
|
for (int i=728;i<14000;i+=100) {
|
|
//float _ec=ec_getECfromADC(i);
|
|
float _ec=ec_getECfromADC(i, ec_calibration_polynom, sizeof(ec_calibration_polynom), ec_calibration_linearize_below_adc, ec_calibration_linear_lowADC, ec_calibration_linear_lowEC);
|
|
|
|
Serial.print(i); Serial.print(","); Serial.print(_ec); Serial.println();
|
|
}
|
|
Serial.println("Waiting 10 seconds because eccalibrationoutput is enabled");
|
|
delay(10000);
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
//Serial.println("time,tempReservoir,ECadcCalib,ECadc,ECadcAdjusted,EC,EC25");
|
|
//Serial.println("time,tempReservoir,ECadcCalib,ECadc,ECadcAdjusted");
|
|
|
|
}
|
|
|
|
void loop() {
|
|
unsigned long loopmillis=millis();
|
|
enableTiming=true; //reactivate
|
|
|
|
#ifdef EC_CALIBRATION_POLYNOM
|
|
ec_loop(loopmillis);
|
|
#endif
|
|
|
|
#ifdef ONE_WIRE_BUS_PIN
|
|
temperature_loop(loopmillis);
|
|
#endif
|
|
|
|
#if defined(EC_CALIBRATION_POLYNOM) && defined(RES_AREA)
|
|
if (!ec_measurementRunning()){ //skip tof read when ec measurement running, because vlxx sensor reading takes quite long per cycle
|
|
waterlevel_loop(loopmillis);
|
|
}
|
|
#elif defined(RES_AREA)
|
|
waterlevel_loop(loopmillis);
|
|
#endif
|
|
|
|
|
|
#ifdef FLOW_PIN
|
|
flow_loop(loopmillis);
|
|
#endif
|
|
//sm_loop(loopmillis);
|
|
|
|
|
|
#if defined(VALVECOUNT) || defined(RELAISCOUNT)
|
|
pump_loop(loopmillis);
|
|
#endif
|
|
|
|
|
|
static bool getReading=false;
|
|
|
|
|
|
if (!eccalibrationoutput) { //Is in normal operation mode
|
|
#ifdef PIN_BUTTON
|
|
if (!digitalRead(PIN_BUTTON)) { //button pressed
|
|
valueError=false;
|
|
Serial.println("Reset ValueError flag by user");
|
|
#ifdef PIN_LED
|
|
digitalWrite(PIN_LED,valueError); //set led before delay to blink if error persists
|
|
#endif
|
|
#ifdef PIN_NEOPIXEL
|
|
pixels.setPixelColor(0, pixels.Color(255*valueError, 0,0));
|
|
pixels.show();
|
|
#endif
|
|
delay(100);
|
|
}
|
|
#endif
|
|
|
|
static bool last_valueError=true;
|
|
if (!valuesStabilized) { //if values are not okay since boot
|
|
#ifdef PIN_LED
|
|
digitalWrite(PIN_LED,(loopmillis/250)%2==0); //blink led
|
|
#endif
|
|
#ifdef PIN_NEOPIXEL
|
|
pixels.setPixelColor(0, pixels.Color(((loopmillis/250)%2==0)*255,((loopmillis/250)%2==0)*255,0));
|
|
pixels.show();
|
|
#endif
|
|
}else{ //LED shows valueError flag status when values were okay once
|
|
if (last_valueError!=valueError) { //update led if valueerror flag changed
|
|
last_valueError=valueError;
|
|
#ifdef PIN_LED
|
|
digitalWrite(PIN_LED,valueError);
|
|
#endif
|
|
#ifdef PIN_NEOPIXEL
|
|
pixels.setPixelColor(0, pixels.Color((loopmillis/250)%2==0,(loopmillis/250)%2==0,0));
|
|
pixels.show();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#ifdef EC_CALIBRATION_POLYNOM
|
|
if (eccalibrationoutput && !digitalRead(PIN_BUTTON) && !getReading) { //Calibration UI . Button Pressed
|
|
if (!isValueArrayOK(ec_calib_array,EC_CALIB_ARRAY_SIZE,EC_ADC_UNAVAILABLE)) { //Calibration Array filled?
|
|
for (uint8_t blink=0;blink<5;blink++) { //Error Blink
|
|
#ifdef PIN_LED
|
|
digitalWrite(PIN_LED,HIGH);
|
|
delay(100);
|
|
digitalWrite(PIN_LED,LOW);
|
|
delay(100);
|
|
#endif
|
|
#ifdef PIN_NEOPIXEL
|
|
pixels.setPixelColor(0, pixels.Color(0,0,255));
|
|
pixels.show();
|
|
delay(100);
|
|
pixels.setPixelColor(0, pixels.Color(0,0,0));
|
|
pixels.show();
|
|
delay(100);
|
|
#endif
|
|
}
|
|
}else{ //Calibration Array is ok, initiate ec reading
|
|
getReading=true;
|
|
force_ec_measurement=true;
|
|
ec_flag_measurement_available=false;
|
|
#ifdef PIN_LED
|
|
digitalWrite(PIN_LED,HIGH);
|
|
#endif
|
|
#ifdef PIN_NEOPIXEL
|
|
pixels.setPixelColor(0, pixels.Color(0,255,0));
|
|
pixels.show();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
if (eccalibrationoutput && ec_flag_measurement_available && getReading) { //Calibration UI
|
|
|
|
ec_flag_measurement_available=false;
|
|
getReading=false;
|
|
#ifdef PIN_LED
|
|
digitalWrite(PIN_LED,LOW);
|
|
#endif
|
|
#ifdef PIN_NEOPIXEL
|
|
pixels.setPixelColor(0, pixels.Color(0,0,0));
|
|
pixels.show();
|
|
#endif
|
|
Serial.print(CLIENT_ID); Serial.print(",");
|
|
Serial.print(loopmillis); Serial.print(",");
|
|
Serial.print(tempCmean_reservoir); Serial.print(",");
|
|
Serial.print(ec_calib_adc); Serial.print(",");
|
|
Serial.print(ec_adc); Serial.print(",");
|
|
Serial.print(ec_adc_adjusted);
|
|
Serial.println();
|
|
}
|
|
#endif
|
|
|
|
if (loopmillis>last_check+2000) { //check values
|
|
|
|
last_check=loopmillis;
|
|
|
|
|
|
bool _noErrorsDuringLoop=true;
|
|
|
|
#ifdef ONE_WIRE_BUS_PIN
|
|
bool tempdevdisconnected=false;
|
|
#ifdef THERMOMETER_ADDR_RESERVOIR
|
|
tempdevdisconnected|= (tempCmean_reservoir==DEVICE_DISCONNECTED_C);
|
|
#endif
|
|
#ifdef THERMOMETER_ADDR_RESERVOIR
|
|
tempdevdisconnected|= (tempCmean_case==DEVICE_DISCONNECTED_C);
|
|
#endif
|
|
|
|
if (tempdevdisconnected) {
|
|
if (!valueError && valuesStabilized) { //error just appeared
|
|
#ifdef THERMOMETER_ADDR_RESERVOIR
|
|
if (tempCmean_reservoir==DEVICE_DISCONNECTED_C) {
|
|
Serial.println("valueError tempCmean_reservoir");
|
|
if (mqtt){
|
|
publishInfo("error/temperature","valueError tempCmean_reservoir");
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef THERMOMETER_ADDR_CASE
|
|
if (tempCmean_case==DEVICE_DISCONNECTED_C) {
|
|
Serial.println("valueError tempCmean_case");
|
|
if (mqtt){
|
|
publishInfo("error/temperature","valueError tempCmean_case");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
}
|
|
valueError=true;
|
|
_noErrorsDuringLoop=false;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
if (sm_mean1==SM_DISCONNECTED || sm_mean2==SM_DISCONNECTED) {
|
|
valueError=true;
|
|
}*/
|
|
#ifdef EC_CALIBRATION_POLYNOM
|
|
if (ec==EC_UNAVAILABLE){
|
|
if (!valueError && valuesStabilized) { //error just appeared
|
|
if (ec==EC_UNAVAILABLE){
|
|
Serial.println("valueError ec");
|
|
if (mqtt){
|
|
publishInfo("error/ec","valueError ec");
|
|
}
|
|
}
|
|
}
|
|
valueError=true;
|
|
_noErrorsDuringLoop=false;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef RES_AREA
|
|
if (distance_unsuccessful_count>20) {
|
|
if (!valueError && valuesStabilized) { //error just appeared
|
|
Serial.println("valueError distance");
|
|
if (mqtt){
|
|
publishInfo("error/waterlevel","valueError distance");
|
|
}
|
|
}
|
|
valueError=true;
|
|
_noErrorsDuringLoop=false;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (_noErrorsDuringLoop && !valuesStabilized) {
|
|
valuesStabilized=true; //gets only set to true once
|
|
valueError=false; //clear error flag once after boot
|
|
Serial.println("Values Stable, clear error flag");
|
|
}
|
|
|
|
if (debug) {
|
|
Serial.println("_______________________");
|
|
Serial.print(millis()/1000.0,2); Serial.println(":");
|
|
|
|
#ifdef THERMOMETER_ADDR_RESERVOIR
|
|
Serial.print("temperature reservoir = ");
|
|
Serial.print(tempCmean_reservoir);
|
|
Serial.println();
|
|
#endif
|
|
#ifdef THERMOMETER_ADDR_CASE
|
|
Serial.print("temperature case = ");
|
|
Serial.print(tempCmean_case);
|
|
Serial.println();
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
Serial.print("sm_mean 1,2,3 = ");
|
|
Serial.print(sm_mean1); Serial.print(",");
|
|
Serial.print(sm_mean2); Serial.print(",");
|
|
Serial.print(sm_mean3);
|
|
Serial.println();
|
|
*/
|
|
|
|
/*
|
|
Serial.print("sm_mean 1,2,3 = ");
|
|
Serial.print(getMean(sm_mean1array,SM_SIZE)); Serial.print(",");
|
|
Serial.print(getMean(sm_mean2array,SM_SIZE)); Serial.print(",");
|
|
Serial.print(getMean(sm_mean3array,SM_SIZE));
|
|
Serial.println();
|
|
Serial.print("sm_max 1,2,3 = ");
|
|
Serial.print(getMax(sm_mean1array,SM_SIZE)); Serial.print(",");
|
|
Serial.print(getMax(sm_mean2array,SM_SIZE)); Serial.print(",");
|
|
Serial.print(getMax(sm_mean3array,SM_SIZE));
|
|
Serial.println();
|
|
Serial.print("sm_min 1,2,3 = ");
|
|
Serial.print(getMin(sm_mean1array,SM_SIZE)); Serial.print(",");
|
|
Serial.print(getMin(sm_mean2array,SM_SIZE)); Serial.print(",");
|
|
Serial.print(getMin(sm_mean3array,SM_SIZE));
|
|
Serial.println();
|
|
//Serial.print(getMax(sm_mean3array,SM_SIZE)); Serial.println();
|
|
*/
|
|
|
|
#ifdef FLOW_PIN
|
|
Serial.print("Flow = "); Serial.print(flow);
|
|
Serial.println();
|
|
#endif
|
|
|
|
#ifdef EC_CALIBRATION_POLYNOM
|
|
Serial.print("EC ec_calib_adc,ec_adc,ec_adc_adjusted = ");
|
|
Serial.print(ec_calib_adc); Serial.print(",");
|
|
Serial.print(ec_adc); Serial.print(",");
|
|
Serial.print(ec_adc_adjusted);
|
|
Serial.println();
|
|
Serial.print("EC ec,ec25 = ");
|
|
Serial.print(ec); Serial.print(",");
|
|
Serial.print(ec25);
|
|
Serial.println();
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef RES_AREA
|
|
Serial.print("distance,Waterlevel,Volume = ");
|
|
Serial.print(distance); Serial.print(",");
|
|
Serial.print(waterlevel); Serial.print(",");
|
|
Serial.print(watervolume); Serial.println();
|
|
|
|
Serial.println();
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
if (mqtt && mqtt_loop(loopmillis)) {
|
|
if (sendallnext_flag) {
|
|
sendallnext_flag=false;
|
|
enableTiming=false;
|
|
}
|
|
|
|
#ifdef THERMOMETER_ADDR_RESERVOIR
|
|
if (tempCmean_reservoir!=DEVICE_DISCONNECTED_C) {
|
|
publishValueTimed("temperature/reservoir",tempCmean_reservoir,2,timing_temperature_reservoir,loopmillis);
|
|
}
|
|
#endif
|
|
#ifdef THERMOMETER_ADDR_CASE
|
|
if (tempCmean_case!=DEVICE_DISCONNECTED_C) {
|
|
publishValueTimed("case/temperature",tempCmean_case,2,timing_temperature_case,loopmillis);
|
|
}
|
|
#endif
|
|
/*
|
|
if (sm_mean1!=SM_DISCONNECTED) {
|
|
publishValueTimed("soilmoisture/sm1",sm_mean1,3,timing_soilmoisture_sm1,loopmillis);
|
|
}
|
|
if (sm_mean2!=SM_DISCONNECTED) {
|
|
publishValueTimed("soilmoisture/sm2",sm_mean2,3,timing_soilmoisture_sm2,loopmillis);
|
|
}
|
|
if (sm_mean3!=SM_DISCONNECTED) {
|
|
publishValueTimed("soilmoisture/sm3",sm_mean3,3,timing_soilmoisture_sm3,loopmillis);
|
|
}
|
|
*/
|
|
|
|
|
|
#ifdef FLOW_PIN
|
|
static float last_flow=0;
|
|
if (valuesStabilized){
|
|
if (flow==0.0 && last_flow!=flow) {
|
|
publishValueTimedOverride("flow",flow,2,timing_flow,loopmillis); //publish without waiting if flow is 0
|
|
}else{
|
|
publishValueTimed("flow",flow,2,timing_flow,loopmillis);
|
|
}
|
|
last_flow=flow;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef RES_AREA
|
|
if (waterlevel!=WATERLEVEL_UNAVAILABLE) {
|
|
bool _published=publishValueTimed("waterlevel/height",waterlevel,2,timing_waterlevel,loopmillis);
|
|
if (_published) { //use height for timing. send calculated volume with it
|
|
publishValue("waterlevel/volume",watervolume,2);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef EC_CALIBRATION_POLYNOM
|
|
if (ec_flag_measurement_available){
|
|
ec_flag_measurement_available=false;
|
|
|
|
if (ec_calib_adc!=0) {
|
|
publishValue("ec/eccalibadc",ec_calib_adc,0);
|
|
}
|
|
|
|
//Probe A
|
|
if (ec_adc!=0) {
|
|
publishValue("ec/adc",ec_adc,0);
|
|
}
|
|
|
|
if (ec_adc_adjusted!=0) {
|
|
publishValue("ec/adcadjusted",ec_adc_adjusted,0);
|
|
}
|
|
if (ec!=EC_UNAVAILABLE){
|
|
publishValue("ec/ec",ec,0);
|
|
publishValue("ec/sc",ec25,0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef VALVECOUNT
|
|
String valvestatestring="";
|
|
static String last_valvestatestring="";
|
|
for (uint8_t i=0;i<VALVECOUNT;i++){
|
|
if (valveOffTime[i]!=0) { //valve on
|
|
valvestatestring+="1";
|
|
}else{
|
|
valvestatestring+="0";
|
|
}
|
|
}
|
|
if (!last_valvestatestring.equals(valvestatestring)) { //has changed
|
|
publishInfo("valve/state",valvestatestring); //Example: valve 2 active: 010000000
|
|
}
|
|
last_valvestatestring=valvestatestring;
|
|
#endif
|
|
|
|
#ifdef RELAISCOUNT
|
|
String relaisstatestring="";
|
|
static String last_relaisstatestring="";
|
|
for (uint8_t i=0;i<RELAISCOUNT;i++){
|
|
if (relaisOffTime[i]!=0) { //valve on
|
|
relaisstatestring+="1";
|
|
}else{
|
|
relaisstatestring+="0";
|
|
}
|
|
}
|
|
if (!last_relaisstatestring.equals(relaisstatestring)) { //has changed
|
|
publishInfo("relais/state",relaisstatestring); //Example: valve 2 active: 010000000
|
|
}
|
|
last_relaisstatestring=relaisstatestring;
|
|
#endif
|
|
|
|
/*
|
|
if (ec_adc!=0) {
|
|
publishValueTimed("ec/adc",ec_adc,0,timing_ec_adc,loopmillis);
|
|
}
|
|
if (ec_calib_adc!=0) {
|
|
publishValueTimed("ec/eccalibadc",ec_calib_adc,0,timing_ec_calibadc,loopmillis);
|
|
}
|
|
if (ec_adc_adjusted!=0) {
|
|
publishValueTimed("ec/adcadjusted",ec_adc_adjusted,0,timing_ec_adcadjusted,loopmillis);
|
|
}
|
|
if (ec!=EC_UNAVAILABLE){
|
|
publishValueTimed("ec/ec",ec,0,timing_ec_ec,loopmillis);
|
|
publishValueTimed("ec/sc",ec25,0,timing_ec_sc,loopmillis);
|
|
}*/
|
|
}
|
|
|
|
}
|
|
|
|
} |