使用mqtt与onenet服务器通讯(实现发布与订阅)
使用mqtt与onenet服务器通讯
根据上一篇文章使用mqtt.fx与onenet服务器模拟通讯已经了解了设备与onenet的通信流程,接下来记录一下通过代码来实现。
参考资料:
1.MQTT指令
2.ESP8266代码编写
一、使用AT指令对MQTT进行设置
/*******************************************************************************
* func name : ESP8266_Init
* function : ESP8266初始化
* argument : void
* retval : void
* note : 倒数第四句中的xx是热点名称,yyyyy是热点密码
(名称和热点最好不要有中文字或特殊符号,容易连接不上)
* author : lu.ciana.598393@gmail.com
* data : 2025/06/13
*
********************************************************************************/
void ESP8266_Init(void)
{
ESP8266_Clear();
usart1_send_str( "0. AT+RST\r\n");
while(ESP8266_SendCmd("AT+RST\r\n", "OK")) //AT测试
delay_ms(500);
usart1_send_str( "1. AT\r\n");
while(ESP8266_SendCmd("AT\r\n", "OK")) //AT测试
delay_ms(500);
usart1_send_str("2. CWMODE\r\n");
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK")) //STA模式
delay_ms(500);
usart1_send_str("3. AT+CWDHCP\r\n");
while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK")) //启用 Station 的 DHCP
delay_ms(500);
usart1_send_str("4. CWJAP\r\n");
while(ESP8266_SendCmd("AT+CWJAP=\"xx\",\"yyyyyyy\"\r\n", "GOT IP")) //连接路由器
delay_ms(500);
usart1_send_str("5. ESP8266 Init OK\r\n");
}
二、设备连接成功后尝试连接onenet平台

!!!注意:该地方et为过期时间的时间戳,需要进行转化为当前时间后的时间或则日期,转化链接:时间戳转换工具
/*******************************************************************************
*
* func name : OneNet_SendData
* function : onenet初始化
* argument : void
* retval : void
* note : 在第三行代码中,aaaaa写入自己的设备名称,bbbbbbb写入自己的产品ID,
cccccc填入自己的password,password的获取请看上一篇文章
[使用mqtt.fx与onenet服务器模拟通讯]
* author : lu.ciana.598393@gmail.com
* data : 2025/06/17
*
*
********************************************************************************/
void OneNet_SendData(void)
{
ESP8266_Clear();
usart1_send_str("6. connect my device\r\n");
while(ESP8266_SendCmd("AT+MQTTUSERCFG=0,1,\"aaaaa\",\"bbbbbb\",\"cccccc\",0,0,\"\"\r\n", "OK")) //AT测试
ESP8266_Clear();
usart1_send_str("7. MQTTCONN\r\n");
while(ESP8266_SendCmd("AT+MQTTCONN=0,\"mqtts.heclouds.com\",1883,1\r\n", "OK")); //连接onenet云平台
}
三、设备发布消息

