Compare commits

..

47 Commits

Author SHA1 Message Date
c611faf475 added docs 2021-04-21 11:45:33 +02:00
1ded57c2fa added readme, removed accidentally added compose 2020-07-30 17:20:03 +02:00
29d22e9c98 more comments and typos 2020-07-30 15:03:14 +02:00
3fdbe940b8 added comments and docker compose file 2020-07-30 13:00:38 +02:00
Max
81eedadb63 cleaned up pin defines in common.h 2020-07-23 20:04:50 +02:00
Max
f6b2fc75dd changed temperature comments, cleaned up 2020-07-23 20:03:20 +02:00
Andrés Uribe Stengel
960699fbc8 Set automatic light and irrigation initially to false. 2020-07-23 18:04:04 +02:00
Sebastian
f04624dccb setupNTP is now called inside connection setup at the end 2020-07-23 15:36:18 +02:00
Sebastian
28dfd4a9f4 now publishes every 60 seconds 2020-07-23 13:09:27 +02:00
Sebastian
68d45f6363 Added some comments 2020-07-23 12:30:44 +02:00
Sebastian
dd2a831ade Merge branch 'develop' into sherzog_dev 2020-07-23 12:20:34 +02:00
Sebastian
903e1e101a added comments to light classes 2020-07-22 23:28:13 +02:00
08bdb58a6c refactored and restructured 2020-07-22 18:55:58 +02:00
Sebastian
c68e136985 Merge branch 'develop' into sherzog_dev 2020-07-22 16:52:35 +02:00
Sebastian
80b6e5269f added comments for ntpManager 2020-07-22 16:52:15 +02:00
5136a46197 replaced device_id with UUID 2020-07-22 16:37:31 +02:00
93561d4273 implemented irrigation logic + autotoggle by mqtt 2020-07-16 10:55:17 +02:00
decdf72f7d resolved unresolved merge conflict xD 2020-07-14 18:23:29 +02:00
Andrés Uribe Stengel
d5aede9895 Merge Conflict 2020-07-14 15:24:37 +02:00
Andrés Uribe Stengel
2cf9157ebb Change brightness to int and change publish intervall to one minute. 2020-07-14 15:22:03 +02:00
Sebastian
18b5c0bfec Added class to get timestamps from NTP server 2020-07-12 18:28:59 +02:00
Andrés Uribe Stengel
855f1d958e Merge branch 'develop' of https://git.it.hs-heilbronn.de/auribest/smart_garden into auribest_dev 2020-07-12 10:28:30 +02:00
Sebastian
a50ca3f4eb Merge branch 'develop' into sherzog_dev 2020-07-11 12:06:15 +02:00
Sebastian
71e58773cb Light will now turn on if lx below minLX also color can be changed based on nanoMeters of prefered light 2020-07-11 10:34:06 +02:00
cbf51999db improved wifi & mqtt; preparation for deep sleep 2020-07-06 21:44:53 +02:00
Sebastian
62bf9a17c5 fixed light subscription 2020-07-06 12:41:14 +02:00
Sebastian
5fb882c59a light treshold will now be set based on mqtt command 2020-07-04 17:09:44 +02:00
Andrés Uribe Stengel
9e3b53a4eb Merge branch 'develop' of https://git.it.hs-heilbronn.de/auribest/smart_garden into auribest_dev 2020-07-04 08:12:56 +02:00
Sebastian
c9dc8f545f Merge branch 'develop' into sherzog_dev 2020-07-04 00:31:53 +02:00
6b20f577be improved sensor data structure 2020-07-03 19:08:38 +02:00
Sebastian
68f75f252d deleted test stuff 2020-07-03 13:21:13 +02:00
Sebastian
88d4561f9e Merge branch 'develop' into sherzog_dev 2020-07-03 12:51:56 +02:00
49fa6d08f4 increased memory for valve task 2020-07-02 22:02:19 +02:00
8512deb0c3 Merge branch 'volkmann_dev' into develop 2020-07-02 21:51:08 +02:00
a6fbbfab73 switched mqtt client; implemented persistence 2020-07-02 21:50:12 +02:00
Andrés Uribe Stengel
5d65dc79d0 Update .gitignore File to ignore Visual Studio Files. 2020-07-02 10:32:16 +02:00
Sebastian
b4ecfed027 added example to read data from MQTT Server 2020-07-02 02:03:30 +02:00
Andrés Uribe Stengel
d64710a841 Prevent moisture to exceed range from 0-100 percent. 2020-07-01 16:54:11 +02:00
Sebastian
03ea93b741 changed mqtt id 2020-07-01 14:31:50 +02:00
Sebastian
e1d7e3e51d Merge branch 'develop' into sherzog_dev 2020-07-01 12:35:34 +02:00
Sebastian
52e3ab8cdc update lightChecker 2020-07-01 10:52:26 +02:00
Sebastian
2827573bbc added lightChecker class and some basic pwm stuff 2020-07-01 10:49:42 +02:00
Sebastian
2dfe3e5246 Merge branch 'develop' into sherzog_dev 2020-06-29 18:35:30 +02:00
Sebastian
a0066f0811 worked at RGBclass 2020-06-29 18:35:12 +02:00
Sebastian
ac9327179d new class LightningChecker to control led 2020-06-28 18:56:04 +02:00
Sebastian
09c045fd2e Merge branch 'develop' into sherzog_dev 2020-06-28 11:40:38 +02:00
Sebastian
6d3af9e18d Changd print message from sensor to light-sensor started 2020-06-28 11:39:05 +02:00
26 changed files with 747 additions and 263 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
.vscode/c_cpp_properties.json .vscode/c_cpp_properties.json
.vscode/launch.json .vscode/launch.json
.vscode/ipch .vscode/ipch
.vs

