【2024 DigiKey 创意大赛】ESP-32C6- 室内数据采集 + MQTT上报
[复制链接]
本帖最后由 御坂10032号 于 2024-10-15 00:48 编辑
简介
在这次的活动中除了服务端的树莓派一共还买了一块ESP32-C6. ESP32C6主要在这个任务中扮演的角色就是数据的采集和上报,同时根据订阅的主题的MQTT消息实现继电器的吸合从而来控制其他的电器。
正文
本章节主要是使用ESP32-C6 来使用I2C驱动好传感器AHT10, 然后将数据上传到部署了HA和MQTT的树莓派5, 同时通过绑定对应的MQTT主题接收回调来根据订阅主题发送的消息从而切换继电器的状态。 这里可以被提供切换的在卧室场景中有: 接入BH1750当环境光照降低到一定的阈值的时候,出发继电器打开台灯,来给环境补光。
当前所使用的物料如下:
- ESP32-C6
- 树莓派5
- AHT10
- 继电器
接线图如下:
那么为了能够达到上文中提及的效果, 这里需要一共有几个问题需要我们解决
1- WIFI连接
2- MQTT连接
3- 传感器数据的读取
4- 数据的上传和回调的处理
1) - 首先我们来解决第一个问题 WIFI连接
在正常在Arduino IDE中安装好ESP32的支持后, 可以很轻松的在示例工程中找到ESP32-C6 wifi connect的代码。
上述代码的主要功能是Wifi的扫描和连接等,我们可以在上述示例代码中修改SSID 和 password 成你本地的WIFI即可正确连接。它的核心代码主要是在 setup阶段的while循环中
// Wait for the WiFi event
while (true) {
switch (WiFi.status()) {
case WL_NO_SSID_AVAIL: Serial.println("[WiFi] SSID not found"); break;
case WL_CONNECT_FAILED:
Serial.print("[WiFi] Failed - WiFi not connected! Reason: ");
return;
break;
case WL_CONNECTION_LOST: Serial.println("[WiFi] Connection was lost"); break;
case WL_SCAN_COMPLETED: Serial.println("[WiFi] Scan is completed"); break;
case WL_DISCONNECTED: Serial.println("[WiFi] WiFi is disconnected"); break;
case WL_CONNECTED:
Serial.println("[WiFi] WiFi is connected!");
Serial.print("[WiFi] IP address: ");
Serial.println(WiFi.localIP());
return;
break;
default:
Serial.print("[WiFi] WiFi Status: ");
Serial.println(WiFi.status());
break;
}
delay(tryDelay);
if (numberOfTries <= 0) {
Serial.print("[WiFi] Failed to connect to WiFi!");
// Use disconnect function to force stop trying to connect
WiFi.disconnect();
return;
} else {
numberOfTries--;
}
}
注意,这里无法连接5G信号的WIFI, 如果一切无误,程序正常连接的话,那么这个while 循环的最后一个 ‘}’ 的代码是不会被执行的, 因此如果需要做其他的初始化工作的话。 在不修改它代码结构的前提下最好将代码放到WIFI got ip 之后。 否则将会造成无法初始化的异常。
关于程序中的按键状态的读取来断开WIFI的功能,我们并不需要可以直接删除。 那么当程序成功连接到WIFI之后则会输出下图的消息
此时WIFI连接的任务我们已经完成了。
2) - 第二个问题我们需要解决的则为 连接MQTT服务器
连接MQTT服务的基本步骤为,在连接好WIFI之后, 通过WIFI client 对象来初始化一个MQTT client 对象, 然后通过这个MQTT client 对象去连接 MQTT服务器。那么如果你在安装MQTT服务的时候设置了运行当前的MQTT服务器被匿名登录的话, 那么则可以不需要输入密码直接访问MQTT,但是如果你并没有设置的话, 那么还需要额外使用MQTTClient对象调用它的 setUsernamePassword(username, password) 来设置MQTT的登陆信息。
如果想要连接MQTT的话,则需要使用Arduino提供的 库, 它可以在库管理器中直接被找到, 如下图所示。
在成功安装完这个库之后, 可以在demo中找到MQTT Client的使用示例。
上述Demo最主要的功能主要是 :
1 - 引入头文件支持
2- 定义MQTT Client ,通过wificlient 创建
3- 定义MQTT连接信息
4- 连接MQTT
5- 绑定接受回调(订阅)
以及下图的发送和绑定消息回调。
我上图圈出来的功能为核心代码, 分别是连接MQTT, 设置接受回调函数, 以及设置接受回调函数下方的订阅主题(没有标注), 主循环中的mqttClient.poll(); 用来保证MQTT可以正确的接收到订阅的消息。 以及最后的回调函数等。
我们将代码稍微整合一下,那么就变成了这样。
#include <WiFi.h>
#include <ArduinoMqttClient.h>
#include <Arduino_JSON.h>
const char *ssid = "XXXX";
const char *password = "XXXX";
#define CONTROL_PIN 4 // 定义控制引脚,用于输出高低电平
WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
const char broker[] = "192.168.1.142";
int port = 1883;
const char switchTopic[] = "switch"; // 主题1
const char sensorTopic[] = "sensor"; // 主题2
void setup() {
Serial.begin(115200); // 初始化串口,用于调试
WiFi.begin(ssid, password);
pinMode(CONTROL_PIN, OUTPUT); // 设置控制引脚为输出模式
digitalWrite(CONTROL_PIN, LOW); // 初始状态设为低电平
int tryDelay = 500;
int numberOfTries = 20;
// Wait for the WiFi event
while (true) {
switch (WiFi.status()) {
case WL_NO_SSID_AVAIL: Serial.println("[WiFi] SSID not found"); break;
case WL_CONNECT_FAILED:
Serial.println("[WiFi] Failed - WiFi not connected! Reason: ");
return;
break;
case WL_CONNECTION_LOST: Serial.println("[WiFi] Connection was lost"); break;
case WL_DISCONNECTED: Serial.println("[WiFi] WiFi is disconnected"); break;
case WL_CONNECTED:
Serial.println("[WiFi] WiFi is connected!");
Serial.print("[WiFi] IP address: ");
Serial.println(WiFi.localIP());
Serial.print("Attempting to connect to the MQTT broker: ");
Serial.println(broker);
mqttClient.setUsernamePassword("root", "mazha1997");
if (!mqttClient.connect(broker, port)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
while (1);
}
Serial.println("You're connected to the MQTT broker!");
mqttClient.onMessage(onMqttMessage); // 设置消息接收回调
Serial.print("Subscribing to topic: ");
Serial.println(switchTopic);
mqttClient.subscribe(switchTopic); // 订阅开关主题
return;
default:
Serial.print("[WiFi] WiFi Status: ");
Serial.println(WiFi.status());
break;
}
delay(tryDelay);
if (numberOfTries <= 0) {
Serial.println("[WiFi] Failed to connect to WiFi!");
WiFi.disconnect();
return;
} else {
numberOfTries--;
}
}
}
void loop() {
mqttClient.poll();
delay(1000); // 每秒刷新一次
}
// MQTT消息接收回调函数
void onMqttMessage(int messageSize) {
// 获取当前消息的主题
String topic = mqttClient.messageTopic();
Serial.print("Received a message with topic '");
Serial.print(topic);
Serial.print("', length ");
Serial.print(messageSize);
Serial.println(" bytes:");
String message = "";
while (mqttClient.available()) {
message += (char)mqttClient.read();
}
Serial.println("Message content: " + message);
// 根据不同的主题处理消息
if (topic == switchTopic) {
Serial.println("Message from topic: switchTopic");
// 将接收到的消息转换为 JSON
JSONVar parsedMessage = JSON.parse(message);
// 检查解析是否成功
if (JSON.typeof(parsedMessage) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
// 获取 status 字段
String status = (const char*)parsedMessage["status"];
if (status == "ON") {
digitalWrite(CONTROL_PIN, HIGH); // 设置 IO4 输出高电平
Serial.println("Switch turned ON, IO4 set to HIGH");
} else if (status == "OFF") {
digitalWrite(CONTROL_PIN, LOW); // 设置 IO4 输出低电平
Serial.println("Switch turned OFF, IO4 set to LOW");
} else {
Serial.println("Unknown status value");
}
}
}
那么在上图的代码中我们即可实现WIFI的连接, 消息的订阅和MQTT的回调。
现在我们只需要来读取AHT10的消息,并且将其发送至MQTT即可。我们在上述的代码上做一下修改,引入
- #include <Wire.h>
- #include <Adafruit_AHTX0.h>
- #include <Arduino_JSON.h>
Wire主要是用来保证IIC通讯, AHTX0的这个库需要自己在库管理器中搜索安装, 而Arduino_JSON 主要适用于JSON的解析和构建。
#include <Wire.h>
#include <Adafruit_AHTX0.h>
#include <WiFi.h>
#include <ArduinoMqttClient.h>
#include <Arduino_JSON.h>
const char *ssid = "XXX";
const char *password = "XXX";
#define SDA_PIN 21 // 定义 SDA 引脚
#define SCL_PIN 22 // 定义 SCL 引脚
#define CONTROL_PIN 4 // 定义控制引脚,用于输出高低电平
Adafruit_AHTX0 aht;
WiFiClient wifiClient;
MqttClient mqttClient(wifiClient);
const char broker[] = "192.168.1.142";
int port = 1883;
const char switchTopic[] = "switch"; // 主题1
const char sensorTopic[] = "sensor"; // 主题2
void setup() {
Serial.begin(115200); // 初始化串口,用于调试
pinMode(CONTROL_PIN, OUTPUT); // 设置控制引脚为输出模式
digitalWrite(CONTROL_PIN, LOW); // 初始状态设为低电平
// 初始化 I2C,使用自定义引脚
Wire.begin(SDA_PIN, SCL_PIN);
if (!aht.begin()) {
Serial.println("Could not find AHT? Check wiring");
while (1) delay(10);
}
Serial.println("AHT10 or AHT20 found");
WiFi.begin(ssid, password);
int tryDelay = 500;
int numberOfTries = 20;
// Wait for the WiFi event
while (true) {
switch (WiFi.status()) {
case WL_NO_SSID_AVAIL: Serial.println("[WiFi] SSID not found"); break;
case WL_CONNECT_FAILED:
Serial.println("[WiFi] Failed - WiFi not connected! Reason: ");
return;
break;
case WL_CONNECTION_LOST: Serial.println("[WiFi] Connection was lost"); break;
case WL_DISCONNECTED: Serial.println("[WiFi] WiFi is disconnected"); break;
case WL_CONNECTED:
Serial.println("[WiFi] WiFi is connected!");
Serial.print("[WiFi] IP address: ");
Serial.println(WiFi.localIP());
Serial.print("Attempting to connect to the MQTT broker: ");
Serial.println(broker);
mqttClient.setUsernamePassword("root", "mazha1997");
if (!mqttClient.connect(broker, port)) {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(mqttClient.connectError());
while (1);
}
Serial.println("You're connected to the MQTT broker!");
mqttClient.onMessage(onMqttMessage); // 设置消息接收回调
Serial.print("Subscribing to topic: ");
Serial.println(switchTopic);
mqttClient.subscribe(switchTopic); // 订阅开关主题
return;
default:
Serial.print("[WiFi] WiFi Status: ");
Serial.println(WiFi.status());
break;
}
delay(tryDelay);
if (numberOfTries <= 0) {
Serial.println("[WiFi] Failed to connect to WiFi!");
WiFi.disconnect();
return;
} else {
numberOfTries--;
}
}
}
void loop() {
// 保证处理 MQTT 消息
mqttClient.poll();
// 获取传感器数据
sensors_event_t humidity, temp;
aht.getEvent(&humidity, &temp); // 获取温度和湿度数据
// 创建JSON对象来存储数据
JSONVar sensorData;
sensorData["temperature"] = String(temp.temperature, 2); // 保留两位小数
sensorData["humidity"] = String(humidity.relative_humidity, 2); // 保留两位小数
// 将JSON数据转换为字符串并发布到"sensor"主题
String payload = JSON.stringify(sensorData);
mqttClient.beginMessage(sensorTopic);
mqttClient.print(payload);
mqttClient.endMessage();
delay(1000); // 每秒刷新一次
}
// MQTT消息接收回调函数
void onMqttMessage(int messageSize) {
// 获取当前消息的主题
String topic = mqttClient.messageTopic();
Serial.print("Received a message with topic '");
Serial.print(topic);
Serial.print("', length ");
Serial.print(messageSize);
Serial.println(" bytes:");
String message = "";
while (mqttClient.available()) {
message += (char)mqttClient.read();
}
Serial.println("Message content: " + message);
// 根据不同的主题处理消息
if (topic == switchTopic) {
Serial.println("Message from topic: switchTopic");
// 将接收到的消息转换为 JSON
JSONVar parsedMessage = JSON.parse(message);
// 检查解析是否成功
if (JSON.typeof(parsedMessage) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
// 获取 status 字段
String status = (const char*)parsedMessage["status"];
if (status == "ON") {
digitalWrite(CONTROL_PIN, HIGH); // 设置 IO4 输出高电平
Serial.println("Switch turned ON, IO4 set to HIGH");
} else if (status == "OFF") {
digitalWrite(CONTROL_PIN, LOW); // 设置 IO4 输出低电平
Serial.println("Switch turned OFF, IO4 set to LOW");
} else {
Serial.println("Unknown status value");
}
}
}
上述的代码则为修改后的代码。 实现的功能则为, 将环境数据上传到Sensor的主题内, 同时又订阅了 Switch的主题, 那么外部接入的传感器为光照传感器的时候, 便可以“感知” 环境的变化,然后开启室内的补光。那么在下个章节我将将上面的程序集成进HA , 然后根据光照强度来配置一个卧室室内的感知自动化。
消息发布的截图
MQTT Sesonr 主题的数据
MQTT 继电器主题的控制:
0ceab0e55c0971eb39b9ad60f91b0a6d
|