STM32 ESP8266连接ONENET

通过Arduino烧录8266固件

cpp 复制代码
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

static WiFiClient espClient;
PubSubClient mqttClient(espClient);

// ONENET MQTT 连接参数
const char* MQTT_SERVER = "183.230.40.96";
const int MQTT_PORT = 1883;
String PRODUCT_ID = "";   // 产品ID
String DEVICE_NAME = "";        // 设备名称
String USERNAME = "";     // MQTT用户名
String PASSWORD = "";  // MQTT密码(Token)

bool paramsReceived = false; 
#define WIFI_SSID "LCDZ"
#define WIFI_PASSWD "LCDZlcdz"

// #define WIFI_SSID "12345678"
// #define WIFI_PASSWD "12345678"

String serialInput = "";   // 缓存串口输入
String lcCommandInput = ""; // 缓存 LC 指令输入

// ONENET MQTT 订阅/发布主题
String getTopicPropertySet() {
  return "$sys/" + PRODUCT_ID + "/" + DEVICE_NAME + "/thing/property/set";
}  // 属性设置主题
String getTopicPropertyPost() {
  return "$sys/" + PRODUCT_ID + "/" + DEVICE_NAME + "/thing/property/post";
}  // 属性上报主题

// 物模型属性存储(简易键值)
#define MAX_PROPERTIES 20 // 最大属性数
struct PropertyData {
  String name;
  String value;
  bool hasValue;
};
PropertyData propertyStore[MAX_PROPERTIES]; // 已接收的属性列表

// 已绑定的物模型属性
String boundProperties[MAX_PROPERTIES]; // 已绑定属性名
int boundCount = 0; // 已绑定数量

// 解析串口参数(LC+Connect)
void parseSerialParams(String input) {
  // 检查是否以 "LC+Connect:" 开头
  if (input.startsWith("LC+Connect:")) {
    // 去掉前缀
    String params = input.substring(11);
    params.trim(); // 去掉首尾空格
    
    // 解析逗号分隔的参数(去掉引号)
    int startPos = 0;
    int paramIndex = 0;
    bool inQuotes = false;
    
    for (int i = 0; i <= params.length(); i++) {
      if (i == params.length() || (!inQuotes && params.charAt(i) == ',')) {
        String param = params.substring(startPos, i);
        param.trim();
        
        // 去掉首尾引号
        if (param.startsWith("\"") && param.endsWith("\"")) {
          param = param.substring(1, param.length() - 1);
        }
        
        // 按索引写入
        if (param.length() > 0) {
          switch (paramIndex) {
            case 0: PRODUCT_ID = param; break;
            case 1: DEVICE_NAME = param; break;
            case 2: USERNAME = param; break;
            case 3: PASSWORD = param; break;
          }
          paramIndex++;
        }
        
        startPos = i + 1;
      } else if (params.charAt(i) == '"') {
        inQuotes = !inQuotes;
      }
    }
    
    if (paramIndex >= 4) {
      paramsReceived = true;
      Serial.println("Parameter:OK");
      // Serial.print("PRODUCT_ID: "); Serial.println(PRODUCT_ID);
      // Serial.print("DEVICE_NAME: "); Serial.println(DEVICE_NAME);
      // Serial.print("USERNAME: "); Serial.println(USERNAME);
      // Serial.print("PASSWORD: "); Serial.println(PASSWORD);
    } else {
      Serial.println("Parameter:error");
    }
  }
}

