Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c611faf475 | |||
| 1ded57c2fa | |||
| 29d22e9c98 | |||
| 3fdbe940b8 | |||
|
|
81eedadb63 | ||
|
|
f6b2fc75dd | ||
|
|
960699fbc8 | ||
|
|
f04624dccb | ||
|
|
28dfd4a9f4 | ||
|
|
68d45f6363 | ||
|
|
dd2a831ade | ||
|
|
903e1e101a | ||
| 08bdb58a6c | |||
|
|
c68e136985 | ||
|
|
80b6e5269f | ||
| 5136a46197 | |||
| 93561d4273 | |||
| decdf72f7d | |||
|
|
d5aede9895 | ||
|
|
2cf9157ebb | ||
|
|
18b5c0bfec | ||
|
|
855f1d958e | ||
|
|
a50ca3f4eb | ||
|
|
71e58773cb | ||
| cbf51999db | |||
|
|
62bf9a17c5 | ||
|
|
5fb882c59a | ||
|
|
9e3b53a4eb | ||
|
|
c9dc8f545f | ||
| 6b20f577be | |||
|
|
68f75f252d | ||
|
|
88d4561f9e | ||
| 49fa6d08f4 | |||
| 8512deb0c3 | |||
| a6fbbfab73 | |||
|
|
5d65dc79d0 | ||
|
|
b4ecfed027 | ||
|
|
d64710a841 | ||
|
|
03ea93b741 | ||
|
|
e1d7e3e51d | ||
|
|
52e3ab8cdc | ||
|
|
2827573bbc | ||
| 861d8b527b | |||
| 32b6850814 | |||
| 47d58777c5 | |||
| 4d685c8edf | |||
|
|
2dfe3e5246 | ||
|
|
a0066f0811 | ||
| 8ad568ea1b | |||
| 2d8a30572b | |||
| a2af5ef149 | |||
|
|
ac9327179d | ||
|
|
09c045fd2e | ||
|
|
6d3af9e18d | ||
|
|
82dfd49210 | ||
|
|
f9cad2f8f4 | ||
|
|
6b56b3f9b6 | ||
|
|
ec56181c08 | ||
|
|
73e0739815 | ||
|
|
2a830468e9 | ||
|
|
ef8299dfbf | ||
|
|
3872b4f5fe | ||
|
|
39c0de57c4 | ||
|
|
9118db1d51 | ||
|
|
83fe6d5d7d | ||
|
|
566fcda0cc | ||
|
|
f5bc52ac3a |
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal 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
67
.travis.yml
Normal 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
7
.vscode/extensions.json
vendored
Normal 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
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"cquery.cacheDirectory": "${workspaceFolder}/.vscode/cquery_cached_index/"
|
||||
}
|
||||
BIN
Dokumentation_SmartGarden.pdf
Normal file
BIN
Dokumentation_SmartGarden.pdf
Normal file
Binary file not shown.
@ -1,2 +1,10 @@
|
||||
# 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
39
include/README
Normal 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
46
lib/README
Normal 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
25
platformio.ini
Normal 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
|
||||
31
src/capacitiveSoilMoistureSensor.cpp
Normal file
31
src/capacitiveSoilMoistureSensor.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
1
src/capacitiveSoilMoistureSensor.h
Normal file
1
src/capacitiveSoilMoistureSensor.h
Normal file
@ -0,0 +1 @@
|
||||
int readCapacitiveSoilMoistureSensor();
|
||||
43
src/common.h
Normal file
43
src/common.h
Normal 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
228
src/connections.cpp
Normal 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
5
src/connections.h
Normal 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
103
src/lightChecker.cpp
Normal 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
4
src/lightChecker.h
Normal file
@ -0,0 +1,4 @@
|
||||
void setupPWM();
|
||||
void setValueNM(int NM);
|
||||
void triggerLight();
|
||||
void getColorBasedOnValueNM(int valueNM);
|
||||
19
src/lightSensor.cpp
Normal file
19
src/lightSensor.cpp
Normal 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
2
src/lightSensor.h
Normal file
@ -0,0 +1,2 @@
|
||||
void setupLightSensor();
|
||||
float readLightSensorValue();
|
||||
45
src/main.cpp
Normal file
45
src/main.cpp
Normal 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
51
src/ntpManager.cpp
Normal 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
4
src/ntpManager.h
Normal file
@ -0,0 +1,4 @@
|
||||
void setupNTP();
|
||||
bool checkForDay();
|
||||
int getCurrentHour();
|
||||
void printTimestamp();
|
||||
65
src/sensors.cpp
Normal file
65
src/sensors.cpp
Normal 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
2
src/sensors.h
Normal file
@ -0,0 +1,2 @@
|
||||
void setupSensors();
|
||||
void readSensors();
|
||||
177
src/store.cpp
Normal file
177
src/store.cpp
Normal 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
29
src/store.h
Normal 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
32
src/temperatureSensor.cpp
Normal 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
3
src/temperatureSensor.h
Normal file
@ -0,0 +1,3 @@
|
||||
void setupTemperatureSensor();
|
||||
float readHumidity();
|
||||
float readTemperature();
|
||||
71
src/valve.cpp
Normal file
71
src/valve.cpp
Normal 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
1
src/valve.h
Normal file
@ -0,0 +1 @@
|
||||
void toggleValve(bool automatic);
|
||||
11
test/README
Normal file
11
test/README
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user