/**
AMICA-1 Prototype
DHT22
Soil Moisture Sensor v.1.2
MTTQ client
Water Cycle
Statistics
ESP32 Logging
Copyright 2022, Antonio Soldati
*/
#include <WiFi.h>
#include "DHTesp.h"
#include "PubSubClient.h"
#include "esp_log.h"
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#define NTP_SERVER "pool.ntp.org"
#define UTC_OFFSET 3600
#define UTC_OFFSET_DST 3600
#define ONBOARD_LED 2
#define LED_GREEN 5
#define LED_RED 4
#define DHT_PIN 15
#define SOIL 35
#define RELAY 14
const char* ssid = "Wokwi-GUEST";
const char* password = "";
String status_string = "";
String timestamp ="";
String temp = "";
String humid = "";
String moist ="";
String realtime = "";
const int dry_value = 2550;
const int wet_value = 600;
const int threshold_high = 70;
const int threshold_low = 20;
bool standby =false;
bool water_cycle = false;
bool pump_on = false;
//arrays for average calculations
float average_hour;
float average_day;
int day_array [24];
int month_array [30];
int second;
int minute;
int hour;
int day;
//char d[97];
String day_string = "";
String month_string ="";
DHTesp dhtSensor;
WiFiClient espClient;
PubSubClient client(espClient);
// MQTT Broker
const char *mqtt_broker = "broker.mqttdashboard.com";
const char *topic_in = "amica/prototype-1/in";
const char *topic_out_status = "amica/prototype-1/out/status";
const char *topic_out_timestamp = "amica/prototype-1/out/timestamp";
const char *topic_out_realtime = "amica/prototype-1/out/realtime";
const char *topic_out_day = "amica/prototype-1/out/day";
const char *topic_out_month = "amica/prototype-1/out/month";
const char *mqtt_username = "";
const char *mqtt_password = "";
const int mqtt_port = 1883;
/**
//Local Time printing function
void printLocalTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Connection Err");
return;
}
//Serial.println(&timeinfo, "%H:%M:%S");
// Serial.println(&timeinfo, "%Y%m%dT%H%M%S");
char timeStringBuff[50]; //50 chars should be enough
//strftime(timeStringBuff, sizeof(timeStringBuff), "%Y%m%dT%H%M%S", &timeinfo);
strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo);
//print like "const char*"
//Serial.println(timeStringBuff);
//Optional: Construct String object
//String asString(timeStringBuff);
timestamp = timeStringBuff;
}
*/
//Local Time calculating function
void getTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
Serial.println("Connection Err");
return;
}
char timeStringBuff[50]; //50 chars should be enough
//strftime(timeStringBuff, sizeof(timeStringBuff), "%Y%m%dT%H%M%S", &timeinfo);
strftime(timeStringBuff, sizeof(timeStringBuff), "%A, %B %d %Y %H:%M:%S", &timeinfo);
//Optional: Construct String object
//String asString(timeStringBuff);
timestamp = timeStringBuff;
}
void setup() {
/**
//logging
Serial.setDebugOutput(true);
//esp_log_level_set("*", ESP_LOG_ERROR); // set all components to ERROR level
//esp_log_level_set("wifi", ESP_LOG_WARN); // enable WARN logs from WiFi stack
//esp_log_level_set("dhcpc", ESP_LOG_INFO); // enable INFO logs from DHCP client
esp_log_level_set("*", ESP_LOG_VERBOSE);
ESP_LOGD("EXAMPLE", "This doesn't show");
log_v("Verbose");
log_d("Debug");
log_i("Info");
log_w("Warning");
log_e("Error");
*/
pinMode(ONBOARD_LED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(RELAY, OUTPUT);
digitalWrite (RELAY, HIGH); // low level trigger relay should be in Normally Open mode
Serial.begin(115200);
dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
//WiFi connection
Serial.print("Connecting to WiFi");
WiFi.begin(ssid, password, 6);
while (WiFi.status() != WL_CONNECTED) {
delay(200);
digitalWrite(LED_GREEN,HIGH);
delay(200);
digitalWrite(LED_GREEN,LOW);
Serial.print(".");
}
Serial.println(" Connected!");
//NTP syncronization
Serial.println("Updating time...");
configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
//MQTT Broker connection
client.setServer(mqtt_broker, mqtt_port);
client.setCallback(callback);
while (!client.connected()) {
String client_id = "esp32-client-";
client_id += String(WiFi.macAddress());
Serial.printf("The client %s connects to the public mqtt broker\n", client_id.c_str());
if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
Serial.println("Public " + String(mqtt_broker) + " connected");
} else {
Serial.print("failed with state ");
Serial.print(client.state());
delay(2000);
}
}
// publish and subscribe
client.publish(topic_out_status, "Hello World, I'm AMICA prototype 1");
client.subscribe(topic_in);
}
void callback(char *topic_in, byte *payload, unsigned int length) {
Serial.print("Message arrived in topic: ");
Serial.println(topic_in);
Serial.print("Message:");
for (int i = 0; i < length; i++) {
Serial.print((char) payload[i]);
}
Serial.println();
Serial.println("-----------------------");
// Switch on the LED if an 1 was received as first character
if ((char)payload[0] == '1') {
digitalWrite(LED_RED, HIGH);
standby = false;
} else {
digitalWrite(LED_RED, LOW);
standby = true;
}
}
void blink(int led, int millisec) {
digitalWrite(led,HIGH);
delay (millisec);
digitalWrite(led,LOW);
}
void shiftLeft (int myarray[], int size) {
//shift any element of an array of integers by 1 position to the left
for(int i=0;i<size-1;i++){
myarray [i] = myarray [i+1];
}
}
String convertMillis (unsigned long tt) {
//converts milliseconds to hour, minutes, seconds
tt = tt / 1000;
int hh = tt/3600;
int mm = (tt-hh*3600)/60;
int ss = tt-hh*3600-mm*60;
String hhmmss = (String(hh) + " h, " + String(mm) + " m, " + String(ss) + " s");
return hhmmss;
}
void loop() {
unsigned long mytime = millis ();
//status check
bool alarm = false;
status_string = "uptime: " + convertMillis (mytime);
//status_string = "Normal";
/**
if ((WiFi.status() != WL_CONNECTED)) {
status_string = "WiFi malfunction";
alarm = true;
}
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
status_string = status_string + " - " + "Global time malfunction";
alarm = true;
}
if (!client.connected()) {
status_string = status_string + " - " + "MQTT malfunction";
alarm = true;
}
if (alarm) {
digitalWrite (LED_RED, HIGH);
digitalWrite (LED_GREEN, LOW);
} else {
digitalWrite (LED_RED, LOW);
digitalWrite (LED_GREEN, HIGH);
}
if (standby) {
blink (LED_RED,250);
}
*/
//reading and mapping percentage soil moisture value
analogReadResolution(12);
int soil_realvalue = analogRead(SOIL);
int soil_value = map (soil_realvalue, dry_value, wet_value, 0, 100);
//Irrigation Cycle
if ( soil_value >= threshold_high ) {
water_cycle = false;
digitalWrite (RELAY, HIGH);
}
if ( soil_value < threshold_low && standby == false ) {
water_cycle = true;
}
if ( water_cycle == true && standby == false ) {
pump_on = true;
digitalWrite (RELAY, LOW);
Serial.println ("Watering Cycle");
blink (LED_GREEN,100);
}
//calculating hourly and daily average soil moisture
if (second == 3600) {
shiftLeft (day_array,24);
day_array [23] = round (average_hour / 3600);
average_hour=0;
second = 0;
if (hour == 24) {
shiftLeft (month_array,30);
month_array [29] = round (average_day / 86400);
hour = 0;
second = 0;
average_hour = 0;
average_day = 0;
} else {
hour = hour + 1;
}
} else {
average_hour += soil_value;
average_day += soil_value;
second = second + 1;
}
for (int i = 0; i < 24; i++) {
day_string = (day_string + day_array[i] + ",");
}
for (int i = 0; i < 30; i++) {
month_string = (month_string + month_array[i] + ",");
}
//payload preparation and sending
//status
int len1 = status_string.length() + 1;
char p1 [len1];
status_string.toCharArray(p1, len1);
client.publish(topic_out_status, p1);
Serial.println (status_string);
//timestamp
getTime ();
int len2 = timestamp.length() + 1;
char p2 [len2];
timestamp.toCharArray(p2, len2);
client.publish(topic_out_timestamp, p2);
//real time telemetry measurements
TempAndHumidity data = dhtSensor.getTempAndHumidity();
temp = String(data.temperature, 1) + "°C";
humid = String(data.humidity, 1) + "%";
moist = String(soil_value, DEC) + "%";
realtime = moist + ", " + temp + ", " + humid; //message CSV format is [moisture]%, [temperature]°C, [humidity]%
int len3 = realtime.length() + 1;
char p3 [len3];
realtime.toCharArray(p3, len3);
client.publish(topic_out_realtime, p3);
//daily and monthly statistics
int len4 = day_string.length();
char p4 [len4];
day_string.toCharArray(p4,len4);
client.publish(topic_out_day, p4);
day_string ="";
int len5 = month_string.length();
char p5 [len5];
month_string.toCharArray(p5,len5);
client.publish(topic_out_month, p5);
month_string ="";
client.loop();
delay(1000-(millis()-mytime)); //calculates exactly 1 sec interval
}