Merge branch 'hydro'

# Conflicts:
#	esp32/PlantControl.code-workspace
#	esp32/include/ControllerConfiguration.h
#	esp32/platformio.ini
#	esp32/src/main.cpp
This commit is contained in:
Empire
2022-04-27 19:19:26 +00:00
15 changed files with 566 additions and 570 deletions

View File

@@ -47,7 +47,7 @@ void Plant::init(void)
this->mSetting->pPumpDuration->setDefaultValue(30);
this->mSetting->pPumpDuration->setValidator([](long candidate)
{ return ((candidate >= 0) && (candidate <= 1000)); });
this->mSetting->pPumpMl->setDefaultValue(0);
this->mSetting->pPumpMl->setDefaultValue(1000);
this->mSetting->pPumpMl->setValidator([](long candidate)
{ return ((candidate >= 0) && (candidate <= 5000)); });
this->mSetting->pPumpPowerLevel->setDefaultValue(100);
@@ -91,7 +91,6 @@ void Plant::initSensors(void)
adcAttachPin(this->mPinSensor);
break;
}
case SHT20:
case NONE:
{
// do nothing
@@ -113,34 +112,6 @@ void Plant::blockingMoistureMeasurement(void)
}
break;
}
case SHT20:
{
//do not assume valid i2c state, reinit
TwoWire sensorWire = TwoWire(0);
sensorWire.setPins(SENSOR_TANK_SDA, SHARED_SCL);
sht20.begin(&sensorWire);
sht20.reset();
delay(100);
if(!sht20.isConnected()){
Serial.println("SHT20 connection error");
}
bool success = sht20.read();
int error = sht20.getError();
if (error)
{
log(LOG_LEVEL_ERROR, "Failure reading SHT20 " + String(error), LOG_SENSOR_MISSING);
}
if (!success || error)
{
this->mMoisture_raw.clear();
this->mMoisture_raw.add(MISSING_SENSOR);
return;
}
mMoisture_raw.add(sht20.getHumidity());
mTemperature_degree.add(sht20.getTemperature());
break;
}
case CAPACITIVE_FREQUENCY:
case NONE:
{
@@ -160,7 +131,6 @@ void Plant::startMoistureMeasurement(void)
pcnt_counter_resume(unit);
break;
}
case SHT20:
case ANALOG_RESISTANCE_PROBE:
case NONE:
{
@@ -192,7 +162,6 @@ void Plant::stopMoistureMeasurement(void)
}
break;
}
case SHT20:
case ANALOG_RESISTANCE_PROBE:
case NONE:
{

View File

@@ -35,12 +35,18 @@
#include <VL53L0X.h>
#include "driver/pcnt.h"
#include "MQTTUtils.h"
#include "esp_ota_ops.h"
#if defined(TIMED_LIGHT_PIN)
#include "ulp-pwm.h"
#endif
/******************************************************************************
* DEFINES
******************************************************************************/
#define AMOUNT_SENOR_QUERYS 8
#define MAX_TANK_DEPTH 2000
#define REBOOT_LOOP_DETECTION_ERROR 5
/******************************************************************************
* FUNCTION PROTOTYPES
@@ -54,9 +60,7 @@ bool determineTimedLightState(bool lowLight);
/******************************************************************************
* NON VOLATILE VARIABLES in DEEP SLEEP
******************************************************************************/
#if defined(TIMED_LIGHT_PIN)
RTC_DATA_ATTR bool timedLightOn = false; /**< allow fast recovery after poweron */
RTC_DATA_ATTR bool timedLightLowVoltageTriggered = false; /**remember if it was shut down due to voltage level */
#endif // TIMED_LIGHT_PIN
@@ -80,6 +84,7 @@ unsigned long setupFinishedTimestamp;
bool pumpStarted = false;
long pumpTarget = -1;
long pumpStartTime = 0;
long lastSendPumpUpdate = 0;
#ifdef FLOWMETER_PIN
long pumpTargetMl = -1;
@@ -127,11 +132,28 @@ Plant mPlants[MAX_PLANTS] = {
* LOCAL FUNCTIONS
******************************************************************************/
void finsihedCycleSucessfully()
{
const esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
{
log(LOG_LEVEL_INFO, "Get State Partition was Successfull", LOG_BOOT_ERROR_DETECTION);
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
{
log(LOG_LEVEL_INFO, "Diagnostics completed successfully! Marking as valid", LOG_BOOT_ERROR_DETECTION);
esp_ota_mark_app_valid_cancel_rollback();
}
}
}
void espDeepSleep(bool afterPump = false)
{
if (mDownloadMode)
{
log(LOG_LEVEL_DEBUG, "abort deepsleep, DownloadMode active", LOG_DEBUG_CODE);
//if we manage to get to the download mode, the device can be restored
finsihedCycleSucessfully();
return;
}
if (aliveWasRead())
@@ -154,17 +176,7 @@ void espDeepSleep(bool afterPump = false)
}
}
//allo hold for all digital pins
gpio_deep_sleep_hold_en();
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_ON);
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON);
#if defined(TIMED_LIGHT_PIN)
gpio_hold_en(TIMED_LIGHT_PIN);
#endif // TIMED_LIGHT_PIN
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
long secondsToSleep = -1;
@@ -187,6 +199,7 @@ void espDeepSleep(bool afterPump = false)
}
}
finsihedCycleSucessfully();
esp_sleep_enable_timer_wakeup((secondsToSleep * 1000U * 1000U));
if (aliveWasRead())
{
@@ -310,22 +323,12 @@ void readPowerSwitchedSensors()
Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentMoistureRaw() << " mV " << mPlants[i].getCurrentMoisturePCT() << "%" << endl;
break;
}
case SHT20:{
Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentTemperature() << "°C " << mPlants[i].getCurrentMoisturePCT() << "rH%" << endl;
break;
}
case NONE : {
}
}
}
//do not assume valid i2c state, force release of hardware
Wire = TwoWire(0);
Wire.setPins(SENSOR_TANK_SDA, SHARED_SCL);
Wire.begin();
waterRawSensor.clear();
tankSensor.setTimeout(500);
long start = millis();
@@ -485,7 +488,12 @@ int determineNextPump(bool isLowLight)
}
else
{
plant.publishState("active");
if(mDownloadMode){
plant.publishState("active+supressed");
}else {
plant.publishState("active");
}
}
if (!plant.isHydroponic())
@@ -603,7 +611,7 @@ void initPumpLogic()
//set targets
#ifdef FLOWMETER_PIN
pumpTargetMl = mPlants[pumpToRun].getPumpDuration();
pumpTargetMl = mPlants[pumpToRun].getPumpMl();
//0-6 are used for moisture measurment
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_7);
@@ -613,8 +621,8 @@ void initPumpLogic()
pcnt_config.unit = unit; // Unidade de contagem PCNT - 0
pcnt_config.channel = PCNT_CHANNEL_0; // Canal de contagem PCNT - 0
pcnt_config.counter_h_lim = INT16_MAX; // Limite maximo de contagem - 20000
pcnt_config.pos_mode = PCNT_COUNT_DIS; // Incrementa contagem na subida do pulso
pcnt_config.neg_mode = PCNT_COUNT_INC; // Incrementa contagem na descida do pulso
pcnt_config.pos_mode = PCNT_COUNT_INC; // Incrementa contagem na subida do pulso
pcnt_config.neg_mode = PCNT_COUNT_DIS; // Incrementa contagem na descida do pulso
pcnt_config.lctrl_mode = PCNT_MODE_KEEP; // PCNT - modo lctrl desabilitado
pcnt_config.hctrl_mode = PCNT_MODE_KEEP; // PCNT - modo hctrl - se HIGH conta incrementando
pcnt_unit_config(&pcnt_config); // Configura o contador PCNT
@@ -622,8 +630,14 @@ void initPumpLogic()
pcnt_counter_clear(unit); // Zera o contador PCNT
pcnt_counter_resume(unit);
#endif
pumpStartTime = millis();
pumpTarget = millis() + (mPlants[pumpToRun].getPumpDuration() * 1000);
log(LOG_LEVEL_INFO, "Starting pump " + String(pumpToRun) + " for " + String(mPlants[pumpToRun].getPumpDuration()) + "s", LOG_PUMP_STARTED_CODE);
#ifdef FLOWMETER_PIN
log(LOG_LEVEL_INFO, "Starting pump " + String(pumpToRun) + " for " + String(mPlants[pumpToRun].getPumpDuration()) + "s or " + String(pumpTargetMl) + "ml", LOG_PUMP_STARTED_CODE);
#else
log(LOG_LEVEL_INFO, "Starting pump " + String(pumpToRun) + " for " + String(mPlants[pumpToRun].getPumpDuration()) + "s", LOG_PUMP_STARTED_CODE);
#endif
//enable power
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
@@ -652,8 +666,8 @@ void pumpActiveLoop()
mqttUpdateTick = true;
}
long duration = millis() - pumpStartTime;
#ifdef FLOWMETER_PIN
int16_t pulses;
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_7);
esp_err_t result = pcnt_get_counter_value(unit, &pulses);
@@ -661,29 +675,29 @@ void pumpActiveLoop()
{
log(LOG_LEVEL_ERROR, LOG_HARDWARECOUNTER_ERROR_MESSAGE, LOG_HARDWARECOUNTER_ERROR_CODE);
targetReached = true;
log(LOG_LEVEL_INFO, "Reached pump target ml " + String(pumpToRun), LOG_PUMP_STARTED_CODE);
}
else
{
/**FLOWMETER_FLOWFACTOR * (L/Min) = F;
given 1L/min -> FLOWMETER_FLOWFACTOR*60 pulses per liter
-> 1000/result -> ml pro pulse;
-> result * pulses ->*/
long pumped = (FLOWMETER_FLOWFACTOR * 60) * pulses / 1000;
if (pumped >= pumpTargetMl)
float mLPumped = pulses/FLOWMETER_PULSES_PER_ML; //mLperMs*duration;
if (mLPumped >= pumpTargetMl)
{
targetReached = true;
pcnt_counter_pause(unit);
mPlants[pumpToRun].setProperty("waterusage").send(String(pumped));
mPlants[pumpToRun].setProperty("pulses").send(String(pulses));
mPlants[pumpToRun].setProperty("waterusage").send(String(mLPumped));
}
else if (mqttUpdateTick)
{
mPlants[pumpToRun].setProperty("waterusage").send(String(pumped));
mPlants[pumpToRun].setProperty("pulses").send(String(pulses));
mPlants[pumpToRun].setProperty("waterusage").send(String(mLPumped));
}
}
#endif
long pumpStarted = pumpTarget - (mPlants[pumpToRun].getPumpDuration() * 1000);
long duration = millis() - pumpStarted;
if (millis() > pumpTarget)
{
mPlants[pumpToRun].setProperty("watertime").send(String(duration));
@@ -696,12 +710,10 @@ void pumpActiveLoop()
if (targetReached)
{
//disable all
digitalWrite(OUTPUT_ENABLE_PUMP, LOW);
for (int i = 0; i < MAX_PLANTS; i++)
{
mPlants[i].deactivatePump();
}
mPlants[pumpToRun].deactivatePump();
//disable loop, to prevent multi processing
pumpStarted = false;
//if runtime is larger than cooldown, else it would run continously
@@ -723,9 +735,7 @@ void safeSetup()
//restore state before releasing pin, to prevent flickering
#if defined(TIMED_LIGHT_PIN)
pinMode(TIMED_LIGHT_PIN, OUTPUT);
digitalWrite(TIMED_LIGHT_PIN, timedLightOn);
gpio_hold_dis(TIMED_LIGHT_PIN);
ulp_pwm_init();
#endif // TIMED_LIGHT_PIN
/* Intialize Plant */
@@ -778,13 +788,15 @@ void safeSetup()
{ return (candidate > 0) && (candidate < (20)); });
#if defined(TIMED_LIGHT_PIN)
timedLightPowerLevel.setDefaultValue(25).setValidator([](long candidate)
{ return (candidate > 0) && (candidate <= (255)); });
timedLightStart.setDefaultValue(18).setValidator([](long candidate)
{ return (candidate > 0) && (candidate < (25)); });
timedLightEnd.setDefaultValue(23).setValidator([](long candidate)
{ return (candidate > 0) && (candidate < (24)); });
timedLightOnlyWhenDark.setDefaultValue(true);
timedLightVoltageCutoff.setDefaultValue(3.8).setValidator([](double candidate)
{ return (candidate > 3.3) && (candidate < (4.2)); });
{ return ((candidate > 3.3 || candidate == -1) && (candidate < (50))); });
#endif // TIMED_LIGHT_PIN
Homie.setLoopFunction(homieLoop);
@@ -800,6 +812,9 @@ void safeSetup()
Homie.setup();
Wire = TwoWire(0);
Wire.setPins(SENSOR_TANK_SDA, SENSOR_TANK_SCL);
Wire.begin();
/************************* Start One-Wire bus ***************/
@@ -830,10 +845,6 @@ void safeSetup()
for (int i = 0; i < MAX_PLANTS; i++)
{
mPlants[i].advertise();
//write to temperature node instead
if(!equalish(mPlants[i].getCurrentTemperature(),PLANT_WITHOUT_TEMPSENSOR)){
mqttWrite(&sensorTemp, "Plant" + String(i), String(mPlants[i].getCurrentTemperature()));
}
}
mPlants[0].setSwitchHandler(switch1);
mPlants[1].setSwitchHandler(switch2);
@@ -1056,7 +1067,17 @@ void plantcontrol()
Serial.flush();
}
bool isLowLight = (mSolarVoltage < SOLAR_CHARGE_MIN_VOLTAGE);
#if defined(TIMED_LIGHT_PIN)
bool isLowLight = mSolarVoltage <= 9;
bool shouldLight = determineTimedLightState(isLowLight);
if(shouldLight){
ulp_pwm_set_level(timedLightPowerLevel.get());
}else {
ulp_pwm_set_level(0);
}
#endif // TIMED_LIGHT_PIN
bool hasWater = true; //FIXME remaining > waterLevelMin.get();
//FIXME no water warning message
pumpToRun = determineNextPump(isLowLight);
@@ -1083,12 +1104,6 @@ void plantcontrol()
{
espDeepSleep();
}
#if defined(TIMED_LIGHT_PIN)
bool shouldLight = determineTimedLightState(isLowLight);
timedLightOn = shouldLight;
digitalWrite(TIMED_LIGHT_PIN, shouldLight);
#endif // TIMED_LIGHT_PIN
}
/** @}*/
@@ -1113,13 +1128,17 @@ bool determineTimedLightState(bool lowLight)
return false;
}
if (((hoursStart > hoursEnd) &&
(getCurrentHour() >= hoursStart || getCurrentHour() <= hoursEnd)) ||
/* Handle e.g. start = 8, end = 21 */
int curHour = getCurrentHour();
bool condition1 = ((hoursStart > hoursEnd) &&
(curHour >= hoursStart || curHour <= hoursEnd));
bool condition2 = /* Handle e.g. start = 8, end = 21 */
((hoursStart < hoursEnd) &&
(getCurrentHour() >= hoursStart && getCurrentHour() <= hoursEnd)))
(curHour >= hoursStart && curHour <= hoursEnd));
timedLightNode.setProperty("debug").send(String(curHour) + " " + String(hoursStart) + " " + String(hoursEnd) + " " + String(condition1) + " " + String(condition2));
if (condition1 || condition2)
{
if (!timedLightLowVoltageTriggered && battery.getVoltage(BATTSENSOR_INDEX_BATTERY) >= timedLightVoltageCutoff.get())
bool voltageOk = !timedLightLowVoltageTriggered && battery.getVoltage(BATTSENSOR_INDEX_BATTERY) >= timedLightVoltageCutoff.get();
if (voltageOk || equalish(timedLightVoltageCutoff.get(), -1))
{
timedLightNode.setProperty("state").send(String("On"));
return true;