Compare commits

..

No commits in common. "develop" and "master" have entirely different histories.

30 changed files with 0 additions and 1128 deletions

6
.gitignore vendored
View File

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

View File

@ -1,67 +0,0 @@
# 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

View File

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

View File

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

Binary file not shown.

View File

@ -1,10 +1,2 @@
# 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

@ -1,39 +0,0 @@
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

View File

@ -1,46 +0,0 @@
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

View File

@ -1,25 +0,0 @@
; 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

@ -1,31 +0,0 @@
/*
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

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

View File

@ -1,43 +0,0 @@
#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,228 +0,0 @@
#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.");
}
}

View File

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

View File

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

View File

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

View File

@ -1,19 +0,0 @@
#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;
}

View File

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

View File

@ -1,45 +0,0 @@
/*
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();
}
}

View File

@ -1,51 +0,0 @@
#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");
}

View File

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

View File

@ -1,65 +0,0 @@
#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);
}

View File

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

View File

@ -1,177 +0,0 @@
#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();
}

View File

@ -1,29 +0,0 @@
// 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,32 +0,0 @@
#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;
}

View File

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

View File

@ -1,71 +0,0 @@
#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. */
}

View File

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

View File

@ -1,11 +0,0 @@
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