Compare commits

..

67 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
861d8b527b code style 2020-06-30 23:14:17 +02:00
32b6850814 refactored mqtt connections 2020-06-30 23:03:06 +02:00
47d58777c5 introduced soil thresholds 2020-06-30 21:15:30 +02:00
4d685c8edf implemented valve control tasks 2020-06-29 23:27:36 +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
8ad568ea1b improvements 2020-06-29 15:34:23 +02:00
2d8a30572b implemented WiFi & MQTT and some improvements in existing code 2020-06-29 00:48:05 +02:00
a2af5ef149 added PINs to header.h 2020-06-28 20:45:06 +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
Andrés Uribe Stengel
82dfd49210 Increase delay time to 2 sec and reformat prints. 2020-06-23 16:44:46 +02:00
Max
f9cad2f8f4 cleand dependencies and added test methods for humitdy&temp sensor 2020-06-23 16:05:27 +02:00
Max
6b56b3f9b6 created some reading methods 2020-06-23 15:54:52 +02:00
Max
ec56181c08 setup basic temperature sample 2020-06-23 15:32:26 +02:00
Andrés Uribe Stengel
73e0739815 Refactor the moisture prints into the main file. 2020-06-23 12:22:17 +02:00
Sebastian
2a830468e9 added lip_deps 2020-06-23 11:56:50 +02:00
Sebastian
ef8299dfbf Added Lightsensor code to project 2020-06-23 11:48:42 +02:00
Andrés Uribe Stengel
3872b4f5fe Add new files for platformio project with arduino framework. 2020-06-23 10:44:42 +02:00
Andrés Uribe Stengel
39c0de57c4 Add .gitignore file. 2020-06-23 10:41:36 +02:00
Andrés Uribe Stengel
9118db1d51 Remove .ino files to restructure project. 2020-06-23 10:39:06 +02:00
Andrés Uribe Stengel
83fe6d5d7d Reformat code so that serial.begin and delays are in header file. 2020-06-22 16:18:22 +02:00
Andrés Uribe Stengel
566fcda0cc Create file for the soil moisture sensor with some logic 2020-06-22 16:13:22 +02:00
Andrés Uribe Stengel
f5bc52ac3a Create main project file 2020-06-22 16:12:16 +02:00
30 changed files with 1128 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.vs

67
.travis.yml Normal file
View File

@ -0,0 +1,67 @@
# Continuous Integration (CI) is the practice, in software
# engineering, of merging all developer working copies with a shared mainline
# several times a day < https://docs.platformio.org/page/ci/index.html >
#
# Documentation:
#
# * Travis CI Embedded Builds with PlatformIO
# < https://docs.travis-ci.com/user/integration/platformio/ >
#
# * PlatformIO integration with Travis CI
# < https://docs.platformio.org/page/ci/travis.html >
#
# * User Guide for `platformio ci` command
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
#
#
# Please choose one of the following templates (proposed below) and uncomment
# it (remove "# " before each line) or use own configuration according to the
# Travis CI documentation (see above).
#
#
# Template #1: General project. Test it using existing `platformio.ini`.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# install:
# - pip install -U platformio
# - platformio update
#
# script:
# - platformio run
#
# Template #2: The project is intended to be used as a library with examples.
#
# language: python
# python:
# - "2.7"
#
# sudo: false
# cache:
# directories:
# - "~/.platformio"
#
# env:
# - PLATFORMIO_CI_SRC=path/to/test/file.c
# - PLATFORMIO_CI_SRC=examples/file.ino
# - PLATFORMIO_CI_SRC=path/to/test/directory
#
# install:
# - pip install -U platformio
# - platformio update
#
# script:
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N

7
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"cquery.cacheDirectory": "${workspaceFolder}/.vscode/cquery_cached_index/"
}

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.

39
include/README Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

25
platformio.ini Normal file
View File

@ -0,0 +1,25 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:az-delivery-devkit-v4]
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
19 #DHT sensor library
AutoConnect@^1.1.7
ArduinoJson@^6.15.2
PubSubClient@^2.8
ArduinoNvs@^2.5
ESPRandom@^1.3.3

View File

