Gateway per sensore resistivo di umidità del suolo
Schema di principio del collegamento a stella
Notare i collegamenti punto-punto tra tutti i dispositivi e il microcontrollore tramite linee esclusive.
Il sensore di livello resistivo
Schema cablaggio
Condizionamento digitale
| Tensione di alimentazione | 3.3V | 5V |
|-----------------------------------------|---------|-----------|-----------|-----------|
| Umido/Secco | Umido | Secco | Umido | Secco |
| Sensore di umidità del suolo capacitivo | 2...8 | 14...21 | 821...824 | 490...549 |
| Sensore di umidità del suolo resistivo | 1024 | 483...505 | 1023 | 344...358 |
Teoricamente si può usare una qualsiasi di queste combinazioni, ma è necessario calibrare il sensore prima di dichiarare che il terreno è bagnato o asciutto.
Per il sensore di umidità del suolo capacitivo si vede anche dalla tabella che la differenza nel valore del sensore per una tensione di esercizio di 3,3V è molto bassa. Pertanto per evitare probabili errori è consigliabile, per il sensore capacitivo, utilizzare una tensione operativa di 5V.
Sensore di umidità del suolo resistivo
Il sensore resistivo di umidità del suolo è composto da 2 sonde con inserimento nel terreno. A seconda della direzione della corrente, una sonda funzionerà come catodo e l’altra come anodo. Generalmente quale sonda sia l’anodo o il catodo è irrilevante per la funzionalità del sensore, perché il sensore misura solo la resistenza ed è quindi indipendente dalla direzione del flusso di corrente.
Il circuito elettrico è chiuso sul terreno che funge da resistenza per il flusso di corrente. Questa resistenza viene misurata e dipende dalla quantità di acqua nel terreno perché l’acqua è un conduttore naturale per l’elettricità. Minore è la resistenza misurata, maggiore è la quantità di acqua nel terreno.
Il flusso di corrente attraverso l’anodo del sensore resistivo di umidità del suolo, che ha contatto con l’acqua, è un ambiente perfetto per l’elettrolisi e quindi la deposizione galvanica. Questa elettrolisi danneggia il sensore e rende il sensore impreciso. Quanto sarà forte l’elettrolisi dipende da quanto spesso e da quanta corrente viene fatta passare attraverso gli elettrodi.
Per evitare il flusso di corrente al sensore, utilizziamo un circuito MOSFET a canale N per scollegare il sensore dall’alimentazione. Inoltre leggiamo il valore del sensore solo una volta ogni tanto.
Librerie del progetto
Dal punto di vista SW non servono librerie particolari tranne quelle per la pubblicazione dei valorri traite MQTT. Una parte del codice va comunque dedicata al condizionamento dei valori misurati dal sensore per tradurli ai valori di interesse di umidità.
Gateway MQTT per la lettura di un solo sensore di umidità del suolo
Utilizziamo il pin digitale per abilitare il flusso di corrente sul gate del MOSFET. Questo chiude il circuito del sensore di umidità del suolo e leggiamo il valore del sensore analogico dopo un breve ritardo di 1 secondo. Dopo che il valore del sensore è stato stampato sul monitor seriale, aspettiamo 1 secondo e disabilitiamo il flusso di corrente attraverso il sensore tirando il Gate del MOSFET LOW.
Poiché ogni pin I/O digitale dell’ESP32, che non viene utilizzato per un’interfaccia di comunicazione, può essere un ingresso analogico, dobbiamo scegliere un pin qualsiasi come ingresso analogico.
//#include <WiFiClientSecure.h>
//#include <ESP8266WiFi.h> per ESP8266
#include <AsyncMqttClient.h>
#include <Ticker.h>
#include <WiFi.h> // per ESP32
// Raspberry Pi Mosquitto MQTT Broker
//#define MQTT_HOST IPAddress(192, 168, 1, 254)
#define MQTT_HOST "test.mosquitto.org"
// For a cloud MQTT broker, type the domain name
//#define MQTT_HOST "example.com"
#define MQTT_PORT 1883
#define WIFI_SSID "myssid"
#define WIFI_PASSWORD "mypsw"
//Temperature MQTT Topic
#define MQTT_PUB "esp/umiditasuolo/"
//#define SensorPin A0 // used for Arduino and ESP8266
#define SensorPin 4 // used for ESP32
#define SENSOR_VCC_PIN 5 // used for ESP32
Ticker mqttReconnectTimer;
Ticker wifiReconnectTimer;
String datastr = "";
AsyncMqttClient mqttClient;
unsigned long previousMillis = 0; // Stores last time temperature was published
const long interval = 2000; // Interval at which to publish sensor readings
byte count = 0;
unsigned long previusMillis = 0;
bool sensor1 = false;
float t1, h1;
void connectToWifi() {
Serial.println("Connecting to Wi-Fi...");
WiFi.mode(WIFI_STA);
//WiFi.disconnect();
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void connectToMqtt() {
Serial.println("Connecting to MQTT...");
mqttClient.connect();
}
void WiFiEvent(WiFiEvent_t event) {
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());
connectToMqtt();
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
Serial.println("WiFi lost connection");
mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
wifiReconnectTimer.once_ms(2000, connectToWifi);
break;
}
}
void onMqttConnect(bool sessionPresent) {
Serial.println("Connected to MQTT.");
Serial.print("Session present: ");
Serial.println(sessionPresent);
}
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
Serial.println("Disconnected from MQTT.");
if (WiFi.isConnected()) {
mqttReconnectTimer.once_ms(2000, connectToMqtt);
}
}
void onMqttPublish(uint16_t packetId) {
Serial.println("Publish acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
}
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
pinMode(SENSOR_VCC_PIN, OUTPUT);
WiFi.onEvent(WiFiEvent);
mqttClient.onConnect(onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
mqttClient.onPublish(onMqttPublish);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
// If your broker requires authentication (username and password), set them below
//mqttClient.setCredentials("REPlACE_WITH_YOUR_USER", "REPLACE_WITH_YOUR_PASSWORD");
connectToWifi();
count = 0;
while (WiFi.status() != WL_CONNECTED && count < 10) {
delay(500);
count++;
Serial.print(".");
}
}
void packData(String &str){
str = "{\"humidity1\":\"";
str += h1;
str += "\"}";
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
Serial.print("Requesting data...");
digitalWrite(SENSOR_VCC_PIN, HIGH);
h1 = analogRead(SensorPin);
digitalWrite(SENSOR_VCC_PIN, LOW);
Serial.println("DONE");
packData(datastr);
// Publish an MQTT message on topic esp32/ds18b20/temperature
uint16_t packetIdPub1 = mqttClient.publish(MQTT_PUB, 1, true, datastr.c_str(), datastr.length());
Serial.print("Pubblicato sul topic %s at QoS 1, packetId: ");
Serial.println(MQTT_PUB);
Serial.println(packetIdPub1);
Serial.print("Messaggio inviato: ");
Serial.println(datastr);
}
}
Gateway MQTT per la lettura periodica di un sensore di umidità del suolo alimentato a batteria
Il codice seguente utilizza la modalità di sleep profondo del microcontrollore ESP32 che consiste nello spegnimento dei due core della CPU e di tutte le periferiche fatta eccezione per un timer HW che viene impostato ad un timeout allo scadere del quale avviene il risveglio della CPU. Lo sleep profondo consente un drastico risparmio di energia nei periodi di inattività che allunga la durata di una eventuale alimentazione a batterie.
L’istruzione esp_deep_sleep_start();
avvia la fase di sleep e viene eseguita una volta sola all’interno del setup(). Il loop() è praticamente inutile e viene lasciato vuoto dato che non è possibile arrivare alla sua esecuzione.
Il risveglio fa ripartire ogni volta il sistema dal setup() per cui tutte le elaborazioni e le eventuali comunicazioni devono avvenire la dentro.
L’istruzione esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR)
imposta il timeout del timer.
La funzione print_wakeup_reason()
stampa sulla seriale il motivo del wakeup.
I motivi possono essere:
- l’evento di timer HW, che sveglia il sistema in periodi di tempo prestabiliti;
- l’evento di tocco tattile di un certo pin;
- un evento di riattivazione esterna: è possibile utilizzare una riattivazione esterna o più risvegli esterni diversi;
- un evento generato dal coprocessore ULP ma questo non sarà trattato nella presente guida.
Nel codice seguente vengono effettuati alcuni tentativi di riconnessione in caso di mancato collegamento del WiFi o di mancata connessione MQTT. Fallito il numero massimo di tentativi si va in sleep profondo e si riprova al prossimo risveglio.
//#include <WiFiClientSecure.h>
//#include <ESP8266WiFi.h> per ESP8266
#include <AsyncMqttClient.h>
#include <WiFi.h> // per ESP32
// Raspberry Pi Mosquitto MQTT Broker
//#define MQTT_HOST IPAddress(192, 168, 1, 254)
#define MQTT_HOST "test.mosquitto.org"
// For a cloud MQTT broker, type the domain name
//#define MQTT_HOST "example.com"
#define MQTT_PORT 1883
#define WIFI_SSID "myssid"
#define WIFI_PASSWORD "mypsw"
//Temperature MQTT Topic
#define MQTT_PUB "esp/umiditasuolo/"
//#define SensorPin A0 // used for Arduino and ESP8266
#define SensorPin 4 // used for ESP32
#define SENSOR_VCC_PIN 5 // used for ESP32
//deep sleep
#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 30 /* Time ESP32 will go to sleep (in seconds) */
RTC_DATA_ATTR int bootCount = 0;
String datastr = "";
AsyncMqttClient mqttClient;
unsigned long previousMillis = 0; // Stores last time temperature was published
const long interval = 2000; // Interval at which to publish sensor readings
byte count = 0;
unsigned long previusMillis = 0;
bool sensor1 = false;
float t1, h1;
/*
Method to print the reason by which ESP32
has been awaken from sleep
*/
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
void connectToWifi() {
Serial.println("Connecting to Wi-Fi...");
WiFi.mode(WIFI_STA);
//WiFi.disconnect();
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void packData(String &str){
str = "{\"humidity1\":\"";
str += h1;
str += "\"}";
}
void loop_once() {
Serial.print("Requesting data...");
digitalWrite(SENSOR_VCC_PIN, HIGH);
h1 = analogRead(SensorPin);
digitalWrite(SENSOR_VCC_PIN, LOW);
Serial.println("DONE");
packData(datastr);
// Publish an MQTT message on topic esp32/ds18b20/temperature
uint16_t packetIdPub1 = mqttClient.publish(MQTT_PUB, 1, true, datastr.c_str(), datastr.length());
Serial.print("Pubblicato sul topic %s at QoS 1, packetId: ");
Serial.println(MQTT_PUB);
Serial.println(packetIdPub1);
Serial.print("Messaggio inviato: ");
Serial.println(datastr);
}
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
pinMode(SENSOR_VCC_PIN, OUTPUT);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
// If your broker requires authentication (username and password), set them below
//mqttClient.setCredentials("REPlACE_WITH_YOUR_USER", "REPLACE_WITH_YOUR_PASSWORD");
count = 0;
connectToWifi();
while (WiFi.status() != WL_CONNECTED && count < 10) {
count++;
Serial.print(".");
delay(500);
}
if(WiFi.status() == WL_CONNECTED){
//se il WiFi è connesso
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
count = 0;
Serial.println("Connecting to MQTT...");
mqttClient.connect();
while (!mqttClient.connected() && WiFi.status() == WL_CONNECTED && count < 10) {
//Serial.print("MQTT lastError: ");
//Serial.println(mqttClient.lastError());
mqttClient.connect();
count++;
Serial.print(".");
delay(500);
}
if(mqttClient.connected()){
//se il WiFi è connesso
Serial.println("MQTT connected");
}
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
/*
First we configure the wake up source
We set our ESP32 to wake up every tot seconds
*/
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds");
loop_once();
Serial.println("Going to sleep now");
delay(1000);
Serial.flush();
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){}
Sitografia:
- https://diyi0t.com/soil-moisture-sensor-tutorial-for-arduino-and-esp8266
- https://randomnerdtutorials.com/esp32-deep-sleep-arduino-ide-wake-up-sources/
- https://randomnerdtutorials.com/esp32-timer-wake-up-deep-sleep/