// 解析并执行 LC+Send 指令
// 格式: LC+Send:"物模型名称":数据
// 数据可为整数/浮点/字符串
void parseLCSendCommand(String input) {
  // 检查是否以 "LC+Send:" 开头
  if (input.startsWith("LC+Send:")) {
    // 去掉前缀
    String params = input.substring(8);
    params.trim();
    
    // 提取模型名和数据,格式: "模型名":数据
    int quoteStart = params.indexOf('"');
    int quoteEnd = params.indexOf('"', quoteStart + 1);
    int colonPos = params.indexOf(':', quoteEnd + 1);
    
    if (quoteStart >= 0 && quoteEnd > quoteStart && colonPos > quoteEnd) {
      // 模型名(去引号)
      String modelName = params.substring(quoteStart + 1, quoteEnd);
      // 数据部分
      String data = params.substring(colonPos + 1);
      data.trim();
      
      // 尝试按数值或字符串发送
      if (modelName.length() > 0 && data.length() > 0) {
        // 判断是否数字
        bool isNumber = false;
        bool isFloat = false;
        
        // 浮点或整数
        if (data.indexOf('.') >= 0) {
          // 浮点数校验
          float testFloat = data.toFloat();
          if (testFloat != 0.0 || data == "0.0" || data == "0" || data.startsWith("0.")) {
            isNumber = true;
            isFloat = true;
          }
        } else {
          // 整数校验
          long testLong = data.toInt();
          if (testLong != 0 || data == "0") {
            // 确认每位都是数字
            bool allDigits = true;
            int startIdx = 0;
            if (data.charAt(0) == '-' || data.charAt(0) == '+') startIdx = 1; // 允许符号位
            
            for (int i = startIdx; i < data.length(); i++) {
              if (!isDigit(data.charAt(i))) {
                allDigits = false;
                break;
              }
            }
            
            if (allDigits && data.length() > startIdx) {
              isNumber = true;
              isFloat = false;
            }
          }
        }
        
        if (isNumber) {
          if (isFloat) {
            // 发送浮点
            float floatValue = data.toFloat();
            sendToONENET(modelName.c_str(), floatValue);
            // Serial.print("LC+Send OK(float): ");
            // Serial.print(modelName);
            // Serial.print(" = ");
            // Serial.println(floatValue, 6); // 显示 6 位小数
          } else {
            // 发送整数
            long longValue = data.toInt();
            // 若超出 int,转换为 float 发送
            sendToONENET(modelName.c_str(), (float)longValue);
            // Serial.print("LC+Send OK(int): ");
            // Serial.print(modelName);
            // Serial.print(" = ");
            // Serial.println(longValue);
          }
        } else {
          // 鍙戦€佸瓧绗︿覆
          sendToONENET(modelName.c_str(), data.c_str());
          // Serial.print("LC+Send OK(string): ");
          // Serial.print(modelName);
          // Serial.print(" = \"");
          // Serial.print(data);
          // Serial.println("\"");
        }
      }
    } 
  }
}

// MQTT 消息回调
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  // 将 payload 转成字符串
  String message = "";
  for (unsigned int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  
  Serial.print("Message received [");
  Serial.print(topic);
  Serial.print("]: ");
  Serial.println(message);
  
  // 解析 JSON
  StaticJsonDocument<200> doc;
  DeserializationError error = deserializeJson(doc, message);
  
  if (error) {
    Serial.print("JSON parse failed: ");
    Serial.println(error.c_str());
    return;
  }
  
  // 根据 topic 处理不同消息
  String topicStr = String(topic);
  if (topicStr == getTopicPropertySet()) {
    // 处理属性下发
    // Serial.println("Property set command received");
    // 可在此解析具体属性,如 doc["params"]["xxx"]

    // 按 OneNET 物模型要求回复 set_reply,避免"设备响应超时"
    String msgId = doc["id"] | "0";
    StaticJsonDocument<128> rsp;
    rsp["id"] = msgId;
    rsp["code"] = 200;
    rsp["msg"] = "success";
    char rspBuf[128];
    size_t rspLen = serializeJson(rsp, rspBuf);
    String replyTopic = "$sys/" + PRODUCT_ID + "/" + DEVICE_NAME + "/thing/property/set_reply";
    bool pubOk = mqttClient.publish(replyTopic.c_str(), (uint8_t*)rspBuf, rspLen);
    // Serial.print("set_reply publish "); Serial.println(pubOk ? "ok" : "fail");
  } else if (topicStr == getTopicPropertyPost()) {
    // 处理属性上报应答
    // Serial.println("Property post response received");
  }
}

// ONENET MQTT 连接
bool connectToONENET() {
  mqttClient.setServer(MQTT_SERVER, MQTT_PORT);
  mqttClient.setCallback(mqttCallback); // 设置回调
  
  // clientId 直接用设备名称
  String clientId = DEVICE_NAME;
  
  // 打印连接参数用于调试
  // Serial.println("=== ONENET MQTT Connection Info ===");
  // Serial.print("Server: "); Serial.println(MQTT_SERVER);
  // Serial.print("Port: "); Serial.println(MQTT_PORT);
  // Serial.print("ClientID: "); Serial.println(clientId);
  // Serial.print("Username: "); Serial.println(USERNAME);
  // Serial.print("Password: "); Serial.println(PASSWORD);
  // Serial.println("===================================");
  
  // 尝试连接
  if (mqttClient.connect(clientId.c_str(), USERNAME.c_str(), PASSWORD.c_str())) {
    Serial.println("ONENET MQTT Connected");
    
    // 订阅 ONENET 相关主题
    String topicSet = getTopicPropertySet();
    String topicPost = getTopicPropertyPost();
    
    if (mqttClient.subscribe(topicSet.c_str())) {
      // Serial.print("Subscribed to: ");
      // Serial.println(topicSet);
    } else {
      // Serial.print("Failed to subscribe to: ");
      // Serial.println(topicSet);
    }
    
    if (mqttClient.subscribe(topicPost.c_str())) {
      // Serial.print("Subscribed to: ");
      // Serial.println(topicPost);
    } else {
      // Serial.print("Failed to subscribe to: ");
      // Serial.println(topicPost);
    }
    
    return true;
  } else {
    Serial.print("ONENET MQTT Connect failed, rc=");
    int state = mqttClient.state();
    Serial.println(state);
    
    // 错误原因
    switch(state) {
      // case -4: Serial.println("Connection timeout"); break;
      // case -3: Serial.println("Connection lost"); break;
      // case -2: Serial.println("Connect failed"); break;
      // case -1: Serial.println("Disconnected"); break;
      // case 1: Serial.println("Bad protocol version"); break;
      // case 2: Serial.println("Bad client ID"); break;
      // case 3: Serial.println("Unavailable"); break;
      // case 4: Serial.println("Bad credentials (Username/Password)"); break;
      // case 5: Serial.println("Unauthorized"); break;
    }
    return false;
  }
}