Binary file not shown.

View File

@ -1,2 +1,10 @@
# Smart_Garden # Smart_Garden
## Requirements
VisualStudioCode with PlatformIO extension
## Build
Configuration is defined in `platformio.ini` file. If you have PlatformIO extension installed, just use built-in build-command.
## Flash to µC
Connect ESP32 to any desired USB-Port and use PlatformIOs upload-command.

View File

@ -13,14 +13,13 @@ platform = espressif32
board = az-delivery-devkit-v4 board = az-delivery-devkit-v4
framework = arduino framework = arduino
monitor_speed = 115200 monitor_speed = 115200
; build_flags = -DCORE_DEBUG_LEVEL=5
lib_deps = lib_deps =
439 #ID of Lightsensor library BH1750 439 #ID of Lightsensor library BH1750
19 #DHT sensor library 19 #DHT sensor library
31 #Adafruit Unified Sensor
AutoConnect@^1.1.7 AutoConnect@^1.1.7
AsyncMqttClient@^0.8.2
ArduinoJson@^6.15.2 ArduinoJson@^6.15.2
MQTT@^2.4.7 PubSubClient@^2.8
; PubSubClient@^2.8 ArduinoNvs@^2.5
; ESPRandom@^1.3.3 ESPRandom@^1.3.3

View File

