diff --git a/esp32/host/upload-settings.sh b/esp32/host/upload-settings.sh index e2c3580..b1ee62c 100755 --- a/esp32/host/upload-settings.sh +++ b/esp32/host/upload-settings.sh @@ -12,6 +12,8 @@ mqttHost=$1 mqttPrefix=$2 homieId=$3 +maxSteps=6 + settingsFile=settings.json if [ ! -f $settingsFile ]; then echo "$settingsFile missing" @@ -20,20 +22,20 @@ if [ ! -f $settingsFile ]; then fi mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "1" -r -echo "Waiting ..." +echo "(1 / $maxSteps) Waiting ..." mosquitto_sub -h $mqttHost -t "${mqttPrefix}${homieId}/#" -R -C 1 set -e -echo "Waiting 30 seconds ..." +echo "(2 / $maxSteps) Waiting 30 seconds ..." sleep 30 mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/\$implementation/config/set" -f $settingsFile -echo "Waiting for reboot ..." +echo "(3 / $maxSteps) Waiting for reboot ..." sleep 1 mosquitto_sub -h $mqttHost -t "${mqttPrefix}${homieId}/#" -R -C 1 -echo "Alive" +echo "(4 / $maxSteps) Alive" sleep 20 -echo "Create Backup ..." +echo "(5 / $maxSteps) Create Backup ..." mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/config/backup/set" -m "true" -r sleep 5 -echo "Shutdown ..." +echo "(6 / $maxSteps) Shutdown ..." mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "0" -r exit 0 diff --git a/esp32/include/ControllerConfiguration.h b/esp32/include/ControllerConfiguration.h index 312934c..bd9e24f 100644 --- a/esp32/include/ControllerConfiguration.h +++ b/esp32/include/ControllerConfiguration.h @@ -66,8 +66,8 @@ #define OUTPUT_ENABLE_PUMP GPIO_NUM_13 /**< GPIO 13 - Enable Pumps */ #define SENSOR_ONEWIRE GPIO_NUM_4 /**< GPIO 12 - Temperatur sensor, Battery and other cool onewire stuff */ -#define SENSOR_TANK_SDA GPIO_NUM_16 /**< GPIO 16 - echo feedback of water sensor */ -#define SENSOR_TANK_SCL GPIO_NUM_17 /**< GPIO 17 - trigger for water sensor */ +#define SENSOR_TANK_SDA GPIO_NUM_17 /**< GPIO 17 - water sensor SDA */ +#define SENSOR_TANK_SCL GPIO_NUM_16 /**< GPIO 16 - water sensor SCL */ #define BUTTON GPIO_NUM_0 /**< GPIO 0 - Fix button of NodeMCU */ #define CUSTOM1_PIN1 GPIO_NUM_34 /** direct gpio */ @@ -75,8 +75,6 @@ #define CUSTOM1_PIN5 GPIO_NUM_2 /** mosfet controlled */ #define CUSTOM1_PIN7 GPIO_NUM_12 /** mosfet controlled */ -#define I2C1_SDA GPIO_NUM_34 /**< GPIO 34 - I2C */ -#define I2C1_SCL GPIO_NUM_35 /**< GPIO 35 - I2C */ /* @} */ /** \addtogroup Configuration @@ -106,12 +104,15 @@ #define MAX_PLANTS 7 #define SOLAR_CHARGE_MIN_VOLTAGE 7 /**< Sun is rising (morning detected) */ #define SOLAR_CHARGE_MAX_VOLTAGE 9 /**< Sun is shining (noon) */ +#define SOLAR_MAX_VOLTAGE_POSSIBLE 100 /**< higher values are treated as not connected sensor */ #define VOLT_MAX_BATT 4.2f #define MAX_CONFIG_SETTING_ITEMS 100 /**< Parameter, that can be configured in Homie */ #define MAX_JSON_CONFIG_FILE_SIZE_CUSTOM 2500 -#define TEMPERATUR_TIMEOUT 3000 /**< 3 Seconds timeout for the temperatur sensors */ +#define TEMPERATUR_TIMEOUT 3000 /**< 3 Seconds timeout for the temperatures sensors */ +#define WATERSENSOR_TIMEOUT 3000 /**< 3 Seconds timeout for the water distance sensor */ +#define WATERSENSOR_CYCLE 5 /**< 5 sensor measurement are performed */ #define DS18B20_RESOLUTION 9 /**< 9bit temperature resolution -> 0.5°C steps */ #define UTC_OFFSET_DE 3600 /* UTC offset in seconds for Germany */ diff --git a/esp32/include/HomieConfiguration.h b/esp32/include/HomieConfiguration.h index 7bceab7..f133962 100644 --- a/esp32/include/HomieConfiguration.h +++ b/esp32/include/HomieConfiguration.h @@ -107,7 +107,7 @@ HomieSetting ntpServer("ntpServer", "NTP server (pool.ntp.org as d HomieSetting mPumpAllowedHourRangeStart##plant = HomieSetting("hourstart" strplant, "Plant" strplant " - Range pump allowed hour start (0-23)"); \ HomieSetting mPumpAllowedHourRangeEnd##plant = HomieSetting("hourend" strplant, "Plant" strplant " - Range pump allowed hour end (0-23)"); \ HomieSetting mPumpOnlyWhenLowLight##plant = HomieSetting("lowLight" strplant, "Plant" strplant " - Enable the Pump only, when there is no sunlight"); \ - HomieSetting mPumpCooldownInSeconds##plant = HomieSetting("delay" strplant, "Plant" strplant " - How long to wait until the pump is activated again (minutes)"); \ + HomieSetting mPumpCooldownInSeconds##plant = HomieSetting("delay" strplant, "Plant" strplant " - How long to wait until the pump is activated again (sec)"); \ HomieSetting pPumpDuration##plant = HomieSetting("pumpDuration" strplant, "Plant" strplant " - time seconds to water when pump is active"); \ HomieSetting pPumpMl##plant = HomieSetting("pumpAmount" strplant, "Plant" strplant " - ml (if using flowmeter) to water when pump is active"); \ HomieSetting pPowerLevel##plant = HomieSetting("powerLevel" strplant, "Plant" strplant " - pwm duty cycle in percent"); \ diff --git a/esp32/include/HomieTypes.h b/esp32/include/HomieTypes.h index 87d763a..9f5a31a 100644 --- a/esp32/include/HomieTypes.h +++ b/esp32/include/HomieTypes.h @@ -50,6 +50,36 @@ static const char *SENSOR_STRING[] = { //special value to indicate a shorted sensor when the plant is not deactivated but the sensor reads short circuit value #define SHORT_CIRCUIT_MODE -5 +/** + * @brief State of plants + * + */ +#define PLANTSTATE_NUM_DEACTIVATED -1 +#define PLANTSTATE_NUM_NO_SENSOR -2 +#define PLANTSTATE_NUM_WET 0x00 +#define PLANTSTATE_NUM_SUNNY_ALARM 0x11 +#define PLANTSTATE_NUM_ACTIVE_ALARM 0x41 +#define PLANTSTATE_NUM_ACTIVE_SUPESSED -3 +#define PLANTSTATE_NUM_ACTIVE 0x40 +#define PLANTSTATE_NUM_SUNNY 0x10 +#define PLANTSTATE_NUM_COOLDOWN_ALARM 0x21 +#define PLANTSTATE_NUM_COOLDOWN 0x20 +#define PLANTSTATE_NUM_AFTERWORK_ALARM 0x31 +#define PLANTSTATE_NUM_AFTERWORK 0x30 + +#define PLANTSTATE_STR_DEACTIVATED "deactivated" +#define PLANTSTATE_STR_NO_SENSOR "nosensor" +#define PLANTSTATE_STR_WET "wet" +#define PLANTSTATE_STR_SUNNY_ALARM "sunny+alarm" +#define PLANTSTATE_STR_ACTIVE_ALARM "active+alarm" +#define PLANTSTATE_STR_ACTIVE_SUPESSED "active+supressed" +#define PLANTSTATE_STR_ACTIVE "active" +#define PLANTSTATE_STR_SUNNY "sunny" +#define PLANTSTATE_STR_COOLDOWN_ALARM "cooldown+alarm" +#define PLANTSTATE_STR_COOLDOWN "cooldown" +#define PLANTSTATE_STR_AFTERWORK_ALARM "after-work+alarm" +#define PLANTSTATE_STR_AFTERWORK "after-work" + typedef struct PlantSettings_t { HomieSetting *pSensorDry; diff --git a/esp32/include/LogDefines.h b/esp32/include/LogDefines.h index b7d4ada..c400a6b 100644 --- a/esp32/include/LogDefines.h +++ b/esp32/include/LogDefines.h @@ -42,4 +42,5 @@ #define LOG_SLEEP_CYCLE 102 #define LOG_MISSING_PUMP -4 #define LOG_BOOT_ERROR_DETECTION 10000 -#endif \ No newline at end of file +#define LOG_SOLAR_CHARGER_MISSING 300 +#endif diff --git a/esp32/include/PlantCtrl.h b/esp32/include/PlantCtrl.h index 91c236d..cae0ccf 100644 --- a/esp32/include/PlantCtrl.h +++ b/esp32/include/PlantCtrl.h @@ -199,7 +199,7 @@ public: return this->mSetting->pPumpOnlyWhenLowLight->get(); } - void publishState(String state); + void publishState(int stateNumber, String stateString); bool switchHandler(const HomieRange &range, const String &value); diff --git a/esp32/src/PlantCtrl.cpp b/esp32/src/PlantCtrl.cpp index 0c8b09b..52101fb 100644 --- a/esp32/src/PlantCtrl.cpp +++ b/esp32/src/PlantCtrl.cpp @@ -208,11 +208,16 @@ void Plant::deactivatePump(void) } } -void Plant::publishState(String state) +void Plant::publishState(int stateNumber, String stateString) { + String buffer; + StaticJsonDocument<200> doc; if (this->mConnected) { - this->mPlant->setProperty("state").send(state); + doc["number"] = stateNumber; + doc["message"] = stateString; + serializeJson(doc, buffer); + this->mPlant->setProperty("state").send(buffer.c_str()); } } diff --git a/esp32/src/main.cpp b/esp32/src/main.cpp index 9594178..6a3a4b1 100644 --- a/esp32/src/main.cpp +++ b/esp32/src/main.cpp @@ -329,11 +329,14 @@ void readPowerSwitchedSensors() } } - waterRawSensor.clear(); - tankSensor.setTimeout(500); + Wire.begin(SENSOR_TANK_SDA, SENSOR_TANK_SCL); + // Source: https://www.st.com/resource/en/datasheet/vl53l0x.pdf + tankSensor.setAddress(0x52); + tankSensor.setBus(&Wire); + delay(50); long start = millis(); bool distanceReady = false; - while (start + 500 > millis()) + while ((start + WATERSENSOR_TIMEOUT) > millis()) { if (tankSensor.init()) { @@ -342,17 +345,18 @@ void readPowerSwitchedSensors() } else { - delay(20); + delay(200); } } if (distanceReady) { + waterRawSensor.clear(); tankSensor.setSignalRateLimit(0.1); // increase laser pulse periods (defaults are 14 and 10 PCLKs) tankSensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18); tankSensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14); tankSensor.setMeasurementTimingBudget(200000); - for (int readCnt = 0; readCnt < 5; readCnt++) + for (int readCnt = 0; readCnt < WATERSENSOR_CYCLE; readCnt++) { if (!tankSensor.timeoutOccurred()) { @@ -362,7 +366,7 @@ void readPowerSwitchedSensors() waterRawSensor.add(distance); } } - delay(10); + delay(50); } Serial << "Distance sensor " << waterRawSensor.getMedian() << " mm" << endl; } @@ -426,7 +430,7 @@ int determineNextPump(bool isLowLight) Plant plant = mPlants[i]; if (!plant.isPumpTriggerActive()) { - plant.publishState("deactivated"); + plant.publishState(PLANTSTATE_NUM_DEACTIVATED, PLANTSTATE_STR_DEACTIVATED); log(LOG_LEVEL_DEBUG, String(String(i) + " Skip deactivated pump"), LOG_DEBUG_CODE); continue; } @@ -434,11 +438,11 @@ int determineNextPump(bool isLowLight) { if (wateralarm) { - plant.publishState("cooldown+alarm"); + plant.publishState(PLANTSTATE_NUM_COOLDOWN_ALARM, PLANTSTATE_STR_COOLDOWN_ALARM); } else { - plant.publishState("cooldown"); + plant.publishState(PLANTSTATE_NUM_COOLDOWN, PLANTSTATE_STR_COOLDOWN); } log(LOG_LEVEL_DEBUG, String(String(i) + " Skipping due to cooldown " + String(rtcLastWateringPlant[i] + plant.getCooldownInSeconds())), LOG_DEBUG_CODE); continue; @@ -447,11 +451,11 @@ int determineNextPump(bool isLowLight) { if (wateralarm) { - plant.publishState("sunny+alarm"); + plant.publishState(PLANTSTATE_NUM_SUNNY_ALARM, PLANTSTATE_STR_SUNNY_ALARM); } else { - plant.publishState("sunny"); + plant.publishState(PLANTSTATE_NUM_SUNNY, PLANTSTATE_STR_SUNNY); } log(LOG_LEVEL_DEBUG, String(String(i) + " No pump required: due to light"), LOG_DEBUG_CODE); @@ -461,7 +465,7 @@ int determineNextPump(bool isLowLight) { if (equalish(plant.getCurrentMoistureRaw(), MISSING_SENSOR)) { - plant.publishState("nosensor"); + plant.publishState(PLANTSTATE_NUM_NO_SENSOR, PLANTSTATE_STR_NO_SENSOR); log(LOG_LEVEL_ERROR, String(String(i) + " No pump possible: missing sensor"), LOG_MISSING_PUMP); continue; } @@ -480,14 +484,17 @@ int determineNextPump(bool isLowLight) { if (wateralarm) { - plant.publishState("active+alarm"); + plant.publishState(PLANTSTATE_NUM_ACTIVE_ALARM, PLANTSTATE_STR_ACTIVE_ALARM); } else { - if(mDownloadMode){ - plant.publishState("active+supressed"); - }else { - plant.publishState("active"); + if (mDownloadMode) + { + plant.publishState(PLANTSTATE_NUM_ACTIVE_SUPESSED, PLANTSTATE_STR_ACTIVE_SUPESSED); + } + else + { + plant.publishState(PLANTSTATE_NUM_ACTIVE, PLANTSTATE_STR_ACTIVE); } } @@ -504,11 +511,11 @@ int determineNextPump(bool isLowLight) { if (wateralarm) { - plant.publishState("after-work+alarm"); + plant.publishState(PLANTSTATE_NUM_AFTERWORK_ALARM, PLANTSTATE_STR_AFTERWORK_ALARM); } else { - plant.publishState("after-work"); + plant.publishState(PLANTSTATE_NUM_AFTERWORK, PLANTSTATE_STR_AFTERWORK); } log(LOG_LEVEL_DEBUG, String(String(i) + " ignored due to time boundary: " + String(plant.getHoursStart()) + " to " + String(plant.getHoursEnd()) + " ( current " + String(getCurrentHour()) + " )"), LOG_DEBUG_CODE); } @@ -516,7 +523,7 @@ int determineNextPump(bool isLowLight) } else { - plant.publishState("wet"); + plant.publishState(PLANTSTATE_NUM_WET, PLANTSTATE_STR_WET); // plant was detected as wet, remove consecutive count consecutiveWateringPlant[i] = 0; } @@ -799,7 +806,6 @@ void safeSetup() { mPlants[i].initSensors(); } - Wire.begin(SENSOR_TANK_SDA, SENSOR_TANK_SCL); readPowerSwitchedSensors(); Homie.setup(); @@ -1034,7 +1040,7 @@ void plantcontrol() readOneWireSensors(); - Serial << "W : " << waterRawSensor.getAverage() << " cm (" << String(waterLevelMax.get() - waterRawSensor.getAverage()) << "%)" << endl; + Serial << "W : " << waterRawSensor.getAverage() << " mm (" << String(waterLevelMax.get() - waterRawSensor.getAverage()) << " mm left)" << endl; float batteryVoltage = battery.getVoltage(BATTSENSOR_INDEX_BATTERY); float chipTemp = battery.getTemperature(); @@ -1042,14 +1048,19 @@ void plantcontrol() if (aliveWasRead()) { - float remaining = waterLevelMax.get() - waterRawSensor.getAverage(); - if (!isnan(remaining)) + /* Publish water values, if available */ + if (waterRawSensor.getCount() > 0) { - sensorWater.setProperty("remaining").send(String(remaining)); - } - if (!isnan(waterRawSensor.getAverage())) - { - sensorWater.setProperty("distance").send(String(waterRawSensor.getAverage())); + float remaining = (waterLevelMax.get() - waterRawSensor.getAverage()); + if (!isnan(remaining)) + { + /* measuring the distance from top -> smaller value means more water: */ + sensorWater.setProperty("remaining").send(String(100.0 - (remaining/100))); + } + if (!isnan(waterRawSensor.getAverage())) + { + sensorWater.setProperty("distance").send(String(waterRawSensor.getAverage())); + } } sensorLipo.setProperty("percent").send(String(100 * batteryVoltage / VOLT_MAX_BATT)); sensorLipo.setProperty("volt").send(String(batteryVoltage)); @@ -1058,7 +1069,11 @@ void plantcontrol() sensorLipo.setProperty("ICA").send(String(battery.getICA())); sensorLipo.setProperty("DCA").send(String(battery.getDCA())); sensorLipo.setProperty("CCA").send(String(battery.getCCA())); - sensorSolar.setProperty("volt").send(String(mSolarVoltage)); + if (mSolarVoltage < SOLAR_MAX_VOLTAGE_POSSIBLE) { + sensorSolar.setProperty("volt").send(String(mSolarVoltage)); + } else { + log(LOG_LEVEL_INFO, String("Ignore unrealistc sun voltage" + String(mSolarVoltage) +"V"), LOG_SOLAR_CHARGER_MISSING); + } sensorTemp.setProperty(TEMPERATUR_SENSOR_CHIP).send(String(chipTemp)); } else @@ -1080,7 +1095,12 @@ bool isLowLight = mSolarVoltage <= 9; #endif // TIMED_LIGHT_PIN bool isLiquid = waterTemp > 5; - bool hasWater = true; // FIXME remaining > waterLevelMin.get(); + bool hasWater = true; // By default activate the pump + if (waterRawSensor.getCount() > 0) + { + hasWater = ( (waterLevelMax.get() - waterRawSensor.getAverage()) > waterLevelMin.get() ); + } + // FIXME no water warning message pumpToRun = determineNextPump(isLowLight); // early aborts @@ -1093,6 +1113,8 @@ bool isLowLight = mSolarVoltage <= 9; { log(LOG_LEVEL_INFO, LOG_PUMP_AND_DOWNLOADMODE, LOG_PUMP_AND_DOWNLOADMODE_CODE); pumpToRun = -1; + } else { + /* Pump can be used :) */ } } else @@ -1101,8 +1123,9 @@ bool isLowLight = mSolarVoltage <= 9; pumpToRun = -1; } } - else{ - log(LOG_LEVEL_ERROR, LOG_VERY_COLD_WATER, LOG_VERY_COLD_WATER_CODE); + else + { + log(LOG_LEVEL_ERROR, LOG_VERY_COLD_WATER, LOG_VERY_COLD_WATER_CODE); pumpToRun = -1; } } diff --git a/esp32test/Esp32DeepSleepTest/platformio.ini b/esp32test/Esp32DeepSleepTest/platformio.ini index 19472e4..1733f51 100644 --- a/esp32test/Esp32DeepSleepTest/platformio.ini +++ b/esp32test/Esp32DeepSleepTest/platformio.ini @@ -12,5 +12,5 @@ platform = espressif32 board = esp32doit-devkit-v1 framework = arduino +lib_deps = pololu/VL53L0X@^1.3.1 build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY -DBOOTLOADER_LOG_LEVEL_VERBOSE -DLOG_DEFAULT_LEVEL_VERBOSE -DCORE_DEBUG_LEVEL=5 -lib_deps = \ No newline at end of file diff --git a/esp32test/Esp32DeepSleepTest/src/main.cpp b/esp32test/Esp32DeepSleepTest/src/main.cpp index a62d476..aced6b8 100644 --- a/esp32test/Esp32DeepSleepTest/src/main.cpp +++ b/esp32test/Esp32DeepSleepTest/src/main.cpp @@ -1,13 +1,83 @@ #include +#include "driver/pcnt.h" +#include + +#define SENSOR_TANK_SDA GPIO_NUM_16 /**< GPIO 16 - echo feedback of water sensor */ +#define SENSOR_TANK_SCL GPIO_NUM_17 /**< GPIO 17 - trigger for water sensor */ + + +#define OUTPUT_SENSOR 14 +#define SENSOR_PLANT 17 + +VL53L0X tankSensor; +bool distanceReady = false; + +void initializeTanksensor() { + Wire.begin(SENSOR_TANK_SDA, SENSOR_TANK_SCL, 100000UL /* 100kHz */); + tankSensor.setBus(&Wire); + delay(100); + tankSensor.setTimeout(500); + long start = millis(); + while (start + 500 > millis()) + { + if (tankSensor.init()) + { + distanceReady = true; + break; + } + else + { + delay(20); + } + } + + if ((distanceReady) && (!tankSensor.timeoutOccurred())) + { + Serial.println("Sensor init done"); + tankSensor.setSignalRateLimit(0.1); + // increase laser pulse periods (defaults are 14 and 10 PCLKs) + tankSensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18); + tankSensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14); + tankSensor.setMeasurementTimingBudget(200000); + tankSensor.startContinuous(); + } else { + Serial.println("Sensor init failed"); + } +} -RTC_SLOW_ATTR uint8_t tick = 0; -RTC_SLOW_ATTR bool dir = true; void setup() { Serial.begin(115200); - + pinMode(OUTPUT_SENSOR, OUTPUT); + + digitalWrite(OUTPUT_SENSOR, HIGH); + Serial.println("Nodemcu ESP32 Start done"); + + initializeTanksensor(); } -void loop(){ - Serial.println("alive"); -} \ No newline at end of file +void loop() { + + delay(500); + + + if ((distanceReady) && (!tankSensor.timeoutOccurred())) + { + uint16_t distance = tankSensor.readRangeSingleMillimeters(); + if (distance > 8000) { + Serial.println("Reset due invalid distance: 8 meter"); + Wire.end(); + delay(1000); + initializeTanksensor(); + } else { + Serial.print("Distance"); + Serial.println(distance); + } + } else { + Serial.println("Timeout"); + tankSensor.stopContinuous(); + Wire.end(); + delay(100); + initializeTanksensor(); + } +}