// 发送数据到 ONENET(数值)
void sendToONENET(const char* propertyName, float value) {
  Serial.println("=== sendToONENET called ===");
  Serial.print("Property: "); Serial.println(propertyName);
  Serial.print("Value: "); Serial.println(value);
  
  if (!mqttClient.connected()) {
    Serial.println("MQTT not connected, reconnecting...");
    if (!connectToONENET()) {
      Serial.println("ERROR: Failed to connect!");
      return;
    }
  } else {
    Serial.println("MQTT connected OK");
  }
  
  // ONENET 物模型上报格式: {"id":"消息ID","params":{"属性":{"value":值}}}
  StaticJsonDocument<300> doc;
  char idStr[20];
  sprintf(idStr, "%lu", millis()); // 使用时间戳作为消息ID
  doc["id"] = idStr;
  
  JsonObject params = doc.createNestedObject("params");
  JsonObject property = params.createNestedObject(propertyName);
  property["value"] = value; // 按 ONENET 要求嵌套 value
  
  char jsonBuffer[300];
  serializeJson(doc, jsonBuffer);
  
  // 打印 JSON 调试
  // Serial.print("JSON: "); Serial.println(jsonBuffer);
  
  String topicPost = getTopicPropertyPost();
  // Serial.print("Topic: "); Serial.println(topicPost);
  // Serial.print("Topic length: "); Serial.println(topicPost.length());
  // Serial.print("JSON length: "); Serial.println(strlen(jsonBuffer));
  
  // 保证 MQTT loop 被调用
  mqttClient.loop();
  delay(10); // 让 MQTT loop 运行一次
  
  // 发布
  bool publishResult = mqttClient.publish(topicPost.c_str(), jsonBuffer);
  // Serial.print("Publish result: "); Serial.println(publishResult ? "true" : "false");
  
  // 再执行几次 loop 确认发送
  mqttClient.loop();
  delay(50); // 稍等以保证发送完成
  mqttClient.loop();
  
  if (publishResult) {
    // Serial.println("SUCCESS: Published!");
    // Serial.print("Topic: "); Serial.println(topicPost);
    // Serial.print("Data: "); Serial.println(jsonBuffer);
  } else {
    // Serial.println("ERROR: Publish failed!");
    // Serial.print("MQTT state: "); Serial.println(mqttClient.state());
    // Serial.print("MQTT connected: "); Serial.println(mqttClient.connected() ? "yes" : "no");
  }
  // Serial.println("========================");
}

// 发送数据到 ONENET(字符串)
void sendToONENET(const char* propertyName, const char* value) {
  Serial.println("=== sendToONENET (string) called ===");
  Serial.print("Property: "); Serial.println(propertyName);
  Serial.print("Value: "); Serial.println(value);
  
  if (!mqttClient.connected()) {
    Serial.println("MQTT not connected, reconnecting...");
    if (!connectToONENET()) {
      Serial.println("ERROR: Failed to connect!");
      return;
    }
  } else {
    Serial.println("MQTT connected OK");
  }
  
  // ONENET 物模型上报格式: {"id":"消息ID","params":{"属性":{"value":值}}}
  StaticJsonDocument<300> doc;
  char idStr[20];
  sprintf(idStr, "%lu", millis()); // 使用时间戳作为消息ID
  doc["id"] = idStr;
  
  JsonObject params = doc.createNestedObject("params");
  JsonObject property = params.createNestedObject(propertyName);
  property["value"] = value; // 按 ONENET 要求嵌套 value
  
  char jsonBuffer[300];
  serializeJson(doc, jsonBuffer);
  
  // 打印 JSON 调试
  Serial.print("JSON: "); Serial.println(jsonBuffer);
  
  String topicPost = getTopicPropertyPost();
  // Serial.print("Topic: "); Serial.println(topicPost);
  // Serial.print("Topic length: "); Serial.println(topicPost.length());
  // Serial.print("JSON length: "); Serial.println(strlen(jsonBuffer));
  
  // 保证 MQTT loop 被调用
  mqttClient.loop();
  delay(10); // 让 MQTT loop 运行一次
  
  // 发布
  bool publishResult = mqttClient.publish(topicPost.c_str(), jsonBuffer);
  Serial.print("Publish result: "); Serial.println(publishResult ? "true" : "false");
  
  // 再执行几次 loop 确认发送
  mqttClient.loop();
  delay(50); // 稍等以保证发送完成
  mqttClient.loop();
  
  if (publishResult) {
    // Serial.println("SUCCESS: Published!");
    // Serial.print("Topic: "); Serial.println(topicPost);
    // Serial.print("Data: "); Serial.println(jsonBuffer);
  } else {
    // Serial.println("ERROR: Publish failed!");
    // Serial.print("MQTT state: "); Serial.println(mqttClient.state());
    // Serial.print("MQTT connected: "); Serial.println(mqttClient.connected() ? "yes" : "no");
  }
  // Serial.println("========================");
}

