Умные оросительные системы, которые автоматически позволяют поддерживать влажность почвы в заданном диапазоне, находят широкое применение в современном мире и способствуют значительному увеличению урожайности поскольку создают оптимальные условия для роста растений.
В данной статье мы рассмотрим создание умной оросительной системы на основе платы NodeMCU ESP8266, датчике DHT11 и датчике влажности почвы. Она будет не только орошать почву, основываясь на значении ее влажности, но также передавать необходимые данные на сервер ThingSpeak, благодаря чему мы сможем следить за работой системы из любой точки земного шара где есть подключение к сети интернет. Основным исполнительным механизмом нашей системы будет водяной насос (water pump) который будет орошать почку необходимым количеством воды, основываясь на значениях влажности почвы, температуры и влажности окружающего воздуха.
Перед созданием данной оросительной системы учтите тот факт, что различные растения требуют различных значений влажности почвы для своего оптимального роста. В данной статье мы использовали растение, оптимальное значение влажности почвы для которого лежит в пределах 50-55%. То есть наш водяной насос будет включаться когда значение влажности почвы будет опускаться ниже 50%, а выключаться только после того, как значение влажности почвы станет больше 55%. Также данные датчика будут передаваться на сервер ThingSpeak, откуда их можно будет мониторить с любой точки Земли.
Ранее на нашем сайте мы рассматривали два проекта подобных умных оросительных систем на основе платы Arduino:
- автоматическая система полива растений на основе Arduino (с оповещениями о своей работе с помощью SMS);
- автоматическая оросительная система на Arduino Uno (наверное, самый простой вариант подобной системы).
Необходимые компоненты
- NodeMCU ESP8266 (купить на AliExpress).
- Датчик влажности почвы (купить на AliExpress).
- Водяной насос (купить на AliExpress).
- Модуль реле (купить на AliExpress).
- Датчик DHT11 (купить на AliExpress).
- Соединительные провода.
Схема проекта
Схема умной оросительной системы на NodeMCU ESP8266 представлена на следующем рисунке.
Внешний вид собранной конструкции проекта показан на следующем рисунке.
Объяснение программы для NodeMCU ESP8266
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Для нашего проекта нам понадобится только одна внешняя библиотека – для работы с датчиком DHT11. Считывать значения с датчика влажности почвы мы будем с аналогового контакта A0 платы ESP8266 NodeMCU, к которому он подключен. Поскольку NodeMCU не может обеспечивать на своих контактах выходное напряжение больше 3.3V, мы будем использовать модуль реле, с помощью которого мы будем управлять водяным насосом, питающимся от 5V. Датчик влажности почвы и датчик DHT11 будут также запитываться от внешнего источника питания напряжением 5V.
Начнем код программы мы с подключения необходимых библиотек.
1 2 |
#include <DHT.h> #include <ESP8266WiFi.h> |
Поскольку в нашем проекте мы будем использовать сервер ThingSpeak, то для взаимодействия с ним будет необходим API ключ с данного сервиса. Как его получить можно прочитать в статье про мониторинг температуры и влажности через Интернет с помощью Arduino.
1 2 |
String apiKey = "X5AQ445IKMBYW31H const char* server = "api.thingspeak.com"; |
Далее укажем в программе идентификатор сети (SSID) и пароль для нашей сети Wi-Fi.
1 2 |
const char *ssid = "CircuitDigest"; const char *pass = "xxxxxxxxxxx"; |
Затем в программе укажем контакт, к которому подключен датчик DHT11, и укажем тип датчика DHT.
1 2 |
#define DHTPIN D3 DHT dht(DHTPIN, DHT11); |
Выход датчика влажности почвы подключен у нас к контакту A0 платы ESP8266 NodeMCU, а водяной насос – к ее контакту D0.
1 2 |
const int moisturePin = A0; const int motorPin = D0; |
С помощью функции millis() мы будем в нашей программе задавать задержку для передачи данных на сервер ThingSpeak – она у нас составит 10 секунд. Мы не будем использовать для этой цели функцию delay() поскольку в результате ее выполнения микроконтроллер останавливает свою работу и не может выполнять в это время другие задачи. Подробнее о том, почему во многих случаях желательно для организации задержек использовать функцию millis() вместо функции delay() вы можете прочитать в статье про многозадачность в Arduino с помощью функции millis().
1 2 |
unsigned long interval = 10000; unsigned long previousMillis = 0; |
Далее зададим режим работы для контакта, к которому подключен водяной насос, на вывод данных. Начнем считывание данных с датчика DHT11.
1 2 3 |
pinMode(motorPin, OUTPUT); digitalWrite(motorPin, LOW); // keep motor off initally dht.begin(); |
Далее осуществим попытку соединения с сетью Wi-Fi используя идентификатор сети и пароль для нее. Если попытка соединения удалась, то продолжим выполнение программы.
1 2 3 4 5 6 7 8 9 |
WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); } |
Запомним время начала работы программы и сохраним его в переменной чтобы потом иметь возможность измерения истекшего времени.
1 |
unsigned long currentMillis = millis(); |
Считаем данные температуры и влажности и сохраним их в соответствующих переменных.
1 2 |
float h = dht.readHumidity(); float t = dht.readTemperature(); |
Если считывание данных с датчика DHT успешно осуществилось, то продолжим выполнение программы, иначе будем осуществлять проверку снова.
1 2 3 4 5 |
if (isnan(h) || isnan(t)) { Serial.println("Failed to read from DHT sensor!"); return; } |
Считаем значение влажности почвы и напечатаем это значение.
1 2 3 4 |
moisturePercentage = ( 100.00 - ( (analogRead(moisturePin) / 1023.00) * 100.00 ) ); Serial.print("Soil Moisture is = "); Serial.print(moisturePercentage); Serial.println("%"); |
Если значение влажности почвы будет ниже заданной границы, мы будем включать водяной насос. Если же оно будет подниматься выше заданной границы, мы будем выключать водяной насос.
1 2 3 4 5 6 7 8 9 |
if (moisturePercentage < 50) { digitalWrite(motorPin, HIGH); } if (moisturePercentage > 50 && moisturePercentage < 55) { digitalWrite(motorPin, HIGH); } if (moisturePercentage > 56) { digitalWrite(motorPin, LOW); } |
Далее через каждые 10 секунд мы будем вызывать функцию sendThingspeak() чтобы затем передать значения влажности почвы, температуры и влажности окружающего воздуха на сервер ThingSpeak.
1 2 3 4 5 |
if ((unsigned long)(currentMillis - previousMillis) >= interval) { sendThingspeak(); previousMillis = millis(); client.stop(); } |
Если в результате выполнения функции sendThingspeak() мы выясним, что наша система успешно подключилась к серверу, мы подготовим строку, содержащую значения влажности почвы, температуры и влажности окружающего воздуха, чтобы затем передать эту строку на сервер ThingSpeak используя API ключ и адрес сервера.
1 2 3 4 5 6 7 8 9 10 |
if (client.connect(server, 80)) { String postStr = apiKey; postStr += "&field1="; postStr += String(moisturePercentage); postStr += "&field2="; postStr += String(t); postStr += "&field3="; postStr += String(h); postStr += "\r\n\r\n"; |
И, наконец, данные будут передаваться на сервер ThingSpeak используя функцию client.print(), которая содержит API ключ, адрес сервера и подготовленную нами для передачи строку.
1 2 3 4 5 6 7 8 9 |
client.print("POST /update HTTP/1.1\n"); client.print("Host: api.thingspeak.com\n"); client.print("Connection: close\n"); client.print("X-THINGSPEAKAPIKEY: " + apiKey + "\n"); client.print("Content-Type: application/x-www-form-urlencoded\n"); client.print("Content-Length: "); client.print(postStr.length()); client.print("\n\n"); client.print(postStr); |
Внешний вид переданных нами данных на сервис ThingSpeak в виде построенных графиков выглядит следующим образом:
На основе данного проекта вы можете создать более "умную" оросительную систему, которая сможет поддерживать несколько различных уровней влажности почвы для различных видов растений.
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
#include <DHT.h> #include <ESP8266WiFi.h> String apiKey = "X5AQ3EGIKMBYW31H"; // введите здесь ваш API ключ с сервиса thingspeak const char* server = "api.thingspeak.com"; const char *ssid = "CircuitLoop"; // Enter your WiFi Name const char *pass = "circuitdigest101"; // Enter your WiFi Password #define DHTPIN D3 // к этому контакту подключен датчик dht11 DHT dht(DHTPIN, DHT11); WiFiClient client; const int moisturePin = A0; // к этому контакту подключен датчик влажности почвы const int motorPin = D0; unsigned long interval = 10000; unsigned long previousMillis = 0; unsigned long interval1 = 1000; unsigned long previousMillis1 = 0; float moisturePercentage; //moisture reading float h; // humidity reading float t; //temperature reading void setup() { Serial.begin(115200); delay(10); pinMode(motorPin, OUTPUT); digitalWrite(motorPin, LOW); // вначале водяной насос будет в выключенном состоянии dht.begin(); Serial.println("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); // печатаем ... до тех пор пока не произойдет соединение } Serial.println(""); Serial.println("WiFi connected"); } void loop() { unsigned long currentMillis = millis(); // сохраняем текущее время h = dht.readHumidity(); // read humidity (считываем влажность) t = dht.readTemperature(); // read temperature (считываем температуру) if (isnan(h) || isnan(t)) { Serial.println("Failed to read from DHT sensor!"); return; } moisturePercentage = ( 100.00 - ( (analogRead(moisturePin) / 1023.00) * 100.00 ) ); if ((unsigned long)(currentMillis - previousMillis1) >= interval1) { Serial.print("Soil Moisture is = "); Serial.print(moisturePercentage); Serial.println("%"); previousMillis1 = millis(); } if (moisturePercentage < 50) { digitalWrite(motorPin, HIGH); // включаем водяной насос } if (moisturePercentage > 50 && moisturePercentage < 55) { digitalWrite(motorPin, HIGH); // включаем водяной насос } if (moisturePercentage > 56) { digitalWrite(motorPin, LOW); // выключаем водяной насос } if ((unsigned long)(currentMillis - previousMillis) >= interval) { sendThingspeak(); // передаем данные на сервер thing speak previousMillis = millis(); client.stop(); } } void sendThingspeak() { if (client.connect(server, 80)) { String postStr = apiKey; // добавляем api ключ в строку postStr postStr += "&field1="; postStr += String(moisturePercentage); // добавляем значение влажности почвы postStr += "&field2="; postStr += String(t); // добавляем значение температуры postStr += "&field3="; postStr += String(h); // добавляем значение влажности postStr += "\r\n\r\n"; client.print("POST /update HTTP/1.1\n"); client.print("Host: api.thingspeak.com\n"); client.print("Connection: close\n"); client.print("X-THINGSPEAKAPIKEY: " + apiKey + "\n"); client.print("Content-Type: application/x-www-form-urlencoded\n"); client.print("Content-Length: "); client.print(postStr.length()); //передаем длину строки client.print("\n\n"); client.print(postStr); // передаем всю строку Serial.print("Moisture Percentage: "); Serial.print(moisturePercentage); Serial.print("%. Temperature: "); Serial.print(t); Serial.print(" C, Humidity: "); Serial.print(h); Serial.println("%. Sent to Thingspeak."); } } |
После дня возьни, вроде все заработало, кроме одного. Не передаются данные на ThingSpeak.
Если вручную вбить в браузере строку:
https://api.thingspeak.com/update?api_key=T012LZPZ37АВFYBR&field1=0.00&field2=23.80&field3=47.40
то все уходит, а из скрипта нет, по моему он не отправляет все что до идентификатора, хотя может я и не прав, не силен в програмировании, но если направить отправляемые данные в серийный порт, то там вот это:
POST /update HTTP/1.1
Host: api.thingspeak.com
Connection: close
X-THINGSPEAKAPIKEY: T012LZPZ37GUFYBR
Content-Type: application/x-www-form-urlencoded
Content-Length: 58
T012LZPZ37АВFYBR&field1=0.00&field2=23.80&field3=47.40
и в этой строчке замелил 1023.00 на 4095.00 ну может датчик другой какой то, но так показывает точно.
moisturePercentage = ( 100.00 - ( (analogRead(moisturePin) / 1023.00) * 100.00 ) );
Может быть попробовать посмотреть эту команду (для передачи данных на ThingSpeak) в других аналогичных проектах? Если необходимо, могу ссылки вам на эти проекты привести. Вы их можете найти самостоятельно по тегу ThingSpeak.
Простите.Ещё раз здравствуйте, а может проблема в самом сигнале с esp8266?Может мало тока для реле, не нужен ли в схеме Мосфет?
Добрый вечер. Да, в этом может быть проблема. Если неправильно выбрали модуль реле, то сигнала с модуля esp8266 может не хватать для его срабатывания. Но эту проблему можно решить с помощью обычного транзистора, не обязательно использовать Mosfet - у нас же нет тут переключений с высокой частотой.
У меня просто есть irf520,как думаете подойдёт?
К сожалению, не могу так сразу подсказать, не работал с таким мосфет.
Если у меня при погружении в воду такие показания выдаются в мониторе порта. Какую влажность выставить в коде. Подскажите пожалуйста
Поставьте условие что если влажность почвы (Moisture Percentage) больше 45, к примеру, то насос должен выключаться. Тогда при влажности 50.54% (как у вас) он будет точно выключаться
Здравствуйте,простите за спам но вот часть моего кода,как у вас.
if (moisturePercentage 30 && moisturePercentage 40) {
digitalWrite(motorPin, LOW); // выключить мотор
Moisture Percentage: 50.54%. Temperature: 22.20 C, Humidity: 46.00%. Sent to Thingspeak.
Перебрал все,уже пробовал другой код на блинк. Насос не выключается и все, какая бы влажность не была. В чем может быть проблема.
Ну у вас только одно условие на выключение насоса, когда влажность находится в диапазоне от 30 до 40? Или есть еще условие? Просто если у вас только одно это условие, а текущая влажность у вас 50.54%, то насос по этому условию никак не выключится поскольку значение 50.54 не попадает в диапазон от 30 до 40.
if (moisturePercentage 30 && moisturePercentage 40)
digitalWrite(motorPin, LOW);
if (moisturePercentage < 30) {
digitalWrite(motorPin, HIGH);
if (moisturePercentage > 40) {
digitalWrite(motorPin, LOW);
По другому не вставлялось простите за спам
Ну у вас при влажности почвы меньше 30 насос должен включаться, а при влажности больше 30 он должен выключаться. Если у вас влажность 50.54, то у вас должно срабатывать условие if (moisturePercentage > 40) и насос должен выключаться. А у вас что происходит?
А у меня он просто качает все время, как только заливаю скетч и не останавливается совсем.
Ну попробуйте тогда загрузить вместо данного скетча простую программу для управления насосом. Например, включите его, потом задержка на 5 сек, затем выключите. Так вы хотя бы проверите исправность насоса. Потому что если он неисправен, то никакие условия в программе уже не помогут
Здравствуйте,сделал все,как у вас,что то не подключается к вайфай или подключается но дальше ничего не работает,а насос качает постоянно.
Артём, ну если сложная программа не работает, то целесообразно разбить ее на несколько простых чтобы найти где ошибка. Чтобы разобраться с подключением к сети WiFi оставьте в программе для данного проекта только ту часть кода, которая отвечает за подключение к сети WiFi, все остальное уберите. И попробуйте выводить в окно монитора последовательной связи большее число параметров, связанных с этим процессом.
Чтобы проверить почему постоянно качает насос проверьте значения, считываемые с датчика влажности почвы и с датчика температуры и влажности. Если они находятся постоянно в границах в которых в программе предусмотрено включение насоса, то естественно он будет качать. Отрегулируйте, при необходимости, эти границы в программе под свой тип почвы
Спасибо за совет, буду пробовать!
Да не за что. Буду признателен если отпишитесь здесь потом о своих успехах в создании данного проекта и возможных трудностях, с которыми могут столкнуться другие пользователи при реализации данного проекта