diff --git a/esp32/PlantControl.code-workspace b/esp32/PlantControl.code-workspace index 13450eb..83e0c1b 100644 --- a/esp32/PlantControl.code-workspace +++ b/esp32/PlantControl.code-workspace @@ -22,7 +22,9 @@ "array": "cpp", "tuple": "cpp", "utility": "cpp", - "fstream": "cpp" + "fstream": "cpp", + "ostream": "cpp", + "sstream": "cpp" } } } diff --git a/esp32/include/ControllerConfiguration.h b/esp32/include/ControllerConfiguration.h index 8a335dd..e3d7479 100644 --- a/esp32/include/ControllerConfiguration.h +++ b/esp32/include/ControllerConfiguration.h @@ -1,51 +1,6 @@ -/** - * @file ControllerConfiguration.h - * @author your name (you@domain.com) - * @brief - * @version 0.1 - * @date 2020-05-30 - * - * @copyright Copyright (c) 2020 - * - * \mainpage Configuration of the controller - * @{ - * Describe the used PINs of the controller - * - * @subpage Controller - * - * @subpage Homie - * - * @subpage Configuration - * - * There are several modes in the controller - * \dot - * digraph Operationmode { - * ranksep=.75; - * poweroff [ label="off" ]; - * mode1 [ label="Mode 1 - Sensor only", shape=box, width=2 ]; - * mode2 [ label="Mode 2 - Wifi enabled", shape=box ]; - * mode3 [ label="Mode 3 - Stay alive", shape=box ]; - * mode1 -> mode2 [ label="wakeup reason", fontsize=10 ]; - * mode1 -> mode2 [ label="Time duration", fontsize=10 ]; - * mode2 -> mode3 [ label="Over the Air Update", fontsize=10 ]; - * mode3 -> mode2 [ label="Over the Air Finished", fontsize=10 ]; - * mode3 -> mode2 [ label="Mqtt Command", fontsize=10 ]; - * mode2 -> mode3 [ label="Mqtt Command", fontsize=10 ]; - * poweroff -> mode1 [ label="deep sleep wakeup", fontsize=10 ]; - * mode1 -> poweroff [ label="enter deep sleep", fontsize=10 ]; - * mode2 -> poweroff [ label="Mqtt queue empty", fontsize=10 ]; - * } - * \enddot - * - * Before entering Deep sleep the controller is configured with an wakeup time. - * - * @} - */ #ifndef CONTROLLER_CONFIG_H #define CONTROLLER_CONFIG_H -/** \addtogroup GPIO Settings - * @{ - */ + #define SENSOR_PLANT0 GPIO_NUM_32 /**< GPIO 32 (ADC1) */ #define SENSOR_PLANT1 GPIO_NUM_33 /**< GPIO 33 (ADC1) */ #define SENSOR_PLANT2 GPIO_NUM_25 /**< GPIO 25 (ADC2) */ @@ -77,12 +32,8 @@ #define I2C1_SDA GPIO_NUM_34 /**< GPIO 34 - I2C */ #define I2C1_SCL GPIO_NUM_35 /**< GPIO 35 - I2C */ -/* @} */ -/** \addtogroup Configuration - * @{ - */ -#define FIRMWARE_VERSION "sw 2.1 hw 0.10b" +#define FIRMWARE_VERSION "sw 2.2 hw 0.10b" #define TIMED_LIGHT_PIN CUSTOM1_PIN5 #define FLOWMETER_PIN CUSTOM1_PIN1 diff --git a/esp32/include/HomieTypes.h b/esp32/include/HomieTypes.h index b3f064d..2e67460 100644 --- a/esp32/include/HomieTypes.h +++ b/esp32/include/HomieTypes.h @@ -13,9 +13,21 @@ #include -#define SENSOR_NONE 0 -#define SENSOR_CAPACITIVE_FREQUENCY_MOD 1 -#define SENSOR_ANALOG_RESISTANCE_PROBE 2 +#define FOREACH_SENSOR(SENSOR) \ + SENSOR(NONE) \ + SENSOR(CAPACITIVE_FREQUENCY) \ + SENSOR(ANALOG_RESISTANCE_PROBE) \ + +#define GENERATE_ENUM(ENUM) ENUM, +#define GENERATE_STRING(STRING) #STRING, + +enum SENSOR_MODE { + FOREACH_SENSOR(GENERATE_ENUM) +}; + +static const char *SENSOR_STRING[] = { + FOREACH_SENSOR(GENERATE_STRING) +}; //plant pump is deactivated, but sensor values are still recorded and published #define DEACTIVATED_PLANT -1 diff --git a/esp32/include/PlantCtrl.h b/esp32/include/PlantCtrl.h index 17fab9e..f90f5fb 100644 --- a/esp32/include/PlantCtrl.h +++ b/esp32/include/PlantCtrl.h @@ -64,15 +64,22 @@ public: void activatePump(void); + String getSensorModeString(){ + SENSOR_MODE mode = getSensorMode(); + return SENSOR_STRING[mode]; + } + bool isHydroponic() { long current = this->mSetting->pSensorDry->get(); return equalish(current, HYDROPONIC_MODE); } - long isSensorMode(int sensorMode) + SENSOR_MODE getSensorMode() { - return this->mSetting->pSensorMode->get() == sensorMode; + int raw_mode = this->mSetting->pSensorMode->get(); + SENSOR_MODE sensorType = static_cast(raw_mode); + return sensorType; } /** @@ -113,18 +120,14 @@ public: float getCurrentMoisturePCT() { - if (isSensorMode(SENSOR_NONE)) - { + switch (getSensorMode()){ + case NONE: return DEACTIVATED_PLANT; - } - if (isSensorMode(SENSOR_CAPACITIVE_FREQUENCY_MOD)) - { - return mapf(mMoisture_raw.getMedian(), MOIST_SENSOR_MAX_FRQ, MOIST_SENSOR_MIN_FRQ, 0, 100); - } - else if (isSensorMode(SENSOR_ANALOG_RESISTANCE_PROBE)) - { + case CAPACITIVE_FREQUENCY: + return mapf(mMoisture_raw.getMedian(), MOIST_SENSOR_MAX_FRQ, MOIST_SENSOR_MIN_FRQ, 0, 100); + case ANALOG_RESISTANCE_PROBE: return mapf(mMoisture_raw.getMedian(), ANALOG_SENSOR_MAX_MV, ANALOG_SENSOR_MIN_MV, 0, 100); - } else { + default: log(LOG_LEVEL_ERROR, LOG_SENSORMODE_UNKNOWN, LOG_SENSORMODE_UNKNOWN_CODE); return DEACTIVATED_PLANT; } @@ -132,14 +135,13 @@ public: float getCurrentMoistureRaw() { - if (isSensorMode(SENSOR_CAPACITIVE_FREQUENCY_MOD)) - { - if (mMoisture_raw.getMedian() < MOIST_SENSOR_MIN_FRQ) + if(getSensorMode() == CAPACITIVE_FREQUENCY){ +if (mMoisture_raw.getMedian() < MOIST_SENSOR_MIN_FRQ) { return MISSING_SENSOR; } } - + return mMoisture_raw.getMedian(); } @@ -149,6 +151,7 @@ public: } void init(void); + void initSensors(void); long getCooldownInSeconds() { diff --git a/esp32/src/PlantCtrl.cpp b/esp32/src/PlantCtrl.cpp index 0409230..7cac4fe 100644 --- a/esp32/src/PlantCtrl.cpp +++ b/esp32/src/PlantCtrl.cpp @@ -12,7 +12,7 @@ #include "PlantCtrl.h" #include "ControllerConfiguration.h" #include "TimeUtils.h" -#include "driver/pcnt.h" +#include "driver/pcnt.h" Plant::Plant(int pinSensor, int pinPump, int plantId, HomieNode *plant, PlantSettings_t *setting) { @@ -27,45 +27,36 @@ void Plant::init(void) { /* Initialize Home Settings validator */ this->mSetting->pSensorDry->setDefaultValue(DEACTIVATED_PLANT); - this->mSetting->pSensorDry->setValidator([](long candidate) { - return (((candidate >= 0.0) && (candidate <= 100.0)) || equalish(candidate,DEACTIVATED_PLANT) || equalish(candidate,HYDROPONIC_MODE)); - }); + this->mSetting->pSensorDry->setValidator([](long candidate) + { return (((candidate >= 0.0) && (candidate <= 100.0)) || equalish(candidate, DEACTIVATED_PLANT) || equalish(candidate, HYDROPONIC_MODE)); }); - this->mSetting->pSensorMode->setDefaultValue(SENSOR_NONE); - this->mSetting->pSensorMode->setValidator([](long candidate) { - return candidate == SENSOR_NONE || candidate == SENSOR_CAPACITIVE_FREQUENCY_MOD || candidate == SENSOR_ANALOG_RESISTANCE_PROBE; - }); + this->mSetting->pSensorMode->setDefaultValue(NONE); + this->mSetting->pSensorMode->setValidator([](long candidate) + { return candidate == NONE || candidate == CAPACITIVE_FREQUENCY || candidate == ANALOG_RESISTANCE_PROBE; }); this->mSetting->pPumpAllowedHourRangeStart->setDefaultValue(8); // start at 8:00 - this->mSetting->pPumpAllowedHourRangeStart->setValidator([](long candidate) { - return ((candidate >= 0) && (candidate <= 23)); - }); + this->mSetting->pPumpAllowedHourRangeStart->setValidator([](long candidate) + { return ((candidate >= 0) && (candidate <= 23)); }); this->mSetting->pPumpAllowedHourRangeEnd->setDefaultValue(20); // stop pumps at 20:00 - this->mSetting->pPumpAllowedHourRangeEnd->setValidator([](long candidate) { - return ((candidate >= 0) && (candidate <= 23)); - }); + this->mSetting->pPumpAllowedHourRangeEnd->setValidator([](long candidate) + { return ((candidate >= 0) && (candidate <= 23)); }); this->mSetting->pPumpOnlyWhenLowLight->setDefaultValue(true); - this->mSetting->pPumpCooldownInSeconds->setDefaultValue(60*60); // 1 hour - this->mSetting->pPumpCooldownInSeconds->setValidator([](long candidate) { - return (candidate >= 0); - }); + this->mSetting->pPumpCooldownInSeconds->setDefaultValue(60 * 60); // 1 hour + this->mSetting->pPumpCooldownInSeconds->setValidator([](long candidate) + { return (candidate >= 0); }); this->mSetting->pPumpDuration->setDefaultValue(30); - this->mSetting->pPumpDuration->setValidator([](long candidate) { - return ((candidate >= 0) && (candidate <= 1000)); - }); + this->mSetting->pPumpDuration->setValidator([](long candidate) + { return ((candidate >= 0) && (candidate <= 1000)); }); this->mSetting->pPumpMl->setDefaultValue(0); - this->mSetting->pPumpMl->setValidator([](long candidate) { - return ((candidate >= 0) && (candidate <= 5000)); - }); + this->mSetting->pPumpMl->setValidator([](long candidate) + { return ((candidate >= 0) && (candidate <= 5000)); }); this->mSetting->pPumpPowerLevel->setDefaultValue(100); - this->mSetting->pPumpPowerLevel->setValidator([](long candidate) { - return ((candidate >= 0) && (candidate <= 100)); - }); - + this->mSetting->pPumpPowerLevel->setValidator([](long candidate) + { return ((candidate >= 0) && (candidate <= 100)); }); /* Initialize Hardware */ - Serial.println("Init PWM controller for pump " + String(mPinPump) + "=" + String(OUTPUT)); + Serial.println("Init PWM controller for pump " + String(mPinPump) + "=" + String(OUTPUT)); Serial.flush(); ledcSetup(this->mPlantId, PWM_FREQ, PWM_BITS); ledcAttachPin(mPinPump, this->mPlantId); @@ -74,82 +65,120 @@ void Plant::init(void) Serial.println("Set GPIO mode " + String(mPinSensor) + "=" + String(ANALOG)); Serial.flush(); pinMode(this->mPinSensor, INPUT); +} - if(isSensorMode(SENSOR_CAPACITIVE_FREQUENCY_MOD)){ +void Plant::initSensors(void){ + switch (getSensorMode()) + { + case CAPACITIVE_FREQUENCY: + { + pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_0 + this->mPlantId); + pcnt_config_t pcnt_config = {}; // Instancia PCNT config - pcnt_unit_t unit = (pcnt_unit_t) (PCNT_UNIT_0 + this->mPlantId); - pcnt_config_t pcnt_config = { }; // Instancia PCNT config + pcnt_config.pulse_gpio_num = this->mPinSensor; // Configura GPIO para entrada dos pulsos + pcnt_config.ctrl_gpio_num = PCNT_PIN_NOT_USED; // Configura GPIO para controle da contagem + 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_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 - pcnt_config.pulse_gpio_num = this->mPinSensor; // Configura GPIO para entrada dos pulsos - pcnt_config.ctrl_gpio_num = PCNT_PIN_NOT_USED; // Configura GPIO para controle da contagem - 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_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 - - - pcnt_counter_pause(unit); // Pausa o contador PCNT - pcnt_counter_clear(unit); // Zera o contador PCNT + pcnt_counter_pause(unit); // Pausa o contador PCNT + pcnt_counter_clear(unit); // Zera o contador PCNT Serial.println("Setup Counter " + String(mPinPump) + "=" + String(LOW)); - } else if (isSensorMode(SENSOR_ANALOG_RESISTANCE_PROBE)){ + break; + } + + case ANALOG_RESISTANCE_PROBE: + { adcAttachPin(this->mPinSensor); - } else if (isSensorMode(SENSOR_NONE)){ - //nothing to do here - } else { - log(LOG_LEVEL_ERROR, LOG_SENSORMODE_UNKNOWN, LOG_SENSORMODE_UNKNOWN_CODE); + break; + } + case NONE: + { + //do nothing + break; + } } - } -void Plant::blockingMoistureMeasurement(void) { - if(isSensorMode(SENSOR_ANALOG_RESISTANCE_PROBE)){ - for(int i = 0;imMoisture_raw.add(analogReadMilliVolts(this->mPinSensor)); +void Plant::blockingMoistureMeasurement(void) +{ + switch (getSensorMode()) + { + case ANALOG_RESISTANCE_PROBE: + { + for (int i = 0; i < ANALOG_REREADS; i++) + { + this->mMoisture_raw.add(analogReadMilliVolts(this->mPinSensor)); delay(5); + break; } - }else if(isSensorMode(SENSOR_CAPACITIVE_FREQUENCY_MOD) || isSensorMode(SENSOR_NONE)){ + } + case CAPACITIVE_FREQUENCY: + case NONE: + { //nothing to do here - } else { - log(LOG_LEVEL_ERROR, LOG_SENSORMODE_UNKNOWN, LOG_SENSORMODE_UNKNOWN_CODE); + break; + } } } - -void Plant::startMoistureMeasurement(void) { - if(isSensorMode(SENSOR_CAPACITIVE_FREQUENCY_MOD)){ - pcnt_unit_t unit = (pcnt_unit_t) (PCNT_UNIT_0 + this->mPlantId); +void Plant::startMoistureMeasurement(void) +{ + switch (getSensorMode()) + { + case CAPACITIVE_FREQUENCY: + { + pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_0 + this->mPlantId); pcnt_counter_resume(unit); - } else if (isSensorMode(SENSOR_ANALOG_RESISTANCE_PROBE) || isSensorMode(SENSOR_NONE)){ - //nothing to do here - } else { - log(LOG_LEVEL_ERROR, LOG_SENSORMODE_UNKNOWN, LOG_SENSORMODE_UNKNOWN_CODE); + break; + } + case ANALOG_RESISTANCE_PROBE: + case NONE: + { + //do nothing here + } } } -void Plant::stopMoistureMeasurement(void) { - if(isSensorMode(SENSOR_CAPACITIVE_FREQUENCY_MOD)){ +void Plant::stopMoistureMeasurement(void) +{ + switch (getSensorMode()) + { + case CAPACITIVE_FREQUENCY: + { int16_t pulses; - pcnt_unit_t unit = (pcnt_unit_t) (PCNT_UNIT_0 + this->mPlantId); - pcnt_counter_pause(unit); + pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_0 + this->mPlantId); + pcnt_counter_pause(unit); esp_err_t result = pcnt_get_counter_value(unit, &pulses); pcnt_counter_clear(unit); - if(result != ESP_OK){ + if (result != ESP_OK) + { log(LOG_LEVEL_ERROR, LOG_HARDWARECOUNTER_ERROR_MESSAGE, LOG_HARDWARECOUNTER_ERROR_CODE); - this-> mMoisture_raw.clear(); - this -> mMoisture_raw.add(-1); - } else { + this->mMoisture_raw.clear(); + this->mMoisture_raw.add(-1); + } + else + { this->mMoisture_raw.add(pulses * (1000 / MOISTURE_MEASUREMENT_DURATION)); } - }else if (isSensorMode(SENSOR_ANALOG_RESISTANCE_PROBE) || isSensorMode(SENSOR_NONE)){ + break; + } + case ANALOG_RESISTANCE_PROBE: + { //nothing to do here - } else { - log(LOG_LEVEL_ERROR, LOG_SENSORMODE_UNKNOWN, LOG_SENSORMODE_UNKNOWN_CODE); + break; + } + case NONE: + { + break; + } } } @@ -165,15 +194,15 @@ void Plant::postMQTTconnection(void) Serial.println(".................."); if (equalish(raw, MISSING_SENSOR)) { - pct = 0; + pct = 0; } if (pct < 0) { - pct = 0; + pct = 0; } if (pct > 100) { - pct = 100; + pct = 100; } this->mPlant->setProperty("moist").send(String(pct)); @@ -193,7 +222,8 @@ void Plant::deactivatePump(void) } } -void Plant::publishState(String state) { +void Plant::publishState(String state) +{ if (this->mConnected) { this->mPlant->setProperty("state").send(state); @@ -203,10 +233,10 @@ void Plant::publishState(String state) { void Plant::activatePump(void) { int plantId = this->mPlantId; - + Serial << "activating pump " << plantId << endl; long desiredPowerLevelPercent = this->mSetting->pPumpPowerLevel->get(); - ledcWrite(this->mPlantId, desiredPowerLevelPercent*PWM_BITS); + ledcWrite(this->mPlantId, desiredPowerLevelPercent * PWM_BITS); if (this->mConnected) { const String OFF = String("ON"); @@ -215,30 +245,38 @@ void Plant::activatePump(void) } } -bool Plant::switchHandler(const HomieRange& range, const String& value) { - if (range.isRange) { - return false; // only one switch is present +bool Plant::switchHandler(const HomieRange &range, const String &value) +{ + if (range.isRange) + { + return false; // only one switch is present } - if ((value.equals("ON")) || (value.equals("On")) || (value.equals("on")) || (value.equals("true"))) { + if ((value.equals("ON")) || (value.equals("On")) || (value.equals("on")) || (value.equals("true"))) + { this->activatePump(); return true; - } else if ((value.equals("OFF")) || (value.equals("Off")) || (value.equals("off")) || (value.equals("false")) ) { + } + else if ((value.equals("OFF")) || (value.equals("Off")) || (value.equals("off")) || (value.equals("false"))) + { this->deactivatePump(); return true; - } else { + } + else + { return false; } } -void Plant::setSwitchHandler(HomieInternals::PropertyInputHandler f) { +void Plant::setSwitchHandler(HomieInternals::PropertyInputHandler f) +{ this->mPump.settable(f); } void Plant::advertise(void) { // Advertise topics - mPump = this->mPlant->advertise("switch").setName("Pump").setDatatype("Boolean"); + mPump = this->mPlant->advertise("switch").setName("Pump").setDatatype("Boolean"); this->mPlant->advertise("lastPump").setName("lastPump").setDatatype("Integer").setUnit("unixtime"); this->mPlant->advertise("moist").setName("Percent").setDatatype("Float").setUnit("%"); this->mPlant->advertise("moistraw").setName("adc").setDatatype("Float").setUnit("3.3/4096V"); diff --git a/esp32/src/main.cpp b/esp32/src/main.cpp index aaf3193..45f21e3 100644 --- a/esp32/src/main.cpp +++ b/esp32/src/main.cpp @@ -254,6 +254,10 @@ void readOneWireSensors() */ void readPowerSwitchedSensors() { + for (int i = 0; i < MAX_PLANTS; i++) + { + Serial << "Sensor " << i << " mode: " << mPlants[i].getSensorModeString() << endl; + } digitalWrite(OUTPUT_ENABLE_SENSOR, HIGH); delay(50); for (int i = 0; i < MAX_PLANTS; i++) @@ -274,21 +278,20 @@ void readPowerSwitchedSensors() for (int i = 0; i < MAX_PLANTS; i++) { - if (mPlants[i].isSensorMode(SENSOR_CAPACITIVE_FREQUENCY_MOD)) + Plant plant = mPlants[i]; + switch (plant.getSensorMode()) { - Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentMoistureRaw() << " hz" << endl; - } - else if (mPlants[i].isSensorMode(SENSOR_ANALOG_RESISTANCE_PROBE)) - { - Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentMoistureRaw() << " mV" << endl; - } - else if (mPlants[i].isSensorMode(SENSOR_NONE)) - { - Serial << "Plant " << i << " measurement: no sensor configured" << endl; - } - else - { - log(LOG_LEVEL_ERROR, LOG_SENSORMODE_UNKNOWN, LOG_SENSORMODE_UNKNOWN_CODE); + case CAPACITIVE_FREQUENCY: { + Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentMoistureRaw() << " hz " << mPlants[i].getCurrentMoisturePCT() << "%" << endl; + break; + } + case ANALOG_RESISTANCE_PROBE : { + Serial << "Plant " << i << " measurement: " << mPlants[i].getCurrentMoistureRaw() << " mV" << mPlants[i].getCurrentMoisturePCT() << "%" << endl; + break; + } + case NONE : { + + } } } @@ -760,6 +763,12 @@ void safeSetup() Homie.setup(); + /* Intialize Plant */ + for (int i = 0; i < MAX_PLANTS; i++) + { + mPlants[i].initSensors(); + } + /************************* Start One-Wire bus ***************/ int tempInitStartTime = millis(); uint8_t sensorCount = 0U;