@ -1,28 +1,31 @@
/* /*
Code for the Capacitive Soil Moisture Sensor Code for the Capacitive Soil Moisture Sensor
*/ */
#include <common.h>
#include <header.h>
const int numReadings = 20; const int numReadings = 20;
void setupCapacitiveSoilMoistureSensor() { // reads soil moisture multiple times and calculates average to eliminate noise
// pinMode(PIN_MS, INPUT); int readCapacitiveSoilMoistureSensor() {
} int total = 0; // the running total
// read from the sensor
int readCapacitiveSoilMoistureSensor() for (int readIndex = 0; readIndex < numReadings; readIndex++) {
{
int total = 0; // the running total
// read from the sensor:
for (int readIndex = 0; readIndex < numReadings; readIndex++)
{
total = total + analogRead(PIN_MS); total = total + analogRead(PIN_MS);
delay(2); delay(2);
} }
int measurement = total / numReadings; int measurement = total / numReadings;
Serial.print("soil moisture raw: ");
Serial.println(measurement); // map measurement to relative soil moisture value in %
// add the reading to the total: int finalRes = map(measurement, VALUE_AIR, VALUE_WATER, 0, 100);
//int measurement = analogRead(PIN_MS); Serial.print("current soil moisture: ");
return map(measurement, VALUE_AIR, VALUE_WATER, 0, 100); Serial.println(finalRes);
// clamp mesurement to a value between 0 and 100
if (finalRes < 0) {
return 0;
} else if (finalRes > 100) {
return 100;
} else {
return finalRes;
}
} }

View File

@ -0,0 +1 @@
int readCapacitiveSoilMoistureSensor();

43
src/common.h Normal file
View File

@ -0,0 +1,43 @@
#include <Arduino.h>
// this common Header file defines commonly used hardware and buildtime constants
// 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
// 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 "/#"

View File

