Проект умная теплица на ESP8266 и ATMega328 + MQTT
- Войдите на сайт для отправки комментариев
Пт, 21/04/2017 - 18:18
Здравствуйте уважаемые форумчане! У нас очень важный проект на летнюю смену в лагере для IT-шников СИРИУС. Поэтому, с вашего позволения, мы хотим разместить код проекта в этой теме. Огромная просьба модераторов не удалять данную тему, так на нее мы закрепим гипперссылку в описании проекта. Заранее спасибо!
Код для микроконтроллера ATMega328
#include <string.h> //библиотека для работы с классом String #include <SoftwareSerial.h> //библиотека для работы с программным UART #include <ESP8266WiFi.h> //библиотека для ESP8266 #include <PubSubClient.h> //библиотека MQTT SoftwareSerial sSerial(14, 12, false, 256); //создаем экземпляр класса и настраиваем программный UART WiFiClient wclient; //создаем экземляр класса const char *ssid = "solnechnie_zaichiki"; // имя вайфай точки доступа const char *pass = "6578027c"; // пароль от точки доступа //const char *mqtt_server = "m11.cloudmqtt.com"; // имя сервера MQTT //const int mqtt_port = 19462; // порт для подключения к серверу MQTT //const char *mqtt_user = "zanfdfbh"; // логин от сервер //const char *mqtt_pass = "kwFcQj__tMkJ"; // пароль от сервера const char *mqtt_server = "m11.cloudmqtt.com"; // имя сервера MQTT const int mqtt_port = 18575; // порт для подключения к серверу MQTT const char *mqtt_user = "msabpptd"; // логин от сервер const char *mqtt_pass = "wUSAFs6Ee0q4"; // пароль от сервера PubSubClient client(wclient, mqtt_server, mqtt_port); String str; //строковая перменная для хранения данных с программного UART unsigned long previousMillis; //переменная для хранения значений таймера int temp, vlaga, ir1, ir2, pochva1, pochva2, svet; char buffer[30]; //буфер для анпарсинга строковой переменной int buf; //буфер для парсинга строковой переменной void setup() { //функция настройки sSerial.begin(9600); //настраиваем скорость программного UART } void loop() { //основной цикл connect(); //запускаем функцию подключения к серверу if (millis() - previousMillis > 2000) { //если прошло 3 секунды previousMillis = millis(); //запоминаем когда это произошло str = ""; //очищаем строковую переменную while (sSerial.available()) { //пока на программном UART есть данные sSerial.setTimeout(10); // устанавливаем тайм-аут связи str = sSerial.readString(); //считывает строку в переменную str.trim(); //обрезаем вначале и в конце строки возможный мусор str.toCharArray(buffer, 30); //превращаем строковую переменную в массив pochva1 = atoi(strtok(buffer, ";")); //записываем в переменную занчаение до первого знака ; pochva2 = atoi(strtok(NULL, ";")); //записываем в переменную занчаение с первого до второго знака ; ir1 = atoi(strtok(NULL, ";")); //записываем в переменную занчаение со второго до третьего знака ; ir2 = atoi(strtok(NULL, ";")); //записываем в переменную занчаение с третьего до четвертого знака ; vlaga = atoi(strtok(NULL, ";")); //записываем в переменную занчаение с четвертого до пятого знака ; temp = atoi(strtok(NULL, ";")); //записываем в переменную занчаение с пятого до шестого знака ; svet = atoi(strtok(NULL, ";")); //записываем в переменную занчаение после шестого знака ; } client.loop(); //готовимся к отправке данных на сервер client.publish("greenhouse/temp", String(temp)); //отправляем данные с температурой client.publish("greenhouse/vlaga", String(vlaga)); //отправляем данные с влажностью client.publish("greenhouse/pochva1", String(pochva1)); //отправляем данные с 1 гигрометра client.publish("greenhouse/pochva2", String(pochva2)); //отправляем данные со 2 гигрометра client.publish("greenhouse/ir1", String(ir1)); //отправляем данные с двери client.publish("greenhouse/ir2", String(ir2)); //отправляем данные с форточки client.publish("greenhouse/svet", String(svet)); //отправляем данные с датчика уровня света } } void connect() { //функция подключения к т/д и серверу if (WiFi.status() != WL_CONNECTED) { //если подключения нет то... WiFi.begin(ssid, pass); //пробуем подключиться к т/д if (WiFi.waitForConnectResult() != WL_CONNECTED) {//если подключения нет то... return; //выходим из функции } } if (WiFi.status() == WL_CONNECTED) { //если есть подключение то... if (!client.connected()) { //если нет подключения к серверу MQTT то... if (client.connect(MQTT::Connect("arduinoClient2") .set_auth(mqtt_user, mqtt_pass))) { //пробуем подключиться к серверу и если подключились... client.set_callback(callback); // обозначаем функцию для приема данных с сервера client.subscribe("greenhouse/ch1"); // подписывааемся по топик с данными для 1 канала реле client.subscribe("greenhouse/ch2"); // подписывааемся по топик с данными для 2 канала реле client.subscribe("greenhouse/ch3"); // подписывааемся по топик с данными для 3 канала реле client.subscribe("greenhouse/ch4"); // подписывааемся по топик с данными для 4 канала реле client.subscribe("greenhouse/angel1"); // подписывааемся по топик с данными для 1 сервопривода client.subscribe("greenhouse/angel2"); // подписывааемся по топик с данными для 2 сервопривода } } } } void callback(const MQTT::Publish& pub) {//функция приема данных с сервера String payload = pub.payload_string(); if (String(pub.topic()) == "greenhouse/ch1") { // проверяем из нужного ли нам топика пришли данные buf = payload.toInt(); // преобразуем полученные данные в тип integer //собираем строку str = "2"; str += ";"; str += buf; sSerial.println(str);//отправляем данные через программный UART на Arduino } if (String(pub.topic()) == "greenhouse/ch2") { // проверяем из нужного ли нам топика пришли данные buf = payload.toInt(); // преобразуем полученные данные в тип integer //собираем строку str = "3"; str += ";"; str += buf; sSerial.println(str);//отправляем данные через программный UART на Arduino } if (String(pub.topic()) == "greenhouse/ch3") { // проверяем из нужного ли нам топика пришли данные buf = payload.toInt(); // преобразуем полученные данные в тип integer //собираем строку str = "4"; str += ";"; str += buf; sSerial.println(str);//отправляем данные через программный UART на Arduino } if (String(pub.topic()) == "greenhouse/ch4") { // проверяем из нужного ли нам топика пришли данные buf = payload.toInt(); // преобразуем полученные данные в тип integer //собираем строку str = "5"; str += ";"; str += buf; sSerial.println(str);//отправляем данные через программный UART на Arduino } if (String(pub.topic()) == "greenhouse/angel1") { // проверяем из нужного ли нам топика пришли данные buf = payload.toInt(); // преобразуем полученные данные в тип integer //собираем строку str = "7"; str += ";"; str += buf; sSerial.println(str); //отправляем данные через программный UART на Arduino } if (String(pub.topic()) == "greenhouse/angel2") { // проверяем из нужного ли нам топика пришли данные buf = payload.toInt(); // преобразуем полученные данные в тип integer //собираем строку str = "8"; str += ";"; str += buf; sSerial.println(str); //отправляем данные через программный UART на Arduino } }
Код для микроконтроллера ESP8266:
#include <Bounce2.h> #include <DHT.h> //библиотека для датчика температуры и влажности #include <Servo.h> //библиотека для работы с серво #include <SoftwareSerial.h> ////библиотека для работы с программным UART #define ch1 2 //дирректива для 1 канала реле (тэны) #define ch2 3 //дирректива для 2 канала реле (лампы) #define ch3 4 //дирректива для 3 канала реле (увлажнитель) #define ch4 5 //дирректива для 4 канала реле (электроклапан) SoftwareSerial sSerial(10, 11); //создаем экземпляр класса и настраиваем программный UART Bounce knopka = Bounce(); //создаем экземпляр класса Servo ser1; //экземпляр класса Servo для 1 сервопривода Servo ser2; //экземпляр класса Servo для 2 сервопривода DHT dht(6, DHT11); //создаем экземпляр класса DHT и настраиваем пин, и тип датчика int pochva1, pochva2, ir1, ir2, vlaga, temp, svet, angel1, angel2, target, state; String str; //строковая перменная для передачи данных по программному UART unsigned long currentTime, handControlTimer, timeServo; //переменная для хранения значений таймера char buffer[30]; bool handControl, dataKnopka, servoState; void setup() { //функция настройки sSerial.begin(9600); //настраиваем скорость программного UART Serial.begin(9600); pinMode(2, OUTPUT); //настраиваем пин на выход pinMode(3, OUTPUT); //настраиваем пин на выход pinMode(4, OUTPUT); //настраиваем пин на выход pinMode(5, OUTPUT); //настраиваем пин на выход pinMode(9, INPUT_PULLUP); //настраиваем пин для кнопки и подтягиваем пин на 5в knopka.attach(9); //назначаем экземпляр класса на 9 пин knopka.interval(10); //устанавливаем интервал защиты от дребезгов ser1.attach(7); //настраиваем пин для 1 сервопривода ser2.attach(8); //настраиваем пин для 2 сервопривода ser1.write(90); //останавливаем серво ser2.write(90); //останавливаем серво dht.begin(); //инициализация датчика DHT } void servoMove() { //функция управления сервоприводами if (ir1 > 1000 || ir2 > 1000) { //если форточка или дверь закрыты то... servoState = 1; //установить защиту от повторного закрытия ser2.write(90); //остановить вращение сервопривода ser1.write(90); //остановить вращение сервопривода } if (svet < 500 && handControl == 0 && servoState == 0) { //если темно, включено автоматическое управление и форточка,дверь открыта... digitalWrite(ch2, HIGH); //включаем лампы ser1.write(180); //закрываем форточку ser2.write(180); //закрываем дверь servoState = 1; //установить защиту от повторного закрытия } else if (svet >= 550 && handControl == 0 && servoState == 1) { // если светло, включено автоматическое управление и форточка,дверь закрыта... digitalWrite(ch2, LOW); //выключаем лампы ser1.write(0); //открываем форточку ser2.write(0); //открываем дверь servoState = 0; //установить защиту от повторного открытия } } void climatcontrol() { //функция климат-контроль while (sSerial.available()) { //пока на программном UART доступны данные str = ""; //очищаем сткроковую переменную sSerial.setTimeout(10); //устанавливаем тайм-аут связи str = sSerial.readString(); //считывает строку в переменную str.trim(); //обрезаем вначале и в конце строки возможный мусор str.toCharArray(buffer, 10); //превращаем строковую переменную в массив target = atoi(strtok(buffer, ";")); //записываем в переменную занчаение до первого знака ; state = atoi(strtok(NULL, ";")); //записываем в переменную занчаение после первого знака ; if (target == 7) { //если переменная приняла значение 7 то... ser1.write(state); //открыть дверь handControl = 1; //включить режим ручного управления handControlTimer = millis(); //запомнить когда это произошло } if (target == 8) { //если переменная приняла значение 8 то... ser1.write(state); //открыть форточку handControl = 1; //включить режим ручного управления handControlTimer = millis(); //запомнить когда это произошло } else { //иначе... digitalWrite(target, state); //установить цифровой пин target в состояние state handControl = 1; //включить режим ручного управления handControlTimer = millis(); //запомнить когда это произошло } } if (knopka.read()) { //если кнопка нажата handControl = !handControl; //переключить режим управления handControlTimer = millis(); //запомнить когда это произошло } pochva1 = analogRead(A0); //читаем данные с 1 датчика влажности почвы pochva2 = analogRead(A1); //читаем данные со 2 датчика влажности почвы (инверсивный) ir1 = analogRead(A2); //читаем данные с 1 датчика отражения ir2 = analogRead(A3); //читаем данные со 2 датчика отражения vlaga = dht.readHumidity(); //читаем данные о влажности temp = dht.readTemperature(); //читаем данные о температуре svet = analogRead(A4); //читаем данные с датчика света if (millis() - handControlTimer > 1800000) { //если с момента включения ручного управления прошло пол-часа handControl = 0; //включить автоматическое управление } servoMove(); if (pochva1 < 100 && pochva2 > 200 && handControl == 0) { //если почва сухая то... digitalWrite(ch4, HIGH); //включаем электроклапан } else if (pochva1 > 150 && pochva2 < 100 && handControl == 0) { //если почва влажная то... digitalWrite(ch4, LOW); //выключаем электроклапан } if (vlaga < 50 && handControl == 0) { //если влажность низкая то... digitalWrite(ch3, HIGH); //включаем увлажнитель } else if (vlaga >= 55 && handControl == 0) { //если влажность высокая то... digitalWrite(ch3, LOW); //выключаем увлажнитель } if (temp < 27 && handControl == 0) { //если температура ниже отметки то... digitalWrite(ch1, HIGH); //включаем тэны } else if (temp >= 27 && handControl == 0) { //если температура выше отметки то... digitalWrite(ch1, LOW); //выключаем тэны } } void sendData() { //функция отправки данных на ESP if (millis() - currentTime > 2000) { //если прошло 3 секунды то... currentTime = millis(); //запоминаем когда это произошло //набираем строку str = ""; str += pochva1; str += ";"; str += pochva2; str += ";"; str += ir1; str += ";"; str += ir2; str += ";"; str += vlaga; str += ";"; str += temp; str += ";"; str += svet; sSerial.println(str); //отправляем на ESP через программный UART } } void loop() { //основной цикл knopka.update(); //обновляем экземпляр класса climatcontrol(); //вызываем функцию климатконтроль sendData(); //вызываем функцию отправки данных на ESP }
И еще пару фотографий:

Регулирока температуры, влажности, управление поливом. Правильно я понял?
Автоматика+отправка данных через MQTT на клиентское приложение например в телефоне+переход на ручное управления как с клиентского приложения так и непосредственно с теплицы
Судя по фото - это скорее всего действующий "макет"... потому, что чтобы там смонтировать "талант" нужен.. Ладно..
Я о другом.. Где схема, например... и каковы результаты? Есть ли отзывы...
Зато проводочки, идущие к земле можно еще и как подвязки использовать.
Точно! Они и так к "земле идут"... Сегодня случайно попал на один канал и... просто восхитился... Не знаю как тут с правилами, но это точно по теме... Автор просто умница.. Особено порадовали его разработки в области датчиков влажности почвы.. А это ссылка скажем так "обзорная". https://www.youtube.com/watch?v=jtVGblaS-50&list=PLDVEo6zzKgcu4BiKLJBmtM7DhfZqe9R0M&index=6
Прошу пояснить, каким образом у вас открывается форточка?
не понятно по фото.
И видео тоже можно было бы снять. как оно работает.