MQTTHandler.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #include "MQTTHandler.h"
  2. #include "WiFiConfig.h"
  3. extern Preferences preferences; // 引用外部定义的 preferences 对象
  4. WiFiClient espClient;
  5. PubSubClient client(espClient);
  6. const char* mqttUser = "yangfei";
  7. const char* mqttPassword = "yangfei";
  8. static int lastGpio18State = LOW; // 保存上一次 GPIO18 的电平状态,默认为 LOW
  9. int mqttConnectionAttempts = 0; // 用于记录 MQTT 连接失败的次数
  10. String targetDeviceID = ""; // 存储从订阅消息中接收的完整数据
  11. // unsigned long lowGpioStartTime = 0; // 记录 GPIO18 低电平的开始时间
  12. bool gpio18HighSent = false; // 标记是否已经发送过 ON 消息
  13. void mqttSetup() {
  14. client.setServer(serverAddress.c_str(), serverPort);
  15. client.setCallback(mqttCallback);
  16. // 初始化 Preferences
  17. preferences.begin("mqtt", false);
  18. targetDeviceID = preferences.getString("targetDeviceID", ""); // 从存储中加载 targetDeviceID
  19. if (targetDeviceID.length() > 0) {
  20. Serial.println("[信息] 从存储中加载 targetDeviceID: " + targetDeviceID);
  21. } else {
  22. Serial.println("[信息] 未找到存储的 targetDeviceID");
  23. }
  24. preferences.end();
  25. }
  26. void mqttCallback(char* topic, byte* payload, unsigned int length) {
  27. String message = "";
  28. for (unsigned int i = 0; i < length; i++) {
  29. message += (char)payload[i];
  30. }
  31. String expectedTopic = "device/24G-" + deviceID + "/control"; // 订阅的主题
  32. if (String(topic) == expectedTopic) {
  33. Serial.println("[信息] 收到控制消息: " + message);
  34. // 添加闪烁任务:发送第三个 MQTT 消息时闪烁 5 次
  35. addBlinkTask(6);
  36. // 将接收到的完整消息存储到 targetDeviceID
  37. targetDeviceID = message;
  38. Serial.println("[信息] 存储 targetDeviceID: " + targetDeviceID);
  39. // 将 targetDeviceID 永久存储
  40. preferences.begin("mqtt", false);
  41. preferences.putString("targetDeviceID", targetDeviceID);
  42. preferences.end();
  43. Serial.println("[信息] 已永久存储 targetDeviceID: " + targetDeviceID);
  44. }
  45. }
  46. bool connectMQTT() {
  47. Serial.println("[信息] 尝试连接到 MQTT 服务器...");
  48. if (client.connect(("24G-" + deviceID).c_str(), mqttUser, mqttPassword)) {
  49. Serial.println("[信息] MQTT 连接成功");
  50. mqttConnectionAttempts = 0; // 连接成功时重置计数器
  51. String topic = "device/24G-" + deviceID + "/control"; // 订阅的主题
  52. client.subscribe(topic.c_str());
  53. return true;
  54. } else {
  55. Serial.print("[错误] MQTT 连接失败, 错误码: ");
  56. Serial.println("服务器地址: " + serverAddress);
  57. Serial.println("服务器端口: " + String(serverPort));
  58. Serial.println(client.state());
  59. mqttConnectionAttempts++; // 连接失败时增加计数器
  60. if (mqttConnectionAttempts >= 3) {
  61. Serial.println("[错误] MQTT 连接失败三次,进入 Wi-Fi 配置模式...");
  62. setupWiFiConfig(); // 进入 Wi-Fi 配置模式
  63. mqttConnectionAttempts = 0; // 重置计数器
  64. }
  65. return false;
  66. }
  67. }
  68. void mqttLoop() {
  69. client.loop();
  70. }
  71. // 实现发送 MQTT 消息的函数
  72. void sendMQTTMessage(const char* message) {
  73. if (client.connected()) {
  74. String topic = "device/24G-" + deviceID + "/gpio18"; // 定义 MQTT 主题
  75. client.publish(topic.c_str(), message);
  76. // 添加闪烁任务:发送第一个 MQTT 消息时闪烁 3 次
  77. addBlinkTask(4);
  78. Serial.println("[信息] 发送 MQTT 消息: " + String(message));
  79. } else {
  80. Serial.println("[错误] MQTT 未连接,无法发送消息");
  81. }
  82. }
  83. // 新增函数:发送继电器控制消息
  84. void sendRelayControlMessage(const char* state) {
  85. if (client.connected() && targetDeviceID.length() > 0) {
  86. // 组合发布主题:device/<targetDeviceID>/relay/control
  87. String topic = "device/" + targetDeviceID + "/relay/control";
  88. String message = String(state);
  89. client.publish(topic.c_str(), message.c_str());
  90. // 添加闪烁任务:发送第二个 MQTT 消息时闪烁 4 次
  91. addBlinkTask(5);
  92. Serial.println("[信息] 发送继电器控制消息到设备 " + targetDeviceID + ": " + message);
  93. } else {
  94. Serial.println("[错误] MQTT 未连接或 targetDeviceID 未设置,无法发送继电器控制消息");
  95. }
  96. }
  97. // 处理 GPIO18 状态并发送 high/low 消息
  98. void handleGpio18State() {
  99. static int lastGpio18State = HIGH; // 保存上一次 GPIO18 的电平状态
  100. int gpio18State = digitalRead(GPIO3_PIN); // 读取 GPIO18 的电平状态
  101. // 如果电平状态发生变化
  102. if (gpio18State != lastGpio18State) {
  103. if (gpio18State == HIGH) {
  104. sendMQTTMessage("high"); // 发送 high
  105. } else {
  106. sendMQTTMessage("low"); // 发送 low
  107. }
  108. lastGpio18State = gpio18State; // 更新上一次的电平状态
  109. }
  110. }
  111. // 处理继电器控制逻辑,发送 ON/OFF 消息
  112. void handleRelayControl() {
  113. static int lastGpio18State = HIGH; // 保存上一次 GPIO18 的电平状态
  114. static unsigned long lowGpioStartTime = 0; // 记录低电平的开始时间
  115. int gpio18State = digitalRead(GPIO3_PIN); // 读取 GPIO18 的电平状态
  116. // 如果电平状态发生变化
  117. if (gpio18State != lastGpio18State) {
  118. if (gpio18State == HIGH) {
  119. // GPIO18 从低电平变为高电平,立即发送 ON
  120. sendRelayControlMessage("ON");
  121. Serial.println("发送:ON");
  122. lowGpioStartTime = 0; // 重置低电平计时
  123. } else if (gpio18State == LOW) {
  124. // GPIO18 从高电平变为低电平,开始计时
  125. lowGpioStartTime = millis(); // 记录低电平开始时间
  126. }
  127. lastGpio18State = gpio18State; // 更新上一次的电平状态
  128. }
  129. // 如果当前是低电平且计时已经开始
  130. if (gpio18State == LOW && lowGpioStartTime != 0) {
  131. if (millis() - lowGpioStartTime >= 15 * 60 * 1000) { // 15 分钟
  132. // 低电平保持 15 分钟,发送 OFF
  133. sendRelayControlMessage("OFF");
  134. Serial.println("发送:OFF");
  135. lowGpioStartTime = 0; // 重置计时
  136. }
  137. }
  138. }