@ -1,19 +1,27 @@
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <MQTT.h> #include <AutoConnect.h>
#include <header.h> #include <PubSubClient.h>
#include <WebServer.h>
#include <WiFi.h>
#include <common.h>
#include <store.h>
#include <valve.h>
#include <ntpManager.h>
extern "C" { extern "C" {
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/timers.h" #include "freertos/timers.h"
} }
#define MQTT_VALVE_COMMAND MQTT_TOPIC_BASE_SUB "/" MQTT_DEVICE_ID "/valve" #include <connections.h>
#define MQTT_SOIL_PROPERTIES MQTT_TOPIC_BASE_SUB "/" MQTT_DEVICE_ID "/soil"
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 mqttReconnectTimer;
TimerHandle_t wifiReconnectTimer; TimerHandle_t wifiReconnectTimer;
// TimerHandle_t mqttProcessingTimer;
TaskHandle_t mqttTask; TaskHandle_t mqttTask;
WebServer Server; WebServer Server;
@ -21,56 +29,64 @@ AutoConnect Portal(Server);
WiFiClient client; WiFiClient client;
AutoConnectConfig Config; AutoConnectConfig Config;
// Config.autoReconnect = true; PubSubClient mqttClient(client);
// WiFi.config(Config);
MQTTClient mqttClient;
// spins wifi up and activates green status light when wifi is conencted
void connectWiFi() { void connectWiFi() {
yield();
Serial.println("Start WiFi..."); Serial.println("Start WiFi...");
if (Portal.begin()) { if (Portal.begin()) {
digitalWrite(PIN_LED_G, HIGH); digitalWrite(PIN_LED_G, HIGH);
} }
} }
// handles MQTT messages and keeps track of MQTT connection. starts reconnect timer if connection lost.
void mqttLoop(void *parameter) { void mqttLoop(void *parameter) {
bool x;
do { do {
x = mqttClient.loop(); yield();
delay(50); mqttClient.loop();
delay(100);
} while (mqttClient.connected()); } while (mqttClient.connected());
Serial.println("Disconnected from MQTT.");
if (!mqttClient.connected()) { if (!mqttClient.connected()) {
Serial.println("Disconnected from MQTT."); Serial.println("Checking WiFi Connection.");
if (WiFi.isConnected()) { if (WiFi.isConnected()) {
Serial.println("WiFi OK. Starting reconnect timer for MQTT.");
xTimerStart(mqttReconnectTimer, 0); xTimerStart(mqttReconnectTimer, 0);
} }
} }
vTaskDelete(NULL); vTaskDelete(NULL);
} }
// setup MQTT connection and spins up task to handle connection and messages
void connectMQTT() { void connectMQTT() {
yield();
if (mqttClient.connected()) return;
Serial.println("Connecting to MQTT..."); Serial.println("Connecting to MQTT...");
mqttClient.begin(MQTT_HOST, MQTT_PORT, client);
mqttClient.connect(MQTT_DEVICE_ID); mqttClient.setServer(MQTT_HOST, MQTT_PORT);
mqttClient.disconnect();
mqttClient.connect(getDeviceIDcharArr());
if (mqttClient.connected()) { if (mqttClient.connected()) {
Serial.println("Connected!"); Serial.println("Connected!");
} else { } else {
Serial.println("NOT Connected!"); Serial.println("NOT Connected!");
} }
mqttClient.subscribe(MQTT_PATH_SUB, 2); mqttClient.subscribe(MQTT_PATH_SUB);
Serial.print("subscribed to: "); Serial.print("subscribed to: ");
Serial.println(MQTT_PATH_SUB); Serial.println(MQTT_PATH_SUB);
xTaskCreate( xTaskCreatePinnedToCore(
mqttLoop, /* Task function. */ mqttLoop,
"mqttLoop", /* String with name of task. */ "mqttLoop",
8192, /* Stack size in bytes. */ 8192,
NULL, /* Parameter passed as input of the task */ NULL,
1, /* Priority of the task. */ 2,
&mqttTask); /* Task handle. */ &mqttTask,
//xTimerStart(mqttProcessingTimer, 0); 1);
} }
void WiFiEvent(WiFiEvent_t event) { // handles lost wifi connection
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
Serial.printf("[WiFi-event] event: %d\n", event); Serial.printf("[WiFi-event] event: %d\n", event);
switch (event) { switch (event) {
case SYSTEM_EVENT_STA_GOT_IP: case SYSTEM_EVENT_STA_GOT_IP:
@ -81,14 +97,20 @@ void WiFiEvent(WiFiEvent_t event) {
break; break;
case SYSTEM_EVENT_STA_DISCONNECTED: case SYSTEM_EVENT_STA_DISCONNECTED:
digitalWrite(PIN_LED_G, LOW); 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(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
// xTimerStop(mqttProcessingTimer, 0);
xTimerStart(wifiReconnectTimer, 0); xTimerStart(wifiReconnectTimer, 0);
break; break;
} }
} }
void onMqttMessage(MQTTClient *client, char topic[], char payload[], int payload_length) { // mqtt callback handles incoming messages
void onMqttMessage(char *topic, byte *payload, unsigned int payload_length) {
Serial.print("Message arrived ["); Serial.print("Message arrived [");
Serial.print(topic); Serial.print(topic);
Serial.print("] "); Serial.print("] ");
@ -97,16 +119,27 @@ void onMqttMessage(MQTTClient *client, char topic[], char payload[], int payload
} }
Serial.println(); Serial.println();
if (strcmp(topic, MQTT_LIGHT_PROPERTIES) == 0) {
Serial.println("receiving light threshold...");
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) { if (strcmp(topic, MQTT_VALVE_COMMAND) == 0) {
Serial.println("toggling valve..."); Serial.println("toggling valve...");
Serial.println(topic); Serial.println(topic);
toggleValve(); toggleValve(false);
} }
if (strcmp(topic, MQTT_SOIL_PROPERTIES) == 0) { if (strcmp(topic, MQTT_SOIL_PROPERTIES) == 0) {
Serial.println("receiving soil thresholds..."); Serial.println("receiving soil thresholds...");
Serial.println(topic); Serial.println(topic);
Serial.println(payload);
// const int capacity = JSON_OBJECT_SIZE(3) + 2 * JSON_OBJECT_SIZE(1);
StaticJsonDocument<1024> doc; StaticJsonDocument<1024> doc;
DeserializationError err = deserializeJson(doc, payload); DeserializationError err = deserializeJson(doc, payload);
if (err == DeserializationError::Ok) { if (err == DeserializationError::Ok) {
@ -118,26 +151,78 @@ void onMqttMessage(MQTTClient *client, char topic[], char payload[], int payload
Serial.println(err.c_str()); 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 setupConnections() { // generates mqtt topics based on device-uuid
Serial.println(); void constructMQTTpaths() {
Serial.println(); 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);
}
// initializes Wifi and MQTT connections
void setupConnections() {
constructMQTTpaths();
Serial.println();
Serial.println();
// disable Watchdog Task. This task made ESP stopped working, probably caused by working with arduino framework combined with multicore-concurrent patterns
disableCore0WDT();
Config.autoReconnect = true;
Portal.config(Config);
// FreeRTOS Timer to handle loss of connections
mqttReconnectTimer = xTimerCreate( mqttReconnectTimer = xTimerCreate(
"mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void *)0, reinterpret_cast<TimerCallbackFunction_t>(connectMQTT)); "mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void *)0, reinterpret_cast<TimerCallbackFunction_t>(connectMQTT));
wifiReconnectTimer = xTimerCreate( wifiReconnectTimer = xTimerCreate(
"wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void *)0, reinterpret_cast<TimerCallbackFunction_t>(connectWiFi)); "wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void *)0, reinterpret_cast<TimerCallbackFunction_t>(connectWiFi));
// mqttProcessingTimer = xTimerCreate(
// "messageTimer", pdMS_TO_TICKS(50), pdTRUE, (void *)0, reinterpret_cast<TimerCallbackFunction_t>(mqttLoop));
WiFi.onEvent(WiFiEvent); WiFi.onEvent(WiFiEvent);
mqttClient.onMessageAdvanced(onMqttMessage);
connectWiFi(); connectWiFi();
connectMQTT();
mqttClient.setCallback(onMqttMessage);
setupNTP();
} }
// method to delegate publish from other modules to connecions module
void publishMessage(const char *topic, const char *msg) { void publishMessage(const char *topic, const char *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.");
}
} }

5
src/connections.h Normal file
View File

@ -0,0 +1,5 @@
void connectWiFi();
void mqttLoop(void *parameter);
void connectMQTT();
void setupConnections();
void publishMessage(const char *topic, const char *msg);

View File

@ -1,77 +0,0 @@
/*
Header file for the SmartGarden project
*/
#define HEADER_H
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <BH1750.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>
#include <WiFi.h>
#include <WebServer.h>
#include <AutoConnect.h>
// 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-timo"
#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);

103
src/lightChecker.cpp Normal file
View File

@ -0,0 +1,103 @@
#include <common.h>
#include <lightChecker.h>
// 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. */
}

4
src/lightChecker.h Normal file
View File

@ -0,0 +1,4 @@
void setupPWM();
void setValueNM(int NM);
void triggerLight();
void getColorBasedOnValueNM(int valueNM);

View File

@ -1,14 +1,19 @@
#include <header.h> #include <BH1750.h>
#include <Wire.h>
#include <lightSensor.h>
// New BH1750 class
BH1750 lightMeter; BH1750 lightMeter;
// Setup for reading light sensor (prepares i2c communication)
void setupLightSensor() { void setupLightSensor() {
Wire.begin(); Wire.begin();
lightMeter.begin(); lightMeter.begin();
Serial.println("Sensor started..."); Serial.println("Sensor started...");
} }
// Method to read and return the light value measured by BH1750 sensor
float readLightSensorValue() { float readLightSensorValue() {
float intensity = lightMeter.readLightLevel(); int intensity = lightMeter.readLightLevel();
return intensity; return intensity;
} }

2
src/lightSensor.h Normal file
View File

@ -0,0 +1,2 @@
void setupLightSensor();
float readLightSensorValue();

View File

@ -2,9 +2,13 @@
Main file for the SmartGarden project Main file for the SmartGarden project
*/ */
#include <header.h> #include <common.h>
#include <sensors.h>
#include <store.h>
#include <connections.h>
#include <string> #define TIME_TO_SLEEP 30
volatile int noSleepTasks = 0;
unsigned long sensorReadTimer = 0; unsigned long sensorReadTimer = 0;
bool valveOpen = false; bool valveOpen = false;
@ -20,22 +24,22 @@ void setup() {
digitalWrite(PIN_VALVE, LOW); digitalWrite(PIN_VALVE, LOW);
setupStore();
setupSensors();
setupConnections(); setupConnections();
setupLightSensor(); // esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000);
setupTemperatureSensor();
setupCapacitiveSoilMoistureSensor();
Serial.println("Setup complete..."); Serial.println("Setup complete...");
Serial.println(); Serial.println();
Serial.println(); Serial.println();
// First read without delay
readSensors();
} }
void loop() { void loop() {
// read sensors // Main loop: read sensors
if (millis() - sensorReadTimer >= FREQUENCY) { if (millis() - sensorReadTimer >= FREQUENCY) {
readSensors(); readSensors();
sensorReadTimer = millis(); sensorReadTimer = millis();
} }
// toggle valve
if (valveOpen) {
}
} }

51
src/ntpManager.cpp Normal file
View File

@ -0,0 +1,51 @@
#include <common.h>
#include <WiFi.h>
#include <ntpManager.h>
// 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");
}

4
src/ntpManager.h Normal file
View File

@ -0,0 +1,4 @@
void setupNTP();
bool checkForDay();
int getCurrentHour();
void printTimestamp();

View File

@ -1,105 +0,0 @@
#include <header.h>
#include <string>
//using namespace std;
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
}
#define MQTT_MOISTURE MQTT_PATH_PUB "moisture"
#define MQTT_TEMPERATURE MQTT_PATH_PUB "temperature"
#define MQTT_HUMIDITY MQTT_PATH_PUB "humidity"
#define MQTT_BRIGHTNESS MQTT_PATH_PUB "brightness"
char buffer[16];
// 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() {
float lxValue = readLightSensorValue();
Serial.print("Light intensity: ");
Serial.print(lxValue);
Serial.println(" lx");
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);
float humidityValue = readHumidity();
Serial.print("Humidity: ");
Serial.println(humidityValue);
sprintf(buffer, "%f", humidityValue);
publishMessage(MQTT_HUMIDITY, buffer);
float temperatureValue = readTemperature();
Serial.print("Temperature: ");
Serial.println(temperatureValue);
sprintf(buffer, "%f", temperatureValue);
publishMessage(MQTT_TEMPERATURE, buffer);
Serial.print("\n");
}
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. */
10000, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
1, /* Priority of the task. */
NULL); /* Task handle. */
}
void setSoilProperties(int FC, int PWP, int SAT) {
fieldCapacity = FC;
permanentWiltingPoint = PWP;
soilSaturation = SAT;
Serial.print("fieldCapacity: ");
Serial.println(fieldCapacity);
Serial.print("permanentWiltingPoint: ");
Serial.println(permanentWiltingPoint);
Serial.print("soilSaturation: ");
Serial.println(soilSaturation);
// TODO save to nvs nonvolatile flash storage
}

