Универсальная метеостанция на ESP32 с печатной платой
В этом проекте я покажу вам, как можно собрать универсальную метеостанцию на базе ESP32 с печатной платой (shield) и отображать показания датчиков на веб-сервере. Веб-сервер отображает данные со всех датчиков и автоматически обновляет показания каждые десять секунд без необходимости перезагрузки веб-страницы.
Видеоурок и демонстрация проекта
Это руководство доступно в видеоформате (смотрите ниже) и в текстовом формате (продолжайте чтение).
Ресурсы
Все необходимые ресурсы для сборки этого проекта перечислены ниже:
Возможности платы метеостанции ESP32
Для сборки этого проекта я разработал печатную плату для платы ESP32 DEVKIT V1 DOIT. Разработанная мной печатная плата работает только с версией, имеющей 30 GPIO.
Я разработал плату расширения (shield) в качестве компактной метеостанции. Печатная плата имеет множество функций, что позволяет использовать её в различных проектах для разных применений. На самом деле, в этом проекте я использовал не все возможности платы.
Кроме того, эта плата расширения может также использоваться как учебная плата, поскольку она содержит некоторые из наиболее часто используемых компонентов при изучении программирования ESP32.
Плата расширения позволяет управлять:
2x SMD-светодиода
1x Кнопка
1x Подстроечный потенциометр (тримпот)
1x Датчик температуры и влажности DHT22
1x Барометрический датчик BMP180
1x Фоторезистор (LDR)
1x Модуль карты MicroSD
2x Клеммные колодки – предоставляющие доступ к 3 GPIO для подключения других компонентов
Модуль карты MicroSD является очень интересным дополнением к плате: он может использоваться для хранения показаний, если вы хотите создать регистратор данных, или для хранения HTML-файла для обслуживания веб-страницы – как мы сделаем в этом проекте. Я считаю, что это лучший и более простой способ создания веб-сервера, требующего более сложных веб-страниц.
Назначение выводов платы расширения ESP32
В следующей таблице описано назначение выводов для каждого компонента на плате:
Компонент |
Назначение выводов ESP32 |
|---|---|
Кнопка |
GPIO 33 |
Подстроечный потенциометр |
GPIO 32 |
Фоторезистор (LDR) |
GPIO 4 |
DHT22 (вывод данных) |
GPIO 15 |
LED1 |
GPIO 27 |
LED2 |
GPIO 26 |
BMP180 |
SDA (GPIO 21); SCL (GPIO 22) |
Модуль SD-карты |
MOSI (GPIO 23); MISO (GPIO 19); CLK (GPIO 18); CS (GPIO 5) |
Свободные GPIO (клеммные колодки) |
GPIO14, GPIO13, GPIO12 |
Примечание: есть небольшая проблема с назначением выводов. В настоящее время библиотека Arduino WiFi использует GPIO 4, который подключён к LDR. Поэтому у вас, вероятно, возникнут проблемы с получением показаний от LDR при использовании библиотеки WiFi. Чтобы это исправить, вы можете припаять провод от LDR к другому свободному GPIO (который поддерживает АЦП).
Тестирование схемы на макетной плате
Перед разработкой платы расширения я собрал схему на макетной плате. Если вы не хотите делать печатную плату, вы всё равно можете следовать этому проекту, собрав схему на макетной плате.
Необходимые компоненты
Для сборки схемы на макетной плате вам понадобятся следующие компоненты:
Вы можете использовать ссылки выше или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!
Схема
Собрав все необходимые компоненты, вы можете собрать схему, следуя приведённой ниже схеме:
Важно: если вы используете другую плату, вам необходимо перепроверить распиновку.
Вот принципиальная схема:
Разработка печатной платы
Убедившись, что схема работает правильно, я разработал печатную плату в KiCad. KiCad – это программное обеспечение с открытым исходным кодом для проектирования печатных плат.
Я не буду подробно описывать процесс разработки печатной платы, но предоставляю все файлы, если вы хотите модифицировать плату для себя. Нажмите здесь, чтобы скачать файлы проекта KiCad.
Заказ печатных плат
Вам не нужно знать, как проектировать печатную плату, чтобы заказать её. Вам просто нужно:
Скачать Gerber-файлы, нажмите здесь, чтобы скачать .zip файл.
Перейдите на JLCPCB.com, нажмите кнопку «QUOTE NOW» и загрузите только что скачанный .zip файл.
Вы увидите сообщение об успешной загрузке внизу. Затем вы можете использовать ссылку «Gerber Viewer» в правом нижнем углу, чтобы проверить, всё ли прошло как ожидалось. Вы можете просматривать верхнюю и нижнюю стороны печатной платы. Вы можете показывать или скрывать маску пайки, шелкографию, медные слои и т.д.
С настройками по умолчанию вы можете заказать 10 печатных плат всего за $2. Однако, если вы хотите выбрать другие настройки, например другой цвет печатной платы, это будет стоить на несколько долларов дороже.
Когда вы довольны своим заказом, нажмите кнопку «SAVE TO CART», чтобы завершить заказ.
Мои печатные платы были изготовлены за 1 день и прибыли через 5 рабочих дней при использовании доставки DHL.
Распаковка
Через неделю я получил свои печатные платы в офис. Всё было хорошо упаковано, и я также получил ручку от JLCPCB.
При ближайшем рассмотрении печатных плат должен сказать, что я действительно впечатлён качеством. Не думаю, что можно получить лучший сервис печатных плат за такую цену.
Пайка компонентов
Следующим шагом была пайка компонентов на печатную плату. Я использовал SMD-светодиоды и SMD-резисторы. Я знаю, что паять SMD-компоненты немного сложно, но они позволяют значительно сэкономить место на печатной плате. Я припаял штыревые разъёмы для подключения ESP32 и датчиков. Таким образом, я могу легко заменить датчики при необходимости.
Вот список всех компонентов, которые нужно припаять на печатную плату:
ESP32 DOIT DEVKIT V1 Board (версия с 30 GPIO) – вы можете приобрести эту плату на Banggood или на eBay
На следующем рисунке показано, как выглядит печатная плата после пайки всех компонентов.
Подготовка платы ESP32 в Arduino IDE
Чтобы загрузить код на ESP32 с помощью Arduino IDE, вам необходимо установить дополнение для Arduino IDE, которое позволяет программировать ESP32 с использованием Arduino IDE и его языка программирования. Следуйте следующему руководству для подготовки вашей Arduino IDE:
**Инструкции для Windows** – Установка платы ESP32 в Arduino IDE
**Инструкции для Mac и Linux** – Установка платы ESP32 в Arduino IDE
Вам также необходимо установить следующие библиотеки:
Код
Следующий шаг – написание кода для считывания показаний датчиков и создания веб-сервера. Код для этого проекта разделён на две части:
Код в Arduino IDE для считывания показаний датчиков и размещения веб-сервера
HTML-файл для создания веб-страницы. Этот HTML-файл должен быть сохранён на карте MicroSD.
Скопируйте приведённый код в Arduino IDE. Код для этого проекта довольно длинный, но его достаточно легко понять. Я также добавил различные комментарии по всему коду. Пока не загружайте код.
/*
* Rui Santos
* Complete Project Details https://randomnerdtutorials.com
*/
// Load required libraries
#include <WiFi.h>
#include "SD.h"
#include "DHT.h"
#include <Wire.h>
#include <Adafruit_BMP085.h>
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// uncomment one of the lines below for whatever DHT sensor type you're using
//#define DHTTYPE DHT11 // DHT 11
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
// GPIO the DHT is connected to
const int DHTPin = 15;
//intialize DHT sensor
DHT dht(DHTPin, DHTTYPE);
// create a bmp object
Adafruit_BMP085 bmp;
// Web page file stored on the SD card
File webFile;
// Set potentiometer GPIO
const int potPin = 32;
// IMPORTANT: At the moment, GPIO 4 doesn't work as an ADC when using the Wi-Fi library
// This is a limitation of this shield, but you can use another GPIO to get the LDR readings
const int LDRPin = 4;
// variables to store temperature and humidity
float tempC;
float tempF;
float humi;
// Variable to store the HTTP request
String header;
// Set web server port number to 80
WiFiServer server(80);
void setup(){
// initialize serial port
Serial.begin(115200);
// initialize DHT sensor
dht.begin();
// initialize BMP180 sensor
if (!bmp.begin()){
Serial.println("Could not find BMP180 or BMP085 sensor");
while (1) {}
}
// initialize SD card
if(!SD.begin()){
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD card attached");
return;
}
// initialize SD card
Serial.println("Initializing SD card...");
if (!SD.begin()) {
Serial.println("ERROR - SD card initialization failed!");
return; // init failed
}
// Connect to Wi-Fi network with SSID and password
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Print local IP address and start web server
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // if new client connects
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) { // client data available to read
char c = client.read(); // read 1 byte (character) from client
header += c;
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
// Send XML file or Web page
// If client already on the web page, browser requests with AJAX the latest
// sensor readings (ESP32 sends the XML file)
if (header.indexOf("update_readings") >= 0) {
// send rest of HTTP header
client.println("Content-Type: text/xml");
client.println("Connection: keep-alive");
client.println();
// Send XML file with sensor readings
sendXMLFile(client);
}
// When the client connects for the first time, send it the index.html file
// stored in the microSD card
else {
// send rest of HTTP header
client.println("Content-Type: text/html");
client.println("Connection: keep-alive");
client.println();
// send web page stored in microSD card
webFile = SD.open("/index.html");
if (webFile) {
while(webFile.available()) {
// send web page to client
client.write(webFile.read());
}
webFile.close();
}
}
break;
}
// every line of text received from the client ends with \r\n
if (c == '\n') {
// last character on line of received text
// starting new line with next character read
currentLineIsBlank = true;
}
else if (c != '\r') {
// a text character was received from client
currentLineIsBlank = false;
}
} // end if (client.available())
} // end while (client.connected())
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
} // end if (client)
}
// Send XML file with the latest sensor readings
void sendXMLFile(WiFiClient cl){
// Read DHT sensor and update variables
readDHT();
// Prepare XML file
cl.print("<?xml version = \"1.0\" ?>");
cl.print("<inputs>");
cl.print("<reading>");
cl.print(tempC);
cl.println("</reading>");
cl.print("<reading>");
cl.print(tempF);
cl.println("</reading>");
cl.print("<reading>");
cl.print(humi);
cl.println("</reading>");
float currentTemperatureC = bmp.readTemperature();
cl.print("<reading>");
cl.print(currentTemperatureC);
cl.println("</reading>");
float currentTemperatureF = (9.0/5.0)*currentTemperatureC+32.0;
cl.print("<reading>");
cl.print(currentTemperatureF);
cl.println("</reading>");
cl.print("<reading>");
cl.print(bmp.readPressure());
cl.println("</reading>");
cl.print("<reading>");
cl.print(analogRead(potPin));
cl.println("</reading>");
// IMPORTANT: Read the note about GPIO 4 at the pin assignment
cl.print("<reading>");
cl.print(analogRead(LDRPin));
cl.println("</reading>");
cl.print("</inputs>");
}
void readDHT(){
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
humi = dht.readHumidity();
// Read temperature as Celsius (the default)
tempC = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
tempF = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again).
if (isnan(humi) || isnan(tempC) || isnan(tempF)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
/*Serial.print("Humidity: ");
Serial.print(humi);
Serial.print(" %\t Temperature: ");
Serial.print(tempC);
Serial.print(" *C ");
Serial.print(tempF);
Serial.println(" *F");*/
}
Перед загрузкой кода вам необходимо изменить следующие строки, добавив ваш SSID и пароль.
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Затем нажмите кнопку загрузки, чтобы загрузить скетч на ESP32. Убедитесь, что вы выбрали правильную плату и COM-порт.
Создайте новый файл в текстовом редакторе и скопируйте следующий код. Также вы можете нажать здесь, чтобы скачать файл index.html.
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Weather Station</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<script>
function DisplayCurrentTime() {
var date = new Date();
var hours = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
var minutes = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
var seconds = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
time = hours + ":" + minutes + ":" + seconds;
var currentTime = document.getElementById("currentTime");
currentTime.innerHTML = time;
};
function GetReadings() {
nocache = "&nocache";
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (this.status == 200) {
if (this.responseXML != null) {
// XML file received - contains sensor readings
var count;
var num_an = this.responseXML.getElementsByTagName('reading').length;
for (count = 0; count < num_an; count++) {
document.getElementsByClassName("reading")[count].innerHTML =
this.responseXML.getElementsByTagName('reading')[count].childNodes[0].nodeValue;
}
}
}
}
// Send HTTP GET request to get the latest sensor readings
request.open("GET", "?update_readings" + nocache, true);
request.send(null);
DisplayCurrentTime();
setTimeout('GetReadings()', 10000);
}
document.addEventListener('DOMContentLoaded', function() {
DisplayCurrentTime();
GetReadings();
}, false);
</script>
<style>
body {
text-align: center;
font-family: "Trebuchet MS", Arial;
}
table {
border-collapse: collapse;
width:60%;
margin-left:auto;
margin-right:auto;
}
th {
padding: 16px;
background-color: #0043af;
color: white;
}
tr {
border: 1px solid #ddd;
padding: 16px;
}
tr:hover {
background-color: #bcbcbc;
}
td {
border: none;
padding: 16px;
}
.sensor {
color:white;
font-weight: bold;
background-color: #bcbcbc;
padding: 8px;
}
</style>
</head>
<body>
<h1>ESP32 Weather Station</h1>
<h3>Last update: <span id="currentTime"></span></h3>
<table>
<tr>
<th>SENSOR</th>
<th>MEASUREMENT</th>
<th>VALUE</th>
</tr>
<tr>
<td><span class="sensor">DHT</span></td>
<td>Temp. Celsius</td>
<td><span class="reading">...</span> *C</td>
</tr>
<tr>
<td><span class="sensor">DHT</span></td>
<td>Temp. Fahrenheit</td>
<td><span class="reading">...</span> *F</td>
</tr>
<tr>
<td><span class="sensor">DHT</span></td>
<td>Humidity</td>
<td><span class="reading">...</span> %</td>
</tr>
<tr>
<td><span class="sensor">BMP180</span></td>
<td>Temp. Celsius</td>
<td><span class="reading">...</span> *C</td>
</tr>
<tr>
<td><span class="sensor">BMP180</span></td>
<td>Temp. Fahrenheit</td>
<td><span class="reading">...</span> *F</td>
</tr>
<tr>
<td><span class="sensor">BMP180</span></td>
<td>Pressure</td>
<td><span class="reading">...</span> Pa</td>
</tr>
<tr>
<td><span class="sensor">POT</span></td>
<td>Position</td>
<td><span class="reading">...</span>/4095</td>
</tr>
<tr>
<td><span class="sensor">LDR</span></td>
<td>Luminosity</td>
<td><span class="reading">...</span>/4095</td>
</tr>
</table>
</body>
</html>
Это HTML-код, и он создаст вашу веб-страницу. В этом файле вы можете изменить внешний вид вашей веб-страницы, заголовки, таблицу и т.д. ESP32 отправит этот HTML-текст в ваш браузер, когда вы сделаете HTTP-запрос по IP-адресу ESP32.
Сохраните файл как index.html. Скопируйте HTML-файл на вашу карту MicroSD и вставьте карту MicroSD в модуль SD-карты.
Теперь всё должно быть готово.
Тестирование веб-сервера метеостанции ESP32
Откройте монитор порта со скоростью 115200 бод и проверьте IP-адрес ESP32.
По завершении проекта у вас будет собственный веб-сервер метеостанции на ESP32, и всё оборудование будет компактно размещено на печатной плате.
Откройте браузер, введите IP-адрес, и вы увидите таблицу с последними показаниями датчиков. Веб-сервер отображает показания DHT22, BMP180, потенциометра и LDR. Показания обновляются каждые 10 секунд без необходимости перезагрузки веб-страницы.
Для обновления показаний без перезагрузки веб-страницы мы используем AJAX. Как вы можете прочитать здесь, AJAX – это мечта разработчика, потому что он может обновлять веб-страницу без её перезагрузки, запрашивать и получать данные с сервера после загрузки страницы и отправлять данные на сервер в фоновом режиме.
Дальнейшее развитие
Есть ещё возможности для улучшения этого проекта: вы можете использовать дополнительные клеммы для подключения других датчиков или реле. Вы также можете запрограммировать ESP32 на запуск события, когда показание опускается ниже или поднимается выше определённого порога. Идея состоит в том, чтобы вы модифицировали предоставленный код для использования платы таким образом, который соответствует вашим конкретным потребностям.
Если вы хотите получить свою собственную универсальную плату метеостанции ESP32, вам нужно просто загрузить .zip файл с Gerber-файлами на сайт JLCPCB. Вы получите высококачественные печатные платы по очень разумной цене.
Заключение
Мы надеемся, что этот проект был для вас полезен. Если вам понравился этот проект, вам также могут понравиться другие связанные проекты:
Спасибо за чтение.