void sendToCloud(const char* modelName, float value) {
  sendToONENET(modelName, value);
  // Serial.print("LC+Send OK: ");
  // Serial.print(modelName);
  // Serial.print(" = ");
  // Serial.println(value);
}

void sendToCloud(const char* modelName, const char* value) {
  sendToONENET(modelName, value);
  // Serial.print("LC+Send OK: ");
  // Serial.print(modelName);
  // Serial.print(" = ");
  // Serial.println(value);
}

void setup() {
  Serial.begin(115200);
  delay(1000); // 等待串口稳定
    
  // 一直等待串口收到 LC+Connect: 参数
  // Serial.println("=== ONENET MQTT Device Starting ===");
  // Serial.println("Waiting for LC+Connect command...");
  // Serial.println("Format: LC+Connect:\"产品ID\",\"设备名称\",\"用户名\",\"密码\"");
  // Serial.println("===================================");
  
  while (!paramsReceived) {
    if (Serial.available() > 0) {
      char c = Serial.read();
      if (c == '\n' || c == '\r') {
        if (serialInput.length() > 0) {
          parseSerialParams(serialInput);
          serialInput = "";
        }
      } else {
        serialInput += c;
      }
    }
  }

  // 初始化 WiFi
  wifiInit(WIFI_SSID, WIFI_PASSWD);

  // 连接 ONENET MQTT
  if (connectToONENET()) {
    Serial.println("ONENET initialized successfully");
  } else {
    Serial.println("ONENET initialization failed");
  }
}

void loop() {
  // 保持 MQTT 连接
  if (!mqttClient.connected()) {
    connectToONENET();
  }
  mqttClient.loop();
  
  // 处理串口输入
  if (Serial.available() > 0) {
    char c = Serial.read();
    
    // 判断是否 LC 指令(以 LC+ 开头)
    if (lcCommandInput.length() == 0 && (c == 'L' || c == 'l')) {
      lcCommandInput += c;
    } else if (lcCommandInput.length() > 0) {
      // 继续接收 LC 指令
      lcCommandInput += c;
      if (c == '\n' || c == '\r') {
        if (lcCommandInput.length() > 0) {
          if (lcCommandInput.startsWith("LC+Send:")) {
            parseLCSendCommand(lcCommandInput);
          } 
          lcCommandInput = "";
        }
      }
      // 防止缓存溢出
      if (lcCommandInput.length() > 200) {
        lcCommandInput = "";
      }
    }
  }
}

// 初始化 WiFi
void wifiInit(const char *ssid, const char *passphrase) {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, passphrase);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("wifi error");
  }
  Serial.println("wifi ok");
}

此固件只需要更改wifi 账号密码 其他参数可以通过指令传进去

#define WIFI_SSID ""

#define WIFI_PASSWD ""

ONENET云平台配置

1.创建产品

说明:红框里面必须一样其他随便

2.创建物模型

标志符就是物模型名称

3.创建设备

选择对应的产品名称

4.生成密钥

python 复制代码
import base64
import hmac
from urllib.parse import quote
import time

# 中国移动官方文档给出的核心秘钥计算算法
def token(id,access_key):
    version = '2018-10-31'
    res = 'products/%s' % id  # 通过产品ID访问产品API
    # 用户自定义token过期时间
    et = str(int(time.time()) + 63072000) # 设置为2年有效时间
    # 签名方法,支持md5、sha1、sha256
    method = 'sha1'
    # 对access_key进行decode
    key = base64.b64decode(access_key)
    # 计算sign
    org = et + '\n' + method + '\n' + res + '\n' + version
    sign_b = hmac.new(key=key, msg=org.encode(), digestmod=method)
    sign = base64.b64encode(sign_b.digest()).decode()
    # value 部分进行url编码,method/res/version值较为简单无需编码
    sign = quote(sign, safe='')
    res = quote(res, safe='')
    # token参数拼接
    token = 'version=%s&res=%s&et=%s&method=%s&sign=%s' % (version, res, et, method, sign)
    return token

username    = "5af2tgcpsX"                                           # 产品ID
accesskey   = ""         # 密钥
password = token(username, accesskey)
print(password)

5.ESP8266连接测试

指令

完整命令列表

  1. 连接命令(LC+Connect)

