ESP8266-Arduino网络编程实例-HightCharts实时图表显示BME280数据

HightCharts实时图表显示BME280数据

Highcharts 是一个纯基于 JavaScript 的图表库,旨在通过添加交互式图表功能来增强 Web 应用程序。 Highcharts 提供了各种各样的图表。 例如折线图、样条图、面积图、条形图、饼图等。

本文将演示如何在ESP8266 Web服务器中使用Hightcharts来实时显示BME280环境传感器的气压、温度、湿度数据。

在本实例中,页面数据通过SPIFFS来储存取,关于SPIFFS,请参考前面文章:

本实例的使用异步Web服务器,请参考前面文章:

本实例使用到的BME280传感器,其驱动在前面的文章是做详细的介绍,请参考:

本实例分两部分实现:

  • ESP8266设备驱动及Web服务器部分
  • Web页面及数据请求逻辑部分

1、硬件准备

  • ESP8266 NodeMCU开发板一块
  • 数据线一条
  • BME280传感器模块一个
  • 杜邦线若干

本次接线图如下:

在这里插入图片描述

BME280 ESP8266
SCK (SCL Pin) GPIO 5
SDI (SDA pin) GPIO 4

2、软件准备

  • Arduino IDE或VSCode + PlatformIO

在前面的文章中,对如何搭建ESP8266开发环境做了详细的介绍,请参考:

ESP8266 NodeMCU的引脚介绍在前面的文章中做了详细的介绍,请参考:

3、代码实现

3.1 ESP8266异步Web服务器及BME驱动实现

驱动使用到如下开源库:

1)导入依赖库头文件

1
2
3
4
5
6
7
8
9
10
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <Hash.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>
#include <Wire.h>
#include <Adafruit\_Sensor.h>
#include <Adafruit\_BME280.h>

实例使用到的开源库如下:

2)设备创建及WiFi连接信息

1
2
3
4
5
Adafruit_BME280 bme;

const char\* ssid = "\*\*\*\*";
const char\* ssid_pwd = "\*\*\*\*";

3)创建Web服务器

1
2
AsyncWebServer server(80);

4)定义BME280温度、湿度、气压数据读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 读取温度
String readBME280Temperature() {
// 读取摄氏度温度值
float t = bme.readTemperature();
// 转换成华氏温度值
//t = 1.8 \* t + 32;
if (isnan(t)) {
Serial.println("Failed to read from BME280 sensor!");
return "";
}
else {
Serial.println(t);
return String(t);
}
}

// 读取湿度
String readBME280Humidity() {
float h = bme.readHumidity();
if (isnan(h)) {
Serial.println("Failed to read from BME280 sensor!");
return "";
}
else {
Serial.println(h);
return String(h);
}
}

// 读取气压
String readBME280Pressure() {
float p = bme.readPressure() / 100.0F;
if (isnan(p)) {
Serial.println("Failed to read from BME280 sensor!");
return "";
}
else {
Serial.println(p);
return String(p);
}
}

5)在setup函数中初始化串口

1
2
3
// 初始化串口
Serial.begin(115200);

6)在setup函数中初始化BME280

1
2
3
4
5
6
7
// 初始化BME280
status = bme.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}

7)在setup函数中初始化SPIFFS

1
2
3
4
5
6
7
// 初始化SPIFFS
if(!SPIFFS.begin()){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}


8)在setup函数中连接WiFi

1
2
3
4
5
6
7
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
Serial.println(WiFi.localIP());

9)在setup函数中注册Web服务器请求路由并启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 
server.on("/", HTTP_GET, [](AsyncWebServerRequest \*request){
request->send(SPIFFS, "/index.html");
});

// highcharts文件
server.on("/highcharts.js", HTTP_GET, [](AsyncWebServerRequest \*request){
request->send(SPIFFS, "/highcharts.js");
});

// 温度请求
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest \*request){
request->send\_P(200, "text/plain", readBME280Temperature().c\_str());
});

// 湿度请求
server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest \*request){
request->send\_P(200, "text/plain", readBME280Humidity().c\_str());
});

// 气压请求
server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest \*request){
request->send\_P(200, "text/plain", readBME280Pressure().c\_str());
});

最后,在setup函数中启动Web服务器

1
2
server.begin();

3.2 Web页面实现

本次应用的Web页面由两个文件组成:

