Cat hungry.png
Здравствуйте! Собираем деньги на перевод материалов по электронике(https://www.allaboutcircuits.com/education/). Реквизиты указаны здесь.

ESP32:Примеры/Веб-сервер на базе ESP32 – отображение данных, считанных датчиком BME280

Материал из Онлайн справочника
Перейти к: навигация, поиск

Перевод: Максим Кузьмин (Cubewriter)
Перевел 2753 статей для сайта.

Контакты:

Проверка/Оформление/Редактирование: Мякишев Е.А.


Ambox content.png Черновик


Веб-сервер на базе ESP32 – отображение данных, считанных датчиком BME280

В этом примере мы научимся создавать простой веб-сервер на базе ESP32, который будет показывать данные, считанные с датчик-модуля BME280. Этот модуль умеет считывать температуру, влажность и давление, что позволяет легко собрать с его помощью небольшую погодную станцию и отслеживать собираемые ею данные при помощи веб-сервера на базе ESP32. Именно этим мы сейчас и займемся.

Что такое датчик BME280

Датчик BME280 считывает температуру, влажность и давление. Давление меняется в зависимости от высоты, поэтому с помощью BME280 можно также измерять высоту. Существует несколько версий этого датчика, но мы будем использовать тот, что показан на фото ниже.

Bme280 cjmcu 1 1.jpg

BME280 может коммуницировать при помощи протоколов передачи данных SPI и I2C (есть версии этого модуля, которые коммуницируют только через I2C, и у них лишь 4 контакта).

При коммуникации с помощью SPI нужно использовать следующие контакты:

  • SCK – это тактовый контакт интерфейса SPI;
  • SDOMISO;
  • SDIMOSI;
  • CS – контакт для выбора чипа («chip select»);

При коммуникации с помощью I2C нужно использовать следующие контакты:

  • SCK – это также контакт SCL
  • SDI – это также контакт SDA

Что нужно сделать перед загрузкой кода

Устанавливаем библиотеку «Adafruit BME280»

Для чтения данных с BME280 нам понадобится библиотека «Adafruit BME280». Чтобы установить ее в IDE Arduino, проделайте следующее:

  1. Кликните тут, чтобы попасть на GitHub-страницу библиотеки, там нажмите на зеленую кнопку «Clone or download», а затем на «Download ZIP». Это должно загрузить на ваш компьютер ZIP-архив с библиотекой;
  2. Распакуйте его. У вас должна получиться папка «Adafruit-BME280-Library-master»;
  3. Переименуйте ее на «Adafruit_BME280_Library»;
  4. Переместите папку «Adafruit_BMPE280_Library» в папку «libraries», которая находится в папке, где установлена IDE Arduino;
  5. Наконец, снова откройте IDE Arduino;

Загрузить библиотеку «Adafruit BME280» в IDE Arduino можно и другим способом. Кликните в IDE Arduino на «Скетч» > «Подключить библиотеку» > «Управлять библиотеками...», введите в поле поиска «adafruit bme280», а затем установите библиотеку, которая появится в поисковой выдаче.

Устанавливаем библиотеку «Adafruit Sensor»

Чтобы воспользоваться библиотекой «Adafruit BME280», нам также понадобится установить в IDE Arduino библиотеку «Adafruit Sensor». Для этого проделайте следующее:

  1. Кликните тут, чтобы открыть GitHub-страницу библиотеки «Adafruit Sensor», там нажмите на зеленую кнопку «Clone or download», а затем на «Download ZIP». Это должно загрузить на ваш компьютер ZIP-архив с библиотекой.
  2. Распакуйте его. У вас должна получиться папка «Adafruit_Sensor-master».
  3. Переименуйте ее на «Adafruit_Sensor».
  4. Переместите папку «Adafruit_Sensor» в папку «libraries», которая находится в папке, где установлена IDE Arduino.
  5. Наконец, снова откройте 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>");

Показываем данные от датчика

Чтобы показать в таблице данные от датчика, нам нужно отправить их между соответствующими тегами <td> и </td>. К примеру, чтобы показать температуру, нужно отправить следующее:

  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. Мы также научились делать веб-сервер, показывающий таблицу с данными, считанными от датчика. Этот проект легко модифицируется под данные от любого другого датчика.

Необходимое оборудование

Схема

Ambox content.png На данной схеме используется плата ESP32 DEVKIT V1, если вы используете другую, сверьтесь с вашей распиновкой.


Мы воспользуемся протоколом I2C. Для этого BME280 нужно подключить к контактам SDA и SCL на плате ESP32 (см. схему ниже).

Esp32 bme280 1 e table 1.PNG

Код

  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. }

См.также

Внешние ссылки