Проект умная теплица на 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
Прошу пояснить, каким образом у вас открывается форточка?
не понятно по фото.
И видео тоже можно было бы снять. как оно работает.