235|4

161

帖子

3

TA的资源

一粒金砂(高级)

楼主
 

【2024 DigiKey 创意大赛】ESP-32C6- 室内数据采集 + MQTT上报 [复制链接]

  本帖最后由 御坂10032号 于 2024-10-15 00:48 编辑

简介

 

在这次的活动中除了服务端的树莓派一共还买了一块ESP32-C6.  ESP32C6主要在这个任务中扮演的角色就是数据的采集和上报,同时根据订阅的主题的MQTT消息实现继电器的吸合从而来控制其他的电器。


 

 

正文

 

本章节主要是使用ESP32-C6 来使用I2C驱动好传感器AHT10, 然后将数据上传到部署了HA和MQTT的树莓派5, 同时通过绑定对应的MQTT主题接收回调来根据订阅主题发送的消息从而切换继电器的状态。 这里可以被提供切换的在卧室场景中有: 接入BH1750当环境光照降低到一定的阈值的时候,出发继电器打开台灯,来给环境补光。

 

当前所使用的物料如下:

  1. ESP32-C6
  2. 树莓派5
  3. AHT10
  4. 继电器

 

接线图如下:

 

 

那么为了能够达到上文中提及的效果, 这里需要一共有几个问题需要我们解决

 

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即可。我们在上述的代码上做一下修改,引入

 

  1. #include <Wire.h>
  2. #include <Adafruit_AHTX0.h>
  3. #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

 

最新回复

我说的是树莓派部署完HA后,树莓派是不是就不能做其他的用了,能不能同时跑多个   详情 回复 发表于 2024-10-16 09:29
点赞 关注
 
 

回复
举报

6060

帖子

6

TA的资源

版主

沙发
 

树莓派部署HA,后还能进行其他功能吗?    

点评

后续要通过它实现自动化控制.  详情 回复 发表于 2024-10-15 23:45
个人签名

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 
 
 

回复

161

帖子

3

TA的资源

一粒金砂(高级)

板凳
 
秦天qintian0303 发表于 2024-10-15 13:07 树莓派部署HA,后还能进行其他功能吗?    

后续要通过它实现自动化控制.

点评

我说的是树莓派部署完HA后,树莓派是不是就不能做其他的用了,能不能同时跑多个  详情 回复 发表于 2024-10-16 09:29
 
 
 

回复

6060

帖子

6

TA的资源

版主

4
 
御坂10032号 发表于 2024-10-15 23:45 后续要通过它实现自动化控制.

我说的是树莓派部署完HA后,树莓派是不是就不能做其他的用了,能不能同时跑多个

点评

当然可以跑多个了, 树莓派并不是跑到HA的系统,而是在容器中跑的, docker 里可以运行多个容器, 所以是隔离不冲突的  详情 回复 发表于 2024-10-16 12:55
个人签名

在爱好的道路上不断前进,在生活的迷雾中播撒光引

 
 
 

回复

161

帖子

3

TA的资源

一粒金砂(高级)

5
 
秦天qintian0303 发表于 2024-10-16 09:29 我说的是树莓派部署完HA后,树莓派是不是就不能做其他的用了,能不能同时跑多个

当然可以跑多个了, 树莓派并不是跑到HA的系统,而是在容器中跑的, docker 里可以运行多个容器, 所以是隔离不冲突的

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表