格式:

LC+Connect:"产品ID","设备名称","用户名","密码"

根据您的配置:

LC+Connect:"5af2tgcpsX","0001","5af2tgcpsX","密码"

  1. 发送数据命令(LC+Send)

发送到 dome1 物模型:

发送整数:

LC+Send:"wd":25LC+Send:"dome1":100LC+Send:"dome1":0LC+Send:"dome1":-10

发送浮点数:

LC+Send:"dome1":25.5LC+Send:"dome1":65.8LC+Send:"dome1":0.0LC+Send:"dome1":-10.3

发送字符串:

LC+Send:"dome1":"hello"LC+Send:"dome1":"status_ok"LC+Send:"dome1":"online"

发送到 dome2 物模型:

发送整数:

LC+Send:"dome2":50LC+Send:"dome2":200LC+Send:"dome2":0LC+Send:"dome2":-20

发送浮点数:

LC+Send:"dome2":50.5LC+Send:"dome2":75.8LC+Send:"dome2":0.0LC+Send:"dome2":-20.5

发送字符串:

LC+Send:"dome2":"world"LC+Send:"dome2":"status_error"LC+Send:"dome2":"offline

5.1连接上云

stm32连接上云

ESP8266.H

cpp 复制代码
#ifndef __ESP8266_H 
#define __ESP8266_H 

#include "sys.h" 

void ESP8266_Init(uint32_t BaudRate);
void Esp8266Cont(void);
void Serial_SendString(char *String);
void SendToCloud(const char *model_name, int value);
void SendToCloudFloat(const char *model_name, float value);
void SendToCloudString(const char *model_name, const char *str_value);

#endif 

ESP8266.C

cpp 复制代码
#include "esp8266.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "lcd.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>  // 增加math.h头文件
#include <ctype.h>  // 增加math.h头文件
#include <limits.h>  // 增加math.h头文件
// 兼容嵌入式环境的NAN定义(如果编译器不支持)
#ifndef NAN
#define NAN (0.0/0.0)
#endif

// 函数声明
uint8_t CheckStringInBuffer(uint8_t *buffer, uint16_t len, const char *str);
int ParseMQTTParameterInt(uint8_t *buffer, uint16_t len, const char *param_name);
float ParseMQTTParameterFloat(uint8_t *buffer, uint16_t len, const char *param_name);
int IsFloatNaN(float value);  // 新增NaN判断函数
void SendToCloud(const char *model_name, int value);
void SendToCloudFloat(const char *model_name, float value);
void SendToCloudString(const char *model_name, const char *str_value);

//数组
#define USART_RX_BUFFER_SIZE  1024

uint8_t Uart_GetRxFlag = 0;
uint8_t usart_rxbuffer[USART_RX_BUFFER_SIZE];  //接收的数据
uint16_t usart_rxlen = 0;
uint8_t usart_txbuffer[USART_RX_BUFFER_SIZE];  //发送数据
extern u8 maxMq2,maxMq135,maxWd,maxSd;
u8 ktd_value = 0;  // 新增ktd参数存储变量

//硬件定义
// USART1 时钟源在 APB2,总线使能要用 RCC_APB2Periph_USART1
#define USARTClock RCC_APB2Periph_USART1  //串口时钟
#define PINClock   RCC_APB2Periph_GPIOA    //管脚时钟
#define DMAClock   RCC_AHBPeriph_DMA1      //DMA时钟
#define TX         GPIO_Pin_9
#define RX         GPIO_Pin_10
#define PIN        GPIOA
#define USART      USART1
#define USARTIRQ   USART1_IRQn

//注意8266设置成115200
// ONENET MQTT连接参数设置
// 产品ID
static const char *product_ID = "5af2tgcpsX";
// 设备名称
static const char *CLIENT_ID = "0001";
// MQTT用户名
static const char *USERNAME = "5af2tgcpsX";
// MQTT密码(Token)
static const char *PASSWORD = "";