65
src/sensors.cpp Normal file
View File

@ -0,0 +1,65 @@
#include <ArduinoJson.h>
#include <capacitiveSoilMoistureSensor.h>
#include <connections.h>
#include <lightChecker.h>
#include <lightSensor.h>
#include <ntpManager.h>
#include <sensors.h>
#include <valve.h>
#include <store.h>
#include <temperatureSensor.h>
#include <common.h>
char buffer[128];
char MQTT_SENSOR_DATA_TOPIC[64];
void setupSensors() {
setupLightSensor();
setupPWM();
setupTemperatureSensor();
// generates MQTT topics based on device ID
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);
}
// function to collect sensor data and delegate collected data as json to mqtt-task
void readSensors() {
Serial.println();
StaticJsonDocument<128> doc;
float lxValue = readLightSensorValue();
Serial.print("Light intensity: ");
Serial.print(lxValue);
Serial.println(" lx");
doc["brightness"] = lxValue;
if (automaticLight && (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);
}

2
src/sensors.h Normal file
View File

@ -0,0 +1,2 @@
void setupSensors();
void readSensors();

177
src/store.cpp Normal file
View File

@ -0,0 +1,177 @@
#include <ArduinoNvs.h>
#include <ESPRandom.h>
#include <common.h>
#include <lightChecker.h>
#include <store.h>
// 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 = false;
bool automaticIrrigation = false;
// Make sure device irrigates until fieldcapacity is reached
bool irrigateUntilFC = false;
// stores properties to non-volatile-flash
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");
}
}
// restores properties from nvs on boot
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);
}
// will be executed in setup process when booting µC
void setupStore() {
NVS.begin("store");
initDeviceID();
restoreSoilProps();
restoreLightProps();
restoreAutoProps();
}
// sets soil properties and calls funtion to persist them
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");
}
}
// restores properties from nvs on boot
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 = "";
// generates device UUID on first boot and stores it to NVS. UUID will be restored from NVS after each subsequent boot
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();
}