@ -0,0 +1,31 @@
/*
Code for the Capacitive Soil Moisture Sensor
*/
#include <common.h>
const int numReadings = 20;
// reads soil moisture multiple times and calculates average to eliminate noise
int readCapacitiveSoilMoistureSensor() {
int total = 0; // the running total
// read from the sensor
for (int readIndex = 0; readIndex < numReadings; readIndex++) {
total = total + analogRead(PIN_MS);
delay(2);
}
int measurement = total / numReadings;
// map measurement to relative soil moisture value in %
int finalRes = map(measurement, VALUE_AIR, VALUE_WATER, 0, 100);
Serial.print("current soil moisture: ");
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 "/#"

228
src/connections.cpp Normal file
View File

@ -0,0 +1,228 @@
#include <ArduinoJson.h>
#include <AutoConnect.h>
#include <PubSubClient.h>
#include <WebServer.h>
#include <WiFi.h>
#include <common.h>
#include <store.h>
#include <valve.h>
#include <ntpManager.h>
extern "C" {
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
}
#include <connections.h>
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;
TaskHandle_t mqttTask;
WebServer Server;
AutoConnect Portal(Server);
WiFiClient client;
AutoConnectConfig Config;
PubSubClient mqttClient(client);
// spins wifi up and activates green status light when wifi is conencted
void connectWiFi() {
yield();
Serial.println("Start WiFi...");
if (Portal.begin()) {
digitalWrite(PIN_LED_G, HIGH);
}
}
// handles MQTT messages and keeps track of MQTT connection. starts reconnect timer if connection lost.
void mqttLoop(void *parameter) {
do {
yield();
mqttClient.loop();
delay(100);
} while (mqttClient.connected());
Serial.println("Disconnected from MQTT.");
if (!mqttClient.connected()) {
Serial.println("Checking WiFi Connection.");
if (WiFi.isConnected()) {
Serial.println("WiFi OK. Starting reconnect timer for MQTT.");
xTimerStart(mqttReconnectTimer, 0);
}
}
vTaskDelete(NULL);
}
// setup MQTT connection and spins up task to handle connection and messages
void connectMQTT() {
yield();
if (mqttClient.connected()) return;
Serial.println("Connecting to MQTT...");
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
mqttClient.disconnect();
mqttClient.connect(getDeviceIDcharArr());
if (mqttClient.connected()) {
Serial.println("Connected!");
} else {
Serial.println("NOT Connected!");
}
mqttClient.subscribe(MQTT_PATH_SUB);
Serial.print("subscribed to: ");
Serial.println(MQTT_PATH_SUB);
xTaskCreatePinnedToCore(
mqttLoop,
"mqttLoop",
8192,
NULL,
2,
&mqttTask,
1);
}
// handles lost wifi connection
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info) {
Serial.printf("[WiFi-event] event: %d\n", event);
switch (event) {
case SYSTEM_EVENT_STA_GOT_IP:
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
connectMQTT();
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
xTimerStart(wifiReconnectTimer, 0);
break;
}
}
// mqtt callback handles incoming messages
void onMqttMessage(char *topic, byte *payload, unsigned int payload_length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < payload_length; i++) {
Serial.print((char)payload[i]);
}
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) {
Serial.println("toggling valve...");
Serial.println(topic);
toggleValve(false);
}
if (strcmp(topic, MQTT_SOIL_PROPERTIES) == 0) {
Serial.println("receiving soil thresholds...");
Serial.println(topic);
StaticJsonDocument<1024> doc;
DeserializationError err = deserializeJson(doc, payload);
if (err == DeserializationError::Ok) {
int fc = doc["fc"];
int pwp = doc["pwp"];
int sat = doc["sat"];
setSoilProperties(fc, pwp, sat);
} else {
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());
}
}
}
// generates mqtt topics based on device-uuid
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);
}
// 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(
"mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void *)0, reinterpret_cast<TimerCallbackFunction_t>(connectMQTT));
wifiReconnectTimer = xTimerCreate(
"wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void *)0, reinterpret_cast<TimerCallbackFunction_t>(connectWiFi));
WiFi.onEvent(WiFiEvent);
connectWiFi();
connectMQTT();
mqttClient.setCallback(onMqttMessage);
setupNTP();
}
// method to delegate publish from other modules to connecions module
void publishMessage(const char *topic, const char *msg) {
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);

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);

19
src/lightSensor.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <BH1750.h>
#include <Wire.h>
#include <lightSensor.h>
// 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() {
int intensity = lightMeter.readLightLevel();
return intensity;
}

2
src/lightSensor.h Normal file
View File

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

45
src/main.cpp Normal file
View File

@ -0,0 +1,45 @@
/*
Main file for the SmartGarden project
*/
#include <common.h>
#include <sensors.h>
#include <store.h>
#include <connections.h>
#define TIME_TO_SLEEP 30
volatile int noSleepTasks = 0;
unsigned long sensorReadTimer = 0;
bool valveOpen = false;
void setup() {
Serial.begin(115200);
sensorReadTimer = millis();
pinMode(PIN_VALVE, OUTPUT);
pinMode(PIN_LED_R, OUTPUT);
pinMode(PIN_LED_G, OUTPUT);
pinMode(PIN_LED_B, OUTPUT);
digitalWrite(PIN_VALVE, LOW);
setupStore();
setupSensors();
setupConnections();
// esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000);
Serial.println("Setup complete...");
Serial.println();
Serial.println();
// First read without delay
readSensors();
}
void loop() {
// Main loop: read sensors
if (millis() - sensorReadTimer >= FREQUENCY) {
readSensors();
sensorReadTimer = millis();
}
}

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();

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();

32
src/temperatureSensor.cpp Normal file
View File

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

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);

11
test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html