ESP8266 и Arduino IDE. Часть 10a: IoT-монитор
Примечание
Оригинал статьи: martyncurrey.com
Этот урок демонстрирует создание станции мониторинга окружающей среды с использованием микроконтроллера ESP8266, которая отображает данные датчиков как на LCD-дисплее, так и на веб-интерфейсе через WebSocket.
Компоненты проекта
Оборудование
NodeMCU V0.9 (плата на базе ESP8266)
Датчик температуры/влажности DHT11 (пин D6)
Фоторезистор LDR (пин A0)
Статусный светодиод (пин D5)
Макетная плата и провода
Программное обеспечение
Arduino IDE с поддержкой ESP8266
Библиотека Adafruit DHT sensor
Библиотека WebSocketsServer
JavaScript/HTML5 для веб-интерфейса
Конфигурация схемы
Проект использует пять основных подключений на NodeMCU:
Аналоговый пин A0 подключён к фоторезистору
Цифровой пин D5 управляет статусным светодиодом
Цифровой пин D6 подключён к датчику DHT11
Пины D2 и D1 (SDA/SCL) зарезервированы для будущей интеграции LCD
Стратегия реализации
Проект разбит на этапы:
Тестирование датчиков — проверка работы DHT11 и LDR через Serial Monitor
Базовый веб-сайт — создание простой веб-страницы с отображением данных в реальном времени
Улучшение веб-сайта — добавление визуальных элементов: шкалы и графики
Интеграция LCD — добавление локального дисплея
Конфигурация WiFi — реализация WiFi Manager для гибкого подключения
Технический подход
Система использует неблокирующую архитектуру таймеров на основе millis() вместо delay(), что позволяет скетчу обрабатывать несколько параллельных задач: обслуживание веб-страниц, управление WebSocket-соединениями и чтение датчиков.
Формат данных: значения датчиков передаются через WebSocket в виде значений, разделённых символом |: humidity|tempC|tempF|brightness
Веб-страница получает эти данные и использует метод JavaScript split() для разбора значений в массив, а затем обновляет соответствующие HTML-элементы.
Скетч 1: Тестирование схемы
//
// ESP8266 and the Arduino IDE Part 10: Environment monitor station
// ESP8266-10_sketch01_Circuit_Test
//
#include "DHT.h"
#define DHTPIN D6
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
byte const pin_LDR = A0;
byte const pin_LED = D5;
float humidity = 0;
float tempC = 0;
float tempF = 0;
int brightness = 0;
boolean DHTvaluesOK = false;
void setup()
{
pinMode(pin_LED, OUTPUT); digitalWrite(pin_LED, LOW);
dht.begin();
Serial.begin(9600);
while (!Serial) {;}
Serial.println("");
Serial.println("STARTED");
}
void loop()
{
Serial.println(" ");
brightness = analogRead(pin_LDR);
Serial.print("Brightness = "); Serial.print(brightness); Serial.print("\t");
humidity = dht.readHumidity();
tempC = dht.readTemperature(); // Read temperature as Celsius (the default)
tempF = dht.readTemperature(true); // Read temperature as Fahrenheit (isFahrenheit = true)
// Check if any reads failed
if (isnan(humidity) || isnan(tempC) || isnan(tempF)) { DHTvaluesOK = false; }
else { DHTvaluesOK = true; }
if (DHTvaluesOK)
{
Serial.print("Humidity: "); Serial.print(humidity); Serial.print(" %\t");
Serial.print("Temperature: "); Serial.print(tempC); Serial.print ("C ");
Serial.print(tempF); Serial.println("F");
}
else
{
Serial.println("DHT11 ERROR");
}
// blink the LED to show the readings have been received.
digitalWrite(pin_LED, HIGH); delay(100); digitalWrite(pin_LED, LOW);
delay(3000);
}
Скетч 2: Шаблон WebSocket
//
// ESP8266 and the Arduino IDE Part 10: Environment monitor station
// ESP8266-10_sketch_02_WebSocket_Template
//
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
String html_1 = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0'/>
<meta charset='utf-8'>
<style>
body { font-size:100%;}
#main { display: table; width: 300px; margin: auto; padding: 10px 10px 10px 10px; border: 3px solid blue; border-radius: 10px; text-align:center;}
p { font-size: 100%; }
</style>
<title>Websockets</title>
</head>
<body>
<div id='main'>
<h3>Websockets</h3>
<div id='content'>
</div>
</div>
</body>
<script>
var Socket;
function init()
{
Socket = new WebSocket('ws://' + window.location.hostname + ':81/');
Socket.onmessage = function(event) { processReceivedCommand(event); };
}
function processReceivedCommand(evt)
{
document.getElementById('rd').textContent = evt.data;
}
function sendText(data) { Socket.send(data); }
window.onload = function(e) { init(); }
</script>
</html>
)=====";
#include <ESP8266WiFi.h>
#include <WebSocketsServer.h>
WiFiServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);
char ssid[] = "ssid";
char pass[]= "password";
void setup()
{
Serial.begin(9600);
Serial.println();
Serial.println("Serial started at 9600");
Serial.println();
Serial.print("Connecting to "); Serial.println("ssid");
WiFi.begin(ssid,pass);
int count = 0;
while ( (WiFi.status() != WL_CONNECTED) && count < 17)
{
Serial.print("."); delay(500); count++;
}
Serial.println("");
if (WiFi.status() != WL_CONNECTED)
{
Serial.print("Failed to connect to "); Serial.println(ssid);
while(1);
}
Serial.print("Connected to "); Serial.println(WiFi.localIP());
server.begin();
Serial.println("Server started");
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}
void loop()
{
webSocket.loop();
WiFiClient client = server.available();
if (client)
{
client.flush();
client.print( header );
client.print( html_1 );
Serial.println("New page served");
}
}
void webSocketEvent(byte num, WStype_t type, uint8_t * payload, size_t length)
{
Serial.println();
Serial.print("WStype = "); Serial.println(type);
Serial.print("WS payload = ");
for(int i = 0; i < length; i++) { Serial.print((char) payload[i]); }
Serial.println();
}
Скетч 3: Базовый веб-сайт с данными датчиков
//
// ESP8266 and the Arduino IDE Part 10: Environment monitor station
// ESP8266-10_sketch03_Basic_Website
//
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
String html_1 = R"=====(
<!DOCTYPE html>
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0'/>
<meta charset='utf-8'>
<style>
body { font-size:100%;}
#main { display: table; width: 300px; margin: 20px auto; padding: 10px 10px 10px 10px; border: 1px solid blue; border-radius: 10px; text-align:center;}
p { font-size: 120%; }
</style>
<title>ESP8266 Part 10</title>
</head>
<body>
<div id='main'>
<h1>ESP8266 monitor</h1>
<div id='content'>
<p>Humidity = <span id='humidity'>00</span>%</p>
<p>Temperature = <span id='tempC'>00</span>°C</p>
<p>Temperature = <span id='tempF'>00</span>°F</p>
<p>Brightness = <span id='brightness'>00</span></p>
<p>Data = <span id='recData'>00</span></p>
</div>
<br />
</div>
</body>
<script>
var Socket;
function init()
{
Socket = new WebSocket('ws://' + window.location.hostname + ':81/');
Socket.onmessage = function(event) { processReceivedCommand(event); };
}
function processReceivedCommand(evt)
{
var data = evt.data;
document.getElementById('recData').textContent = data;
var tmp = data.split('|');
document.getElementById('humidity').textContent = tmp[0];
document.getElementById('tempC').textContent = tmp[1];
document.getElementById('tempF').textContent = tmp[2];
document.getElementById('brightness').textContent = tmp[3];
}
function sendText(data) { Socket.send(data); }
window.onload = function(e) { init(); }
</script>
</html>
)=====";
#include <ESP8266WiFi.h>
#include <WebSocketsServer.h>
WiFiServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);
char ssid[] = "mySSID";
char pass[]= "myPassword";
#include "DHT.h"
#define DHTPIN D6
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
byte const pinLED = D5;
byte const pinLDR = A0;
int brightness = 0;
float humidity = 0;
float tempC = 0;
float tempF = 0;
boolean DHTreadingsOK = false;
boolean updateWebpage = false;
long sensorUpdateFrequency = 5000;
long timeNow = 0;
long timePrev = 0;
void setup()
{
pinMode(pinLED, OUTPUT); digitalWrite(pinLED, LOW);
dht.begin();
Serial.begin(9600);
while (!Serial) {;}
Serial.println();
Serial.println("Serial started at 9600");
Serial.println();
Serial.print("Connecting to "); Serial.println(ssid);
WiFi.begin(ssid,pass);
int count = 0;
digitalWrite(pinLED, HIGH);
while ( (WiFi.status() != WL_CONNECTED) && count < 17)
{
Serial.print("."); count++; delay(500);
}
digitalWrite(pinLED, LOW);
if (WiFi.status() != WL_CONNECTED)
{
Serial.println(""); Serial.print("Failed to connect to "); Serial.println(ssid);
while(1);
}
Serial.println("");
Serial.print("Connected. IP address = "); Serial.println(WiFi.localIP());
server.begin();
Serial.println("Server started");
webSocket.begin();
Serial.println("websocket started");
Serial.println("");
Serial.println("");
webSocket.onEvent(webSocketEvent);
}
void loop()
{
webSocket.loop();
WiFiClient client = server.available();
if (client)
{
client.flush();
client.print( header );
client.print( html_1 );
Serial.println("New page served");
Serial.println("");
}
timeNow = millis();
if (timeNow - timePrev >= sensorUpdateFrequency)
{
timePrev = timeNow;
updateSensors();
}
}
void updateSensors()
{
brightness = analogRead(pinLDR);
humidity = dht.readHumidity();
tempC = dht.readTemperature();
tempF = dht.readTemperature(true);
if (isnan(humidity) || isnan(tempC) || isnan(tempF))
{
Serial.println("Error reading from the DHT11.");
}
else
{
String data = "";
data = String(data + byte(humidity) );
data = String(data + "|");
data = String(data + tempC);
data = String(data + "|");
data = String(data + tempF);
data = String(data + "|");
data = String(data + brightness);
webSocket.broadcastTXT(data);
Serial.println(data);
digitalWrite(pinLED, HIGH); delay(50); digitalWrite(pinLED, LOW);
}
}
void webSocketEvent(byte num, WStype_t type, uint8_t * payload, size_t length)
{
Serial.println("");
Serial.print("WStype = "); Serial.println(type);
Serial.print("WS payload = ");
for(int i = 0; i < length; i++) { Serial.print((char) payload[i]); }
Serial.println();
}
Часть 10a охватывает начальное тестирование схемы и базовую реализацию WebSocket. Последующие части расширяют интерфейс продвинутыми визуализациями и дополнительными функциями.