29
src/store.h Normal file
View File

@ -0,0 +1,29 @@
// Fieldcapacity of the Ground in Percentage: Standard is Humus
extern int fieldCapacity;
// PWP of the Ground in Percentage: Standard is Humus
extern int permanentWiltingPoint;
// Ground completely saturated by (Percentage): Standard is Humus
extern int soilSaturation;
// Minimum light value before light turns on
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();

View File

@ -1,24 +1,24 @@
#include <header.h> #include <DHT.h>
#include <DHT_U.h>
#include <common.h>
#define DHTPIN PIN_DHT11 #define DHTPIN PIN_DHT11
#define DHTTYPE DHT11 #define DHTTYPE DHT11
// set pin and type
DHT_Unified dht(DHTPIN, DHTTYPE); DHT_Unified dht(DHTPIN, DHTTYPE);
// initialize temperature sensor
void setupTemperatureSensor() { void setupTemperatureSensor() {
// Serial.begin(9600); // Serial.begin(9600);
dht.begin(); dht.begin();
Serial.println(F("DHT11 Unified Sensor Ready")); Serial.println(F("DHT11 Unified Sensor Ready"));
sensor_t sensor; sensor_t sensor;
dht.temperature().getSensor(&sensor); dht.temperature().getSensor(&sensor);
} }
// Get humidity event and its value. // Get humidity event and its value.
float readHumidity(){ float readHumidity(){
sensors_event_t event; sensors_event_t event;
dht.humidity().getEvent(&event); dht.humidity().getEvent(&event);
return event.relative_humidity; return event.relative_humidity;

3
src/temperatureSensor.h Normal file
View File

@ -0,0 +1,3 @@
void setupTemperatureSensor();
float readHumidity();
float readTemperature();

71
src/valve.cpp Normal file
View File

@ -0,0 +1,71 @@
#include <capacitiveSoilMoistureSensor.h>
#include <common.h>
#include <store.h>
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
}
#include <valve.h>
// open valve and set status led to blue while irrigating
bool openValve() {
digitalWrite(PIN_VALVE, HIGH);
digitalWrite(PIN_LED_G, LOW);
digitalWrite(PIN_LED_B, HIGH);
return true;
}
// close valve and set status back to green
bool closeValve() {
digitalWrite(PIN_VALVE, LOW);
digitalWrite(PIN_LED_G, HIGH);
digitalWrite(PIN_LED_B, LOW);
return false;
}
// function to handle valve control, see documentation for visual representation of control flow
void valveTask(void *parameter) {
// this parameter is used to determine if task was triggered manually by mqtt or by the sensor loop
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);
}
// Creates a new task which handles valve control concurrently
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. */
}

1
src/valve.h Normal file
View File

@ -0,0 +1 @@
void toggleValve(bool automatic);