ESP32:Примеры/Веб-сервер на базе ESP32 – отображение данных, считанных датчиком BME280
![]() |
Черновик |
Содержание
Веб-сервер на базе ESP32 – отображение данных, считанных датчиком BME280
В этом примере мы научимся создавать простой веб-сервер на базе ESP32, который будет показывать данные, считанные с датчик-модуля BME280. Этот модуль умеет считывать температуру, влажность и давление, что позволяет легко собрать с его помощью небольшую погодную станцию и отслеживать собираемые ею данные при помощи веб-сервера на базе ESP32. Именно этим мы сейчас и займемся.
Что такое датчик BME280
Датчик BME280 считывает температуру, влажность и давление. Давление меняется в зависимости от высоты, поэтому с помощью BME280 можно также измерять высоту. Существует несколько версий этого датчика, но мы будем использовать тот, что показан на фото ниже.
BME280 может коммуницировать при помощи протоколов передачи данных SPI и I2C (есть версии этого модуля, которые коммуницируют только через I2C, и у них лишь 4 контакта).
При коммуникации с помощью SPI нужно использовать следующие контакты:
- SCK – это тактовый контакт интерфейса SPI;
- SDO – MISO;
- SDI – MOSI;
- CS – контакт для выбора чипа («chip select»);
При коммуникации с помощью I2C нужно использовать следующие контакты:
Что нужно сделать перед загрузкой кода
Устанавливаем библиотеку «Adafruit BME280»
Для чтения данных с BME280 нам понадобится библиотека «Adafruit BME280». Чтобы установить ее в IDE Arduino, проделайте следующее:
- Кликните тут, чтобы попасть на GitHub-страницу библиотеки, там нажмите на зеленую кнопку «Clone or download», а затем на «Download ZIP». Это должно загрузить на ваш компьютер ZIP-архив с библиотекой;
- Распакуйте его. У вас должна получиться папка «Adafruit-BME280-Library-master»;
- Переименуйте ее на «Adafruit_BME280_Library»;
- Переместите папку «Adafruit_BMPE280_Library» в папку «libraries», которая находится в папке, где установлена IDE Arduino;
- Наконец, снова откройте IDE Arduino;
Загрузить библиотеку «Adafruit BME280» в IDE Arduino можно и другим способом. Кликните в IDE Arduino на «Скетч» > «Подключить библиотеку» > «Управлять библиотеками...», введите в поле поиска «adafruit bme280», а затем установите библиотеку, которая появится в поисковой выдаче.
Устанавливаем библиотеку «Adafruit Sensor»
Чтобы воспользоваться библиотекой «Adafruit BME280», нам также понадобится установить в IDE Arduino библиотеку «Adafruit Sensor». Для этого проделайте следующее:
- Кликните тут, чтобы открыть GitHub-страницу библиотеки «Adafruit Sensor», там нажмите на зеленую кнопку «Clone or download», а затем на «Download ZIP». Это должно загрузить на ваш компьютер ZIP-архив с библиотекой.
- Распакуйте его. У вас должна получиться папка «Adafruit_Sensor-master».
- Переименуйте ее на «Adafruit_Sensor».
- Переместите папку «Adafruit_Sensor» в папку «libraries», которая находится в папке, где установлена IDE Arduino.
- Наконец, снова откройте IDE Arduino.
Задаем учетные данные сети
Строчки кода ниже нужно отредактировать, вписав в них данные своей сети: SSID и пароль. Найти эти строчки можно по соответствующему комментарию:
// здесь пишем учетные данные своей сети:
const char* ssid = "";
const char* password = "";
Как работает этот код
Вначале мы подключаем библиотеку «WiFi» и другие библиотеки, необходимые для считывания данных с датчика BME280:
#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>
Если вы собираетесь использовать протокол SPI, вам нужно убрать комментарии у этих строчек. Но будем использовать I2C, поэтому знаки комментария у этого фрагмента оставим.
/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/
Примечание: При использовании SPI вам нужно будет задать правильные GPIO-контакты ESP32. Вы можете использовать либо шину HSPI, либо шину VSPI (подробнее смотрите в таблице ниже).
SPI | MOSI | MISO | CLK | CS |
---|---|---|---|---|
HSPI | GPIO13 | GPIO12 | GPIO14 | GPIO15 |
VSPI | GPIO23 | GPIO19 | GPIO18 | GPIO5 |
Далее в коде стоит строчка с переменной «SEALEVELPRESSURE_HPA».
#define SEALEVELPRESSURE_HPA (1013.25)
В ней будет храниться давление на уровне моря в гектопаскалях (эту единицу измерения вместе с барами используется для измерения давления). Это значение необходимо для расчета высоты при помощи давления – мы будем сравнить текущее давление с давлением на уровне моря и таким образом высчитывать приблизительную высоту. В строчке выше задано значение, которое подходит для определенного региона, поэтому для получения более точных результатов вам нужно заменить его на давление на уровне моря, корректное для вашего региона.
В следующей строчке создаем объект класса «Adafruit_BME280» под названием «bme». С ее помощью скетч будет по умолчанию коммуницировать с датчиком при помощи протокола I2C:
Adafruit_BME280 bme; // I2C
Если вы предпочитаете использовать SPI, вам нужно закомментировать строчку выше, а также раскомментировать одну из строчек ниже (в зависимости от того, какой SPI вы используете – аппаратным или программным).
// аппаратный SPI:
//Adafruit_BME280 bme(BME_CS);
// программный SPI:
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
Опять же, в следующих строчках внутри двойных кавычек вставляем SSID и пароль для своей сети:
const char* ssid = "";
const char* password = "";
Затем создаем веб-сервер на порте «80»:
WiFiServer server(80);
Строчка ниже создает переменную для хранения заголовка HTTP-запроса:
String header;
setup()
В setup() запускаем последовательную коммуникацию на скорости 115200 бод (для отладки):
Serial.begin(115200);
Проверяем, инициализировался ли датчик BME280:
1 if (!bme.begin(0x76)) {
2 Serial.println("Could not find a valid BME280 sensor, check wiring!");
3 // "Невозможно найти корректный датчик BME280, проверьте подключение!"
4 while (1);
5 }
Фрагмент ниже запускает WiFi-коммуникацию при помощи функции WiFi.begin(ssid, password), потом ждет, пока соединение наладится, и печатает в монитор порта IP-адрес ESP32.
1 // подключаемся к WiFi-сети при помощи SSID и пароля:
2 Serial.print("Connecting to "); // "Подключение к"
3 Serial.println(ssid);
4 WiFi.begin(ssid, password);
5 while (WiFi.status() != WL_CONNECTED) {
6 delay(500);
7 Serial.print(".");
8 }
9 // печатаем локальный IP-адрес и запускаем веб-сервер:
10 Serial.println("");
11 Serial.println("WiFi connected."); // "WiFi подключен."
12 Serial.println("IP address: "); // "IP-адрес: "
13 Serial.println(WiFi.localIP());
14 server.begin();
loop()
В блоке loop() программируем, что произойдет, когда новый клиент установит соединение с веб-сервером. ESP32 всегда прослушивает входящих клиентов при помощи этой строчки:
// начинаем прослушивать входящих клиентов:
WiFiClient client = server.available();
Получив запрос от клиента, сохраняем входящие данные. Далее идет цикл while(), который будет работать, пока клиент будет оставаться подключенным к серверу. Не рекомендуем менять фрагмент кода ниже (но если вы хорошо разбираетесь в том, как это все работает, то вперед):
1 if (client) { // если подключился новый клиент,
2 Serial.println("New Client."); // печатаем сообщение
3 // «Новый клиент.»
4 // в мониторе порта;
5 String currentLine = ""; // создаем строку для хранения
6 // входящих данных от клиента;
7 while (client.connected()) { // цикл while() будет работать
8 // все то время, пока клиент
9 // будет подключен к серверу;
10 if (client.available()) { // если у клиента есть данные,
11 // которые можно прочесть,
12 char c = client.read(); // считываем байт, а затем
13 Serial.write(c); // печатаем его в мониторе порта
14 header += c;
15 if (c == '\n') { // если этим байтом является
16 // символ новой строки
17 // если мы получим два символа новой строки подряд,
18 // то это значит, что текущая строчка пуста;
19 // это конец HTTP-запроса клиента,
20 // а значит – пора отправлять ответ:
21 if (currentLine.length() == 0) {
22 // HTTP-заголовки всегда начинаются
23 // с кода ответа (например, «HTTP/1.1 200 OK»)
24 // и информации о типе контента
25 // (чтобы клиент понимал, что получает);
26 // в конце пишем пустую строчку:
27 client.println("HTTP/1.1 200 OK");
28 client.println("Content-type:text/html");
29 client.println("Connection: close");
30 // "Соединение: отключено"
31 client.println();
Показываем веб-страницу
Далее отправляем ответ клиенту, содержащий HTML-код для создания веб-страницы.
Веб-страница отправляется клиенту при помощи метода client.println(). Параметром у этой функции задается то, что нужно отправить клиенту.
Фрагмент кода ниже отправляет веб-страницу, показывающую данные от датчика в таблице.
1 // показываем веб-страницу с помощью этого HTML-кода:
2 client.println("<!DOCTYPE html><html>");
3 client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
4 client.println("<link rel=\"icon\" href=\"data:,\">");
5 // задаем CSS-стили для таблицы:
6 client.println("<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial;}");
7 client.println("table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }");
8 client.println("th { padding: 12px; background-color: #0043af; color: white; }");
9 client.println("tr { border: 1px solid #ddd; padding: 12px; }");
10 client.println("tr:hover { background-color: #bcbcbc; }");
11 client.println("td { border: none; padding: 12px; }");
12 client.println(".sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px; }");
13
14 // заголовок веб-страницы:
15 client.println("</style></head><body><h1>ESP32 with BME280</h1>");
16 client.println("<table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>");
17 client.println("<tr><td>Temp. Celsius</td><td><span class=\"sensor\">");
18 client.println(bme.readTemperature());
19 client.println(" *C</span></td></tr>");
20 client.println("<tr><td>Temp. Fahrenheit</td><td><span class=\"sensor\">");
21 client.println(1.8 * bme.readTemperature() + 32);
22 client.println(" *F</span></td></tr>");
23 client.println("<tr><td>Pressure</td><td><span class=\"sensor\">");
24 client.println(bme.readPressure() / 100.0F);
25 client.println(" hPa</span></td></tr>");
26 client.println("<tr><td>Approx. Altitude</td><td><span class=\"sensor\">");
27 client.println(bme.readAltitude(SEALEVELPRESSURE_HPA));
28 client.println(" m</span></td></tr>");
29 client.println("<tr><td>Humidity</td><td><span class=\"sensor\">");
30 client.println(bme.readHumidity());
31 client.println(" %</span></td></tr>");
32 client.println("</body></html>");
Показываем данные от датчика
Чтобы показать в таблице данные от датчика, нам нужно отправить их между соответствующими тегами и . К примеру, чтобы показать температуру, нужно отправить следующее:
1 client.println("<tr><td>Temp. Celsius</td><td><span class=\"sensor\">");
2 client.println(bme.readTemperature());
3 client.println(" *C</span></td></tr>");
Примечание: Тег нужен, чтобы задать CSS-стиль для строго определенной части текста. В нашем случае используется, чтобы присвоить данным от датчика класс «sensor».
По умолчанию таблица показывает температуру в градусах Цельсия и Фаренгейта. Если вы хотите, чтобы температура показывалась только в Фаренгейтах, закомментируйте три строчки ниже:
1 /*client.println("<tr><td>Temp. Celsius</td><td><span class=\"sensor\">");
2 client.println(bme.readTemperature());
3 client.println(" *C</span></td></tr>");*/
Отключение соединения
Наконец, выслав ответ, очищаем переменную «header» и завершаем соединение с клиентом при помощи функции client.stop().
1 // очищаем переменную «header»:
2 header = "";
3 // отключаем соединение:
4 client.stop();
Итак, в этом примере мы научились, как считывать температуру, влажность, давление и приблизительную высоту при помощи датчика BME280. Мы также научились делать веб-сервер, показывающий таблицу с данными, считанными от датчика. Этот проект легко модифицируется под данные от любого другого датчика.
Необходимое оборудование
- Плата ESP32 - 1шт.;
- Датчик BME280 - 1шт.;
- Провода перемычки;
- Макетная плата - 1шт.;
Схема
![]() |
На данной схеме используется плата ESP32 DEVKIT V1, если вы используете другую, сверьтесь с вашей распиновкой. |
Мы воспользуемся протоколом I2C. Для этого BME280 нужно подключить к контактам SDA и SCL на плате ESP32 (см. схему ниже).
Код
1 /*********
2 Руи Сантос
3 Более подробно об этом проекте на: http://randomnerdtutorials.com
4 *********/
5
6 // загружаем библиотеку для WiFi и другие библиотеки:
7 #include <WiFi.h>
8 #include <Wire.h>
9 #include <Adafruit_BME280.h>
10 #include <Adafruit_Sensor.h>
11
12 // при использовании SPI убираем комментарии у строчек ниже:
13 /*#include <SPI.h>
14 #define BME_SCK 18
15 #define BME_MISO 19
16 #define BME_MOSI 23
17 #define BME_CS 5*/
18
19 #define SEALEVELPRESSURE_HPA (1013.25)
20
21 Adafruit_BME280 bme; // I2C
22 // аппаратный SPI:
23 //Adafruit_BME280 bme(BME_CS);
24 // программный SPI:
25 //Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
26
27 // ставим здесь учетные данные своей сети:
28 const char* ssid = "";
29 const char* password = "";
30
31 // выставляем номер порта на «80»:
32 WiFiServer server(80);
33
34 // создаем переменную для хранения HTTP-запроса:
35 String header;
36
37 void setup() {
38 Serial.begin(115200);
39 bool status;
40
41 // настройки по умолчанию
42 // (вы также можете использовать объект библиотеки Wire):
43 //status = bme.begin();
44 if (!bme.begin(0x76)) {
45 Serial.println("Could not find a valid BME280 sensor, check wiring!");
46 // "Невозможно найти корректный датчик BME280,проверьте подключение!"
47 while (1);
48 }
49
50 // подключаемся к WiFi-сети при помощи SSID и пароля:
51 Serial.print("Connecting to "); // "Подключение к"
52 Serial.println(ssid);
53 WiFi.begin(ssid, password);
54 while (WiFi.status() != WL_CONNECTED) {
55 delay(500);
56 Serial.print(".");
57 }
58 // печатаем локальный IP-адрес и запускаем веб-сервер:
59 Serial.println("");
60 Serial.println("WiFi connected."); // "WiFi подключен."
61 Serial.println("IP address: "); // "IP-адрес: "
62 Serial.println(WiFi.localIP());
63 server.begin();
64 }
65
66 void loop(){
67 // начинаем прослушивать входящих клиентов:
68 WiFiClient client = server.available();
69
70 if (client) { // если подключился новый клиент,
71 Serial.println("New Client."); // печатаем сообщение
72 // «Новый клиент.»
73 // в мониторе порта;
74 String currentLine = ""; // создаем строку для хранения
75 // входящих данных от клиента;
76 while (client.connected()) { // цикл while() будет работать
77 // все то время, пока клиент
78 // будет подключен к серверу;
79 if (client.available()) { // если у клиента есть данные,
80 // которые можно прочесть,
81 char c = client.read(); // считываем байт, а затем
82 Serial.write(c); // печатаем его в мониторе порта
83 header += c;
84 if (c == '\n') { // если этим байтом является
85 // символ новой строки
86 // если мы получим два символа новой строки подряд,
87 // то это значит, что текущая строчка пуста;
88 // это конец HTTP-запроса клиента,
89 // а значит – пора отправлять ответ:
90 if (currentLine.length() == 0) {
91 // HTTP-заголовки всегда начинаются
92 // с кода ответа (например, «HTTP/1.1 200 OK»)
93 // и информации о типе контента
94 // (чтобы клиент понимал, что получает);
95 // в конце пишем пустую строчку:
96 client.println("HTTP/1.1 200 OK");
97 client.println("Content-type:text/html");
98 client.println("Connection: close");
99 // "Соединение: отключено"
100 client.println();
101
102 // показываем веб-страницу с помощью этого HTML-кода:
103 client.println("<!DOCTYPE html><html>");
104 client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
105 client.println("<link rel=\"icon\" href=\"data:,\">");
106 // задаем CSS-стили для таблицы:
107 client.println("<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial;}");
108 client.println("table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }");
109 client.println("th { padding: 12px; background-color: #0043af; color: white; }");
110 client.println("tr { border: 1px solid #ddd; padding: 12px; }");
111 client.println("tr:hover { background-color: #bcbcbc; }");
112 client.println("td { border: none; padding: 12px; }");
113 client.println(".sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px; }");
114
115 // заголовок веб-страницы:
116 client.println("</style></head><body><h1>ESP32 with BME280</h1>");
117 client.println("<table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>");
118 client.println("<tr><td>Temp. Celsius</td><td><span class=\"sensor\">");
119 client.println(bme.readTemperature());
120 client.println(" *C</span></td></tr>");
121 client.println("<tr><td>Temp. Fahrenheit</td><td><span class=\"sensor\">");
122 client.println(1.8 * bme.readTemperature() + 32);
123 client.println(" *F</span></td></tr>");
124 client.println("<tr><td>Pressure</td><td><span class=\"sensor\">");
125 client.println(bme.readPressure() / 100.0F);
126 client.println(" hPa</span></td></tr>");
127 client.println("<tr><td>Approx. Altitude</td><td><span class=\"sensor\">");
128 client.println(bme.readAltitude(SEALEVELPRESSURE_HPA));
129 client.println(" m</span></td></tr>");
130 client.println("<tr><td>Humidity</td><td><span class=\"sensor\">");
131 client.println(bme.readHumidity());
132 client.println(" %</span></td></tr>");
133 client.println("</body></html>");
134
135 // конец HTTP-ответа задается
136 // с помощью дополнительной пустой строки:
137 client.println();
138 // выходим из цикла while():
139 break;
140 } else { // если получили символ новой строки,
141 // очищаем переменную «currentLine»
142 currentLine = "";
143 }
144 } else if (c != '\r') { // если получили любые данные,
145 // кроме символа возврата каретки,
146 currentLine += c; // добавляем эти данные
147 // в конец строки «currentLine»
148 }
149 }
150 }
151 // очищаем переменную «header»:
152 header = "";
153 // отключаем соединение:
154 client.stop();
155 Serial.println("Client disconnected.");
156 // "Клиент отключился.")
157 Serial.println("");
158 }
159 }
См.также
Внешние ссылки