2020-11-06 22:19:16 +01:00
/** \addtogroup Controller
* @ {
*
2020-09-07 18:18:46 +02:00
* @ file main . cpp
* @ author Ollo
* @ brief PlantControl
* @ version 0.1
* @ date 2020 - 05 - 01
*
* @ copyright Copyright ( c ) 2020
*/
2020-12-21 16:12:46 +01:00
/******************************************************************************
* INCLUDES
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-07-01 21:19:51 +02:00
# include "LogDefines.h"
2021-06-29 22:09:30 +02:00
# include "FileUtils.h"
2021-07-01 21:19:51 +02:00
# include "TimeUtils.h"
2020-09-07 18:18:46 +02:00
# include "PlantCtrl.h"
# include "ControllerConfiguration.h"
2020-10-16 16:22:48 +02:00
# include "HomieConfiguration.h"
2020-12-13 17:17:54 +01:00
# include "DallasTemperature.h"
2020-09-07 18:18:46 +02:00
# include <Homie.h>
2020-10-19 01:39:56 +02:00
# include "time.h"
2020-09-20 19:18:47 +02:00
# include "esp_sleep.h"
2020-10-16 19:26:05 +02:00
# include "RunningMedian.h"
2020-11-28 01:45:57 +01:00
# include "WakeReason.h"
2020-10-21 16:43:26 +02:00
# include <stdint.h>
2020-11-06 21:00:11 +01:00
# include <math.h>
2020-12-13 17:17:54 +01:00
# include <OneWire.h>
2021-02-26 09:33:16 +01:00
# include "DS2438.h"
2021-05-24 20:07:22 +02:00
# include "soc/soc.h"
# include "soc/rtc_cntl_reg.h"
2021-06-06 21:23:21 +02:00
# include <Wire.h>
# include <VL53L0X.h>
2020-09-07 18:18:46 +02:00
2020-12-21 16:12:46 +01:00
/******************************************************************************
* DEFINES
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-11-04 21:57:40 +01:00
# define AMOUNT_SENOR_QUERYS 8
2021-07-01 20:39:51 +02:00
# define MAX_TANK_DEPTH 2000
2021-05-24 19:37:27 +02:00
# define TEST_TOPIC "roundtrip\0"
2021-07-01 21:19:51 +02:00
# define LOG_TOPIC "log\0"
2021-05-27 22:53:49 +02:00
# define BACKUP_TOPIC "$implementation / config / backup / set\0"
2021-06-29 22:09:30 +02:00
# define CONFIG_FILE " / homie / config.json"
# define CONFIG_FILE_BACKUP " / homie / config.json.bak"
2021-05-24 19:37:27 +02:00
2021-05-27 22:53:49 +02:00
# define getTopic(test, topic) \
char * topic = new char [ strlen ( Homie . getConfiguration ( ) . mqtt . baseTopic ) + strlen ( Homie . getConfiguration ( ) . deviceId ) + 1 + strlen ( test ) + 1 ] ; \
strcpy ( topic , Homie . getConfiguration ( ) . mqtt . baseTopic ) ; \
strcat ( topic , Homie . getConfiguration ( ) . deviceId ) ; \
strcat ( topic , " / " ) ; \
strcat ( topic , test ) ;
2020-11-06 19:15:43 +01:00
2020-12-21 16:12:46 +01:00
/******************************************************************************
* FUNCTION PROTOTYPES
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-07-01 21:19:51 +02:00
void log ( int level , String message , int code ) ;
2021-08-29 20:45:50 +02:00
int determineNextPump ( bool lowLight ) ;
2021-07-01 20:50:47 +02:00
void plantcontrol ( ) ;
2021-05-24 14:58:35 +02:00
void readPowerSwitchedSensors ( ) ;
2021-08-29 20:45:50 +02:00
bool determineTimedLightState ( bool lowLight ) ;
2020-12-21 16:12:46 +01:00
/******************************************************************************
* NON VOLATILE VARIABLES in DEEP SLEEP
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-07-01 23:09:02 +02:00
RTC_DATA_ATTR int lastPumpRunning = - 1 ; /**< store last successfully waterd plant */
RTC_DATA_ATTR long lastWaterValue = 0 ; /**< to calculate the used water per plant */
2021-08-29 20:45:50 +02:00
# if defined(TIMED_LIGHT_PIN)
RTC_DATA_ATTR bool timedLightOn = false ; /**< allow fast recovery after poweron */
# endif // TIMED_LIGHT_PIN
2021-02-26 09:33:16 +01:00
2021-07-01 23:09:02 +02:00
RTC_DATA_ATTR long rtcLastWateringPlant [ MAX_PLANTS ] = { 0 } ;
2021-08-23 00:58:37 +02:00
RTC_DATA_ATTR long consecutiveWateringPlant [ MAX_PLANTS ] = { 0 } ;
2021-02-26 09:33:16 +01:00
2020-12-21 16:12:46 +01:00
/******************************************************************************
* LOCAL VARIABLES
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-04-07 22:42:31 +02:00
bool volatile mDownloadMode = false ; /**< Controller must not sleep */
2021-05-26 21:46:33 +02:00
bool volatile mSensorsRead = false ; /**< Sensors are read without Wifi or MQTT */
2021-05-24 19:37:27 +02:00
bool volatile mAliveWasRead = false ;
bool volatile mMQTTReady = false ;
2020-10-16 19:26:05 +02:00
2020-09-21 20:42:24 +02:00
bool mConfigured = false ;
2021-02-26 09:33:16 +01:00
long nextBlink = 0 ; /**< Time needed in main loop to support expected blink code */
2020-09-07 18:18:46 +02:00
2020-10-16 19:26:05 +02:00
RunningMedian waterRawSensor = RunningMedian ( 5 ) ;
2021-05-26 21:46:33 +02:00
float mSolarVoltage = 0.0f ; /**< Voltage from solar panels */
2021-05-24 14:58:35 +02:00
unsigned long setupFinishedTimestamp ;
2021-02-16 22:25:12 +01:00
/*************************** Hardware abstraction *****************************/
2020-10-16 19:26:05 +02:00
2021-04-07 18:49:59 +02:00
OneWire oneWire ( SENSOR_ONEWIRE ) ;
2020-12-13 17:17:54 +01:00
DallasTemperature sensors ( & oneWire ) ;
2021-05-27 21:43:15 +02:00
DS2438 battery ( & oneWire , 0.0333333f , AMOUNT_SENOR_QUERYS ) ;
2021-06-06 21:23:21 +02:00
VL53L0X tankSensor ;
2020-09-07 18:18:46 +02:00
2020-11-04 21:57:40 +01:00
Plant mPlants [ MAX_PLANTS ] = {
Plant ( SENSOR_PLANT0 , OUTPUT_PUMP0 , 0 , & plant0 , & mSetting0 ) ,
Plant ( SENSOR_PLANT1 , OUTPUT_PUMP1 , 1 , & plant1 , & mSetting1 ) ,
Plant ( SENSOR_PLANT2 , OUTPUT_PUMP2 , 2 , & plant2 , & mSetting2 ) ,
Plant ( SENSOR_PLANT3 , OUTPUT_PUMP3 , 3 , & plant3 , & mSetting3 ) ,
Plant ( SENSOR_PLANT4 , OUTPUT_PUMP4 , 4 , & plant4 , & mSetting4 ) ,
Plant ( SENSOR_PLANT5 , OUTPUT_PUMP5 , 5 , & plant5 , & mSetting5 ) ,
Plant ( SENSOR_PLANT6 , OUTPUT_PUMP6 , 6 , & plant6 , & mSetting6 ) } ;
2020-12-21 16:12:46 +01:00
/******************************************************************************
* LOCAL FUNCTIONS
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-10-20 18:06:37 +02:00
2021-07-01 20:50:47 +02:00
void espDeepSleepFor ( long seconds , bool activatePump )
2020-11-04 21:57:40 +01:00
{
2021-04-07 22:42:31 +02:00
if ( mDownloadMode )
2020-11-04 21:57:40 +01:00
{
2021-07-01 22:06:50 +02:00
log ( LOG_LEVEL_DEBUG , " abort deepsleep, DownloadMode active " , LOG_DEBUG_CODE ) ;
2020-10-31 03:51:35 +01:00
return ;
}
2021-07-01 20:50:47 +02:00
if ( mAliveWasRead )
2021-05-26 21:46:33 +02:00
{
2021-05-24 19:37:27 +02:00
for ( int i = 0 ; i < 10 ; i + + )
2020-11-04 21:57:40 +01:00
{
2021-05-24 19:37:27 +02:00
long cTime = getCurrentTime ( ) ;
if ( cTime < 100000 )
{
delay ( 100 ) ;
}
else
{
break ;
}
2020-10-31 19:25:13 +01:00
}
2021-07-01 23:09:02 +02:00
if ( getCurrentTime ( ) < 100000 )
{
log ( LOG_LEVEL_DEBUG , " NTP timeout before deepsleep " , LOG_DEBUG_CODE ) ;
}
2020-10-31 19:25:13 +01:00
}
2021-08-29 20:45:50 +02:00
//allo hold for all digital pins
gpio_deep_sleep_hold_en ( ) ;
2020-10-31 19:25:13 +01:00
2020-10-28 20:00:24 +01:00
esp_sleep_pd_config ( ESP_PD_DOMAIN_RTC_PERIPH , ESP_PD_OPTION_OFF ) ;
2020-10-31 19:25:13 +01:00
esp_sleep_pd_config ( ESP_PD_DOMAIN_RTC_SLOW_MEM , ESP_PD_OPTION_ON ) ;
2021-07-01 23:09:02 +02:00
esp_sleep_pd_config ( ESP_PD_DOMAIN_RTC_FAST_MEM , ESP_PD_OPTION_ON ) ;
2020-11-04 21:57:40 +01:00
if ( activatePump )
{
2021-02-26 09:33:16 +01:00
esp_sleep_pd_config ( ESP_PD_DOMAIN_XTAL , ESP_PD_OPTION_ON ) ;
2021-08-29 20:45:50 +02:00
2021-05-24 15:50:04 +02:00
gpio_hold_en ( OUTPUT_ENABLE_PUMP ) ; //pump pwr
2020-11-04 21:57:40 +01:00
}
else
{
2021-05-24 15:50:04 +02:00
gpio_hold_dis ( OUTPUT_ENABLE_PUMP ) ; //pump pwr
2021-04-07 18:49:59 +02:00
digitalWrite ( OUTPUT_ENABLE_PUMP , LOW ) ;
digitalWrite ( OUTPUT_ENABLE_SENSOR , LOW ) ;
2020-11-04 21:57:40 +01:00
for ( int i = 0 ; i < MAX_PLANTS ; i + + )
{
2020-10-23 19:32:12 +02:00
mPlants [ i ] . deactivatePump ( ) ;
}
2020-10-23 16:47:40 +02:00
}
2021-05-24 19:37:27 +02:00
gpio_hold_en ( OUTPUT_PUMP0 ) ;
gpio_hold_en ( OUTPUT_PUMP1 ) ;
gpio_hold_en ( OUTPUT_PUMP2 ) ;
gpio_hold_en ( OUTPUT_PUMP3 ) ;
gpio_hold_en ( OUTPUT_PUMP4 ) ;
gpio_hold_en ( OUTPUT_PUMP5 ) ;
gpio_hold_en ( OUTPUT_PUMP6 ) ;
2021-08-29 20:45:50 +02:00
# if defined(TIMED_LIGHT_PIN)
gpio_hold_en ( TIMED_LIGHT_PIN ) ;
# endif // TIMED_LIGHT_PIN
2020-10-21 20:46:09 +02:00
2020-11-04 21:57:40 +01:00
esp_sleep_enable_timer_wakeup ( ( seconds * 1000U * 1000U ) ) ;
2021-07-01 20:50:47 +02:00
if ( mAliveWasRead )
2021-05-26 21:46:33 +02:00
{
2021-07-01 23:09:02 +02:00
delay ( 1000 ) ;
2021-05-24 15:50:04 +02:00
Homie . prepareToSleep ( ) ;
2021-05-26 21:46:33 +02:00
}
else
{
2021-05-24 15:50:04 +02:00
esp_deep_sleep_start ( ) ;
}
2020-10-21 19:50:05 +02:00
}
2021-05-24 14:58:35 +02:00
//requires homie being started
2021-07-01 20:50:47 +02:00
void readOneWireSensors ( )
2021-05-26 21:46:33 +02:00
{
2021-05-29 21:37:27 +02:00
for ( uint8_t i = 0 ; i < sensors . getDeviceCount ( ) ; i + + )
2021-04-07 20:27:42 +02:00
{
2021-05-29 21:37:27 +02:00
uint8_t ds18b20Address [ 8 ] ;
2021-05-27 21:43:15 +02:00
bool valid = false ;
float temp = - 127 ;
for ( int retry = 0 ; retry < AMOUNT_SENOR_QUERYS & & ! valid ; retry + + )
{
2021-05-29 21:37:27 +02:00
bool validAddress = sensors . getAddress ( ds18b20Address , i ) ;
if ( validAddress & & sensors . validFamily ( ds18b20Address ) )
2021-05-27 21:43:15 +02:00
{
2021-05-29 21:37:27 +02:00
temp = sensors . getTempC ( ds18b20Address ) ;
if ( temp ! = - 127 )
{
valid = true ;
}
else
{
delay ( 10 ) ;
}
2021-05-27 21:43:15 +02:00
}
}
2021-06-06 21:23:21 +02:00
if ( ! valid )
{
2021-05-29 21:37:27 +02:00
//wrong family or crc errors on each retry
continue ;
}
2021-05-27 21:43:15 +02:00
2021-05-29 21:37:27 +02:00
char buf [ sizeof ( ds18b20Address ) * 2 ] ;
2021-04-07 20:27:42 +02:00
snprintf ( buf , sizeof ( buf ) , " %.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X " ,
ds18b20Address [ 0 ] ,
ds18b20Address [ 1 ] ,
ds18b20Address [ 2 ] ,
ds18b20Address [ 3 ] ,
ds18b20Address [ 4 ] ,
ds18b20Address [ 5 ] ,
ds18b20Address [ 6 ] ,
ds18b20Address [ 7 ] ) ;
2021-05-27 21:43:15 +02:00
if ( valid )
2021-05-26 21:46:33 +02:00
{
2021-05-29 21:37:27 +02:00
Serial < < " DS18S20 Temperatur " < < String ( buf ) < < " : " < < temp < < " °C " < < endl ;
2021-06-06 21:23:21 +02:00
if ( strcmp ( lipoSensorAddr . get ( ) , buf ) = = 0 )
2021-05-27 21:43:15 +02:00
{
2021-07-01 20:50:47 +02:00
if ( mAliveWasRead )
2021-05-27 21:43:15 +02:00
{
sensorTemp . setProperty ( TEMPERATUR_SENSOR_LIPO ) . send ( String ( temp ) ) ;
}
Serial < < " Lipo Temperatur " < < temp < < " °C " < < endl ;
}
2021-06-06 21:23:21 +02:00
if ( strcmp ( waterSensorAddr . get ( ) , buf ) = = 0 )
2021-05-26 21:46:33 +02:00
{
2021-07-01 20:50:47 +02:00
if ( mAliveWasRead )
2021-05-27 21:43:15 +02:00
{
sensorTemp . setProperty ( TEMPERATUR_SENSOR_WATER ) . send ( String ( temp ) ) ;
}
Serial < < " Water Temperatur " < < temp < < " °C " < < endl ;
2021-04-07 20:27:42 +02:00
}
2021-05-27 21:43:15 +02:00
/* Always send the sensor address with the temperatur value */
2021-07-01 20:50:47 +02:00
if ( mAliveWasRead )
2021-05-26 21:46:33 +02:00
{
2021-05-27 21:43:15 +02:00
sensorTemp . setProperty ( String ( buf ) ) . send ( String ( temp ) ) ;
2021-05-24 14:58:35 +02:00
}
2021-05-27 22:53:49 +02:00
}
else
{
2021-05-29 21:37:27 +02:00
Serial < < " DS18S20 sensor " < < String ( buf ) < < " could not be read " < < temp < < endl ;
2021-05-26 21:46:33 +02:00
}
2020-12-13 17:17:54 +01:00
}
2021-02-26 09:33:16 +01:00
battery . update ( ) ;
mSolarVoltage = battery . getVoltage ( BATTSENSOR_INDEX_SOLAR ) * SOLAR_VOLT_FACTOR ;
2021-05-24 14:58:35 +02:00
Serial . flush ( ) ;
}
/**
* @ brief Sensors , that are connected to GPIOs , mandatory for WIFI .
* These sensors ( ADC2 ) can only be read when no Wifi is used .
*/
void readPowerSwitchedSensors ( )
{
digitalWrite ( OUTPUT_ENABLE_SENSOR , HIGH ) ;
2021-07-21 21:23:58 +02:00
delay ( 50 ) ;
2021-07-01 20:39:51 +02:00
for ( int i = 0 ; i < MAX_PLANTS ; i + + )
{
2021-07-21 21:23:58 +02:00
mPlants [ i ] . startMoistureMeasurement ( ) ;
2021-07-01 20:39:51 +02:00
}
2021-07-21 21:23:58 +02:00
delay ( MOISTURE_MEASUREMENT_DURATION ) ;
for ( int i = 0 ; i < MAX_PLANTS ; i + + )
2020-12-13 17:17:54 +01:00
{
2021-07-21 21:23:58 +02:00
mPlants [ i ] . stopMoistureMeasurement ( ) ;
2020-12-13 17:17:54 +01:00
}
2021-07-21 21:34:14 +02:00
for ( int i = 0 ; i < MAX_PLANTS ; i + + )
{
Serial < < " Plant " < < i < < " measurement: " < < mPlants [ i ] . getCurrentMoisture ( ) < < " hz " < < endl ;
}
2021-07-01 20:39:51 +02:00
waterRawSensor . clear ( ) ;
2021-06-06 21:23:21 +02:00
Wire . setPins ( SENSOR_TANK_TRG , SENSOR_TANK_ECHO ) ;
Wire . begin ( ) ;
tankSensor . setTimeout ( 500 ) ;
long start = millis ( ) ;
bool distanceReady = false ;
while ( start + 500 > millis ( ) )
2021-05-24 14:58:35 +02:00
{
2021-06-06 21:23:21 +02:00
if ( tankSensor . init ( ) )
2021-05-24 14:58:35 +02:00
{
2021-06-06 21:23:21 +02:00
distanceReady = true ;
break ;
}
else
{
delay ( 20 ) ;
2021-05-24 14:58:35 +02:00
}
}
2021-06-06 21:23:21 +02:00
if ( distanceReady )
{
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 ) ;
2021-05-24 14:58:35 +02:00
2021-06-06 21:23:21 +02:00
for ( int readCnt = 0 ; readCnt < 5 ; readCnt + + )
{
2021-07-01 21:19:51 +02:00
if ( ! tankSensor . timeoutOccurred ( ) )
{
2021-07-01 20:39:51 +02:00
uint16_t distance = tankSensor . readRangeSingleMillimeters ( ) ;
2021-07-01 21:19:51 +02:00
if ( distance < MAX_TANK_DEPTH )
{
waterRawSensor . add ( distance ) ;
2021-07-01 20:39:51 +02:00
}
2021-06-06 21:23:21 +02:00
}
delay ( 10 ) ;
}
Serial < < " Distance sensor " < < waterRawSensor . getMedian ( ) < < " mm " < < endl ;
}
else
{
2021-07-01 23:09:02 +02:00
log ( LOG_LEVEL_WARN , LOG_TANKSENSOR_FAIL_DETECT , LOG_TANKSENSOR_FAIL_DETECT_CODE ) ;
2021-06-06 21:23:21 +02:00
}
2020-11-06 21:00:11 +01:00
2020-10-16 19:26:05 +02:00
/* deactivate the sensors */
2021-04-07 18:49:59 +02:00
digitalWrite ( OUTPUT_ENABLE_SENSOR , LOW ) ;
2020-09-07 18:18:46 +02:00
}
2021-05-26 21:46:33 +02:00
void onMessage ( char * incoming , char * payload , AsyncMqttClientMessageProperties properties , size_t len , size_t index , size_t total )
{
2021-05-27 22:53:49 +02:00
getTopic ( TEST_TOPIC , testTopic ) ;
if ( strcmp ( incoming , testTopic ) = = 0 )
2021-05-26 21:46:33 +02:00
{
2021-05-24 19:37:27 +02:00
mAliveWasRead = true ;
}
2021-05-27 22:53:49 +02:00
delete testTopic ;
getTopic ( BACKUP_TOPIC , backupTopic ) ;
if ( strcmp ( incoming , backupTopic ) = = 0 )
2021-05-26 22:25:12 +02:00
{
2021-07-01 21:19:51 +02:00
if ( strcmp ( payload , " true " ) = = 0 )
{
2021-06-29 22:09:30 +02:00
bool backupSucessful = copyFile ( CONFIG_FILE , CONFIG_FILE_BACKUP ) ;
printFile ( CONFIG_FILE_BACKUP ) ;
2021-07-01 23:09:02 +02:00
if ( backupSucessful )
{
log ( LOG_LEVEL_INFO , LOG_BACKUP_SUCCESSFUL , LOG_BACKUP_SUCCESSFUL_CODE ) ;
}
else
{
log ( LOG_LEVEL_INFO , LOG_BACKUP_FAILED , LOG_BACKUP_FAILED_CODE ) ;
2021-07-01 21:19:51 +02:00
}
2021-06-29 22:09:30 +02:00
Homie . getMqttClient ( ) . publish ( backupTopic , 2 , true , " false " ) ;
}
2021-05-26 22:25:12 +02:00
}
2021-05-27 22:53:49 +02:00
delete backupTopic ;
}
2021-05-24 19:37:27 +02:00
2020-11-04 21:57:40 +01:00
void onHomieEvent ( const HomieEvent & event )
{
switch ( event . type )
{
2021-05-24 15:50:04 +02:00
case HomieEventType : : READY_TO_SLEEP :
esp_deep_sleep_start ( ) ;
break ;
2020-11-04 21:57:40 +01:00
case HomieEventType : : SENDING_STATISTICS :
break ;
case HomieEventType : : MQTT_READY :
2021-05-26 21:46:33 +02:00
if ( mSensorsRead )
{
2021-04-07 21:54:53 +02:00
Serial . printf ( " Timeout occured... too late! \r \n " ) ;
return ;
}
mSensorsRead = true ; // MQTT is working, deactivate timeout logic
2020-11-04 21:57:40 +01:00
configTime ( 0 , 0 , ntpServer . get ( ) ) ;
2021-02-26 09:33:16 +01:00
2021-05-24 19:37:27 +02:00
{
2021-05-27 22:53:49 +02:00
getTopic ( TEST_TOPIC , testopic )
2021-05-26 21:46:33 +02:00
Homie . getMqttClient ( )
2021-05-27 22:53:49 +02:00
. subscribe ( testopic , 2 ) ;
Homie . getMqttClient ( ) . publish ( testopic , 2 , false , " ping " ) ;
2021-05-24 19:37:27 +02:00
Homie . getMqttClient ( ) . onMessage ( onMessage ) ;
2021-05-27 22:53:49 +02:00
getTopic ( BACKUP_TOPIC , backupTopic )
Homie . getMqttClient ( )
. subscribe ( backupTopic , 2 ) ;
2021-05-24 19:37:27 +02:00
}
mMQTTReady = true ;
2020-10-28 20:31:18 +01:00
2020-11-04 21:57:40 +01:00
break ;
case HomieEventType : : OTA_STARTED :
for ( int i = 0 ; i < MAX_PLANTS ; i + + )
{
mPlants [ i ] . deactivatePump ( ) ;
}
2021-05-24 20:07:22 +02:00
WRITE_PERI_REG ( RTC_CNTL_BROWN_OUT_REG , 0 ) ;
digitalWrite ( OUTPUT_ENABLE_PUMP , HIGH ) ;
delay ( 100 ) ;
WRITE_PERI_REG ( RTC_CNTL_BROWN_OUT_REG , 1 ) ;
2021-04-07 22:42:31 +02:00
mDownloadMode = true ;
2020-11-04 21:57:40 +01:00
break ;
case HomieEventType : : OTA_SUCCESSFUL :
2021-04-07 18:49:59 +02:00
digitalWrite ( OUTPUT_ENABLE_SENSOR , LOW ) ;
2021-05-24 20:07:22 +02:00
digitalWrite ( OUTPUT_ENABLE_PUMP , LOW ) ;
2020-11-04 21:57:40 +01:00
ESP . restart ( ) ;
break ;
default :
break ;
2020-09-07 18:18:46 +02:00
}
2020-10-19 01:39:56 +02:00
}
2020-09-07 18:18:46 +02:00
2021-08-29 20:45:50 +02:00
int determineNextPump ( bool isLowLight )
2020-11-04 21:57:40 +01:00
{
2020-11-04 21:10:22 +01:00
int pumpToUse = - 1 ;
2020-11-04 21:57:40 +01:00
for ( int i = 0 ; i < MAX_PLANTS ; i + + )
{
2021-08-23 00:58:37 +02:00
bool wateralarm = consecutiveWateringPlant [ i ] > = pumpIneffectiveWarning . get ( ) ;
if ( wateralarm )
{
log ( LOG_LEVEL_ERROR , String ( String ( i ) + " Plant still dry after " + String ( consecutiveWateringPlant [ i ] ) + " watering attempts " ) , LOG_PUMP_INEFFECTIVE ) ;
}
2020-11-04 21:10:22 +01:00
Plant plant = mPlants [ i ] ;
2021-04-07 21:26:11 +02:00
if ( ! plant . isPumpTriggerActive ( ) )
{
2021-07-01 22:06:50 +02:00
plant . publishState ( " deactivated " ) ;
2021-07-01 22:15:55 +02:00
log ( LOG_LEVEL_DEBUG , String ( String ( i ) + " Skip deactivated pump " ) , LOG_DEBUG_CODE ) ;
2021-04-07 21:26:11 +02:00
continue ;
}
2021-07-01 20:39:51 +02:00
if ( ( rtcLastWateringPlant [ i ] + plant . getCooldownInSeconds ( ) ) > getCurrentTime ( ) )
2021-05-26 21:46:33 +02:00
{
2021-08-23 00:58:37 +02:00
if ( wateralarm )
{
plant . publishState ( " cooldown+alarm " ) ;
}
else
{
plant . publishState ( " cooldown " ) ;
}
2021-07-01 22:15:55 +02:00
log ( LOG_LEVEL_DEBUG , String ( String ( i ) + " Skipping due to cooldown " + String ( rtcLastWateringPlant [ i ] + plant . getCooldownInSeconds ( ) ) ) , LOG_DEBUG_CODE ) ;
2021-04-07 21:26:11 +02:00
continue ;
}
2020-11-04 21:57:40 +01:00
if ( ! isLowLight & & plant . isAllowedOnlyAtLowLight ( ) )
{
2021-08-23 00:58:37 +02:00
if ( wateralarm )
{
plant . publishState ( " sunny+alarm " ) ;
}
else
{
plant . publishState ( " sunny " ) ;
}
2021-07-01 22:15:55 +02:00
log ( LOG_LEVEL_DEBUG , String ( String ( i ) + " No pump required: due to light " ) , LOG_DEBUG_CODE ) ;
2020-10-19 01:39:56 +02:00
continue ;
}
2021-08-23 00:58:37 +02:00
if ( equalish ( plant . getCurrentMoisture ( ) , MISSING_SENSOR ) )
2020-11-06 21:00:11 +01:00
{
2021-07-01 22:06:50 +02:00
plant . publishState ( " nosensor " ) ;
2021-07-01 23:09:02 +02:00
log ( LOG_LEVEL_ERROR , String ( String ( i ) + " No pump possible: missing sensor " ) , LOG_MISSING_PUMP ) ;
2020-11-04 23:28:08 +01:00
continue ;
}
2020-11-04 21:57:40 +01:00
if ( plant . isPumpRequired ( ) )
{
2021-05-26 21:46:33 +02:00
/* Handle e.g. start = 21, end = 8 */
2021-05-26 20:19:27 +02:00
if ( ( ( plant . getHoursStart ( ) > plant . getHoursEnd ( ) ) & &
2021-05-26 21:46:33 +02:00
( getCurrentHour ( ) > = plant . getHoursStart ( ) | | getCurrentHour ( ) < = plant . getHoursEnd ( ) ) ) | |
2021-05-26 20:19:27 +02:00
/* Handle e.g. start = 8, end = 21 */
( ( plant . getHoursStart ( ) < plant . getHoursEnd ( ) ) & &
2021-05-26 21:46:33 +02:00
( getCurrentHour ( ) > = plant . getHoursStart ( ) & & getCurrentHour ( ) < = plant . getHoursEnd ( ) ) ) | |
2021-05-26 20:19:27 +02:00
/* no time from NTP received */
2021-05-26 21:46:33 +02:00
( getCurrentTime ( ) < 10000 ) )
2021-05-26 20:19:27 +02:00
{
2021-08-23 00:58:37 +02:00
if ( wateralarm )
{
plant . publishState ( " active+alarm " ) ;
}
else
{
plant . publishState ( " active " ) ;
}
consecutiveWateringPlant [ i ] + + ;
2021-07-01 22:15:55 +02:00
log ( LOG_LEVEL_DEBUG , String ( String ( i ) + " Requested pumping " ) , LOG_DEBUG_CODE ) ;
2021-04-07 21:26:11 +02:00
pumpToUse = i ;
2021-05-26 21:46:33 +02:00
}
else
{
2021-08-23 00:58:37 +02:00
if ( wateralarm )
{
plant . publishState ( " after-work+alarm " ) ;
}
else
{
plant . publishState ( " after-work " ) ;
}
2021-07-01 22:15:55 +02:00
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 ) ;
2021-04-07 21:26:11 +02:00
}
continue ;
2020-11-04 21:57:40 +01:00
}
else
{
2021-07-01 22:06:50 +02:00
plant . publishState ( " wet " ) ;
2021-08-23 00:58:37 +02:00
//plant was detected as wet, remove consecutive count
consecutiveWateringPlant [ i ] = 0 ;
2020-09-07 18:18:46 +02:00
}
}
2020-11-04 21:10:22 +01:00
return pumpToUse ;
2020-10-19 01:39:56 +02:00
}
2020-09-07 18:18:46 +02:00
2020-10-13 20:16:28 +02:00
/**
* @ brief Handle Mqtt commands to keep controller alive
*
* @ param range multiple transmitted values ( not used for this function )
* @ param value single value
* @ return true when the command was parsed and executed succuessfully
* @ return false on errors when parsing the request
*/
2020-11-04 21:57:40 +01:00
bool aliveHandler ( const HomieRange & range , const String & value )
{
2021-07-01 23:09:02 +02:00
if ( range . isRange )
{
2020-11-04 21:57:40 +01:00
return false ; // only one controller is present
2021-07-01 22:06:50 +02:00
}
2021-07-01 23:09:02 +02:00
2020-11-04 21:57:40 +01:00
if ( value . equals ( " ON " ) | | value . equals ( " On " ) | | value . equals ( " 1 " ) )
{
2021-04-07 22:42:31 +02:00
mDownloadMode = true ;
2020-11-04 21:57:40 +01:00
}
else
{
2021-05-26 21:46:33 +02:00
if ( mDownloadMode )
{
2021-05-24 19:37:27 +02:00
esp_restart ( ) ;
}
2021-04-07 22:42:31 +02:00
mDownloadMode = false ;
2020-11-04 21:57:40 +01:00
}
2020-10-13 20:16:28 +02:00
return true ;
}
2021-05-24 19:37:27 +02:00
bool notStarted = true ;
2020-11-04 21:57:40 +01:00
void homieLoop ( )
{
2021-05-26 21:46:33 +02:00
if ( mMQTTReady & & mAliveWasRead & & notStarted )
{
2021-05-24 19:37:27 +02:00
Serial . println ( " received alive & mqtt is ready " ) ;
notStarted = false ;
2021-07-01 20:50:47 +02:00
plantcontrol ( ) ;
2021-05-24 19:37:27 +02:00
}
2020-10-20 20:12:27 +02:00
}
2021-08-23 00:58:37 +02:00
bool switch1 ( const HomieRange & range , const String & value )
{
2021-07-09 22:51:50 +02:00
return mPlants [ 0 ] . switchHandler ( range , value ) ;
}
2021-08-23 00:58:37 +02:00
bool switch2 ( const HomieRange & range , const String & value )
{
2021-07-09 22:51:50 +02:00
return mPlants [ 1 ] . switchHandler ( range , value ) ;
}
2021-08-23 00:58:37 +02:00
bool switch3 ( const HomieRange & range , const String & value )
{
2021-07-09 22:51:50 +02:00
return mPlants [ 2 ] . switchHandler ( range , value ) ;
}
2021-08-23 00:58:37 +02:00
bool switch4 ( const HomieRange & range , const String & value )
{
2021-07-09 22:51:50 +02:00
return mPlants [ 3 ] . switchHandler ( range , value ) ;
}
2021-08-23 00:58:37 +02:00
bool switch5 ( const HomieRange & range , const String & value )
{
2021-07-09 22:51:50 +02:00
return mPlants [ 4 ] . switchHandler ( range , value ) ;
}
2021-08-23 00:58:37 +02:00
bool switch6 ( const HomieRange & range , const String & value )
{
2021-07-09 22:51:50 +02:00
return mPlants [ 5 ] . switchHandler ( range , value ) ;
}
2021-08-23 00:58:37 +02:00
bool switch7 ( const HomieRange & range , const String & value )
{
2021-07-09 22:51:50 +02:00
return mPlants [ 6 ] . switchHandler ( range , value ) ;
}
2021-04-07 21:54:53 +02:00
/**
* @ brief Startup function
* Is called once , the controller is started
*/
void setup ( )
2020-11-04 21:57:40 +01:00
{
2021-04-07 21:57:00 +02:00
/* reduce power consumption */
setCpuFrequencyMhz ( 80 ) ;
2021-04-07 21:54:53 +02:00
Serial . begin ( 115200 ) ;
2021-05-24 14:58:35 +02:00
2021-05-26 21:46:33 +02:00
Serial < < " Wifi mode set to " < < WIFI_OFF < < " to allow analog2 useage " < < endl ;
2021-05-24 14:58:35 +02:00
WiFi . mode ( WIFI_OFF ) ;
Serial . flush ( ) ;
2021-08-29 20:45:50 +02:00
//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 ) ;
# endif // TIMED_LIGHT_PIN
2021-07-01 20:39:51 +02:00
gpio_hold_dis ( OUTPUT_PUMP0 ) ;
gpio_hold_dis ( OUTPUT_PUMP1 ) ;
gpio_hold_dis ( OUTPUT_PUMP2 ) ;
gpio_hold_dis ( OUTPUT_PUMP3 ) ;
gpio_hold_dis ( OUTPUT_PUMP4 ) ;
gpio_hold_dis ( OUTPUT_PUMP5 ) ;
gpio_hold_dis ( OUTPUT_PUMP6 ) ;
gpio_hold_dis ( OUTPUT_ENABLE_PUMP ) ;
2021-04-07 21:54:53 +02:00
/* Intialize Plant */
for ( int i = 0 ; i < MAX_PLANTS ; i + + )
{
mPlants [ i ] . init ( ) ;
}
// read button
pinMode ( BUTTON , INPUT ) ;
// Power pins
pinMode ( OUTPUT_ENABLE_PUMP , OUTPUT ) ;
2021-05-24 20:07:22 +02:00
2021-05-24 14:58:35 +02:00
digitalWrite ( OUTPUT_ENABLE_PUMP , LOW ) ;
2021-05-24 20:07:22 +02:00
2021-04-07 21:54:53 +02:00
pinMode ( OUTPUT_ENABLE_SENSOR , OUTPUT ) ;
if ( HomieInternals : : MAX_CONFIG_SETTING_SIZE < MAX_CONFIG_SETTING_ITEMS )
{
2021-06-29 23:49:48 +02:00
//increase the config settings
2021-05-24 14:58:35 +02:00
Serial < < " Limits.hpp is not adjusted, please search for this string and increase " < < endl ;
return ;
2021-04-07 21:54:53 +02:00
}
2021-06-29 23:49:48 +02:00
if ( HomieInternals : : MAX_JSON_CONFIG_FILE_SIZE < MAX_JSON_CONFIG_FILE_SIZE_CUSTOM )
{
//increase the config settings
Serial < < " Limits.hpp is not adjusted, please search for this string and increase " < < endl ;
return ;
}
2021-04-07 21:54:53 +02:00
2021-04-07 22:10:26 +02:00
/************************* Start One-Wire bus ***************/
2021-05-24 14:58:35 +02:00
int tempInitStartTime = millis ( ) ;
2021-04-07 22:10:26 +02:00
uint8_t sensorCount = 0U ;
/* Required to read the temperature at least once */
2021-05-24 14:58:35 +02:00
while ( ( sensorCount = = 0 | | ! battery . isFound ( ) ) & & millis ( ) < tempInitStartTime + TEMPERATUR_TIMEOUT )
2021-04-07 22:10:26 +02:00
{
sensors . begin ( ) ;
battery . begin ( ) ;
sensorCount = sensors . getDS18Count ( ) ;
delay ( 50 ) ;
}
2021-05-29 21:37:27 +02:00
Serial < < " DS18S20 count: " < < sensorCount < < " found in " < < ( millis ( ) - tempInitStartTime ) < < " ms " < < endl ;
2021-05-24 14:58:35 +02:00
Serial . flush ( ) ;
2021-04-07 22:10:26 +02:00
/* Measure temperature TODO idea: move this into setup */
if ( sensorCount > 0 )
{
2021-05-29 21:37:27 +02:00
//sensors.setResolution(DS18B20_RESOLUTION);
2021-04-07 22:10:26 +02:00
sensors . requestTemperatures ( ) ;
}
2021-05-24 14:58:35 +02:00
Serial < < " Reading sensors start " < < endl ;
Serial . flush ( ) ;
readPowerSwitchedSensors ( ) ;
Serial < < " Reading sensors end " < < endl ;
Serial . flush ( ) ;
2021-04-07 21:54:53 +02:00
/************************* Start Homie Framework ***************/
2020-10-16 20:36:07 +02:00
Homie_setFirmware ( " PlantControl " , FIRMWARE_VERSION ) ;
2021-06-29 22:09:30 +02:00
Homie . disableLedFeedback ( ) ;
Homie_setBrand ( " PlantControl " ) ;
2020-10-16 20:36:07 +02:00
// Set default values
2020-11-04 21:57:40 +01:00
2020-10-21 19:50:05 +02:00
//in seconds
2021-08-23 00:58:37 +02:00
deepSleepTime . setDefaultValue ( 600 ) . setValidator ( [ ] ( long candidate )
{ return ( candidate > 0 ) & & ( candidate < ( 60 * 60 * 2 ) /** 2h max sleep */ ) ; } ) ;
2020-10-31 12:44:49 +01:00
deepSleepNightTime . setDefaultValue ( 600 ) ;
2020-10-21 19:50:05 +02:00
wateringDeepSleep . setDefaultValue ( 5 ) ;
2020-11-01 20:17:21 +01:00
ntpServer . setDefaultValue ( " pool.ntp.org " ) ;
2020-10-21 19:50:05 +02:00
2020-11-04 21:57:40 +01:00
/* waterLevelMax 1000 */ /* 100cm in mm */
waterLevelMin . setDefaultValue ( 50 ) ; /* 5cm in mm */
waterLevelWarn . setDefaultValue ( 500 ) ; /* 50cm in mm */
waterLevelVol . setDefaultValue ( 5000 ) ; /* 5l in ml */
2021-04-07 19:40:31 +02:00
lipoSensorAddr . setDefaultValue ( " " ) ;
waterSensorAddr . setDefaultValue ( " " ) ;
2021-08-29 20:45:50 +02:00
pumpIneffectiveWarning . setDefaultValue ( 5 ) . setValidator ( [ ] ( long candidate )
2021-08-23 00:58:37 +02:00
{ return ( candidate > 0 ) & & ( candidate < ( 20 ) ) ; } ) ;
2021-08-29 20:45:50 +02:00
# if defined(TIMED_LIGHT_PIN)
timedLightStart . setDefaultValue ( 18 ) . setValidator ( [ ] ( long candidate )
{ return ( candidate > 0 ) & & ( candidate < ( 25 ) ) ; } ) ;
timedLightEnd . setDefaultValue ( 23 ) . setValidator ( [ ] ( long candidate )
{ return ( candidate > 0 ) & & ( candidate < ( 22 ) ) ; } ) ;
timedLightOnlyWhenDark . setDefaultValue ( true ) ;
timedLightVoltageCutoff . setDefaultValue ( 3.8 ) . setValidator ( [ ] ( double candidate )
{ return ( candidate > 3.3 ) & & ( candidate < ( 4.2 ) ) ; } ) ;
# endif // TIMED_LIGHT_PIN
2020-10-20 20:12:27 +02:00
Homie . setLoopFunction ( homieLoop ) ;
2020-10-21 18:14:51 +02:00
Homie . onEvent ( onHomieEvent ) ;
2021-05-26 21:46:33 +02:00
2020-10-20 20:12:27 +02:00
Homie . setup ( ) ;
2020-10-16 21:50:42 +02:00
mConfigured = Homie . isConfigured ( ) ;
2020-11-04 21:57:40 +01:00
if ( mConfigured )
{
for ( int i = 0 ; i < MAX_PLANTS ; i + + )
{
2020-10-23 16:20:34 +02:00
mPlants [ i ] . advertise ( ) ;
}
2021-07-09 22:51:50 +02:00
mPlants [ 0 ] . setSwitchHandler ( switch1 ) ;
mPlants [ 1 ] . setSwitchHandler ( switch2 ) ;
mPlants [ 2 ] . setSwitchHandler ( switch3 ) ;
mPlants [ 3 ] . setSwitchHandler ( switch4 ) ;
mPlants [ 4 ] . setSwitchHandler ( switch5 ) ;
mPlants [ 5 ] . setSwitchHandler ( switch6 ) ;
mPlants [ 6 ] . setSwitchHandler ( switch7 ) ;
2020-12-21 17:07:15 +01:00
sensorTemp . advertise ( TEMPERATUR_SENSOR_LIPO )
. setName ( TEMPERATURE_NAME )
. setDatatype ( NUMBER_TYPE )
. setUnit ( TEMPERATURE_UNIT ) ;
sensorTemp . advertise ( TEMPERATUR_SENSOR_WATER )
. setName ( TEMPERATURE_NAME )
. setDatatype ( NUMBER_TYPE )
. setUnit ( TEMPERATURE_UNIT ) ;
2021-02-16 22:29:07 +01:00
sensorTemp . advertise ( TEMPERATUR_SENSOR_CHIP )
. setName ( TEMPERATURE_NAME )
. setDatatype ( NUMBER_TYPE )
. setUnit ( TEMPERATURE_UNIT ) ;
2020-09-21 20:42:24 +02:00
sensorLipo . advertise ( " percent " )
2020-11-04 21:57:40 +01:00
. setName ( " Percent " )
2020-12-21 17:07:15 +01:00
. setDatatype ( NUMBER_TYPE )
2020-11-04 21:57:40 +01:00
. setUnit ( " % " ) ;
2020-09-21 20:42:24 +02:00
sensorLipo . advertise ( " volt " )
2020-11-04 21:57:40 +01:00
. setName ( " Volt " )
2020-12-21 17:07:15 +01:00
. setDatatype ( NUMBER_TYPE )
2020-11-04 21:57:40 +01:00
. setUnit ( " V " ) ;
2020-09-21 20:42:24 +02:00
sensorSolar . advertise ( " percent " )
2020-11-04 21:57:40 +01:00
. setName ( " Percent " )
2020-12-21 17:07:15 +01:00
. setDatatype ( NUMBER_TYPE )
2020-11-04 21:57:40 +01:00
. setUnit ( " % " ) ;
2020-09-21 20:42:24 +02:00
sensorSolar . advertise ( " volt " )
2020-11-04 21:57:40 +01:00
. setName ( " Volt " )
2020-12-21 17:07:15 +01:00
. setDatatype ( NUMBER_TYPE )
2020-11-04 21:57:40 +01:00
. setUnit ( " V " ) ;
2020-12-21 17:07:15 +01:00
sensorWater . advertise ( " remaining " ) . setDatatype ( NUMBER_TYPE ) . setUnit ( " % " ) ;
2021-05-26 21:46:33 +02:00
}
else
{
2021-07-01 21:19:51 +02:00
if ( doesFileExist ( CONFIG_FILE ) )
{
2021-06-29 22:09:30 +02:00
printFile ( CONFIG_FILE ) ;
}
2021-07-01 21:19:51 +02:00
if ( doesFileExist ( CONFIG_FILE_BACKUP ) )
{
2021-06-29 22:09:30 +02:00
printFile ( CONFIG_FILE_BACKUP ) ;
bool restoredConfig = copyFile ( CONFIG_FILE_BACKUP , CONFIG_FILE ) ;
2021-07-01 21:19:51 +02:00
if ( restoredConfig )
{
deleteFile ( CONFIG_FILE_BACKUP ) ;
espDeepSleepFor ( 1 , false ) ;
return ;
2021-06-29 22:09:30 +02:00
}
}
2021-07-01 20:50:47 +02:00
readOneWireSensors ( ) ;
2021-06-06 21:23:21 +02:00
//prevent BOD to be paranoid
WRITE_PERI_REG ( RTC_CNTL_BROWN_OUT_REG , 0 ) ;
2021-05-26 21:46:33 +02:00
digitalWrite ( OUTPUT_ENABLE_PUMP , HIGH ) ;
delay ( 100 ) ;
2021-06-06 21:23:21 +02:00
WRITE_PERI_REG ( RTC_CNTL_BROWN_OUT_REG , 1 ) ;
2021-04-07 21:54:53 +02:00
Serial . println ( " Initial Setup. Start Accesspoint... " ) ;
2021-04-07 22:42:31 +02:00
mDownloadMode = true ;
2020-10-16 20:36:07 +02:00
}
2021-04-07 21:54:53 +02:00
stayAlive . advertise ( " alive " ) . setName ( " Alive " ) . setDatatype ( NUMBER_TYPE ) . settable ( aliveHandler ) ;
2021-05-24 14:58:35 +02:00
setupFinishedTimestamp = millis ( ) ;
2020-09-07 18:18:46 +02:00
}
/**
* @ brief Cyclic call
* Executs the Homie base functionallity or triggers sleeping , if requested .
*/
2020-11-04 21:57:40 +01:00
void loop ( )
{
2021-05-24 14:58:35 +02:00
Homie . loop ( ) ;
2021-02-26 09:33:16 +01:00
/* Toggel Senor LED to visualize mode 3 */
2021-04-07 22:42:31 +02:00
if ( mDownloadMode )
2021-02-26 09:33:16 +01:00
{
if ( nextBlink < millis ( ) )
{
2021-04-07 18:49:59 +02:00
digitalWrite ( OUTPUT_ENABLE_SENSOR , ! digitalRead ( OUTPUT_ENABLE_SENSOR ) ) ;
2021-05-26 21:46:33 +02:00
if ( mConfigured )
{
nextBlink = millis ( ) + 500 ;
}
else
{
if ( lastPumpRunning > = 0 & & lastPumpRunning < MAX_PLANTS )
{
mPlants [ lastPumpRunning ] . deactivatePump ( ) ;
}
2021-05-27 21:43:15 +02:00
if ( lastPumpRunning > = MAX_PLANTS )
2021-05-26 21:46:33 +02:00
{
digitalWrite ( OUTPUT_ENABLE_PUMP , LOW ) ;
2021-05-26 22:25:12 +02:00
nextBlink = millis ( ) + 500 ;
2021-05-26 21:46:33 +02:00
}
2021-05-26 22:25:12 +02:00
else
{
2021-05-26 21:46:33 +02:00
lastPumpRunning + + ;
2021-05-26 22:25:12 +02:00
nextBlink = millis ( ) + 5000 ;
}
if ( lastPumpRunning < MAX_PLANTS )
{
2021-05-26 21:46:33 +02:00
mPlants [ lastPumpRunning ] . activatePump ( ) ;
}
}
2021-02-26 09:33:16 +01:00
}
}
2021-05-26 21:46:33 +02:00
else
{
2021-05-24 14:58:35 +02:00
unsigned long timeSinceSetup = millis ( ) - setupFinishedTimestamp ;
2021-05-26 21:46:33 +02:00
if ( ( timeSinceSetup > MQTT_TIMEOUT ) & & ( ! mSensorsRead ) )
{
2021-04-07 21:54:53 +02:00
mSensorsRead = true ;
/* Disable Wifi and put modem into sleep mode */
WiFi . mode ( WIFI_OFF ) ;
2021-05-26 21:46:33 +02:00
Serial < < " Wifi mode set to " < < WIFI_OFF < < " mqqt was no reached within " < < timeSinceSetup < < " ms , fallback to offline mode " < < endl ;
2021-05-24 14:58:35 +02:00
Serial . flush ( ) ;
2021-07-01 20:50:47 +02:00
plantcontrol ( ) ;
2021-04-07 21:54:53 +02:00
}
2020-11-04 21:57:40 +01:00
}
2020-10-16 20:36:07 +02:00
2021-04-07 21:54:53 +02:00
/** Timeout always stopping the ESP -> no endless power consumption */
2021-07-09 19:40:51 +02:00
if ( millis ( ) > ESP_STALE_TIMEOUT & & ! mDownloadMode )
2020-11-04 21:57:40 +01:00
{
2021-02-26 09:33:16 +01:00
Serial < < ( millis ( ) / 1000 ) < < " not terminated watchdog reset " < < endl ;
2020-10-16 20:36:07 +02:00
Serial . flush ( ) ;
2021-02-26 09:33:16 +01:00
esp_restart ( ) ;
2020-11-01 20:42:45 +01:00
}
2020-09-07 18:18:46 +02:00
}
2020-11-06 22:19:16 +01:00
2021-04-07 21:54:53 +02:00
/***
* @ fn plantcontrol
* Main function , doing the logic
*/
2021-07-01 20:50:47 +02:00
void plantcontrol ( )
2021-04-07 21:54:53 +02:00
{
if ( lastPumpRunning ! = - 1 )
{
2021-04-07 22:18:52 +02:00
long waterDiff = waterRawSensor . getAverage ( ) - lastWaterValue ;
mPlants [ lastPumpRunning ] . setProperty ( " waterusage " ) . send ( String ( waterDiff ) ) ;
/* TODO convert diff into volume (milli liter) */
Serial < < " Plant " < < lastPumpRunning < < " : Water diff " < < waterDiff < < " mm " < < endl ;
2021-04-07 21:54:53 +02:00
}
2021-04-07 22:18:52 +02:00
2021-07-09 21:50:51 +02:00
if ( mAliveWasRead )
2021-04-07 21:54:53 +02:00
{
2021-07-09 21:50:51 +02:00
for ( int i = 0 ; i < MAX_PLANTS ; i + + )
2021-04-07 21:54:53 +02:00
{
2021-07-09 21:50:51 +02:00
mPlants [ i ] . postMQTTconnection ( ) ;
2021-08-23 00:58:37 +02:00
mPlants [ i ] . setProperty ( " consecutivePumps " ) . send ( String ( consecutiveWateringPlant [ i ] ) ) ;
2021-04-07 21:54:53 +02:00
}
}
2021-05-24 15:50:04 +02:00
2021-07-09 21:50:51 +02:00
readOneWireSensors ( ) ;
2021-04-07 21:54:53 +02:00
Serial < < " W : " < < waterRawSensor . getAverage ( ) < < " cm ( " < < String ( waterLevelMax . get ( ) - waterRawSensor . getAverage ( ) ) < < " %) " < < endl ;
lastWaterValue = waterRawSensor . getAverage ( ) ;
2021-04-07 22:42:31 +02:00
float batteryVoltage = battery . getVoltage ( BATTSENSOR_INDEX_BATTERY ) ;
float chipTemp = battery . getTemperature ( ) ;
2021-05-24 15:50:04 +02:00
Serial < < " Chip Temperatur " < < chipTemp < < " °C " < < endl ;
2021-04-07 22:42:31 +02:00
2021-07-01 20:50:47 +02:00
if ( mAliveWasRead )
2021-05-26 21:46:33 +02:00
{
2021-07-14 21:14:03 +02:00
float remaining = waterLevelMax . get ( ) - waterRawSensor . getAverage ( ) ;
2021-08-23 00:58:37 +02:00
if ( ! isnan ( remaining ) )
{
2021-07-14 21:14:03 +02:00
sensorWater . setProperty ( " remaining " ) . send ( String ( remaining ) ) ;
}
2021-08-23 00:58:37 +02:00
if ( ! isnan ( waterRawSensor . getAverage ( ) ) )
{
2021-07-14 21:14:03 +02:00
sensorWater . setProperty ( " distance " ) . send ( String ( waterRawSensor . getAverage ( ) ) ) ;
}
2021-05-24 15:50:04 +02:00
sensorLipo . setProperty ( " percent " ) . send ( String ( 100 * batteryVoltage / VOLT_MAX_BATT ) ) ;
sensorLipo . setProperty ( " volt " ) . send ( String ( batteryVoltage ) ) ;
sensorLipo . setProperty ( " current " ) . send ( String ( battery . getCurrent ( ) ) ) ;
sensorLipo . setProperty ( " Ah " ) . send ( String ( battery . getAh ( ) ) ) ;
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 ) ) ;
sensorTemp . setProperty ( TEMPERATUR_SENSOR_CHIP ) . send ( String ( chipTemp ) ) ;
2021-05-26 21:46:33 +02:00
}
else
{
2021-05-24 15:50:04 +02:00
Serial . println ( " Skipping MQTT, offline mode " ) ;
Serial . flush ( ) ;
}
2021-04-07 21:54:53 +02:00
2021-08-29 20:45:50 +02:00
bool isLowLight = ( mSolarVoltage < SOLAR_CHARGE_MIN_VOLTAGE ) ;
2021-04-07 21:54:53 +02:00
bool hasWater = true ; //FIXMEmWaterGone > waterLevelMin.get();
//FIXME no water warning message
2021-08-29 20:45:50 +02:00
lastPumpRunning = determineNextPump ( isLowLight ) ;
2021-04-07 21:54:53 +02:00
if ( lastPumpRunning ! = - 1 & & ! hasWater )
{
2021-07-01 22:06:50 +02:00
log ( LOG_LEVEL_ERROR , LOG_PUMP_BUTNOTANK_MESSAGE , LOG_PUMP_BUTNOTANK_CODE ) ;
2021-05-26 21:46:33 +02:00
}
2021-04-07 22:42:31 +02:00
else if ( lastPumpRunning ! = - 1 & & hasWater )
2021-04-07 21:54:53 +02:00
{
2021-04-07 22:42:31 +02:00
if ( mDownloadMode )
2021-04-07 21:54:53 +02:00
{
2021-07-01 22:06:50 +02:00
log ( LOG_LEVEL_INFO , LOG_PUMP_AND_DOWNLOADMODE , LOG_PUMP_AND_DOWNLOADMODE_CODE ) ;
2021-04-07 21:54:53 +02:00
}
else
{
2021-05-24 20:07:22 +02:00
//prevent BOD to be paranoid
WRITE_PERI_REG ( RTC_CNTL_BROWN_OUT_REG , 0 ) ;
2021-04-07 21:54:53 +02:00
digitalWrite ( OUTPUT_ENABLE_PUMP , HIGH ) ;
2021-05-24 20:07:22 +02:00
delay ( 100 ) ;
WRITE_PERI_REG ( RTC_CNTL_BROWN_OUT_REG , 1 ) ;
2021-04-07 21:54:53 +02:00
rtcLastWateringPlant [ lastPumpRunning ] = getCurrentTime ( ) ;
mPlants [ lastPumpRunning ] . activatePump ( ) ;
}
}
2021-08-29 20:45:50 +02:00
# if defined(TIMED_LIGHT_PIN)
bool shouldLight = determineTimedLightState ( isLowLight ) ;
timedLightOn = shouldLight ;
digitalWrite ( TIMED_LIGHT_PIN , shouldLight ) ;
# endif // TIMED_LIGHT_PIN
2021-04-07 22:42:31 +02:00
/* Always handle one of the deep sleep duration */
2021-04-07 21:54:53 +02:00
if ( lastPumpRunning = = - 1 | | ! hasWater )
{
if ( mSolarVoltage < SOLAR_CHARGE_MIN_VOLTAGE )
{
2021-07-01 22:06:50 +02:00
log ( LOG_LEVEL_INFO , String ( String ( mSolarVoltage ) + " V! No pumps to activate and low light, deepSleepNight " ) , LOG_NOPUMP_LOWLIGHT ) ;
2021-07-01 20:50:47 +02:00
espDeepSleepFor ( deepSleepNightTime . get ( ) , false ) ;
2021-04-07 21:54:53 +02:00
}
else
{
2021-07-01 22:06:50 +02:00
log ( LOG_LEVEL_INFO , " No pumps to activate, deepSleep " , LOG_NOPUMPS ) ;
2021-07-01 20:50:47 +02:00
espDeepSleepFor ( deepSleepTime . get ( ) , false ) ;
2021-04-07 21:54:53 +02:00
}
}
else
{
2021-07-01 20:50:47 +02:00
espDeepSleepFor ( wateringDeepSleep . get ( ) , true ) ;
2021-04-07 21:54:53 +02:00
}
}
2020-11-29 05:04:46 +01:00
/** @}*/
2021-07-01 21:19:51 +02:00
2021-08-29 20:45:50 +02:00
bool determineTimedLightState ( bool lowLight ) {
bool onlyAllowedWhenDark = timedLightOnlyWhenDark . get ( ) ;
long hoursStart = timedLightStart . get ( ) ;
long hoursEnd = timedLightEnd . get ( ) ;
//ntp missing
if ( getCurrentTime ( ) < 10000 ) {
timedLightNode . setProperty ( " state " ) . send ( String ( " Off, missing ntp " ) ) ;
return false ;
}
if ( onlyAllowedWhenDark & & ! lowLight ) {
timedLightNode . setProperty ( " state " ) . send ( String ( " Off, not dark " ) ) ;
return false ;
}
if ( ( ( hoursStart > hoursEnd ) & &
( getCurrentHour ( ) > = hoursStart | | getCurrentHour ( ) < = hoursEnd ) ) | |
/* Handle e.g. start = 8, end = 21 */
( ( hoursStart < hoursEnd ) & &
( getCurrentHour ( ) > = hoursStart & & getCurrentHour ( ) < = hoursEnd ) ) )
{
if ( battery . getVoltage ( BATTSENSOR_INDEX_BATTERY ) > = timedLightVoltageCutoff . get ( ) ) {
timedLightNode . setProperty ( " state " ) . send ( String ( " On " ) ) ;
return true ;
} else {
timedLightNode . setProperty ( " state " ) . send ( String ( " Off, due to missing voltage " ) ) ;
return false ;
}
} else {
timedLightNode . setProperty ( " state " ) . send ( String ( " Off, outside worktime " ) ) ;
return false ;
}
}
2021-07-01 21:19:51 +02:00
void log ( int level , String message , int statusCode )
{
String buffer ;
StaticJsonDocument < 200 > doc ;
doc [ " level " ] = level ;
doc [ " message " ] = message ;
doc [ " statusCode " ] = statusCode ;
serializeJson ( doc , buffer ) ;
if ( mAliveWasRead )
{
getTopic ( LOG_TOPIC , logTopic )
Homie . getMqttClient ( )
. subscribe ( logTopic , 2 ) ;
Homie . getMqttClient ( ) . publish ( logTopic , 2 , false , buffer . c_str ( ) ) ;
delete logTopic ;
}
Serial < < statusCode < < " @ " < < level < < " : " < < message < < endl ;
2021-07-01 22:06:50 +02:00
}