/*******************************************************************************
*
* func name : extract_value
* function : 解析onnet下发的JSON数据
* argument : const char* json:储存onenet回复的指令
const char* key: 需要查找的值
* retval : @char*:返回需要查找的数值
* note : 例:extract_value(buf,id);
在储存onnet1回复指令buf中查找id的值,以便于下面订阅时需要用到id
* author : lu.ciana.598393@gmail.com
* data : 2025/06/17
*
********************************************************************************/
char* extract_value(const char* json, const char* key)
{
char search_key[20]; // 用于存储构造的关键字
const char* start; // 用于指向关键字的起始位置
const char* end; // 用于指向值的结束位置
int length; // 用于存储值的长度
char* value; // 用于存储提取的值
// 构造查找的关键字,例如 "id" 或 "power"
snprintf(search_key, sizeof(search_key), "\"%s\":", key);
// 查找关键字
start = strstr(json, search_key);
if (start == NULL)
{
// printf("Key '%s' not found in JSON\n", key);
return NULL;
}
// 跳过关键字,找到值的开始位置
start += strlen(search_key);
// 检查值是否被引号包围(字符串类型)
if (*start == '"')
{
start++; // 跳过开头的引号
end = strchr(start, '"'); // 找到结尾的引号
if (end == NULL)
{
// printf("Invalid JSON format for key '%s'\n", key);
return NULL;
}
length = end - start; // 计算值的长度
value = (char*)malloc(length + 1); // 分配内存
if (value == NULL)
{
// printf("Memory allocation failed\n");
return NULL;
}
strncpy(value, start, length); // 复制值
value[length] = '\0'; // 添加字符串结束符
}
else // 数字类型
{
end = start;
while (*end >= '0' && *end <= '9') // 找到数字的结束位置
{
end++;
}
length = end - start; // 计算数字的长度
value = (char*)malloc(length + 1); // 分配内存
if (value == NULL)
{
// printf("Memory allocation failed\n");
return NULL;
}
strncpy(value, start, length); // 复制数字
value[length] = '\0'; // 添加字符串结束符
}
return value;
}
/*******************************************************************************
* func name : OneNet_Pub
* function : 设备设置为发布模式并发布数据
* argument : @temp :填写自己所要发布的数据
* retval : void
* note : 在第三行代码中,aaaaa写入自己的设备名称,bbbbbbb写入自己的产品ID,
这里我需要发布检测到的温度发送到onenet,通过传参的方式传进来发布
* author : lu.ciana.598393@gmail.com
* data : 2025/06/13
*
********************************************************************************/
void OneNet_Pub(float temp)
{
ESP8266_Clear();
usart1_send_str("8. sub reply topic\r\n");
while(ESP8266_SendCmd("AT+MQTTSUB=0,\"$sys/bbbbbbb/aaaaa/thing/property/post/reply\",0\r\n", "OK")) //AT发布数据
ESP8266_Clear();
usart1_send_str("9. pub temp\r\n");
snprintf(buf, sizeof(buf),"AT+MQTTPUB=0,\"$sys/bbbbbb/aaaaa/thing/property/post\",\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{\\\"temp\\\":{\\\"value\\\":%.1f\\}}}\",0,0\r\n",temp);
usart1_send_str(buf);
while(ESP8266_SendCmd(buf, "OK"))
usart1_send_str("10. Pub OK\r\n");
}
四、设备订阅消息

/*******************************************************************************
*
* func name : OneNet_Sub
* function : 设备设置为订阅模式
* argument : void
* retval : void
* note : 在第三行代码中,aaaaa写入自己的设备名称,bbbbbbb写入自己的产品ID
* author : lu.ciana.598393@gmail.com
* data : 2024/04/22
*
*
********************************************************************************/
void OneNet_Sub(void)
{
ESP8266_Clear();
usart1_send_str("8. sub topic\r\n");
while(ESP8266_SendCmd("AT+MQTTSUB=0,\"$sys/bbbbbbb/aaaaa/thing/property/set\",0\r\n", "OK")) //AT订阅消息
usart1_send_str("9. Sub OK\r\n");
}
/*******************************************************************************
*
* func name : OneNet_WaitMessage
* function : 等待订阅的下发数据
* argument : void
* retval : void
* note : 进入循环等待订阅下发的指令调节灯泡亮度,在onenet中定义的标识符为power,
因此解析JSON格式发过来的指令得到power的数据
* author : lu.ciana.598393@gmail.com
* data : 2024/04/22
*
********************************************************************************/
void OneNet_WaitMessage(void)
{
char *result1 = NULL;
char *result2 = NULL;
int id =0 ;
int power =0;
if(strstr((const char*)esp8266_buf,"power") != '\0')
{
printf("%s\r\n",esp8266_buf);
result1 = extract_value((const char*)esp8266_buf,"id");
result2 = extract_value((const char*)esp8266_buf,"power");
id = atoi(result1);
power = atoi(result2);
// ESP8266_Clear();
OneNet_Sub_Reply(id);
printf("id = %d\r\n",id);
printf("power = %d\r\n",power);
TIM_SetCompare4(TIM3,power);
delay_ms(20);
ESP8266_Clear();
}
}

浙公网安备 33010602011771号