在本教程中,您将学习如何使用 ESP32 创建 Web 服务器以显示来自 BME280 传感器模块的读数。BME280 传感器测量温度、湿度和压力。因此,您可以轻松构建一个小型紧凑型气象站,并使用 ESP32 网络服务器监控测量结果。这就是我们在这个项目中要做的。
在继续本教程之前,您应该在 Arduino IDE 中安装 ESP32 插件。
目录
所需零件
要学习本教程,您需要以下部分:
- ESP32 DOIT DEVKIT V1 板
- BME280传感器模块
- 面包板
- 连接线
BME280 传感器模块简介
BME280 传感器模块读取温度、湿度和压力。由于压力随海拔高度而变化,因此您还可以估算海拔高度。该传感器模块有多个版本,但我们使用的是下图所示的版本。
传感器可以使用 SPI 或 I2C 通信协议进行通信(该传感器的某些模块仅与 I2C 通信,这些模块仅带有四个引脚)。
要使用 SPI 通信协议,请使用以下引脚:
- SCK——这是 SPI 时钟引脚
- SDO——MISO
- SDI – MOSI
- CS——片选
要使用 I2C 通信协议,传感器使用以下引脚:
- SCK——这也是 SCL 引脚
- SDI——这也是 SDA 引脚
原理图
我们将使用 I2C 通信与 BME280 传感器模块。为此,将传感器连接到 ESP32SDA和SCL引脚,如下图所示。
(该原理图使用带有 36 个 GPIO 的 ESP32 DEVKIT V1 模块版本——如果您使用的是其它型号,请检查您正在使用的电路板的引出线。)
安装 BME280 库
要从 BME280 传感器模块获取读数,我们将使用Adafruit_BME280 库。可按照以下步骤在您的 Arduino IDE 中安装该库:
打开您的 Arduino IDE 并转到 Sketch > Include Library > Manage Libraries。库管理器应打开。
在搜索框中搜索“ adafruit bme280 ”并安装库。
安装 Adafruit_Sensor 库
要使用 BME280 库,您还需要安装Adafruit_Sensor 库。可按照以下步骤在您的 Arduino IDE 中安装该库:
转到Sketch > Include Library > Manage Libraries ,然后在搜索框中键入“ Adafruit Unified Sensor ”。一直向下滚动以找到库并安装它。
安装库后,重新启动 Arduino IDE。
读取温度、湿度和压力
为了熟悉 BME280 传感器,我们将使用库中的示例程序来了解如何读取温度、湿度和压力。
安装 BME280 库和 Adafruit_Sensor 库后,打开 Arduino IDE 并转到“文件” > “示例” > “Adafruit BME280 库” > “bme280 test”。
/*********
https://www.qutaojiao.com
*********/
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
unsigned long delayTime;
void setup() {
Serial.begin(9600);
Serial.println(F("BME280 test"));
bool status;
// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
Serial.println("-- Default Test --");
delayTime = 1000;
Serial.println();
}
void loop() {
printValues();
delay(delayTime);
}
void printValues() {
Serial.print("Temperature = ");
Serial.print(bme.readTemperature());
Serial.println(" *C");
// Convert temperature to Fahrenheit
/*Serial.print("Temperature = ");
Serial.print(1.8 * bme.readTemperature() + 32);
Serial.println(" *F");*/
Serial.print("Pressure = ");
Serial.print(bme.readPressure() / 100.0F);
Serial.println(" hPa");
Serial.print("Approx. Altitude = ");
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(" m");
Serial.print("Humidity = ");
Serial.print(bme.readHumidity());
Serial.println(" %");
Serial.println();
}
库
代码首先包含所需的库
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
SPI通讯
由于我们将使用 I2C 通信,您可以注释以下行:
/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/
注意:如果您使用 SPI 通信,则需要更改引脚定义以使用 ESP32 GPIO。对于 ESP32 上的 SPI 通信,您可以使用 HSPI 或 VSPI 引脚,如下表所示:
SEALEVELPRESSURE_HPA
一个变量叫做SEALEVELPRESSURE_HPA被定义。
#define SEALEVELPRESSURE_HPA (1013.25)
这节省了海平面的压力,单位为百帕(相当于毫巴)。该变量用于通过将给定压力与海平面压力进行比较来估算给定压力的高度。此示例使用默认值,但为了获得更准确的结果,请将该值替换为您所在位置的当前海平面压力。
I2C接口
本示例默认使用 I2C 通信。如您所见,您只需要创建一个Adafruit_BME280称为对象bme.
Adafruit_BME280 bme; // I2C
如果您想使用 SPI,则需要注释前一行并取消注释以下行之一,具体取决于您使用的是硬件 SPI 还是软件 SPI。
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI
Setup()
在函数 setup() 里面你开始串行通信
Serial.begin(9600);
并且传感器被初始化:
status = bme.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}
printValues()
在函数 loop() 里面, 这printValues()函数从 BME280 读取值并在串行监视器中打印结果。
void loop() { printValues(); delay(delayTime); }
读取温度、湿度、压力和估计高度很简单如下:
- bme.readTemperature() 方法– 读取摄氏温度;
- bme.readHumidity()– 读取绝对湿度;
- bme.readPressure() 方法– 以 hPa 为单位读取压力(百帕斯卡 = 毫巴);
- bme.readAltitude(SEALEVELPRESSURE_HPA)– 根据海平面压力估算高度(以米为单位)。
将代码上传到您的 ESP32,并以 9600 的波特率打开串行监视器。您应该会看到串行监视器上显示的读数。
在 HTML 中创建表格
正如您在帖子开头看到的那样,我们在网页中显示读数,其中包含由 ESP32 提供的表格。因此,我们需要编写 HTML 文本来构建表格。
要在 HTML 中创建表格,您可以使用<table>和</table>标记。
要创建行,您可以使用<tr>和</tr>标记。表格标题使用<th>和</th>标签定义,每个表格单元格使用<td>和</td>标签定义。
要为我们的读数创建表格,请使用以下 html 文本:
<table>
<tr>
<th>MEASUREMENT</th>
<th>VALUE</th>
</tr>
<tr>
<td>Temp. Celsius</td>
<td>--- *C</td>
</tr>
<tr>
<td>Temp. Fahrenheit</td>
<td>--- *F</td>
</tr>
<tr>
<td>Pressure</td>
<td>--- hPa</td>
</tr>
<tr>
<td>Approx. Altitude</td>
<td>--- meters</td></tr>
<tr>
<td>Humidity</td>
<td>--- %</td>
</tr>
</table>
我们使用一个名为 MEASUREMENT 的单元格和另一个名为 VALUE 的单元格创建表头。然后,我们使用<tr>和</tr>标签创建六行来显示每个读数。在每一行中,我们使用<td>和</td>标签创建两个单元格,一个带有测量名称,另一个用于保存测量值。然后应将三个破折号“—”替换为 BME 传感器的实际测量值。
您可以将此文本保存为table.html,将文件拖到您的浏览器中,然后查看您拥有的内容。前面的 HTML 文本创建了下表。
该表未应用任何样式。您可以使用 CSS 根据自己的喜好设置表格样式。您可能会发现此链接很有用:CSS 样式表。
创建网络服务器
现在您知道如何从传感器获取读数,以及如何构建表格来显示结果,是时候构建 Web 服务器了。如果您学习过其它 ESP32 教程,应该熟悉大部分代码。
将以下代码复制到您的 Arduino IDE。先不要上传它。首先,您需要修改 Wifi SSID 和密码:
修改以下行以在双引号之间包含您的 SSID 和密码。
const char* ssid = "";
const char* password = "";
然后,检查您是否选择了正确的开发板和 COM 端口,并将代码上传到您的 ESP32。上传后打开串口监视器,波特率为115200,复制ESP32的IP地址。
打开浏览器,粘贴 IP 地址,您应该会看到最新的传感器读数。要更新读数,您只需刷新网页即可。
如何运作
首先,您包括 WiFi 库和从 BME280 传感器读取所需的库。
// Load Wi-Fi library
#include <WiFi.h>
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <Adafruit_Sensor.h>
下一行定义了一个变量来保存海平面的压力。要获得更准确的高度估计,请用您所在位置的当前海平面压力替换该值。
#define SEALEVELPRESSURE_HPA (1013.25)
在以下行中,您创建一个名为 Adafruit_BME280 的对象bme默认情况下使用 I2C 与传感器建立通信。
Adafruit_BME280 bme; // I2C
如前所述,您需要在双引号内的以下行中插入您的 ssid 和密码。
const char* ssid = "";
const char* password = "";
然后,将 Web 服务器设置为端口 80。
// Set web server port number to 80
WiFiServer server(80);
以下行创建一个变量来存储 HTTP 请求的标头:
String header;
setup()
在函数 setup() 里面,我们以 115200 的波特率开始串行通信以进行调试。
Serial.begin(115200);
您检查 BME280 传感器是否已成功初始化。
if (!bme.begin(0x76)) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
以下几行开始 Wi-Fi 连接WiFi.begin(ssid, 密码), 等待连接成功并在串行监视器中打印 ESP IP 地址。
// 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();
loop()
在函数 loop() 里面,我们对新客户端与 Web 服务器建立连接时发生的情况进行编程。ESP 始终使用此行侦听传入的客户端:
WiFiClient client = server.available(); // Listen for incoming clients
当收到来自客户端的请求时,我们将保存传入的数据。只要客户端保持连接,后面的 while 循环就会运行。我们不建议更改代码的以下部分,除非您确切地知道自己在做什么。
if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == 'n') { // if the byte is a newline character
// 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 (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
显示 HTML 网页
您需要做的下一件事是使用 HTML 文本向客户端发送响应以构建网页。
使用此表达式将网页发送到客户端客户端.println(). 您应该输入要作为参数发送给客户端的内容。
以下代码片段发送网页以在表格中显示传感器读数。
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name="viewport" content="width=device-width, initial-scale=1">");
client.println("<link rel="icon" href="data:,">");
// CSS to style the on/off buttons
// Feel free to change the background-color and font-size attributes to fit your preferences
client.println("<style>body { text-align: center; font-family: "Trebuchet MS", Arial;}");
client.println("table { border-collapse: collapse; width:35%; margin-left:auto; margin-right:auto; }");
client.println("th { padding: 12px; background-color: #0043af; color: white; }");
client.println("tr { border: 1px solid #ddd; padding: 12px; }");
client.println("tr:hover { background-color: #bcbcbc; }");
client.println("td { border: none; padding: 12px; }");
client.println(".sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px; }");
// Web Page Heading
client.println("</style></head><body><h1>ESP32 with BME280</h1>");
client.println("<table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>");
client.println("<tr><td>Temp. Celsius</td><td><span class="sensor">");
client.println(bme.readTemperature());
client.println(" *C</span></td></tr>");
client.println("<tr><td>Temp. Fahrenheit</td><td><span class="sensor">");
client.println(1.8 * bme.readTemperature() + 32);
client.println(" *F</span></td></tr>");
client.println("<tr><td>Pressure</td><td><span class="sensor">");
client.println(bme.readPressure() / 100.0F);
client.println(" hPa</span></td></tr>");
client.println("<tr><td>Approx. Altitude</td><td><span class="sensor">");
client.println(bme.readAltitude(SEALEVELPRESSURE_HPA));
client.println(" m</span></td></tr>");
client.println("<tr><td>Humidity</td><td><span class="sensor">");
client.println(bme.readHumidity());
client.println(" %</span></td></tr>");
client.println("</body></html>");
完整的 HTML 网页:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
body {
text-align: center;
font-family: "Trebuchet MS", Arial;
}
table {
border-collapse: collapse;
width:35%;
margin-left:auto;
margin-right:auto;
}
th {
padding: 12px;
background-color: #0043af;
color: white;
}
tr {
border: 1px solid #ddd;
padding: 12px;
}
tr:hover {
background-color: #bcbcbc;
}
td {
border: none; padding: 12px;
}
.sensor {
color:white;
font-weight: bold;
background-color: #bcbcbc;
padding: 1px;
}
</style>
</head>
<body>
<h1>ESP32 with BME280</h1>
<table>
<tr>
<th>MEASUREMENT</th>
<th>VALUE</th>
</tr>
<tr>
<td>Temp. Celsius</td>
<td><span class="sensor">--- *C</span></td></tr>
<tr>
<td>Temp. Fahrenheit</td>
<td><span class="sensor">--- *F</span></td>
</tr>
<tr>
<td>Pressure</td>
<td><span class="sensor">--- hPa</span></td>
</tr>
<tr>
<td>Approx. Altitude</td>
<td><span class="sensor">--- m</span></td>
</tr>
<tr>
<td>Humidity</td>
<td><span class="sensor">--- %</span></td>
</tr>
</body>
</html>
显示传感器读数
要在表格上显示传感器读数,我们只需将它们发送到相应的 <td> 和 </td> 标签之间。例如显示温度:
client.println("<tr><td>Temp. Celsius</td><td><span class="sensor">");
client.println(bme.readTemperature());
client.println(" *C</span></td></tr>");
注意:<span> 标签可用于设置文本特定部分的样式。在这种情况下,我们使用 <span> 标签将传感器读数包含在一个名为“sensor”的类中。这对于使用 CSS 设置文本特定部分的样式很有用。
默认情况下,该表以摄氏度和华氏度显示温度读数。如果只想以华氏度显示温度,可以注释以下三行。
/*client.println("<tr><td>Temp. Celsius</td><td><span class="sensor">");
client.println(bme.readTemperature());
client.println(" *C</span></td></tr>");*/
关闭连接
最后,当响应结束时,我们清除标头变量,并停止与客户端的连接客户端停止().
// Clear the header variable
header = "";
// Close the connection
client.stop();
总结
总之,在本项目中,您学习了如何使用 BME280 传感器模块读取温度、湿度、压力和估计高度。您还学习了如何构建一个显示带有传感器读数的表格的 Web 服务器。您可以轻松修改此项目以显示来自任何其它传感器的数据。