你是否想过,如何用简单的硬件和代码,实时监测环境数据并上传到云端?
无论是农业、气象监测,还是智能家居,环境数据的采集和分析都至关重要。在这篇文章中,我将带你一步步实现一个基于 Arduino 和 ESP8266 的环境监测系统。从硬件连接到代码编写,再到通过 MQTT 协议 将数据上传到云端,整个过程清晰易懂,适合初学者和爱好者。让我们一起动手,打造一个属于自己的物联网环境监测系统吧!
1. 概述
目标
实现一个能够实时采集环境数据(如温度、湿度、光照、土壤湿度等)并通过 WiFi 上传到云端的系统。
核心组件
- Arduino R3:负责传感器数据采集。
- ESP8266:负责 WiFi 连接和数据上传。
- MQTT 协议:用于将数据发布到云端(EMQX 公共 Broker)。
适用场景
- 智能农业:监测土壤湿度、光照强度,实现精准灌溉。
- 气象监测:采集温度、湿度、气压等数据。
- 智能家居:监测室内环境参数,联动其他智能设备。
2. 准备阶段
好的!以下是优化后的 软件部分,直接跟随在你的内容后面:
软件
1. 安装 Arduino IDE 软件
- 下载并安装 Arduino IDE:
- 访问 Arduino 官方网站。
- 根据你的操作系统(Windows、macOS、Linux)下载最新版本的 Arduino IDE。
- 运行安装程序,按照提示完成安装。
2. 配置 Arduino IDE
- 打开 Arduino IDE,点击 文件 -> 首选项。
- 在 附加开发板管理器网址 中添加以下 URL:
https://arduino.esp8266.com/stable/package_esp8266com_index.json - 点击 确定 保存设置。
3. 安装库
-
DHT 传感器库:
- 点击 工具 -> 管理库。
- 在搜索框中输入
DHT
。 - 找到
DHT sensor library by Adafruit
,点击 安装。
-
Adafruit BMP085 Unified 库:
- 点击 工具 -> 管理库。
- 在搜索框中输入
Adafruit BMP085 Unified
。 - 找到
Adafruit BMP085 Unified by Adafruit
,点击 安装。- 注意:
Adafruit Unified Sensor
库会自动作为依赖库安装。
- 注意:
-
PubSubClient 库:
- 点击 工具 -> 管理库。
- 在搜索框中输入
PubSubClient
。 - 找到
PubSubClient by Nick O'Leary
,点击 安装。
4. 验证库是否安装成功
- 点击 文件 -> 示例。
- 查看是否有以下库的示例代码:
DHT sensor library
Adafruit BMP085 Unified
PubSubClient
- 如果有,说明库已正确安装。
硬件
- Arduino Uno
- ESP8266 WiFi 模块
- DHT11 温湿度传感器
- BMP180 气压传感器
- 光敏传感器
- 土壤湿度传感器
- 杜邦线若干
- 面包板(可选)
硬件连接说明
以下是传感器与 Arduino 以及 ESP8266 的具体连接方式:
1. DHT11 温湿度传感器
DHT11 引脚 | Arduino 引脚 |
---|---|
VCC | 5V |
GND | GND |
DATA | D3 |
2. BMP180 气压传感器
BMP180 引脚 | Arduino 引脚 |
---|---|
VCC | 3.3V |
GND | GND |
SCL | A5 (SCL) |
SDA | A4 (SDA) |
3. 光敏传感器
光敏传感器引脚 | Arduino 引脚 |
---|---|
VCC | 5V |
GND | GND |
OUT | A3 |
4. 土壤湿度传感器
土壤湿度传感器引脚 | Arduino 引脚 |
---|---|
VCC | 5V |
GND | GND |
OUT | A1 |
5. ESP8266 WiFi 模块
ESP8266 引脚 | Arduino 引脚 |
---|---|
VCC | 3.3V |
GND | GND |
TX | RX (D0) |
RX | TX (D1) |
CH_PD | 3.3V |
注意:ESP8266 的电压为 3.3V,切勿直接连接到 Arduino 的 5V 引脚,否则可能损坏模块。
3. 代码实现
Arduino 部分:传感器数据采集
Arduino 的任务很简单:读取传感器的数据,然后等着 ESP8266 来问它要数据。以下是具体实现:
1. 引入库与引脚定义
首先,我们需要引入一些库,这些库能帮我们轻松读取传感器的数据。然后定义一下传感器连接的引脚。
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>
#define LIGHT_SENSOR_PIN A3 // 光敏传感器接在 A3 引脚
#define SOIL_SENSOR_PIN A1 // 土壤湿度传感器接在 A1 引脚
#define DHT_PIN 3 // DHT11 温湿度传感器接在 D3 引脚
#define DHT_TYPE DHT11 // 使用 DHT11 类型
- DHT 库用来读取温湿度传感器数据。
- Adafruit_BMP085_U 库用来读取气压传感器数据。
- 引脚定义部分就是把传感器接到 Arduino 的哪个引脚上。
注意:DHT11 和 BMP180 的引脚连接要正确,尤其是 VCC 和 GND,接反了可能会烧坏传感器!
2. 初始化传感器
在 setup()
函数里,我们要初始化串口通信和传感器。
DHT dht(DHT_PIN, DHT_TYPE);
Adafruit_BMP085_Unified bmp(10085);
void setup() {
Serial.begin(9600); // 初始化串口通信,波特率 9600
dht.begin(); // 初始化 DHT11 传感器
Wire.begin(); // 初始化 I2C 通信
if (!bmp.begin()) { // 初始化 BMP180 传感器
Serial.println("错误:BMP180 初始化失败!");
}
}
Serial.begin(9600)
是初始化串口通信,用来和 ESP8266 聊天。dht.begin()
和bmp.begin()
是初始化温湿度和气压传感器。
注意:如果串口通信没设置好,Arduino 和 ESP8266 就没办法正常“聊天”了。确保波特率一致(这里用的是 9600)。
3. 读取传感器数据
在 loop()
函数里,Arduino 会一直等着 ESP8266 发指令过来。ESP8266 会问:“嘿,温度是多少?” Arduino 就会回答:“25°C!”
void loop() {
if (Serial.available() > 0) { // 检查串口是否有数据
String command = Serial.readStringUntil('\n'); // 读取 ESP8266 发来的指令
command.trim(); // 去掉多余的空白字符
command.toUpperCase(); // 把指令转换成大写
if (command == "LIGHT") { // 如果 ESP8266 问光强
Serial.println(readLightSensor()); // Arduino 回答光强值
} else if (command == "SOIL") { // 如果 ESP8266 问土壤湿度
Serial.println(readSoilSensor()); // Arduino 回答土壤湿度值
} else if (command == "TEMPERATURE") { // 如果 ESP8266 问温度
float temperature = readTemperature(); // Arduino 读取温度
if (!isnan(temperature)) Serial.println(temperature); // 回答温度值
}
}
}
Serial.available()
检查串口有没有数据。Serial.readStringUntil('\n')
读取 ESP8266 发来的指令。readLightSensor()
、readSoilSensor()
和readTemperature()
是读取传感器数据的函数。
注意:如果 ESP8266 发来的指令格式不对(比如多了空格或少了个字母),Arduino 可能就“听不懂”了。所以指令要写对!
4. 传感器数据读取函数
这些函数就是 Arduino 用来读取传感器数据的“小工具”。
int readLightSensor() {
return analogRead(LIGHT_SENSOR_PIN); // 读取光敏传感器的值
}
int readSoilSensor() {
return analogRead(SOIL_SENSOR_PIN); // 读取土壤湿度传感器的值
}
float readTemperature() {
float temperature = NAN; // 初始化温度值
for (int i = 0; i < 3; i++) { // 尝试读取 3 次
temperature = dht.readTemperature(); // 读取温度
if (!isnan(temperature)) break; // 如果读取成功,跳出循环
delay(100); // 等待 100ms 再试
}
return temperature; // 返回温度值
}
analogRead()
用来读取模拟信号(比如光敏传感器和土壤湿度传感器)。dht.readTemperature()
用来读取温度数据,还加了简单的错误处理。
注意:DHT11 有时候会读取失败,返回 NAN
(不是数字)。所以这里加了重试机制,最多试 3 次。
ESP8266 部分:数据上传与 MQTT 通信
ESP8266 的任务是连接 WiFi,然后从 Arduino 那里拿到数据,再通过 MQTT 协议把数据上传到云端。以下是具体实现:
1. 引入库与配置
首先,引入 WiFi 和 MQTT 所需的库,然后配置 WiFi 和 MQTT 服务器的信息。
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
const char* ssid = "user_id"; // WiFi 名称
const char* password = "user_password"; // WiFi 密码
const char* mqtt_server = "broker.emqx.io"; // MQTT 服务器地址
const int mqtt_port = 1883; // MQTT 服务器端口
WiFiClient espClient;
PubSubClient mqttClient(espClient);
ESP8266WiFi
库用来连接 WiFi。PubSubClient
库用来实现 MQTT 协议通信。
注意:WiFi 名称和密码要写对,不然 ESP8266 连不上网络。
2. 初始化与连接
在 setup()
函数里,初始化串口通信、连接 WiFi 和 MQTT 服务器。
void setup() {
Serial.begin(9600); // 初始化串口通信
connectWiFi(); // 连接 WiFi
mqttClient.setServer(mqtt_server, mqtt_port); // 设置 MQTT 服务器
connectMQTT(); // 连接 MQTT 服务器
}
connectWiFi()
和connectMQTT()
是自定义函数,分别用来连接 WiFi 和 MQTT 服务器。
注意:如果 MQTT 服务器地址或端口写错了,ESP8266 就没办法上传数据了。
3. 连接 WiFi
以下是连接 WiFi 的函数实现。
void connectWiFi() {
WiFi.begin(ssid, password); // 开始连接 WiFi
while (WiFi.status() != WL_CONNECTED) { // 检查是否连接成功
delay(1000); // 等待 1 秒
Serial.println("正在连接 WiFi ..."); // 打印连接状态
}
Serial.println("成功连接 WiFi ..."); // 连接成功
}
WiFi.begin()
尝试连接指定的 WiFi 网络。WiFi.status()
检查连接状态,直到连接成功。
注意:如果 WiFi 信号太弱,ESP8266 可能会连接失败。可以试试靠近路由器。
4. 连接 MQTT 服务器
以下是连接 MQTT 服务器的函数实现。
void connectMQTT() {
while (!mqttClient.connected()) { // 检查是否连接成功
if (mqttClient.connect("Client_id", "user_id", "user_password")) { // 尝试连接
Serial.println("成功连接 MQTT Broker"); // 连接成功
} else {
delay(5000); // 等待 5 秒再试
}
}
}
mqttClient.connect()
尝试连接 MQTT 服务器,支持用户名和密码验证。
注意:如果 MQTT 服务器需要用户名和密码,一定要写对,不然连接会失败。
5. 发布传感器数据
在 loop()
函数里,ESP8266 会定期从 Arduino 那里拿到数据,然后通过 MQTT 协议上传到云端。
void loop() {
if (!mqttClient.connected()) connectMQTT(); // 检查 MQTT 连接
mqttClient.loop(); // 保持 MQTT 连接
static unsigned long lastPublishTime = 0; // 记录上次上传时间
if (millis() - lastPublishTime >= 5000) { // 每 5 秒上传一次
publishSensorData(); // 上传传感器数据
lastPublishTime = millis(); // 更新上次上传时间
}
}
publishSensorData()
是自定义函数,用来从 Arduino 获取数据并上传到 MQTT 主题。
注意:上传频率不要太快,否则可能会给服务器造成压力。这里设置的是每 5 秒上传一次。
6. 数据发布函数
以下是发布传感器数据的函数实现。
void publishSensorData() {
const char* commands[] = {"LIGHT", "SOIL", "TEMPERATURE"}; // 要发送的指令
const char* topics[] = {"home/sensor/light", "home/sensor/soil", "home/sensor/temperature"}; // MQTT 主题
for (int i = 0; i < 3; i++) {
sendCommand(commands[i]); // 向 Arduino 发送指令
String value = readSerialResponse(); // 读取 Arduino 返回的数据
String json = "{\"value\":\"" + value + "\"}"; // 把数据打包成 JSON 格式
mqttClient.publish(topics[i], json.c_str()); // 发布到 MQTT 主题
}
}
sendCommand()
向 Arduino 发送指令,比如“LIGHT”就是问光强值。readSerialResponse()
读取 Arduino 返回的数据。mqttClient.publish()
把数据发布到对应的 MQTT 主题。
注意:MQTT 主题名称要写对,不然数据会发到错误的地方。
4. 系统架构与工作原理
系统架构图
[传感器] -> [Arduino] -> [串口通信] -> [ESP8266] -> [WiFi] -> [MQTT Broker] -> [云端]
工作原理
- Arduino 采集传感器数据。
- ESP8266 通过串口从 Arduino 获取数据。
- ESP8266 将数据通过 MQTT 协议上传到 EMQX 公共 Broker。
- 数据以 JSON 格式存储,可供其他设备或平台订阅和使用。
5. 实际效果展示
数据上传效果
通过 MQTT 客户端(如 MQTTX)订阅主题,查看实时数据。示例数据:
{
"value": "25",
"unit": "°C"
}
效果
-
硬件连接:
-
用户接收:
6. 总结与扩展建议
总结
- 这个项目,我们用 Arduino 和 ESP8266 实现了一个简单的环境监测系统
- 通过MQTT协议,能够实时采集并上传环境数据。
- 整个过程不仅有趣,还能让你学到很多关于硬件连接、代码编写和物联网协议的知识。
- 无论你是初学者还是有一定经验的爱好者,这个项目都能为你提供宝贵的实践经验。
扩展建议
如果你对这个项目感兴趣,还可以尝试一些有趣的扩展:
- 添加更多传感器:比如空气质量传感器或二氧化碳传感器,让系统能监测更多环境参数。
- 搭建可视化平台:如果你喜欢折腾,可以尝试用 Grafana 或 Node-RED 搭建一个漂亮的数据展示界面,实时查看环境数据的变化。
- 对接手机或智能家居:将数据接入手机 App 或智能家居平台,实现远程监控和自动化控制,比如温度过高时自动打开风扇。
- 使用私有 MQTT Broker:如果你对数据安全性有更高要求,可以尝试搭建自己的 MQTT 服务器。
这个项目的潜力很大,完全可以根据你的兴趣和需求自由发挥。无论是用来学习、 DIY,还是解决实际问题,它都能带来很多乐趣和成就感!
如果你对物联网或环境监测感兴趣,不妨动手试试这个项目!
无论是学习硬件连接、代码编写,还是探索 MQTT 协议,这个项目都能为你提供宝贵的实践经验。如果你有任何问题或想法,欢迎在评论区留言,我们一起交流讨论!
链接
- GitHub 仓库链接 (提供硬件代码和用户测试代码)
- Gitee 仓库链接
- EMQX 产品文档
- Arduino R3 官方文档