void ESP8266_Init(uint32_t BaudRate)
{
	/*开启时钟*/
	// USART1 位于 APB2,总线时钟必须用 APB2 使能,否则串口会假死
	RCC_APB2PeriphClockCmd(USARTClock, ENABLE); // 开启 USART1 时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟
    RCC_AHBPeriphClockCmd(DMAClock, ENABLE);  // 使能 DMA1 时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = TX;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(PIN, &GPIO_InitStructure);					//将PA2引脚初始化为复用推挽输出
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = RX;
	GPIO_Init(PIN, &GPIO_InitStructure);					//将PA3引脚初始化为上拉输入
	
	/*USART初始化*/
	USART_InitTypeDef USART_InitStructure;					//定义结构体变量
	USART_InitStructure.USART_BaudRate = BaudRate;				//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	//模式,发送模式和接收模式均选择
	USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位
	USART_Init(USART, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART2
    
    /*中断输出配置*/
	USART_ITConfig(USART, USART_IT_IDLE, ENABLE);			//开启串口空闲中断
    
    // RX DMA1 通道5 (USART1_RX 对应 DMA1 Channel5)
	DMA_InitTypeDef DMA_InitStructure;											 //定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART->DR;		 //外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	 //外设数据宽度,选择字节,对应8为的USART数据寄存器
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;			 //外设地址自增,选择失能,始终以USART数据寄存器为源
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)usart_rxbuffer;		     //存储器基地址,给定存放USART数据寄存器的全局数组
	DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;			 //存储器数据宽度,选择字节,与源数据宽度对应
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;						 //存储器地址自增,选择使能,每次转运后,数组移到下一个位置
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;							 //数据传输方向,选择由外设到存储器
	DMA_InitStructure.DMA_BufferSize = USART_RX_BUFFER_SIZE;		             //转运的数据大小(转运次数)
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;								 //模式,选择正常模式(非循环)
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;								     //非存储器到存储器模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;						 //优先级,选择中等
	DMA_Init(DMA1_Channel5, &DMA_InitStructure);								     //将结构体变量交给DMA_Init,配置DMA1的通道5
    
    /*使能USART2的DMA接收*/
    USART_DMACmd(USART, USART_DMAReq_Rx, ENABLE);
    
    /*启动DMA接收*/
    DMA_Cmd(DMA1_Channel5, ENABLE);
		
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = USARTIRQ;		//选择配置NVIC
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设
		
	/*USART使能*/
	USART_Cmd(USART, ENABLE);								//使能USART2,串口开始运行
}

void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART, USART_IT_IDLE) == SET)		//判断是否是USART2的接收事件触发的中断
	{
        /* 清除空闲中断标志 */
        volatile uint32_t temp;
        temp = USART->SR;   // 读取状态寄存器
        temp = USART->DR;   // 读取数据寄存器
        (void)temp;
        
        /* 计算接收到的数据长度 */
        usart_rxlen = USART_RX_BUFFER_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5);
        
        /* 设置接收完成标志 */
        if (usart_rxlen > 0)
        {
            Uart_GetRxFlag = 1;            
        }       
        
        /* 重新启动DMA接收 */
        DMA_Cmd(DMA1_Channel5, DISABLE);                                // 关闭DMA
        DMA_SetCurrDataCounter(DMA1_Channel5, USART_RX_BUFFER_SIZE);  // 重新设置传输数量
        DMA_Cmd(DMA1_Channel5, ENABLE);                                // 重新开启DMA
	}
}

void serialPortForwarding(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)		//遍历数组
	{
        USART_SendData(USART, Array[i]);		//将字节数据写入数据寄存器
	    while (USART_GetFlagStatus(USART, USART_FLAG_TXE) == RESET);	//等待发送完成
	}
}

