Система мониторинга окружающей среды, основанная на использовании микроконтроллера ESP32, представляет собой универсальное решение для сбора и анализа данных о температуре и влажности в реальном времени. Она включает в себя несколько ключевых компонентов, обеспечивающих её функциональность и надежность.
Компоненты системы:
Микроконтроллер ESP32
Сердцем системы является микроконтроллер ESP32, который управляет всеми компонентами устройства и осуществляет связь с внешним миром посредством Wi-Fi. Этот мощный модуль обладает встроенным процессором, памятью и поддержкой множества интерфейсов, что делает его идеальным выбором для IoT-приложений. Основные характеристики ESP32 включают:
- Процессор: Двухъядерный процессор Tensilica Xtensa LX6 с тактовой частотой до 240 МГц.
- Память: 520 КБ SRAM и 16 МБ флеш-память.
- Интерфейсы: UART, SPI, I²C, I²S, PWM, ADC, DAC, SDIO, Ethernet MAC, Wi-Fi, Bluetooth.
- Поддержка Wi-Fi и Bluetooth: Встроенная поддержка стандартов IEEE 802.11 b/g/n и Bluetooth v4.2 BR/EDR и BLE.
Для измерения температуры система использует цифровые датчики DS18B20, работающие по протоколу 1-Wire. Эти датчики обладают следующими характеристиками:
- Диапазон измерения температуры: От -55°C до +125°C.
- Точность: Измерение температуры с точностью до ±0.5°C в диапазоне от -10°C до +85°C.
- Протокол 1-Wire: Позволяет подключить множество датчиков к одному проводу, что упрощает разводку схемы.
- Энергопотребление: Низкое потребление энергии, что критично для автономных систем.
Датчики влажности и температуры DHT22
Помимо DS18B20, система оснащена двумя датчиками DHT22, которые позволяют одновременно измерять температуру и относительную влажность воздуха. Характеристики DHT22:
- Диапазон измерения температуры: От -40°C до +80°C с точностью ±0.5°C.
- Диапазон измерения влажности: От 0% до 99.9% с точностью ±2%.
- Время отклика: До 2 секунд.
- Напряжение питания: 3.3V — 6V.
OLED дисплей SSH1106_128x64
Для визуализации собранных данных используется OLED дисплей с разрешением 128×64 пикселей. Этот компактный дисплей подключается через интерфейс I²C и позволяет отображать текущее состояние датчиков в удобочитаемом формате. Основные характеристики OLED дисплея SSH1106_128x64:
- Разрешение: 128×64 пикселя.
- Тип экрана: OLED (Organic Light Emitting Diode), что обеспечивает высокую точность изображений.
- Интерфейс: Подключение через интерфейс I²C позволяет эффективно интегрировать дисплей в систему.
- Размер: Небольшой размер экрана облегчает его использование в компактных устройствах.
Программы и библиотеки
Управление системой осуществляется с помощью специализированного программного обеспечения, написанного на языке C++, которое использует различные библиотеки:
- ESPAsyncWebServer: Создание веб-сервера для удаленного доступа к данным.
- GyverOLED: Обслуживание OLED дисплея, включая удобные методы для вывода текста и графики.
- OneWire: Работа с датчиками DS18B20 по протоколу 1-Wire.
- DHT: Работа с датчиками DHT22 для измерения температуры и влажности.
Скетч:
#include <ESPAsyncWebServer.h> #ifdef ESP32 #include <WiFi.h> #include <AsyncTCP.h> #else #include <ESP8266WiFi.h> #include <ESPAsyncTCP.h> #endif //---Для датчиков DS18B20 #include<OneWire.h> //подключение библиотеки 1-Wire для ESP32 ЗИП ФАЙЛ OneWire-master #define POWER_MODE 0 //режим питания для шины 1-Wire, 0- внешнее, 1-паразитное // линия №1 шины 1-Wire датчиков температуры DS18B20 OneWire sensDs (14); // 5 датчиков подключены к выводу 14 >> (линия №1 шины 1-Wire) // массив адресов датчиков DS18B20 (уникальный адрес) byte sensAdd_L1[5][8]={ // адреса датчиков на макетной плате {0x28,0x1B,0x15,0x3E,0x00,0x00,0x00,0x4E}, //уникальный адрес 1 датчика {0x28,0x4A,0xFF,0x3C,0x00,0x00,0x00,0x37}, //уникальный адрес 2 датчика {0x28,0x97,0xAA,0x3C,0x00,0x00,0x00,0xFE}, //уникальный адрес 3 датчика {0x28,0xC4,0xE3,0x3C,0x00,0x00,0x00,0x93}, //уникальный адрес 4 датчика {0x28,0x00,0xED,0x3D,0x00,0x00,0x00,0xFD}, //уникальный адрес 5 датчика }; boolean flagWire_L1; // флаг состояния 1 линии 1-Wire _ true – присутствие устройств, false – нет устройств (обрыв линии) boolean flagCRC_L1[5]; // флаги ошибки считывания данных с датчика DS18B20 , true - (ошибка СRC или обрыв линии датчика) boolean flag_timeDS_L1; // флаг активности временной задержки для преобразования температуры byte Nstr_L1; // номер строки массива (номер-индекс датчика) byte bufData_L1[9]; // буфер данных, 9 байт считанных с датчика byte fazaDS_L1 ; // номер фазы в цикле инициализации DS18B20 float titDS_L1[5]; // измеренная температура >>массив из 5 значений температуры uint64_t timeDS_L1; // временная задержка для преобразования датчиком температуры и временная задержка в фазе цикла измерения 5 //--- // линия №2 шины 1-Wire датчиков температуры DS18B20 OneWire sensDs2 (27); // 5 датчиков подключены к выводу 27 >> (линия №2 шины 1-Wire) // массив адресов датчиков DS18B20 (уникальный адрес) byte sensAdd_L2[5][8]={ // адреса датчиков на макетной плате {0x28,0xB7,0x0F,0x3E,0x00,0x00,0x00,0xAE}, //уникальный адрес 1 датчика {0x28,0xAD,0xFB,0x3D,0x00,0x00,0x00,0x0B}, //уникальный адрес 2 датчика {0x28,0x4A,0x11,0x3D,0x00,0x00,0x00,0xC0}, //уникальный адрес 3 датчика {0x28,0x0B,0xBC,0x3E,0x00,0x00,0x00,0xD5}, //уникальный адрес 4 датчика {0x28,0xC6,0x13,0x3E,0x00,0x00,0x00,0x5C}, //уникальный адрес 5 датчика }; boolean flagWire_L2; // флаг состояния 1 линии 1-Wire _ true – присутствие устройств, false – нет устройств (обрыв линии) boolean flagCRC_L2[5]; // флаги ошибки считывания данных с датчика DS18B20 , true - (ошибка СRC или обрыв линии датчика) boolean flag_timeDS_L2; // флаг активности временной задержки для преобразования температуры byte Nstr_L2; // номер строки массива (номер-индекс датчика) byte bufData_L2[9]; // буфер данных, 9 байт считанных с датчика byte fazaDS_L2 ; // номер фазы в цикле инициализации DS18B20 float titDS_L2[5]; // измеренная температура >>массив из 5 значений температуры uint64_t timeDS_L2; // временная задержка для преобразования датчиком температуры и временная задержка в фазе цикла измерения 5 //--- //---Для датчиков DHT #include "DHT.h" // Раскомментируйте одну из строк ниже в зависимости от того, какой датчик вы используете! //#define DHTTYPE DHT11 // DHT 11 //#define DHTTYPE DHT21 // DHT 21 (AM2301) #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 //датчик DHT №01 uint8_t DHTPin_1 = 13; // пин подключения датчика DHT DHT dht_1(DHTPin_1, DHTTYPE); float TempDHT_1; // температура DHT float HumDHT_1; // влажность DHT boolean flagFautDht_1; // флаг ошибки считывания данных с датчика DHT22 uint64_t timeDht_1; // период временной задержки для измерения датчиком DHT22 //--- //датчик DHT №02 uint8_t DHTPin_2 = 19; // пин подключения датчика DHT DHT dht_2(DHTPin_2, DHTTYPE); float TempDHT_2; // температура DHT float HumDHT_2; // влажность DHT boolean flagFautDht_2; // флаг ошибки считывания данных с датчика DHT22 uint64_t timeDht_2; // период временной задержки для измерения датчиком DHT22 //--- //---Для дисплея //---Для OLED дисплея (SSH1106_128x64 1,3) с 4-мя контактами I2C #include <GyverOLED.h> //Лёгкая и быстрая библиотека для OLED дисплея GyverOLED<SSH1106_128x64> oled; uint64_t timeOled; // период временной задержки для смены информации на дисплее uint8_t kadrOled; // номер кадра uint8_t timekadrOled; // длительность кадра в секундах //--- const char* ssid = "Table DS18B20_DHT22"; const char *password = "01234567"; // пароль обязательно должен быть длиннее 8-ми символов // Настройки IP адреса IPAddress local_IP(192,168,13,2); IPAddress gateway(192,168,13,2); IPAddress subnet(255,255,255,0); // Создаем объект AsyncWebServer на порту 80 AsyncWebServer server(80); //---страница показаний датчиков DS18B20, DHT22-------------------------------- const char table_html[] PROGMEM = R"rawliteral( <!DOCTYPE HTML> <html> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>ESP32 opros DS18B20, DHT</title> <style> html { font-family: Times; display: inline-block; margin: 0px auto; text-align: center;} body{margin-top: 10px;} h1 {color: #444444;margin: 20px auto 10px;} h3 {color: #FF8077;margin-bottom: 10px;} p {font-size: 20px;color: #888;margin-bottom: 7px;} caption {border: solid 1px silver;background-color:#DDDDDD;font-size:18px; td{text-align:center;font-size:25px;} </style> </head> <body> <h3>ESP32 опрос датчиков DS18B20, DHT</h3> <center> <table border="1" width="340" height="26" cellspacing="2"> <tr> <td width="180" style="font-size:16px;text-align: left;">Период опроса 3 сек.</td> <td style="font-size:18px;color:#0000FF;text-align:center;"><b><i><span id="connect">text1 </span></i></b></td> </tr> </table> <br> <table border="0" width="340" height="50" cellspacing="2"> <caption><i><b>Температура-T(°С) на линии №1<br> датчиков DS18B20</b></i></caption> <tr> <td width="240" style="font-size:16px;">Показания датчика №1</td> <td >Т11:</td> <td style="font-size:20px;text-align:center;"><span id="T11_html">T11</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Показания датчика №2</td> <td>Т12:</td> <td style="font-size:20px;text-align:center;"><span id="T12_html">T12</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Показания датчика №3</td> <td>Т13:</td> <td style="font-size:20px;text-align:center;"><span id="T13_html">T13</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Показания датчика №4</td> <td>Т14:</td> <td style="font-size:20px;text-align:center;"><span id="T14_html">T14</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Показания датчика №5</td> <td>Т15:</td> <td style="font-size:20px;text-align:center;"><span id="T15_html">T15</span></td> </tr> </table> <br> <table border="0" width="340" height="50" cellspacing="2"> <caption><i><b>Температура-T(°С) на линии №2<br> датчиков DS18B20</b></i></caption> <tr> <td width="240" style="font-size:16px;">Показания датчика №1</td> <td >Т21:</td> <td style="font-size:20px;text-align:center;"><span id="T21_html">T21</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Показания датчика №2</td> <td>Т22:</td> <td style="font-size:20px;text-align:center;"><span id="T22_html">T22</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Показания датчика №3</td> <td>Т23:</td> <td style="font-size:20px;text-align:center;"><span id="T23_html">T23</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Показания датчика №4</td> <td>Т24:</td> <td style="font-size:20px;text-align:center;"><span id="T24_html">T24</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Показания датчика №5</td> <td>Т25:</td> <td style="font-size:20px;text-align:center;"><span id="T25_html">T25</span></td> </tr> </table> <br> <table border="0" width="340" height="50" cellspacing="2"> <caption><i><b>Температура-T(°С),<br>влажность-H(%) датчиков DHT22</b></i0></caption> <tr> <td width="240" style="font-size:16px;">Температура датчика №1</td> <td >Т31:</td> <td style="font-size:20px;text-align:center;"><span id="T31_html">T31</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Отн. влажность датчика №1</td> <td>H31:</td> <td style="font-size:20px;text-align:center;"><span id="H31_html">H31</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Температура датчика №2</td> <td>Т41:</td> <td style="font-size:20px;text-align:center;"><span id="T41_html">T41</span></td> </tr> <tr> <td style="font-size:16px;text-align: left;">Отн. влажность датчика №2</td> <td>H41:</td> <td style="font-size:20px;text-align:center;"><span id="H41_html">H41</span></td> </tr> </table> </center> <script> var text1 = document.getElementById('connect'); var T11 = document.getElementById('T11_html'); var T12 = document.getElementById('T12_html'); var T13 = document.getElementById('T13_html'); var T14 = document.getElementById('T14_html'); var T15 = document.getElementById('T15_html'); var T21 = document.getElementById('T21_html'); var T22 = document.getElementById('T22_html'); var T23 = document.getElementById('T23_html'); var T24 = document.getElementById('T24_html'); var T25 = document.getElementById('T25_html'); var T31 = document.getElementById('T31_html'); var H31 = document.getElementById('H31_html'); var T41 = document.getElementById('T41_html'); var H41 = document.getElementById('H41_html'); oprosData(); setInterval(oprosData,3000); function oprosData(){ document.getElementById('connect').innerHTML = "disconnect"; var dataJson = '/text_json?'; fetch(dataJson) .then(function(response){return response.json();}) .then(function(result) { text1.innerHTML = result.text1; T11.innerHTML = result.T11; T12.innerHTML = result.T12; T13.innerHTML = result.T13; T14.innerHTML = result.T14; T15.innerHTML = result.T15; T21.innerHTML = result.T21; T22.innerHTML = result.T22; T23.innerHTML = result.T23; T24.innerHTML = result.T24; T25.innerHTML = result.T25; T31.innerHTML = result.T31; H31.innerHTML = result.H31; T41.innerHTML = result.T41; H41.innerHTML = result.H41; }); }; </script> </body> </html> )rawliteral"; //------------------------------------------------------------------------------------------------------------------- //=================================================================================================================== void setup() { Serial.begin(115200); // для точки доступа WiFi.softAPConfig(local_IP, gateway, subnet); WiFi.softAP(ssid, password); //WiFi.softAP(ssid); delay(100); /* // для обычного Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi.."); } */ IPAddress IP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(IP); // Распечатать локальный IP-адрес //Serial.println(WiFi.localIP()); // Маршрут для корневой веб-страницы server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "text/html", table_html); }); // Маршрут для Json запроса server.on("/text_json", HTTP_GET, [] (AsyncWebServerRequest *request) { //значения для ключей json формата String stTIT1; String stTIT2; String stTIT3; String stTIT4; String stTIT5; String stTIT21; String stTIT22; String stTIT23; String stTIT24; String stTIT25; String stTIT31; String stH31; String stTIT41; String stH41; //--- //JSON (англ. JavaScript Object Notation) — текстовый формат обмена данными, основанный на JavaScript. if (flagWire_L1==false)// если обрыв линии №1 1-Wire {stTIT1 = stTIT2 = stTIT3 = stTIT4 = stTIT5 = "\"ERROR\"";} else { //если ошибка CRC датчика if (flagCRC_L1[0]) {stTIT1= "\"-CRC-\"";} else {stTIT1=String(titDS_L1[0],1);} if (flagCRC_L1[1]) {stTIT2= "\"-CRC-\"";} else {stTIT2=String(titDS_L1[1],1);} if (flagCRC_L1[2]) {stTIT3= "\"-CRC-\"";} else {stTIT3=String(titDS_L1[2],1);} if (flagCRC_L1[3]) {stTIT4= "\"-CRC-\"";} else {stTIT4=String(titDS_L1[3],1);} if (flagCRC_L1[4]) {stTIT5= "\"-CRC-\"";} else {stTIT5=String(titDS_L1[4],1);} } if (flagWire_L2==false)// если обрыв линии №2-Wire {stTIT21 = stTIT22 = stTIT23 = stTIT24= stTIT25= "\"ERROR\"";} else { if (flagCRC_L2[0]) {stTIT21= "\"-CRC-\"";} else {stTIT21=String(titDS_L2[0],1);} if (flagCRC_L2[1]) {stTIT22= "\"-CRC-\"";} else {stTIT22=String(titDS_L2[1],1);} if (flagCRC_L2[2]) {stTIT23= "\"-CRC-\"";} else {stTIT23=String(titDS_L2[2],1);} if (flagCRC_L2[3]) {stTIT24= "\"-CRC-\"";} else {stTIT24=String(titDS_L2[3],1);} if (flagCRC_L2[4]) {stTIT25= "\"-CRC-\"";} else {stTIT25=String(titDS_L2[4],1);} } if (flagFautDht_1 ==true)//ошибка считывания датчика DHT {stTIT31 = stH31= "\"FAUT\"";} else { stTIT31=String(TempDHT_1,1); stH31=String(HumDHT_1,0); } if (flagFautDht_2 ==true)//ошибка считывания датчика DHT {stTIT41 = stH41= "\"FAUT\"";} else { stTIT41=String(TempDHT_2,1); stH41=String(HumDHT_2,0); } String response = ""; // ответ клиенту response += "{\"text1\": \"connect ESP32\",\"T11\": "+(stTIT1); response += ",\"T12\": "+(stTIT2); response += ",\"T13\": "+(stTIT3); response += ",\"T14\": "+(stTIT4); response += ",\"T15\": "+(stTIT5); response += ",\"T21\": "+(stTIT21); response += ",\"T22\": "+(stTIT22); response += ",\"T23\": "+(stTIT23); response += ",\"T24\": "+(stTIT24); response += ",\"T25\": "+(stTIT25); response += ",\"T31\": "+(stTIT31); response += ",\"H31\": "+(stH31); response += ",\"T41\": "+(stTIT41); response += ",\"H41\": "+(stH41)+" }"; request->send(200, "application/json", response); }); //------------------------------------------------------------------------------------------------------------------------ //---для датчиков DHT pinMode(DHTPin_1, INPUT); dht_1.begin(); pinMode(DHTPin_2, INPUT); dht_2.begin(); //--- oled.init(); // инициализация дисплея //--- загрузка в дисплей oled.clear(); // очистить дисплей (или буфер) oled.setScale(1); oled.setCursorXY(7, 0); // курсор в (пиксель X, пиксель Y) oled.print("opros DS18B20,DHT22"); oled.setScale(3); oled.setCursorXY(0, 17); // курсор в (пиксель X, пиксель Y) oled.print("ESP 32"); oled.setScale(1); oled.setCursorXY(0,48); // курсор в (пиксель X, пиксель Y) oled.print("WEBSER:AP132.168.13.2"); oled.update(); delay(7000); // Запустить сервер server.begin(); Serial.println("HTTP server started"); } //=============================================================================================================== void loop() { oprosDS18B20_L1(); // процедура опроса датчиков DS18B20 линии №1 полный цикл oprosDht_1(); // процедура опроса датчика DHT №1 oprosDS18B20_L2(); // процедура опроса датчиков DS18B20 линии №2 oprosDht_2(); // процедура опроса датчика DHT №2 oledText_I2(); // процедура вывода информации на дисплей } //=================================================================================================================== //---Процедура измерения температуры датчикоми DS18B20 на линии №1------------------------------------------------------------ void oprosDS18B20_L1() {//опрос датчиков по полному циклу - при конветировании температуры потребление энергии только одним датчиком, // но увеличивается время опроса каждого датчика (время на 1 датчик * кол-во датчиков на линии плюс время промежуточного кода структуры LOOP) switch (fazaDS_L1) { case 0: if (sensDs.reset()==true) // сброс шины { flagWire_L1=true; // на линии 1-Wire устройства присутствуют fazaDS_L1 =1; } else { flagWire_L1=false; // “обрыв” линии 1-wire или нет устройств 1-wire на линии fazaDS_L1 =5; // переводим цикл опроса датчика в фазу 5 Serial.println("*L1_1wi:faut");// для отладки- неполадки с линией 1-Wire } break; // команда инициализация датчика по адресу датчика case 1: sensDs.write(0x55,POWER_MODE); fazaDS_L1 =2; // посылаем адрес датчика sensDs.write(sensAdd_L1 [Nstr_L1][0],POWER_MODE); // 1 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][1],POWER_MODE); // 2 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][2],POWER_MODE); // 3 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][3],POWER_MODE); // 4 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][4],POWER_MODE); // 5 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][5],POWER_MODE); // 6 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][6],POWER_MODE); // 7 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][7],POWER_MODE); // 8 байт адреса // команда конвертирования температуры sensDs.write(0x44,POWER_MODE); fazaDS_L1 =2; break; case 2: // организуем временную задержку для измерения температуры датчиком DS18B20 - по даташифту минимальное время 750 mc if ((millis()- timeDS_L1)>=800) // для UNO примерно раз в 50 дней количество миллисекунд сбрасывается на ноль. { timeDS_L1 = millis(); fazaDS_L1 =3; // по окончании временной задержки переход в следующую фазу цикла } break; case 3: if (sensDs.reset()==true) // сброс шины { flagWire_L1=true;// на линии 1-Wire устройства присутствуют fazaDS_L1 =4; // продолжаем цикл опроса датчиков } else { flagWire_L1=false; // “обрыв” линии 1-wire или нет устройств 1-wire на линии fazaDS_L1=5; // переводим цикл опроса датчика в фазу 5 Serial.println("*L1_1-Wire:faut 2");// для отладки } break; // команда инициализация датчика по адресу датчика case 4: sensDs.write(0x55,POWER_MODE); // посылаем адрес датчика sensDs.write(sensAdd_L1 [Nstr_L1][0],POWER_MODE); // 1 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][1],POWER_MODE); // 2 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][2],POWER_MODE); // 3 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][3],POWER_MODE); // 4 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][4],POWER_MODE); // 5 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][5],POWER_MODE); // 6 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][6],POWER_MODE); // 7 байт адреса sensDs.write(sensAdd_L1 [Nstr_L1][7],POWER_MODE); // 8 байт адреса // команда чтения sensDs.write(0xBE,POWER_MODE); bufData_L1[0]=sensDs.read(); // чтение памяти датчика bufData_L1[1]=sensDs.read(); // чтение памяти датчика bufData_L1[2]=sensDs.read(); // чтение памяти датчика bufData_L1[3]=sensDs.read(); // чтение памяти датчика bufData_L1[4]=sensDs.read(); // чтение памяти датчика bufData_L1[5]=sensDs.read(); // чтение памяти датчика bufData_L1[6]=sensDs.read(); // чтение памяти датчика bufData_L1[7]=sensDs.read(); // чтение памяти датчика bufData_L1[8]=sensDs.read(); // чтение памяти датчика, // данные готовы if (OneWire::crc8(bufData_L1,8)==bufData_L1[8]) //проверка CRC { //данные правильные flagCRC_L1[Nstr_L1]=false; if(bitRead(bufData_L1[1],7)==1) //температура <0 градусов { //если температура отрицательная, то данные в дополнительном коде // преобазуем в прямой код byte t12=~bufData_L1[0]; byte t22=~bufData_L1[1];//инверсия байт titDS_L1[Nstr_L1]=(float)(((int)t12 |((int)t22<<8)+1) *0.0625+0.03125)*-1; // измеренная отрицательная температура // где 0.0625 град. С - вес разряда, а 0.03125 = 0.0625/2 половинка разряда для большей точности // при разрешение преобразования в 12 бит. // источник >> https://mypractic.ru/-Урок 26. Подключение термодатчиков DS18B20 к Ардуино. Библиотека OneWire. Точный Ардуино термометр-регистратор. // в этом же источнике приведен пример вывода по одному байту в линию 1- Wire c использованием таймера по времени. } else {//температура >=0 градусов titDS_L1[Nstr_L1]=(float)((int)bufData_L1[0] |((int)bufData_L1[1]<<8)) *0.0625+0.03125; // измеренная температура } // передача температуры на компьютер Serial.print("*L1T"); Serial.print(Nstr_L1); Serial.print(":"); Serial.println(titDS_L1[Nstr_L1]); } // конец условия по правильной температуре else { // ошибка CRC flagCRC_L1[Nstr_L1]=true; Serial.print("*L1T"); Serial.print(Nstr_L1); Serial.print(":"); Serial.println("_CRC"); } // конец условия по ошибки CRC // цикл измерения закончен, перевод в следующий цикл для следующего датчика fazaDS_L1=5; Nstr_L1= Nstr_L1+1; // Nstr_L1 >> кол-во датчиков на линии (число строк массива уникальных адресов датчиков) if (Nstr_L1>=5) Nstr_L1=0;// переходим на нулевой датчик в линии break; case 5: if (flagWire_L1==false) // если обрыв линии, то опрос ведем через 5 секунд, дабы не "засорять" вывод информации { if (flag_timeDS_L1==true) { if ((millis()- timeDS_L1)>=5000) // временная задержка 2 секунды { fazaDS_L1 =0;// переходим на нулевую фазу (начало цикла) flag_timeDS_L1=false; //сбрасываем флаг активности временной задержки } } else { timeDS_L1 = millis(); flag_timeDS_L1=true; //устанавливаем флаг активности временной задержки fazaDS_L1 =5; //остаемся в этой же фазе цикла } } else { fazaDS_L1 =0;// // переходим на нулевую фазу (начало цикла) при измерении без ошибки линии } break; }// завершение цикла switch (fazaDS) } // завершение процедуры измерения температуры датчиком DS18B20 //-------------------------------------------------------------------------------------------------------------------------------------- //---Процедура измерения температуры датчикоми DS18B20 на линии №2---------------------------------------------------------- void oprosDS18B20_L2() {//опрос датчиков по неполному циклу >> используем команду "пропуск ROM" - при конвентировании температуры потребление энергии всеми датчиками на линии, //время опроса каждого датчика немного больше времени преобразования температуры == 800 мс.плюс время промежуточного кода структуры LOOP switch (fazaDS_L2) { case 0: if (sensDs2.reset()==true) // сброс шины { flagWire_L2=true; // на линии 1-Wire устройства присутствуют fazaDS_L2 =1; } else { flagWire_L2=false; // “обрыв” линии 1-wire или нет устройств 1-wire на линии fazaDS_L2=5; // переводим цикл опроса датчика в фазу 5 Serial.println("*L2_1-Wire:faut");// для отладки } break;// выход из цикла // команда пропуск ROM case 1: sensDs2.write(0xCC,POWER_MODE); // команда конвертирования температуры sensDs2.write(0x44,POWER_MODE); fazaDS_L2 =2; break; // организуем временную задержку 900 mc для измерения температуры датчиком DS18B20 - по даташифту минимальное время 750 mc case 2: if (flag_timeDS_L2==true) { /* В отличие от Ардуино счетчики ESP32 имеют разрядность 64 бита, что позволяет реализовывать временные циклы со значительно более длительными периодами, не заботясь о переполнении счетчика. */ if ((millis()- timeDS_L2)>=900) // для UNO примерно раз в 50 дней количество миллисекунд сбрасывается на ноль. { fazaDS_L2 =3; // переходим на следующую фазу flag_timeDS_L2=false; // сбрасываем флаг активности временной задержки } } else { timeDS_L2 = millis(); // присваиваем время flag_timeDS_L2=true; // устанавливаем флаг активности временной задержки fazaDS_L2 =2; // переходим на эту же фазу } break; case 3: if (sensDs2.reset()==true) // сброс шины { flagWire_L2=true;// на линии 1-Wire устройства присутствуют fazaDS_L2=4; } else { flagWire_L2=false; // “обрыв” линии 1-wire или нет устройств на линии 1-wire fazaDS_L2=5; // переводим цикл опроса датчика в фазу 5 Serial.println("*L2_1-Wire:faut 2");// для отладки } break; // команда инициализация датчика по адресу датчика case 4: sensDs2.write(0x55,POWER_MODE); // посылаем адрес датчика sensDs2.write(sensAdd_L2 [Nstr_L2][0],POWER_MODE); // 1 байт адреса sensDs2.write(sensAdd_L2 [Nstr_L2][1],POWER_MODE); // 2 байт адреса sensDs2.write(sensAdd_L2 [Nstr_L2][2],POWER_MODE); // 3 байт адреса sensDs2.write(sensAdd_L2 [Nstr_L2][3],POWER_MODE); // 4 байт адреса sensDs2.write(sensAdd_L2 [Nstr_L2][4],POWER_MODE); // 5 байт адреса sensDs2.write(sensAdd_L2 [Nstr_L2][5],POWER_MODE); // 6 байт адреса sensDs2.write(sensAdd_L2 [Nstr_L2][6],POWER_MODE); // 7 байт адреса sensDs2.write(sensAdd_L2 [Nstr_L2][7],POWER_MODE); // 8 байт адреса // команда чтения памяти sensDs2.write(0xBE,POWER_MODE); // чтение 9 регистров датчика bufData_L2[0]=sensDs2.read(); // чтение памяти датчика 0 байт: температура мл. bufData_L2[1]=sensDs2.read(); // чтение памяти датчика 1 байт: температура ст. bufData_L2[2]=sensDs2.read(); // чтение памяти датчика 2 байт: TH регистр bufData_L2[3]=sensDs2.read(); // чтение памяти датчика 3 байт: TL регитстр bufData_L2[4]=sensDs2.read(); // чтение памяти датчика 4 байт: Регистр конфигурации bufData_L2[5]=sensDs2.read(); // чтение памяти датчика 5 байт: зарезирвирован (FFh) bufData_L2[6]=sensDs2.read(); // чтение памяти датчика 6 байт: зарезирвирован (0Ch) bufData_L2[7]=sensDs2.read(); // чтение памяти датчика 7 байт: зарезирвирован (10h) bufData_L2[8]=sensDs2.read(); // чтение памяти датчика 8 байт: циклический код CRC // данные готовы if (OneWire::crc8(bufData_L2,8)==bufData_L2[8]) //проверка CRC { //данные правильные flagCRC_L2[Nstr_L2]=false; if(bitRead(bufData_L2[1],7)==1) { //если температура отрицательная, то данные в дополнительном коде // преобазуем в прямой код byte t12=~bufData_L2[0]; byte t22=~bufData_L2[1];//инверсия байт titDS_L2[Nstr_L2]=(float)(((int)t12 |((int)t22<<8)+1) *0.0625+0.03125)*-1; // измеренная отрицательная температура } else {//температура >=0 градусов titDS_L2[Nstr_L2]=(float)((int)bufData_L2[0] |((int)bufData_L2[1]<<8)) *0.0625+0.03125; // измеренная температура } // передача температуры на компьютер Serial.print("*L2T"); Serial.print(Nstr_L2); Serial.print(":"); Serial.println(titDS_L2[Nstr_L2]); } // конец условия по правильной температуре else { // ошибка CRC flagCRC_L2[Nstr_L2]=true; Serial.print("*L2T"); Serial.print(Nstr_L2); Serial.print(":"); Serial.println("_CRC"); } // конец условия по ошибки CRC // цикл опроса закончен, перевод в следующий цикл для следующего датчика Nstr_L2= Nstr_L2+1; if (Nstr_L2>=5) // количество датчиков на линии согласно массиву адресов { Nstr_L2=0;// переходим на нулевой датчик в линии fazaDS_L2=5; } else { fazaDS_L2=3; // переходим на следующий датчик } break; case 5: if (flagWire_L2==false) // если обрыв линии, то опрос ведем через 5 секунд, дабы не "засорять" вывод информации { if (flag_timeDS_L2==true) { if ((millis()- timeDS_L2)>=5000) // временная задержка 5 секунд { fazaDS_L2 =0;// переходим на нулевую фазу (начало цикла) flag_timeDS_L2=false; //сбрасываем флаг активности временной задержки } } else { timeDS_L2 = millis(); flag_timeDS_L2=true; //устанавливаем флаг активности временной задержки fazaDS_L2 =5; //остаемся в этой же фазе цикла } } else { fazaDS_L2 =0; // переходим на нулевую фазу (начало цикла) при измерении без ошибки линии } break; }// конец цикла switch (fazaDS_L2) }//--- end процедуры oprosDS18B20_L2() //---------------------------------------------------------------------------------------------------------------------------------------------- //---процедура опроса датчика DHT №01 void oprosDht_1() { //Delay(2000); // 2 секунды - необходимая временная задержка для измерения датчиком DHT22 if (millis()- timeDht_1>=2000) { timeDht_1= millis(); // для UNO примерно раз в 50 дней количество миллисекунд сбрасывается на ноль HumDHT_1=dht_1.readHumidity(); // считываем влажность TempDHT_1=dht_1.readTemperature(); // считываем температуру // проверка удачно ли прошло ли считывание if (isnan(HumDHT_1) || isnan (TempDHT_1)) { flagFautDht_1 =true; // установка флага ошибки считывания Serial.println ("*DHT_1:ERROR"); } else { flagFautDht_1 =false; // сброс флага ошибки считывания Serial.print ("*dhtH_1:"); Serial.println (HumDHT_1); Serial.print ("*dhtT_1:"); Serial.println (TempDHT_1); } } }//---конец процедуры опроса датчика DHT №01 //---процедура опроса датчика DHT №02 void oprosDht_2() { //Delay(2000); // 2 секунды - необходимая временная задержка для измерения датчиком DHT22 if (millis()- timeDht_2>=2000) { timeDht_2= millis(); // для UNO примерно раз в 50 дней количество миллисекунд сбрасывается на ноль HumDHT_2=dht_2.readHumidity(); // считываем влажность TempDHT_2=dht_2.readTemperature(); // считываем температуру // проверка удачно ли прошло ли считывание if (isnan(HumDHT_2) || isnan (TempDHT_2)) { flagFautDht_2 =true; // установка флага ошибки считывания Serial.println ("*DHT_2:ERROR"); } else { flagFautDht_2 =false; // сброс флага ошибки считывания Serial.print ("*dhtH_2:"); Serial.println (HumDHT_2); Serial.print ("*dhtT_2:"); Serial.println (TempDHT_2); } } }//---конец процедуры опроса датчика DHT №02 //---процедура вывода информации на дисплей------------------------------------------------------------ void oledText_I2() { if ((millis()- timeOled)>=1000) { timeOled = millis(); timekadrOled=timekadrOled+1; if (timekadrOled>=3){kadrOled=1;} //отображение показаний датчика T12,T13,T14,T15 7сек. if (timekadrOled>=10){kadrOled=2;} //отображение показаний 1 датчика DHT 2сек. if (timekadrOled>=12){kadrOled=3;} //отображение показаний датчика T21 3сек if (timekadrOled>=15){kadrOled=4;} //отображение показаний датчика T22,T23,T24,T25 7сек. if (timekadrOled>=22){kadrOled=5;} //отображение показаний 2 датчика DHT 2сек. if (timekadrOled>=24){kadrOled=0;timekadrOled=0;} //отображение показаний датчика T11 3сек. Serial.print("kadrOled=");Serial.println(kadrOled);//для отладки } switch (kadrOled) { case 0: { // температура T11 oled.clear(); // очистить дисплей (или буфер) oled.setScale(1); oled.setCursorXY(5,0); // курсор в (пиксель X, пиксель Y) oled.print("L01 SENSOR: DS18B20"); oled.setScale(2); oled.setCursorXY(0, 21); // курсор в (пиксель X, пиксель Y) oled.print("T1:"); oled.setScale(3); oled.setCursorXY(40, 18); // курсор в (пиксель X, пиксель Y) if (flagCRC_L1[0]||!flagWire_L1) {oled.print("FAUT");} // флаг ошибки CRC датчика или флаг состояния 1 линии 1-Wire else {oled.print(titDS_L1[0],1);} oled.setScale(1); oled.setCursorXY(15,48); // курсор в (пиксель X, пиксель Y) oled.print("IP: ");oled.print("168.192.13.2"); oled.update(); break; } case 1: { // температура T12,T13,T14,T15 oled.clear(); // очистить дисплей (или буфер) oled.setScale(1); oled.setCursorXY(5,0); // курсор в (пиксель X, пиксель Y) oled.print("L01 SENSOR: DS18B20"); oled.setCursorXY(0, 21); // курсор в (пиксель X, пиксель Y) oled.print("T2"); oled.setScale(2); oled.setCursorXY(15, 15); // курсор в (пиксель X, пиксель Y) if (flagCRC_L1[1]||!flagWire_L1) {oled.print("FAUT");} // флаг ошибки CRC датчика или флаг состояния 1 линии 1-Wire else {oled.print(titDS_L1[1],1);} oled.setScale(1); oled.setCursorXY(67, 21); // курсор в (пиксель X, пиксель Y) oled.print("T3"); oled.setScale(2); oled.setCursorXY(82, 15); // курсор в (пиксель X, пиксель Y) if (flagCRC_L1[2]||!flagWire_L1) {oled.print("FAUT");} // флаг ошибки CRC датчика или флаг состояния 1 линии 1-Wire else {oled.print(titDS_L1[2],1);} oled.setScale(1); oled.setCursorXY(0, 46); // курсор в (пиксель X, пиксель Y) oled.print("T4"); oled.setScale(2); oled.setCursorXY(15, 41); // курсор в (пиксель X, пиксель Y) if (flagCRC_L1[3]||!flagWire_L1) {oled.print("FAUT");} // флаг ошибки CRC датчика или флаг состояния 1 линии 1-Wire else {oled.print(titDS_L1[3],1);} oled.setScale(1); oled.setCursorXY(67, 46); // курсор в (пиксель X, пиксель Y) oled.print("T5"); oled.setScale(2); oled.setCursorXY(82, 41); // курсор в (пиксель X, пиксель Y) if (flagCRC_L1[4]||!flagWire_L1) {oled.print("FAUT");} // флаг ошибки CRC датчика или флаг состояния 1 линии 1-Wire else {oled.print(titDS_L1[4],1);} oled.update(); break; } case 2: { // DHT22 T31 - температура, H31 - относительная влажность oled.clear(); // очистить дисплей (или буфер) oled.setScale(1); oled.setCursorXY(5, 0); // курсор в (пиксель X, пиксель Y) oled.print("SENSOR TYPE: DHT22"); if (!flagFautDht_1) { oled.setScale(2); oled.setCursorXY(0, 15); // курсор в (пиксель X, пиксель Y) oled.print("T31:"); oled.setScale(3); oled.setCursorXY(48, 12); // курсор в (пиксель X, пиксель Y) oled.print(TempDHT_1,1); oled.setScale(2); oled.setCursorXY(0, 43); // курсор в (пиксель X, пиксель Y) oled.print("H31:"); oled.setScale(3); oled.setCursorXY(54, 40); // курсор в (пиксель X, пиксель Y) oled.print(HumDHT_1,0); oled.print("%"); } else { oled.setScale(2); oled.setCursorXY(10, 25); // курсор в (пиксель X, пиксель Y) oled.print("FAUT DHT"); } oled.update(); break; } case 3: { // температура T21 oled.clear(); // очистить дисплей (или буфер) oled.setScale(1); oled.setCursorXY(5,0); // курсор в (пиксель X, пиксель Y) oled.print("L02 SENSOR: DS18B20"); oled.setScale(2); oled.setCursorXY(0, 21); // курсор в (пиксель X, пиксель Y) oled.print("T1:"); oled.setScale(3); oled.setCursorXY(40, 18); // курсор в (пиксель X, пиксель Y) if (flagCRC_L2[0]||!flagWire_L2) {oled.print("FAUT");} // флаг состояния 2 линии 1-Wire else {oled.print(titDS_L2[0],1);} oled.setScale(1); oled.setCursorXY(15,48); // курсор в (пиксель X, пиксель Y) oled.print("IP: "); oled.print("168.192.13.2"); //oled.print(WiFi.localIP());// для режима STA WebSer>Режим станции (STA) (WiFi Station). oled.update(); break; } case 4: { // температура T22,T23,T24,T25 oled.clear(); // очистить дисплей (или буфер) oled.setScale(1); oled.setCursorXY(5,0); // курсор в (пиксель X, пиксель Y) oled.print("L02 SENSOR: DS18B20"); oled.setCursorXY(0, 21); // курсор в (пиксель X, пиксель Y) oled.print("T2"); oled.setScale(2); oled.setCursorXY(15, 15); // курсор в (пиксель X, пиксель Y) if (flagCRC_L2[1]||!flagWire_L2) {oled.print("FAUT");} // флаг ошибки CRC датчика или флаг состояния 1 линии 1-Wire else {oled.print(titDS_L2[1],1);} oled.setScale(1); oled.setCursorXY(67, 21); // курсор в (пиксель X, пиксель Y) oled.print("T3"); oled.setScale(2); oled.setCursorXY(82, 15); // курсор в (пиксель X, пиксель Y) if (flagCRC_L2[2]||!flagWire_L2) {oled.print("FAUT");} // флаг ошибки CRC датчика или флаг состояния 1 линии 1-Wire else {oled.print(titDS_L2[2],1);} oled.setScale(1); oled.setCursorXY(0, 46); // курсор в (пиксель X, пиксель Y) oled.print("T4"); oled.setScale(2); oled.setCursorXY(15, 41); // курсор в (пиксель X, пиксель Y) if (flagCRC_L2[3]||!flagWire_L2) {oled.print("FAUT");} // флаг ошибки CRC датчика или флаг состояния 1 линии 1-Wire else {oled.print(titDS_L2[3],1);} oled.setScale(1); oled.setCursorXY(67, 46); // курсор в (пиксель X, пиксель Y) oled.print("T5"); oled.setScale(2); oled.setCursorXY(82, 41); // курсор в (пиксель X, пиксель Y) if (flagCRC_L2[4]||!flagWire_L2) {oled.print("FAUT");} // флаг ошибки CRC датчика или флаг состояния 1 линии 1-Wire else {oled.print(titDS_L2[4],1);} oled.update(); break; } case 5: { // DHT22 T41 - температура, H41 - относительная влажность oled.clear(); // очистить дисплей (или буфер) oled.setScale(1); oled.setCursorXY(5, 0); // курсор в (пиксель X, пиксель Y) oled.print("SENSOR TYPE: DHT22"); if (!flagFautDht_2) { oled.setScale(2); oled.setCursorXY(0, 15); // курсор в (пиксель X, пиксель Y) oled.print("T41:"); oled.setScale(3); oled.setCursorXY(48, 12); // курсор в (пиксель X, пиксель Y) oled.print(TempDHT_2,1); oled.setScale(2); oled.setCursorXY(0, 43); // курсор в (пиксель X, пиксель Y) oled.print("H41:"); oled.setScale(3); oled.setCursorXY(54, 40); // курсор в (пиксель X, пиксель Y) oled.print(HumDHT_2,0); oled.print("%"); } else { oled.setScale(2); oled.setCursorXY(10, 25); // курсор в (пиксель X, пиксель Y) oled.print("FAUT DHT"); } oled.update(); break; } } } //---------------------------------------------------------------------------------------------------------------------------------------------
- Пин 14 (
sensDs
):- Используется для подключения линии №1 шины 1-Wire, к которой подключено пять датчиков DS18B20. Через этот пин осуществляется обмен данными с этими датчиками.
- Пин 27 (
sensDs2
):- Используется для подключения линии №2 шины 1-Wire, к которой также подключено пять датчиков DS18B20. Аналогично пину 14, через этот пин идет обмен данными с датчиками второй линии.
- Пин 13 (
DHTPin_1
) и пин 19 (DHTPin_2
):- Используются для подключения двух датчиков DHT22. Эти пины отвечают за получение данных о температуре и влажности от соответствующих датчиков.
При подключении датчиков DS18B20 к ESP32 необходимо использовать подтягивающий резистор между линией данных и питанием (VCC). На коротких линиях чаще всего применяется резистор 4.7 кОм, но на длинных линиях для надежной передачи сигнала могут понадобиться резисторы меньшего номинала. В данном проекте использовались телефонные кабели ШТЛП-2 и ШТЛП-4, и наиболее стабильная работа была достигнута с резистором 3.2 кОм.
Принципы работы
Система работает следующим образом:
- Инициализация компонентов: При старте устройства происходит настройка всех необходимых модулей и библиотек, устанавливаются параметры Wi-Fi соединения, создаются объекты для работы с датчиками и дисплеем.
- Сбор данных: Периодически проводится опрос датчиков DS18B20 и DHT22 для получения текущих значений температуры и влажности. Данные обрабатываются и проверяются на наличие ошибок, таких как неправильное значение CRC или обрыв линий связи.
- Отображение данных: Собранные данные выводятся на OLED дисплей. Информация на экране меняется в соответствии с установленным интервалом обновления, что позволяет отслеживать изменения параметров окружающей среды в реальном времени.
- Удаленный доступ: Пользователи могут получить доступ к данным через веб-интерфейс, созданный с помощью библиотеки ESPAsyncWebServer. Для обновления данных без необходимости перезагрузки веб-страницы используется технология JSON, которая позволяет динамически обновлять информацию на странице.
Применение
Такая система находит применение в различных областях, где требуется мониторинг параметров окружающей среды. Примеры использования включают:
- Контроль микроклимата в теплицах и оранжереях: Поддержание оптимальных условий для роста растений требует постоянного контроля температуры и влажности.
- Мониторинг условий хранения продуктов и медикаментов: Многие продукты и медикаменты требуют строгого соблюдения температурного режима и уровня влажности.
- Автоматизация систем отопления и кондиционирования: Полученные данные можно использовать для автоматической регулировки температуры и влажности в помещениях.
Заключение
Система мониторинга окружающей среды на основе ESP32 представляет собой гибкое и мощное решение для автоматизации процессов сбора и анализа данных о температуре и влажности. Использование современных технологий и компонентов обеспечивает высокую точность измерений и удобство эксплуатации. Такая система может стать незаменимым инструментом в различных сферах деятельности, связанных с контролем климатических условий.
Автор проекта — Vid_PB (vid_pb@mail.ru)
Фото проекта:
Дополнительный материал: