diff --git a/.gitignore b/.gitignore index 89cc49c..3d1fbd7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .vscode/c_cpp_properties.json .vscode/launch.json .vscode/ipch +.vs diff --git a/platformio.ini b/platformio.ini index 346cfd9..28c817a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,6 +13,7 @@ platform = espressif32 board = az-delivery-devkit-v4 framework = arduino monitor_speed = 115200 +; build_flags = -DCORE_DEBUG_LEVEL=5 lib_deps = 439 #ID of Lightsensor library BH1750 @@ -24,4 +25,4 @@ lib_deps = ; MQTT@^2.4.7 PubSubClient@^2.8 ArduinoNvs@^2.5 - ; ESPRandom@^1.3.3 \ No newline at end of file + ESPRandom@^1.3.3 \ No newline at end of file diff --git a/src/capacitiveSoilMoistureSensor.cpp b/src/capacitiveSoilMoistureSensor.cpp index 3b67af6..776046d 100644 --- a/src/capacitiveSoilMoistureSensor.cpp +++ b/src/capacitiveSoilMoistureSensor.cpp @@ -1,8 +1,7 @@ /* Code for the Capacitive Soil Moisture Sensor */ - -#include +#include const int numReadings = 20; @@ -10,24 +9,24 @@ void setupCapacitiveSoilMoistureSensor() { // pinMode(PIN_MS, INPUT); } -int readCapacitiveSoilMoistureSensor() -{ - int total = 0; // the running total +int readCapacitiveSoilMoistureSensor() { + int total = 0; // the running total // read from the sensor: - for (int readIndex = 0; readIndex < numReadings; readIndex++) - { + for (int readIndex = 0; readIndex < numReadings; readIndex++) { total = total + analogRead(PIN_MS); delay(2); } int measurement = total / numReadings; - Serial.print("soil moisture raw: "); - Serial.println(measurement); - // add the reading to the total: - //int measurement = analogRead(PIN_MS); - if (map(measurement, VALUE_AIR, VALUE_WATER, 0, 100) < 0) { + int finalRes = map(measurement, VALUE_AIR, VALUE_WATER, 0, 100); + Serial.print("current soil moisture: "); + Serial.println(finalRes); + + if (finalRes < 0) { return 0; - } else if (map(measurement, VALUE_AIR, VALUE_WATER, 0, 100) > 100) { + } else if (finalRes > 100) { return 100; - } else {return map(measurement, VALUE_AIR, VALUE_WATER, 0, 100);} + } else { + return finalRes; + } } \ No newline at end of file diff --git a/src/capacitiveSoilMoistureSensor.h b/src/capacitiveSoilMoistureSensor.h new file mode 100644 index 0000000..573661d --- /dev/null +++ b/src/capacitiveSoilMoistureSensor.h @@ -0,0 +1,2 @@ +void setupCapacitiveSoilMoistureSensor(); +int readCapacitiveSoilMoistureSensor(); \ No newline at end of file diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..faaa43c --- /dev/null +++ b/src/common.h @@ -0,0 +1,46 @@ +#include + +// PUBLISH FREQUENCY (MS) +#define FREQUENCY 60000 + +// fix for core panic during wifi initialization +// #define configMINIMAL_STACK_SIZE 4096 +// #define CONFIG_TIMER_TASK_STACK_SIZE 8192 + +// BH1750 lightsensor +#define MIN_LIGHT 0 +#define MAX_LIGHT 54612 + +// DHT11 +#define PIN_DHT11 14 + +// MQ-135 +#define PIN_MQ135_A 12 +#define PIN_MQ135_D 13 + +// MOISTURE SENSOR // A7 +#define PIN_MS 35 +#define VALUE_WATER 1650 +#define VALUE_AIR 3500 + +// Ventil +#define PIN_VALVE 32 +#define MAX_VALVE_TIMEOUT 5000 + +// STATUS LED +#define PIN_LED_R 2 +#define PIN_LED_G 0 +#define PIN_LED_B 4 + +// LIGHT LED +#define LIGHT_LED_PIN_R 27 +#define LIGHT_LED_PIN_G 26 +#define LIGHT_LED_PIN_B 25 +#define MAX_LIGHT_TIMEOUT (FREQUENCY - 5) + +// MQTT +#define MQTT_HOST "mqtt.timovolkmann.de" +#define MQTT_PORT 1883 +#define MQTT_TOPIC_BASE_SUB "smartgarden/commands" +#define MQTT_TOPIC_BASE_PUB "smartgarden/updates" +#define MQTT_PATH_SUB MQTT_TOPIC_BASE_SUB "/#" diff --git a/src/connections.cpp b/src/connections.cpp index 3830f28..2f7d557 100644 --- a/src/connections.cpp +++ b/src/connections.cpp @@ -1,20 +1,26 @@ #include -// #include +#include #include -#include +#include +#include +#include +#include +#include extern "C" { -#include "freertos/FreeRTOS.h" -#include "freertos/timers.h" + #include "freertos/FreeRTOS.h" + #include "freertos/timers.h" } -#define MQTT_VALVE_COMMAND MQTT_TOPIC_BASE_SUB "/" MQTT_DEVICE_ID "/valve" -#define MQTT_SOIL_PROPERTIES MQTT_TOPIC_BASE_SUB "/" MQTT_DEVICE_ID "/soil" +#include + +char MQTT_VALVE_COMMAND[72]; +char MQTT_SOIL_PROPERTIES[72]; +char MQTT_LIGHT_PROPERTIES[72]; +char MQTT_AUTO_PROPERTIES[72]; TimerHandle_t mqttReconnectTimer; TimerHandle_t wifiReconnectTimer; - -// TimerHandle_t mqttProcessingTimer; TaskHandle_t mqttTask; WebServer Server; @@ -22,12 +28,10 @@ AutoConnect Portal(Server); WiFiClient client; AutoConnectConfig Config; -// Config.autoReconnect = true; -// WiFi.config(Config); -// MQTTClient mqttClient; PubSubClient mqttClient(client); void connectWiFi() { + yield(); Serial.println("Start WiFi..."); if (Portal.begin()) { digitalWrite(PIN_LED_G, HIGH); @@ -36,14 +40,15 @@ void connectWiFi() { void mqttLoop(void *parameter) { do { + yield(); mqttClient.loop(); - delay(50); + delay(100); } while (mqttClient.connected()); Serial.println("Disconnected from MQTT."); if (!mqttClient.connected()) { Serial.println("Checking WiFi Connection."); if (WiFi.isConnected()) { - Serial.println("Starting reconnect timer for MQTT."); + Serial.println("WiFi OK. Starting reconnect timer for MQTT."); xTimerStart(mqttReconnectTimer, 0); } } @@ -51,10 +56,13 @@ void mqttLoop(void *parameter) { } void connectMQTT() { + yield(); + if (mqttClient.connected()) return; Serial.println("Connecting to MQTT..."); // mqttClient.begin(MQTT_HOST, MQTT_PORT, client); mqttClient.setServer(MQTT_HOST, MQTT_PORT); - mqttClient.connect(MQTT_DEVICE_ID); + mqttClient.disconnect(); + mqttClient.connect(getDeviceIDcharArr()); if (mqttClient.connected()) { Serial.println("Connected!"); } else { @@ -63,17 +71,17 @@ void connectMQTT() { mqttClient.subscribe(MQTT_PATH_SUB); Serial.print("subscribed to: "); Serial.println(MQTT_PATH_SUB); - xTaskCreate( - mqttLoop, /* Task function. */ - "mqttLoop", /* String with name of task. */ - 8192, /* Stack size in bytes. */ - NULL, /* Parameter passed as input of the task */ - 1, /* Priority of the task. */ - &mqttTask); /* Task handle. */ - //xTimerStart(mqttProcessingTimer, 0); + xTaskCreatePinnedToCore( + mqttLoop, + "mqttLoop", + 8192, + NULL, + 2, + &mqttTask, + 1); } -void WiFiEvent(WiFiEvent_t event) { +void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) { Serial.printf("[WiFi-event] event: %d\n", event); switch (event) { case SYSTEM_EVENT_STA_GOT_IP: @@ -84,14 +92,18 @@ void WiFiEvent(WiFiEvent_t event) { break; case SYSTEM_EVENT_STA_DISCONNECTED: digitalWrite(PIN_LED_G, LOW); + // workaround for 202 auth failed bug: https://github.com/espressif/arduino-esp32/issues/2501 + if (info.disconnected.reason == 202) { + Serial.println("weirdbehaviour"); + Serial.println(info.disconnected.reason); + ESP.restart(); + } xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi - // xTimerStop(mqttProcessingTimer, 0); xTimerStart(wifiReconnectTimer, 0); break; } } -// void onMqttMessage(MQTTClient *client, char topic[], char payload[], int payload_length) { void onMqttMessage(char *topic, byte *payload, unsigned int payload_length) { Serial.print("Message arrived ["); Serial.print(topic); @@ -101,17 +113,27 @@ void onMqttMessage(char *topic, byte *payload, unsigned int payload_length) { } Serial.println(); + if (strcmp(topic, MQTT_LIGHT_PROPERTIES) == 0) { + Serial.println("receiving light treshold..."); + Serial.println(topic); + StaticJsonDocument<1024> doc; + DeserializationError err = deserializeJson(doc, payload); + if (err == DeserializationError::Ok) { + int nm = doc["nm"]; + int minLX = doc["minLX"]; + setLightProperties(nm, minLX); + } else { + Serial.println(err.c_str()); + } + } if (strcmp(topic, MQTT_VALVE_COMMAND) == 0) { Serial.println("toggling valve..."); Serial.println(topic); - toggleValve(); + toggleValve(false); } if (strcmp(topic, MQTT_SOIL_PROPERTIES) == 0) { Serial.println("receiving soil thresholds..."); Serial.println(topic); - //Serial.println(reinterpret_cast(payload)); - // Serial.println(payload); - // const int capacity = JSON_OBJECT_SIZE(3) + 2 * JSON_OBJECT_SIZE(1); StaticJsonDocument<1024> doc; DeserializationError err = deserializeJson(doc, payload); if (err == DeserializationError::Ok) { @@ -123,28 +145,75 @@ void onMqttMessage(char *topic, byte *payload, unsigned int payload_length) { Serial.println(err.c_str()); } } + if (strcmp(topic, MQTT_AUTO_PROPERTIES) == 0) { + Serial.println("receiving auto settings..."); + Serial.println(topic); + StaticJsonDocument<1024> doc; + DeserializationError err = deserializeJson(doc, payload); + if (err == DeserializationError::Ok) { + bool ir = doc["irrigation"]; + bool li = doc["light"]; + setAutoProperties(li, ir); + } else { + Serial.println(err.c_str()); + } + } +} + +void constructMQTTpaths() { + strcpy(MQTT_VALVE_COMMAND, MQTT_TOPIC_BASE_SUB "/"); + strcat(MQTT_VALVE_COMMAND, getDeviceIDcharArr()); + strcat(MQTT_VALVE_COMMAND, "/valve"); + + strcpy(MQTT_SOIL_PROPERTIES, MQTT_TOPIC_BASE_SUB "/"); + strcat(MQTT_SOIL_PROPERTIES, getDeviceIDcharArr()); + strcat(MQTT_SOIL_PROPERTIES, "/soil"); + + strcpy(MQTT_LIGHT_PROPERTIES, MQTT_TOPIC_BASE_SUB "/"); + strcat(MQTT_LIGHT_PROPERTIES, getDeviceIDcharArr()); + strcat(MQTT_LIGHT_PROPERTIES, "/light"); + + strcpy(MQTT_AUTO_PROPERTIES, MQTT_TOPIC_BASE_SUB "/"); + strcat(MQTT_AUTO_PROPERTIES, getDeviceIDcharArr()); + strcat(MQTT_AUTO_PROPERTIES, "/automatic"); + + Serial.println("MQTT_COMMANDS:"); + Serial.println(MQTT_VALVE_COMMAND); + Serial.println(MQTT_SOIL_PROPERTIES); + Serial.println(MQTT_LIGHT_PROPERTIES); + Serial.println(MQTT_AUTO_PROPERTIES); } void setupConnections() { + constructMQTTpaths(); Serial.println(); Serial.println(); + disableCore0WDT(); + // disableCore1WDT(); + Config.autoReconnect = true; + // Config.autoReset = true; + Portal.config(Config); mqttReconnectTimer = xTimerCreate( "mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void *)0, reinterpret_cast(connectMQTT)); wifiReconnectTimer = xTimerCreate( "wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void *)0, reinterpret_cast(connectWiFi)); - // mqttProcessingTimer = xTimerCreate( - // "messageTimer", pdMS_TO_TICKS(50), pdTRUE, (void *)0, reinterpret_cast(mqttLoop)); - + WiFi.onEvent(WiFiEvent); - // mqttClient.onMessageAdvanced(onMqttMessage); + connectWiFi(); + connectMQTT(); + mqttClient.setCallback(onMqttMessage); - connectWiFi(); } void publishMessage(const char *topic, const char *msg) { - mqttClient.publish(topic, msg); - // mqttClient.publish(topic, msg, true, 1); + if(mqttClient.connected()) { + mqttClient.publish(topic, msg); + Serial.print(topic); + Serial.println(" successfully sent."); + } else { + Serial.println("couldn't send message. waiting for reconnect."); + } } diff --git a/src/connections.h b/src/connections.h new file mode 100644 index 0000000..7e035fa --- /dev/null +++ b/src/connections.h @@ -0,0 +1,5 @@ +void connectWiFi(); +void mqttLoop(void *parameter); +void connectMQTT(); +void setupConnections(); +void publishMessage(const char *topic, const char *msg); \ No newline at end of file diff --git a/src/header.h b/src/header.h deleted file mode 100644 index 0905fc4..0000000 --- a/src/header.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - Header file for the SmartGarden project -*/ - -#define HEADER_H - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// fix for core panic during wifi initialization -// #define configMINIMAL_STACK_SIZE 2048 -// #define CONFIG_TIMER_TASK_STACK_SIZE 8192 - -// DHT11 -#define PIN_DHT11 14 - -// MQ-135 -#define PIN_MQ135_A 12 -#define PIN_MQ135_D 13 - -// MOISTURE SENSOR // A7 -#define PIN_MS 35 -#define VALUE_WATER 1650 -#define VALUE_AIR 3500 - -// Ventil -#define PIN_VALVE 32 -#define MAX_VALVE_TIMEOUT 10000 - -// LED -#define PIN_LED_R 2 -#define PIN_LED_G 0 -#define PIN_LED_B 4 - -// MQTT -#define MQTT_HOST "mqtt.timovolkmann.de" -#define MQTT_PORT 1883 -#define MQTT_DEVICE_ID "esp-max" -#define MQTT_TOPIC_BASE_SUB "smartgarden/commands" -#define MQTT_TOPIC_BASE_PUB "smartgarden/updates" -#define MQTT_PATH_PUB MQTT_TOPIC_BASE_PUB "/" MQTT_DEVICE_ID "/" -#define MQTT_PATH_SUB MQTT_TOPIC_BASE_SUB "/#" -// MQTT_DEVICE_ID "/#" - -// PUBLISH FREQUENCY (MS) -#define FREQUENCY 3000 - -// moisture -extern void setupCapacitiveSoilMoistureSensor(); -extern int readCapacitiveSoilMoistureSensor(); - -// light -extern void setupLightSensor(); -extern float readLightSensorValue(); - -// temperature & humidity -extern void setupTemperatureSensor(); -extern float readHumidity(); -extern float readTemperature(); - -// mqtt & wifi -extern void setupConnections(); -extern void publishMessage(const char *topic, const char *msg); - -// sensors -void readSensors(); -void toggleValve(); -void setSoilProperties(int FC, int PWP, int SAT); -void setupStore(); \ No newline at end of file diff --git a/src/lightChecker.cpp b/src/lightChecker.cpp new file mode 100644 index 0000000..90b5600 --- /dev/null +++ b/src/lightChecker.cpp @@ -0,0 +1,103 @@ +#include +#include + +// Bool to check if light is already active +bool lightActive = false; +// Colors in Array: purple, blue, green, yellow, orange, red, white +int colorValueArray[][3] = {{125,0,125}, {0,0,255}, {0,255,0}, {255,255,0}, {255,140,0}, {255,0,0}, {255,255,255}}; +// Pointer for color array +int colorCounter = 6; + +// Setting PWM properties +const int freq = 5000; +const int ledChannelRed = 0; +const int ledChannelGreen = 1; +const int ledChannelBlue= 2; +const int resolution = 8; + +// Setup method for PWM configurations +void setupPWM() { + // Set pins as output + pinMode(LIGHT_LED_PIN_R, OUTPUT); + pinMode(LIGHT_LED_PIN_G, OUTPUT); + pinMode(LIGHT_LED_PIN_B, OUTPUT); + + // Configure LED PWM functionalitites + ledcSetup(ledChannelRed, freq, resolution); + ledcSetup(ledChannelGreen, freq, resolution); + ledcSetup(ledChannelBlue, freq, resolution); + + // Attach the channel to the GPIO2 to be controlled + ledcAttachPin(LIGHT_LED_PIN_R, ledChannelRed); + ledcAttachPin(LIGHT_LED_PIN_G, ledChannelGreen); + ledcAttachPin(LIGHT_LED_PIN_B, ledChannelBlue); +} + +// Takes a int value representing nanometers and sets color array pointer to appropriate color +void setValueNM(int NM) { + getColorBasedOnValueNM(NM); +} + +// Logic to set color array pointer +void getColorBasedOnValueNM(int valueNM) { + if (valueNM <= 420) { // Purple + colorCounter = 0; + } + else if (valueNM <= 490) { // Blue + colorCounter = 1; + } + else if (valueNM <= 575) { // Green + colorCounter = 2; + } + else if (valueNM <= 585) { // Yellow + colorCounter = 3; + } + else if (valueNM <= 650) { // Orange + colorCounter = 4; + } + else if (valueNM > 650) { // 650 to 750 is Red + colorCounter = 5; + } + Serial.println("New color set based on: "); + Serial.print(valueNM); +} + +// Shuts down the light +bool shutdownLight() { + ledcWrite(ledChannelRed, 0); + ledcWrite(ledChannelGreen, 0); + ledcWrite(ledChannelBlue, 0); + return false; +} + +// Activates the light based on colorValueArray +bool activateLight() { + ledcWrite(ledChannelRed, colorValueArray[colorCounter][0]); + ledcWrite(ledChannelGreen, colorValueArray[colorCounter][1]); + ledcWrite(ledChannelBlue, colorValueArray[colorCounter][2]); + return true; +} + +// Activate light for given amount of time if not already activated +void lightTask(void *parameter) { + unsigned long lightTimeoutTimer = millis(); + if (lightActive == false) { + lightActive = activateLight(); + // If current time is below or equal to light timeout do nothing + while (millis() - lightTimeoutTimer <= MAX_LIGHT_TIMEOUT) { + } + lightActive = shutdownLight(); + } + vTaskDelete(NULL); +} + +// Trigger for lightTask +void triggerLight() { + xTaskCreate( + lightTask, /* Task function. */ + "lightTask", /* String with name of task. */ + 2048, /* Stack size in bytes. */ + NULL, /* Parameter passed as input of the task */ + 1, /* Priority of the task. */ + NULL); /* Task handle. */ +} \ No newline at end of file diff --git a/src/lightChecker.h b/src/lightChecker.h new file mode 100644 index 0000000..df385d4 --- /dev/null +++ b/src/lightChecker.h @@ -0,0 +1,4 @@ +void setupPWM(); +void setValueNM(int NM); +void triggerLight(); +void getColorBasedOnValueNM(int valueNM); \ No newline at end of file diff --git a/src/lightSensor.cpp b/src/lightSensor.cpp index 776a3fd..1340a46 100644 --- a/src/lightSensor.cpp +++ b/src/lightSensor.cpp @@ -1,14 +1,20 @@ -#include +#include +#include +#include +#include +// New BH1750 class BH1750 lightMeter; +// Setup for reading light sensor (prepares i2c communication) void setupLightSensor() { Wire.begin(); lightMeter.begin(); Serial.println("Sensor started..."); } +// Method to read and return the light value measured by BH1750 sensor float readLightSensorValue() { - float intensity = lightMeter.readLightLevel(); + int intensity = lightMeter.readLightLevel(); return intensity; } \ No newline at end of file diff --git a/src/lightSensor.h b/src/lightSensor.h new file mode 100644 index 0000000..b07a3b6 --- /dev/null +++ b/src/lightSensor.h @@ -0,0 +1,2 @@ +void setupLightSensor(); +float readLightSensorValue(); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 3a609ee..f12a32d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,9 +2,13 @@ Main file for the SmartGarden project */ -#include +#include +#include +#include +#include -#include +#define TIME_TO_SLEEP 30 +volatile int noSleepTasks = 0; unsigned long sensorReadTimer = 0; bool valveOpen = false; @@ -20,23 +24,22 @@ void setup() { digitalWrite(PIN_VALVE, LOW); + setupStore(); + setupSensors(); setupConnections(); - setupLightSensor(); - setupTemperatureSensor(); - setupCapacitiveSoilMoistureSensor(); + // esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000); Serial.println("Setup complete..."); Serial.println(); Serial.println(); + // First read without delay + readSensors(); } void loop() { - // read sensors + // Main loop: read sensors if (millis() - sensorReadTimer >= FREQUENCY) { readSensors(); sensorReadTimer = millis(); } - // toggle valve - if (valveOpen) { - } -} +} \ No newline at end of file diff --git a/src/ntpManager.cpp b/src/ntpManager.cpp new file mode 100644 index 0000000..db88641 --- /dev/null +++ b/src/ntpManager.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +// Url to ntp server +const char* ntpServer = "pool.ntp.org"; +// Time offset based on gmt in seconds +const long gmtOffset_sec = 3600; +// Offset in seconds for daylight saving time +const int daylightOffset_sec = 3600; + +// Start and end hour of the date +const int dayStart = 8; +const int dayEnd = 20; +// Time info structure comes from time.h +struct tm timeinfo; + +// If wifi is connected configure time of esp32 +void setupNTP() { + if (WiFi.isConnected()) { + Serial.println("WiFi OK. Getting Timestamp"); + configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); + printTimestamp(); + } +} + +// Method to check if it is currently day or night +bool checkForDay() { + printTimestamp(); + if((getCurrentHour() > dayStart) && (getCurrentHour() < dayEnd)) { + return true; + } + else { + return false; + } +} + +// Method to get current hour in 24 hour format +int getCurrentHour() { + int currentHour = timeinfo.tm_hour; + return currentHour; +} + +// Method to print a timestamp +void printTimestamp() { + if(!getLocalTime(&timeinfo)){ + Serial.println("Failed to obtain time"); + return; + } + Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); +} \ No newline at end of file diff --git a/src/ntpManager.h b/src/ntpManager.h new file mode 100644 index 0000000..0fb627b --- /dev/null +++ b/src/ntpManager.h @@ -0,0 +1,4 @@ +void setupNTP(); +bool checkForDay(); +int getCurrentHour(); +void printTimestamp(); \ No newline at end of file diff --git a/src/peripherals.cpp b/src/peripherals.cpp deleted file mode 100644 index 6d93cf9..0000000 --- a/src/peripherals.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include -#include -#include - -#include -//using namespace std; -extern "C" { -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -} - -#define MQTT_SENSOR_DATA MQTT_PATH_PUB "data" - -char buffer[128]; - -// Feldkapazität des Bodens in Prozent: Standard ist Humus -int fieldCapacity = 44; -// PWP des Bodens in Prozent: Standard ist Humus -int permanentWiltingPoint = 25; -// Boden vollständig gesättigt bei (Prozent): Standard ist Humus -int soilSaturation = 69; - -void readSensors() { - StaticJsonDocument<128> doc; - float lxValue = readLightSensorValue(); - Serial.print("Light intensity: "); - Serial.print(lxValue); - Serial.println(" lx"); - doc["brightness"] = lxValue; - //sprintf(buffer, "%f", lxValue); - //publishMessage(MQTT_BRIGHTNESS, buffer); - - int mstValue = readCapacitiveSoilMoistureSensor(); - Serial.print("Soil moisture: "); - Serial.println(mstValue); - // sprintf(buffer, "%i", mstValue); - // publishMessage(MQTT_MOISTURE, buffer); - doc["moisture"] = mstValue; - - float humidityValue = readHumidity(); - Serial.print("Humidity: "); - Serial.println(humidityValue); - // sprintf(buffer, "%f", humidityValue); - // publishMessage(MQTT_HUMIDITY, buffer); - doc["humidity"] = humidityValue; - - float temperatureValue = readTemperature(); - Serial.print("Temperature: "); - Serial.println(temperatureValue); - // sprintf(buffer, "%f", temperatureValue); - // publishMessage(MQTT_TEMPERATURE, buffer); - doc["temperature"] = temperatureValue; - Serial.print("\n"); - serializeJson(doc, buffer); - publishMessage(MQTT_SENSOR_DATA, buffer); -} - -bool openValve() { - digitalWrite(PIN_VALVE, HIGH); - digitalWrite(PIN_LED_G, LOW); - digitalWrite(PIN_LED_B, HIGH); - return true; -} - -bool closeValve() { - digitalWrite(PIN_VALVE, LOW); - digitalWrite(PIN_LED_G, HIGH); - digitalWrite(PIN_LED_B, LOW); - return false; -} - -void valveTask(void *parameter) { - unsigned long valveTimeoutTimer = millis(); - bool valveOpen = openValve(); - - while (valveOpen) { - delay(500); - int mstValue = readCapacitiveSoilMoistureSensor(); - - if (millis() - valveTimeoutTimer >= MAX_VALVE_TIMEOUT) { - valveOpen = closeValve(); - } - // if (mstValue > 80) { - // valveOpen = closeValve(); - // } - } - vTaskDelete(NULL); -} - -void toggleValve() { - xTaskCreate( - valveTask, /* Task function. */ - "valveTask", /* String with name of task. */ - 2048, /* Stack size in bytes. */ - NULL, /* Parameter passed as input of the task */ - 1, /* Priority of the task. */ - NULL); /* Task handle. */ -} - -void persistSoilProps(int FC, int PWP, int SAT) { - Serial.println("persistSoilProps"); - bool f = NVS.setInt("fieldCapacity", FC); - bool p = NVS.setInt("permanentWilt", PWP); - bool s = NVS.setInt("soilSaturation", SAT); - if (f && p && s) { - Serial.println("Soil properties sucessfully stored."); - NVS.commit(); - } - else { - Serial.println("error occured while trying to persist soil properties"); - } -} - -void restoreSoilProps() { - Serial.println("restoreSoilProps"); - int fc = NVS.getInt("fieldCapacity"); - int pwp = NVS.getInt("permanentWilt"); - int sat = NVS.getInt("soilSaturation"); - if (fc != 0) { - fieldCapacity = fc; - } - if (pwp != 0) { - permanentWiltingPoint = pwp; - } - if (sat != 0) { - soilSaturation = sat; - } - Serial.print(fieldCapacity); - Serial.print(permanentWiltingPoint); - Serial.print(soilSaturation); -} - -void setupStore() { - NVS.begin("store"); - restoreSoilProps(); -} - -void setSoilProperties(int FC, int PWP, int SAT) { - fieldCapacity = FC; - permanentWiltingPoint = PWP; - soilSaturation = SAT; - persistSoilProps(FC, PWP, SAT); - Serial.print("new fieldCapacity: "); - Serial.println(fieldCapacity); - Serial.print("new permanentWiltingPoint: "); - Serial.println(permanentWiltingPoint); - Serial.print("new soilSaturation: "); - Serial.println(soilSaturation); -} - diff --git a/src/sensors.cpp b/src/sensors.cpp new file mode 100644 index 0000000..71f3e00 --- /dev/null +++ b/src/sensors.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char buffer[128]; +char MQTT_SENSOR_DATA_TOPIC[64]; + +void setupSensors() { + setupLightSensor(); + setupPWM(); + setupTemperatureSensor(); + setupCapacitiveSoilMoistureSensor(); + setupNTP(); + + strcpy(MQTT_SENSOR_DATA_TOPIC, MQTT_TOPIC_BASE_PUB "/"); + strcat(MQTT_SENSOR_DATA_TOPIC, getDeviceIDcharArr()); + strcat(MQTT_SENSOR_DATA_TOPIC, "/data"); + Serial.println("MQTT_SENSOR_DATA_TOPIC:"); + Serial.println(MQTT_SENSOR_DATA_TOPIC); +} + +void readSensors() { + Serial.println(); + StaticJsonDocument<128> doc; + float lxValue = readLightSensorValue(); + Serial.print("Light intensity: "); + Serial.print(lxValue); + Serial.println(" lx"); + doc["brightness"] = lxValue; + if ((lxValue < minimumLightValueLX) && checkForDay()) { + triggerLight(); + } + + int mstValue = readCapacitiveSoilMoistureSensor(); + Serial.print("Soil moisture: "); + Serial.println(mstValue); + + if (automaticIrrigation) { + toggleValve(true); + } + + doc["moisture"] = mstValue; + + float humidityValue = readHumidity(); + Serial.print("Humidity: "); + Serial.println(humidityValue); + + doc["humidity"] = humidityValue; + + float temperatureValue = readTemperature(); + Serial.print("Temperature: "); + Serial.println(temperatureValue); + + doc["temperature"] = temperatureValue; + serializeJson(doc, buffer); + publishMessage(MQTT_SENSOR_DATA_TOPIC, buffer); +} diff --git a/src/sensors.h b/src/sensors.h new file mode 100644 index 0000000..8f52f9e --- /dev/null +++ b/src/sensors.h @@ -0,0 +1,2 @@ +void setupSensors(); +void readSensors(); \ No newline at end of file diff --git a/src/store.cpp b/src/store.cpp new file mode 100644 index 0000000..8a814aa --- /dev/null +++ b/src/store.cpp @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include + +// Fieldcapacity of the Ground in Percentage: Standard is Humus +int fieldCapacity = 44; +// PWP of the Ground in Percentage: Standard is Humus +int permanentWiltingPoint = 25; +// Ground completely saturated by (Percentage): Standard is Humus +int soilSaturation = 69; +// Minimum light value before light turns on +int minimumLightValueLX = 50; +// Switches for automatic light and irrigation control +bool automaticLight = true; +bool automaticIrrigation = true; +// Make sure device irrigates until fieldcapacity is reached +bool irrigateUntilFC = false; + +void persistSoilProps(int FC, int PWP, int SAT) { + Serial.println("persistSoilProps"); + bool f = NVS.setInt("fieldCapacity", FC); + bool p = NVS.setInt("permanentWilt", PWP); + bool s = NVS.setInt("soilSaturation", SAT); + if (f && p && s) { + Serial.println("Soil properties sucessfully stored."); + NVS.commit(); + } + else { + Serial.println("error occured while trying to persist soil properties"); + } +} + +void restoreSoilProps() { + Serial.println("restoreSoilProps"); + int fc = NVS.getInt("fieldCapacity"); + int pwp = NVS.getInt("permanentWilt"); + int sat = NVS.getInt("soilSaturation"); + if (fc != 0) { + fieldCapacity = fc; + } + if (pwp != 0) { + permanentWiltingPoint = pwp; + } + if (sat != 0) { + soilSaturation = sat; + } + Serial.print("fieldCapacity: "); + Serial.println(fieldCapacity); + Serial.print("permanentWiltingPoint: "); + Serial.println(permanentWiltingPoint); + Serial.print("soilSaturation: "); + Serial.println(soilSaturation); +} + +void setupStore() { + NVS.begin("store"); + initDeviceID(); + restoreSoilProps(); + restoreLightProps(); + restoreAutoProps(); +} + +void setSoilProperties(int FC, int PWP, int SAT) { + fieldCapacity = FC; + permanentWiltingPoint = PWP; + soilSaturation = SAT; + persistSoilProps(FC, PWP, SAT); + Serial.print("new fieldCapacity: "); + Serial.println(fieldCapacity); + Serial.print("new permanentWiltingPoint: "); + Serial.println(permanentWiltingPoint); + Serial.print("new soilSaturation: "); + Serial.println(soilSaturation); +} + +// Method to persist save the given lightning properties to NVS +void persistLightProps(int NM, int minLX) { + Serial.println("persistLightProps"); + bool n = NVS.setInt("nanoMeter", NM); + bool m = NVS.setInt("minimumLux", minLX); + if (n && m) { + Serial.println("Light properties sucessfully stored."); + NVS.commit(); + } + else { + Serial.println("error occured while trying to persist light properties"); + } +} + +// Method to restore light properties from Non-Volatile Storage (NVS) +void restoreLightProps() { + Serial.println("restoreLightProps"); + int nm = NVS.getInt("nanoMeter"); + int minLX = NVS.getInt("minimumLux"); + if (nm != 0) { + setValueNM(nm); + } + if (minLX != 0) { + minimumLightValueLX = minLX; + } + Serial.println(minimumLightValueLX); +} + +// Method to set given light properties +void setLightProperties(int NM, int minLX) { + setValueNM(NM); + minimumLightValueLX = minLX; + persistLightProps(NM, minLX); + Serial.print("new minimum Light Value LX: "); + Serial.println(minimumLightValueLX); +} + +void persistAutoProps(bool light, bool irrigation) { + Serial.println("persistAutoProps"); + // Saved in NVS as Integer: 1 = true, 2 = false, 0 = not persisted, use standard settings + bool n = NVS.setInt("automaticLight", (light ? 1 : 2)); + bool m = NVS.setInt("automaticIrrigation", (irrigation ? 1 : 2)); + if (n && m) { + NVS.commit(); + Serial.println("Auto properties sucessfully stored."); + } else { + Serial.println("error occured while trying to persist auto properties"); + } +} + +void restoreAutoProps() { + Serial.println("restoreLightProps"); + int li = NVS.getInt("automaticLight"); + int ir = NVS.getInt("automaticIrrigation"); + automaticLight = (bool) (li != 2); + automaticIrrigation = (bool) (ir != 2); + Serial.println(minimumLightValueLX); +} + +void setAutoProperties(bool light, bool irrigation) { + automaticLight = light; + automaticIrrigation = irrigation; + persistAutoProps(light, irrigation); + Serial.print("new auto settings: "); + Serial.println(automaticLight); + Serial.println(automaticIrrigation); +} + +String DEVICE_ID = ""; + +void initDeviceID() { + DEVICE_ID = NVS.getString("UUID"); + if (!DEVICE_ID.isEmpty()) { + Serial.print("Fetched Device ID from NVS: "); + Serial.println(DEVICE_ID); + } else { + uint8_t uuid[16]; + ESPRandom::uuid(uuid); + DEVICE_ID = ESPRandom::uuidToString(uuid); + NVS.setString("UUID", DEVICE_ID); + NVS.commit(); + Serial.print("New Device ID: "); + Serial.println(DEVICE_ID); + } +} + +// Returns the device ID +String getDeviceID() { + return DEVICE_ID; +} + +const char* getDeviceIDcharArr() { + return DEVICE_ID.c_str(); +} \ No newline at end of file diff --git a/src/store.h b/src/store.h new file mode 100644 index 0000000..5830269 --- /dev/null +++ b/src/store.h @@ -0,0 +1,29 @@ +// Feldkapazität des Bodens in Prozent: Standard ist Humus +extern int fieldCapacity; +// PWP des Bodens in Prozent: Standard ist Humus +extern int permanentWiltingPoint; +// Boden vollständig gesättigt bei (Prozent): Standard ist Humus +extern int soilSaturation; +// Helligkeitswert der mindestens vorhanden sein muss +extern int minimumLightValueLX; +// switches for automatic light and irrigation control +extern bool automaticLight; +extern bool automaticIrrigation; +// make sure device irrigates until fieldcapacity is reached +extern bool irrigateUntilFC; +// Device UUID will be set on first boot. +extern String DEVICE_ID; + +void persistSoilProps(int FC, int PWP, int SAT); +void restoreSoilProps(); +void setupStore(); +void setSoilProperties(int FC, int PWP, int SAT); +void persistLightProps(int NM, int minLX); +void restoreLightProps(); +void setLightProperties(int NM, int minLX); +void persistAutoProps(bool light, bool irrigation); +void restoreAutoProps(); +void setAutoProperties(bool light, bool irrigation); +void initDeviceID(); +String getDeviceID(); +const char* getDeviceIDcharArr(); \ No newline at end of file diff --git a/src/temperatureSensor.cpp b/src/temperatureSensor.cpp index 61ba0be..83ffdc1 100644 --- a/src/temperatureSensor.cpp +++ b/src/temperatureSensor.cpp @@ -1,7 +1,9 @@ -#include +#include +#include +#include + #define DHTPIN PIN_DHT11 - #define DHTTYPE DHT11 diff --git a/src/temperatureSensor.h b/src/temperatureSensor.h new file mode 100644 index 0000000..a53a7aa --- /dev/null +++ b/src/temperatureSensor.h @@ -0,0 +1,3 @@ +void setupTemperatureSensor(); +float readHumidity(); +float readTemperature(); \ No newline at end of file diff --git a/src/valve.cpp b/src/valve.cpp new file mode 100644 index 0000000..29c7d07 --- /dev/null +++ b/src/valve.cpp @@ -0,0 +1,66 @@ +#include +#include +#include + +extern "C" { + #include "freertos/FreeRTOS.h" + #include "freertos/task.h" +} + +#include + +bool openValve() { + digitalWrite(PIN_VALVE, HIGH); + digitalWrite(PIN_LED_G, LOW); + digitalWrite(PIN_LED_B, HIGH); + return true; +} + +bool closeValve() { + digitalWrite(PIN_VALVE, LOW); + digitalWrite(PIN_LED_G, HIGH); + digitalWrite(PIN_LED_B, LOW); + return false; +} + +void valveTask(void *parameter) { + bool isAutomatic = (bool)parameter; + Serial.print(isAutomatic); + Serial.println(" Valve task triggered."); + unsigned long valveTimeoutTimer = millis(); + bool valveOpen = false; + int initialSoilMoisture = readCapacitiveSoilMoistureSensor(); + + if (initialSoilMoisture <= permanentWiltingPoint || irrigateUntilFC) { + valveOpen = openValve(); + irrigateUntilFC = true; + } else { + Serial.println("Soil contains enough water. No irrigation needed. "); + } + + while (valveOpen) { + delay(500); + int mstValue = readCapacitiveSoilMoistureSensor(); + + if (mstValue > fieldCapacity) { // && isAutomatic + Serial.println("Field capacity reached. No irrigation needed. "); + valveOpen = closeValve(); + irrigateUntilFC = false; + } + if (millis() - valveTimeoutTimer >= MAX_VALVE_TIMEOUT) { + Serial.println("Irrigation timeout reached. close valve. "); + valveOpen = closeValve(); + } + } + vTaskDelete(NULL); +} + +void toggleValve(bool automatic) { + xTaskCreate( + valveTask, /* Task function. */ + "valveTask", /* String with name of task. */ + 2048, /* Stack size in bytes. */ + &automatic, /* Parameter passed as input of the task */ + 3, /* Priority of the task. */ + NULL); /* Task handle. */ +} diff --git a/src/valve.h b/src/valve.h new file mode 100644 index 0000000..5017d35 --- /dev/null +++ b/src/valve.h @@ -0,0 +1 @@ +void toggleValve(bool automatic);