1)index.html构建Web页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/highcharts.js"> </script>
<style>
body {
min-width: 310px;
max-width: 800px;
height: 400px;
margin: 0 auto;
}
h2 {
font-family: Arial;
font-size: 2.5rem;
text-align: center;
}
</style>
</head>
<body>
<h2>ESP Weather Station</h2>
<div id="chart-temperature" class="container"></div>
<div id="chart-humidity" class="container"></div>
<div id="chart-pressure" class="container"></div>
</body>
<script>
var chartT = new Highcharts.Chart({
chart:{ renderTo : 'chart-temperature' },
title: { text: 'BME280 Temperature' },
series: [{
showInLegend: false,
data: []
}],
plotOptions: {
line: { animation: false,
dataLabels: { enabled: true }
},
series: { color: '#059e8a' }
},
xAxis: { type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: { text: 'Temperature (Celsius)' }
//title: { text: 'Temperature (Fahrenheit)' }
},
credits: { enabled: false }
});
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var x = (new Date()).getTime(),
y = parseFloat(this.responseText);
//console.log(this.responseText);
if(chartT.series[0].data.length > 40) {
chartT.series[0].addPoint([x, y], true, true, true);
} else {
chartT.series[0].addPoint([x, y], true, false, true);
}
}
};
xhttp.open("GET", "/temperature", true);
xhttp.send();
}, 2000 ) ;

var chartH = new Highcharts.Chart({
chart:{ renderTo:'chart-humidity' },
title: { text: 'BME280 Humidity' },
series: [{
showInLegend: false,
data: []
}],
plotOptions: {
line: { animation: false,
dataLabels: { enabled: true }
}
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: { text: 'Humidity (%)' }
},
credits: { enabled: false }
});
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var x = (new Date()).getTime(),
y = parseFloat(this.responseText);
//console.log(this.responseText);
if(chartH.series[0].data.length > 40) {
chartH.series[0].addPoint([x, y], true, true, true);
} else {
chartH.series[0].addPoint([x, y], true, false, true);
}
}
};
xhttp.open("GET", "/humidity", true);
xhttp.send();
}, 2000 ) ;

var chartP = new Highcharts.Chart({
chart:{ renderTo:'chart-pressure' },
title: { text: 'BME280 Pressure' },
series: [{
showInLegend: false,
data: []
}],
plotOptions: {
line: { animation: false,
dataLabels: { enabled: true }
},
series: { color: '#18009c' }
},
xAxis: {
type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: { text: 'Pressure (hPa)' }
},
credits: { enabled: false }
});
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var x = (new Date()).getTime(),
y = parseFloat(this.responseText);
//console.log(this.responseText);
if(chartP.series[0].data.length > 40) {
chartP.series[0].addPoint([x, y], true, true, true);
} else {
chartP.series[0].addPoint([x, y], true, false, true);
}
}
};
xhttp.open("GET", "/pressure", true);
xhttp.send();
}, 2000 ) ;
</script>
</html>

首先,包含hightcharts文件

1
2
<script src="/highcharts.js"> </script>

接着,创建三个div标签用来显示hightcharts图表

1
2
3
4
<div id="chart-temperature" class="container"></div>
<div id="chart-humidity" class="container"></div>
<div id="chart-pressure" class="container"></div>

接着,创建Chart对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var chartT = new Highcharts.Chart({
chart:{ renderTo : 'chart-temperature' },
title: { text: 'BME280 Temperature' },
series: [{
showInLegend: false,
data: []
}],
plotOptions: {
line: { animation: false,
dataLabels: { enabled: true }
},
series: { color: '#059e8a' }
},
xAxis: { type: 'datetime',
dateTimeLabelFormats: { second: '%H:%M:%S' }
},
yAxis: {
title: { text: 'Temperature (Celsius)' }
//title: { text: 'Temperature (Fahrenheit)' }
},
credits: { enabled: false }
});

最后通过定时器定时向Web服务器发起数据请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var x = (new Date()).getTime(),
y = parseFloat(this.responseText);
//console.log(this.responseText);
if(chartT.series[0].data.length > 40) {
chartT.series[0].addPoint([x, y], true, true, true);
} else {
chartT.series[0].addPoint([x, y], true, false, true);
}
}
};
xhttp.open("GET", "/temperature", true);
xhttp.send();
}, 2000 ) ;

对于湿度和气压数请求及据显示的步骤也是一样的。
运行结果:
在这里插入图片描述

文章来源: https://iotsmart.blog.csdn.net/article/details/127424504