u8 step;
void Serialportparsing(){
    if(Uart_GetRxFlag){
        //串口一转运方便调试
        serialPortForwarding(usart_rxbuffer, usart_rxlen);
			  
        // 检查接收到的数据并更新状态机
        if(CheckStringInBuffer(usart_rxbuffer, usart_rxlen, "Parameter:OK"))
        {
            if(step < 1) step = 1;
        }
        if(CheckStringInBuffer(usart_rxbuffer, usart_rxlen, "wifi ok"))
        {
            if(step < 2) step = 2;
        }
        if(CheckStringInBuffer(usart_rxbuffer, usart_rxlen, "ONENET MQTT Connected") ||
           CheckStringInBuffer(usart_rxbuffer, usart_rxlen, "MQTT Connected!"))
        {
            step = 3;
        }
        
        // 检查是否是MQTT下行控制指令
        if(CheckStringInBuffer(usart_rxbuffer, usart_rxlen, "\"method\":\"control\"") &&
           CheckStringInBuffer(usart_rxbuffer, usart_rxlen, "\"params\""))
        {
            step = 3;
            OLED_ShowCH(0,0,"1");
            // 解析ktd参数(新增)
            int temp_val = ParseMQTTParameterInt(usart_rxbuffer, usart_rxlen, "ktd");
            if(temp_val >= 0)
            {
                ktd_value = (u8)temp_val;
							 
            }
            
        }
        else if(step == 3)
        {
            // 兼容旧格式解析
            int temp_val = ParseMQTTParameterInt(usart_rxbuffer, usart_rxlen, "maxqt");
            if(temp_val >= 0) maxMq135 = (u8)temp_val;
            
            temp_val = ParseMQTTParameterInt(usart_rxbuffer, usart_rxlen, "maxyw");
            if(temp_val >= 0) maxMq2 = (u8)temp_val;
            
            temp_val = ParseMQTTParameterInt(usart_rxbuffer, usart_rxlen, "maxwd");
            if(temp_val >= 0) maxWd = (u8)temp_val;
            
            temp_val = ParseMQTTParameterInt(usart_rxbuffer, usart_rxlen, "maxsd");
            if(temp_val >= 0) maxSd = (u8)temp_val;
        }
			  
        // 清空接收缓冲区
        memset(usart_rxbuffer, 0, usart_rxlen);
        usart_rxlen = 0;
        Uart_GetRxFlag = 0;
    }
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART, Byte);
	while (USART_GetFlagStatus(USART, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

u8 step = 0;

// 检查接收缓冲区中是否包含指定字符串
uint8_t CheckStringInBuffer(uint8_t *buffer, uint16_t len, const char *str)
{
	uint16_t str_len = strlen(str);
	
	if(str_len == 0 || len < str_len) return 0;
	
	for(uint16_t i = 0; i <= len - str_len; i++)
	{
		if(memcmp(&buffer[i], str, str_len) == 0)
		{
			return 1;
		}
	}
	return 0;
}

/**
 * 解析MQTT JSON中的整数参数
 * 支持格式:{"method":"control","params":{"ktd":3}}
 * @param buffer: 接收缓冲区
 * @param len: 数据长度
 * @param param_name: 要解析的参数名
 * @return: 解析到的整数值,-1表示解析失败
 */
int ParseMQTTParameterInt(uint8_t *buffer, uint16_t len, const char *param_name)
{
    // 安全检查
    if(buffer == NULL || param_name == NULL || len == 0) return -1;
    
    // 将缓冲区转为字符串(添加结束符)
    char temp_buf[USART_RX_BUFFER_SIZE + 1];
    memcpy(temp_buf, buffer, len);
    temp_buf[len] = '\0';
    
    // 查找params对象起始位置
    char *params_start = strstr(temp_buf, "\"params\":{");
    if(params_start == NULL) return -1;
    
    // 移动到params对象内部
    params_start += 9; // 跳过 "\"params\":{"
    
    // 查找参数名
    char param_pattern[32];
    snprintf(param_pattern, sizeof(param_pattern), "\"%s\":", param_name);
    char *param_pos = strstr(params_start, param_pattern);
    
    if(param_pos == NULL) return -1;
    
    // 移动到参数值位置
    param_pos += strlen(param_pattern);
    
    // 跳过空格/制表符/换行符
    while(*param_pos == ' ' || *param_pos == '\t' || *param_pos == '\n' || *param_pos == '\r')
    {
        param_pos++;
        if(param_pos >= temp_buf + len) return -1;
    }
    
    // 检查是否是数字(支持负数)
    if(!isdigit(*param_pos) && *param_pos != '-') return -1;
    
    // 解析整数
    char *end_ptr;
    long value = strtol(param_pos, &end_ptr, 10);
    
    // 验证解析结果
    if(end_ptr == param_pos || value > INT_MAX || value < INT_MIN) return -1;
    
    return (int)value;
}

/**
 * 解析MQTT JSON中的浮点参数
 * @param buffer: 接收缓冲区
 * @param len: 数据长度
 * @param param_name: 要解析的参数名
 * @return: 解析到的浮点数值,NAN表示解析失败
 */
float ParseMQTTParameterFloat(uint8_t *buffer, uint16_t len, const char *param_name)
{
    // 安全检查
    if(buffer == NULL || param_name == NULL || len == 0) return NAN;
    
    // 将缓冲区转为字符串(添加结束符)
    char temp_buf[USART_RX_BUFFER_SIZE + 1];
    memcpy(temp_buf, buffer, len);
    temp_buf[len] = '\0';
    
    // 查找params对象起始位置
    char *params_start = strstr(temp_buf, "\"params\":{");
    if(params_start == NULL) return NAN;
    
    // 移动到params对象内部
    params_start += 9;
    
    // 查找参数名
    char param_pattern[32];
    snprintf(param_pattern, sizeof(param_pattern), "\"%s\":", param_name);
    char *param_pos = strstr(params_start, param_pattern);
    
    if(param_pos == NULL) return NAN;
    
    // 移动到参数值位置
    param_pos += strlen(param_pattern);
    
    // 跳过空格/制表符/换行符
    while(*param_pos == ' ' || *param_pos == '\t' || *param_pos == '\n' || *param_pos == '\r')
    {
        param_pos++;
        if(param_pos >= temp_buf + len) return NAN;
    }
    
    // 检查是否是数字(支持负数和小数点)
    if(!isdigit(*param_pos) && *param_pos != '-' && *param_pos != '.') return NAN;
    
    // 解析浮点数
    char *end_ptr;
    float value = strtof(param_pos, &end_ptr);
    
    // 验证解析结果
    if(end_ptr == param_pos) return NAN;
    
    return value;
}

/**
 * 判断浮点数是否为NAN(兼容嵌入式环境)
 * @param value: 要判断的浮点值
 * @return: 1表示是NAN,0表示不是
 */
int IsFloatNaN(float value)
{
    // NAN的特性:不等于自身
    return (value != value) ? 1 : 0;
}

// 发送整数数据到云端
void SendToCloud(const char *model_name, int value)
{
	char send_cmd[128];
	sprintf(send_cmd, "LC+Send:\"%s\":%d\r\n", model_name, value);
	Serial_SendString(send_cmd);
}

// 发送浮点数数据到云端
void SendToCloudFloat(const char *model_name, float value)
{
    // 检查是否为无效值
    if(IsFloatNaN(value)) return;
    
	char send_cmd[128];
	sprintf(send_cmd, "LC+Send:\"%s\":%.2f\r\n", model_name, value);
	Serial_SendString(send_cmd);
}

// 发送字符串数据到云端
void SendToCloudString(const char *model_name, const char *str_value)
{
    if(str_value == NULL) return;
    
	char send_cmd[128];
	sprintf(send_cmd, "LC+Send:\"%s\":\"%s\"\r\n", model_name, str_value);
	Serial_SendString(send_cmd);
}

//连接8266
u8 step0_sent = 0;
u8 isShow=1;
u8 lin=6;
void Esp8266Cont(){	   
	switch(step){
		case 0:
			if(step0_sent == 0)
			{ 
                char connect_cmd[512];
                snprintf(connect_cmd, sizeof(connect_cmd),
                         "LC+Connect:\"%s\",\"%s\",\"%s\",\"%s\"\r\n",
                         product_ID, CLIENT_ID, USERNAME, PASSWORD);
                Serial_SendString(connect_cmd);
				step0_sent = 1;
			}
			isShow?OLED_ShowCH(0,lin,"Parameter"):OLED_ShowCH(0,lin,"");
			break;
			
		case 1:
			isShow?OLED_ShowCH(0,lin,"await WIFI"):OLED_ShowCH(0,lin,"");
			break;
			
		case 2:
			isShow?OLED_ShowCH(0,lin,"await MQTT"):OLED_ShowCH(0,lin,"");
			break;
			
		case 3:
			isShow?OLED_ShowCH(0,lin,"MQTT  ONLINE"):OLED_ShowCH(0,lin,"");
			break;
	}
	
	Serialportparsing();
}

说明:更改对应的头文件

static const char *product_ID = "5af2tgcpsX";

// 设备名称

static const char *CLIENT_ID = "0001";

// MQTT用户名

static const char *USERNAME = "5af2tgcpsX";

// MQTT密码(Token)

static const char *PASSWORD = "";

反向控制部分

在ParseMQTTParameterInt修改物体模型名称即可

main

cpp 复制代码
ESP8266_Init(115200); //初始化


void oneNet(){
			if(time_count>=200){
			   Esp8266Cont();
				 maxMq2=maxMq2;
			   maxSd=maxSd;
			   maxMq135=maxMq135;
			   maxMq2=maxMq2;
//				 Serialportparsing();
				 time_count=0; 
//				 BEEP=!BEEP;
				 if(step>=3){
				    SendToCloud("hw",HW);
					  delay_ms(1);
					 	SendToCloud("qt",mq135);
					  delay_ms(1);
					 	SendToCloud("sd",humi);
					  delay_ms(1);
					 	SendToCloud("wd",temp);
					  delay_ms(1);
					 	SendToCloud("yw",mq2);
					  delay_ms(1);		
//					 	SendToCloud("qt",mq135);
//					  delay_ms(1);
//					 	SendToCloud("sd",humi);
//					  delay_ms(1);
//					 	SendToCloud("wd",temp);
//					  delay_ms(1);
//					 	SendToCloud("yw",mq2);
					  delay_ms(1);						 
				 }
			}


}
相关推荐
v先v关v住v获v取2 小时前
12米折叠式高空作业车工作臂设计9张cad+三维图+设计说明书
科技·单片机·51单片机
单片机系统设计2 小时前
基于STM32的水质检测系统
网络·stm32·单片机·嵌入式硬件·毕业设计·水质检测
唔好理总之好犀利3 小时前
FreeRTOS中断内使用taskENTER_CRITICAL()进入临界区
单片机·嵌入式硬件
csg11073 小时前
PIC单片机入门实战(一):PIC16F1824/PIC12F1822,从振荡器与Timer1开始
单片机·嵌入式硬件·物联网
清风6666663 小时前
基于单片机的车辆超载报警系统设计及人数检测设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
QQ_21932764553 小时前
基于单片机的自动售货机系统设计
单片机·嵌入式硬件
Herbert_hwt4 小时前
C语言结构体操作符详解:从入门到精通的全方位指南
c语言
Y1rong4 小时前
STM32之IIC
stm32·单片机
Nautiluss5 小时前
一起调试XVF3800麦克风阵列(九)
linux·人工智能·嵌入式硬件·音频·语音识别·dsp开发
代码游侠5 小时前
应用——MQTT客户端开发
服务器·c语言·开发语言·数据结构·算法