首页 > 其他分享 >STM32与华为云IoTDA的上传下达

STM32与华为云IoTDA的上传下达

时间:2024-08-15 16:23:14浏览次数:11  
标签:IoTDA cJSON 上传下达 JSON data STM32 json root id

实现了Mqtt的链接后,需要与华为云IoTDA进行属性上报、命令解析、命令应答等。需要参照华为云官网给出的API参考使用前必读_设备接入 IoTDA

1. 华为云函数构建

1.1. 设备属性上报

该功能就是用于设备按产品模型中定义的格式将属性数据上报给平台,最通常的温度信息设备状态等,上报后上位机可以通过设备影子消息来获取上报的属性。

Topic

上行:$oc/devices/{device_id}/sys/properties/report

参数说明

字段名

必选/可选

类型

参数描述

services

必选

List<ServiceProperty>

设备服务数据列表(具体结构参考下表ServiceProperty定义表)。

ServiceProperty结构定义:

字段名

必选/可选

类型

参数描述

service_id

必选

String

设备的服务ID,由创建的产品模型确定。

properties

必选

Object

设备服务的属性列表,具体字段在设备关联的产品模型中定义,可以设置多个字段。

event_time

可选

String

设备采集数据UTC时间(格式可选:秒级别:yyyyMMdd'T'HHmmss'Z',毫秒级别:yyyy-MM-dd'T'HH:mm:ss.SSS'Z'),如:20161219T114920Z或者2020-08-12T12:12:12.333Z。

设备上报数据不带该参数或参数格式错误时,则数据上报时间以平台时间为准。

官方示例
Topic: $oc/devices/{device_id}/sys/properties/report  
数据格式:  
{
    "services": [{
            "service_id": "Temperature",
            "properties": {
                "value": 57,
                "value2": 60
            },
            "event_time": "20151212T121212Z"
        },
        {
            "service_id": "Battery",
            "properties": {
                "level": 80,
                "level2": 90
            },
            "event_time": "20151212T121212Z"
        }
    ]
}

event_time项为可选项,在实现过程中没有选用

封装函数

共封装了两个函数分别为”Mqtt_Json_Publish“,”Mqtt_Number_Publish“分别向属性中传输int型数字和Json结构,由于项目中除了4G通信还有蓝牙通信的方式所以有的属性类型直接设置成了json对象,如果需要传输其他类型只需对应修改构建json;

#define TOPIC_UP	("$oc/devices/{device_id}/sys/properties/report")


/**
  * @brief  组装json数据并发布
  * @param  属性的json字符串,string:属性名,data:属性内容json
  * @retval 0:构建json失败;1成功
  */
uint8_t Mqtt_Json_Publish(const char *string,cJSON *data)
{
	cJSON *root = cJSON_CreateObject(); // 创建根对象  
	if (root == NULL)
	{  
		printf("Failed to create root JSON object.\n"); 	
		return 0;  
	}
	 cJSON *services = cJSON_AddArrayToObject(root, "services"); // 在根对象中添加一个名为 "services" 的数组  
	if (services == NULL)
	{  
		printf("Failed to add services array to root JSON object.\n");  
		cJSON_Delete(root); 
		return 0;  
	}
	cJSON *service = cJSON_CreateObject(); // 创建服务对象  
	if (service == NULL)
	{  
		printf("Failed to create service JSON object.\n");  
		cJSON_Delete(root);
		return 0;  
	}  

	cJSON *properties = cJSON_CreateObject(); // 创建属性对象  
	if (properties == NULL)
	{  
		printf("Failed to create properties JSON object.\n");  
		cJSON_Delete(service);  
		cJSON_Delete(root); 		
		return 0;  
	}
	//将服务ID添加到属性对象
	cJSON_AddStringToObject(service, "service_id", "id"); 
	// 将环境对象添加到属性对象  
	cJSON_AddItemToObject(properties, string, data);  
	// 将属性对象添加到服务对象  
	cJSON_AddItemToObject(service, "properties", properties); 
	// 将服务对象添加到服务数组  
	cJSON_AddItemToArray(services, service); 
	// 将根对象转换为 JSON 字符串  
	char *lte_json_body = cJSON_Print(root);  
	if (lte_json_body == NULL)
	{  
		printf("Failed to print JSON object.\n");    
		cJSON_Delete(properties);  
		cJSON_Delete(service);  
		cJSON_Delete(root);  
		return 0;  
	}  
	if(is_mqtt_connected==1)//mqtt 连接成功
	{
		// 打印 JSON 字符串  
//		printf("HUAWEI YUN JSON: %s\n", lte_json_body); 
		//发送
		memset(mqtt_topic,0,TOPIC_LENGTH);
		sprintf(mqtt_topic,TOPIC_UP);
		Mqtt_Publish(mqtt_topic,(unsigned char *)lte_json_body);
	}
    // 释放内存  
	cJSON_Delete(root);  
	cJSON_free(lte_json_body); 
	return 1;
}

/**
  * @brief  组装json数据并发布
  * @param  属性的json字符串,string:属性名,state:属性内容为int
  * @retval 0:构建json失败;1成功
  */
uint8_t Mqtt_Number_Publish(const char *string,int state)
{
	cJSON *root = cJSON_CreateObject(); // 创建根对象  
	if (root == NULL)
	{  
		printf("Failed to create root JSON object.\n"); 	
		return 0;  
	}
	 cJSON *services = cJSON_AddArrayToObject(root, "services"); // 在根对象中添加一个名为 "services" 的数组  
	if (services == NULL)
	{  
		printf("Failed to add services array to root JSON object.\n");  
		cJSON_Delete(root); 
		return 0;  
	}
	cJSON *service = cJSON_CreateObject(); // 创建服务对象  
	if (service == NULL)
	{  
		printf("Failed to create service JSON object.\n");  
		cJSON_Delete(root);
		return 0;  
	}  

	cJSON *properties = cJSON_CreateObject(); // 创建属性对象  
	if (properties == NULL)
	{  
		printf("Failed to create properties JSON object.\n");  
		cJSON_Delete(service);  
		cJSON_Delete(root); 		
		return 0;  
	}
	//将服务ID添加到属性对象
	cJSON_AddStringToObject(service, "service_id", "monitor&control"); 
	// 将环境对象添加到属性对象  
	cJSON_AddNumberToObject(properties, string, state);  
	// 将属性对象添加到服务对象  
	cJSON_AddItemToObject(service, "properties", properties); 
	// 将服务对象添加到服务数组  
	cJSON_AddItemToArray(services, service); 
	// 将根对象转换为 JSON 字符串  
	char *lte_json_body = cJSON_Print(root);  
	if (lte_json_body == NULL)
	{  
		printf("Failed to print JSON object.\n");    
		cJSON_Delete(properties);  
		cJSON_Delete(service);  
		cJSON_Delete(root);  
		return 0;  
	}  
	if(is_mqtt_connected==1)//mqtt 连接成功
	{
		// 打印 JSON 字符串  
//		printf("HUAWEI YUN JSON: %s\n", lte_json_body); 
		//发送
		memset(mqtt_topic,0,TOPIC_LENGTH);
		sprintf(mqtt_topic,TOPIC_UP);
		Mqtt_Publish(mqtt_topic,(unsigned char *)lte_json_body);
	}
    // 释放内存  
	cJSON_Delete(root);  
	cJSON_free(lte_json_body); 
	return 1;
}
1.2. 解析平台命令下发

用于平台向设备下发设备控制命令。平台下发命令后,需要设备及时将命令的执行结果返回给平台,如果设备没回响应,平台会认为命令执行超时。

Topic

下行: $oc/devices/{device_id}/sys/commands/request_id={request_id}

上行:$oc/devices/{device_id}/sys/commands/response/request_id={request_id}

说明:
  • {request_id}用于唯一标识这次请求。设备侧收到下行请求的topic带该参数时,上行响应的topic需要将该参数值返回给平台。
  • 应用通过平台下发设备命令时,平台会生成唯一ID(command_id)用于标识该命令,并返回给应用。同时该唯一标识会通过设备命令下行Topic中的requst_id携带给设备。
  • 设备无法提前感知该request_id,在订阅该Topic时请使用通配符“#”来替代“request_id={request_id}”即为:$oc/devices/{device_id}/sys/commands/#。

命令订阅是我们对request_id可以用“#”来替代,但在上报响应式必须使用下发的request_id,因此在下发命令后需要解析对应的request_id。

下行请求参数说明

字段名

必选/可选

类型

参数描述

object_device_id

可选

String

  • 平台下发时,若为直连设备,不携带该参数。
  • 平台下发时,若为网关子设备,该参数为Topic中设备的子设备Id。

service_id

可选

String

设备的服务ID,在设备关联的产品模型中定义。

command_name

可选

String

设备命令名称,在设备关联的产品模型中定义。

paras

必选

Object

设备命令的执行参数,具体字段在设备关联的产品模型中定义。

上行响应参数说明

命令应答的json格式,具体字段在设备关联的产品模型中定义。

字段名

必选/可选

类型

参数描述

result_code

可选

Integer

标识命令的执行结果,0表示成功,其他表示设备执行结果为失败。不带默认为0。

response_name

可选

String

命令的响应名称。

paras

可选

Object

命令的响应参数,具体字段在设备关联的产品模型中定义。

下行请求示例
Topic: $oc/devices/{device_id}/sys/commands/request_id={request_id} 
数据格式:  
{
  "object_device_id": "{object_device_id}",
  "command_name": "ON_OFF",
  "service_id": "WaterMeter",
  "paras": {
    "value": "1"
  }
}
上行响应示例
Topic:$oc/devices/{device_id}/sys/commands/response/request_id={request_id}
数据格式:  
{
    "result_code": 0,
    "response_name": "COMMAND_RESPONSE",
    "paras": {
        "result": "success"
    }
}
命令解析函数封装
int8_t Parse_MqttCmd(uint8_t *data,char * request_id)
{
	// 寻找JSON数据的开始位置  
	const char *json_start = strstr((char *)data, "{"); 
	if (json_start == NULL) 
	{  
		printf("JSON data not found in the received string.\n");  
		return -1;  
	}  
	size_t json_length = strlen(json_start);
	
	// 分配内存并复制JSON数据  
	char *json_data = (char *)malloc(json_length + 1);  
	if (json_data == NULL) {  
		printf("Memory allocation failed.\n");  
		return -1;  
	}  
	strncpy(json_data, json_start, json_length);  
	json_data[json_length] = '\0'; // 添加null终止符  
	 // 检查JSON数据是否完整  
	if (!is_json_complete(json_data)) 
	{  
		printf("Received JSON data is incomplete or malformed.\n");  
		cJSON_free(json_data);  
		return -1;  
	}  
	// 如果完整,则可以进一步解析JSON数据  
	cJSON *root = cJSON_Parse(json_data);  
	if (root == NULL)
	{  
		printf("Failed to parse JSON data.\n");  
		cJSON_free(json_data);  
		return -1;  
	}  
	// ... 在这里处理JSON数据 ...  
		
	// 获取并打印"command_name"字段的值  
	cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "command_name");

	//判断"command_name"字段的值选择控制类型
	if (name && cJSON_IsString(name) && (name->valuestring != NULL))
	{  
		char * command_name = name->valuestring;
		printf("Name: %s\n", command_name);
		
		//判断控制命令
		if(strstr(name->valuestring,"light"))
		{
			cJSON *paras = cJSON_GetObjectItemCaseSensitive(root, "paras");/*获取obj中的paras的json*/
			if (paras && cJSON_IsObject(paras))
			{
				cJSON *sw_item = cJSON_GetObjectItemCaseSensitive(paras, "sw");/*开关状态*/  
				if (sw_item != NULL && cJSON_IsBool(sw_item))
				{  
					int sw_value = sw_item->valueint; // cJSON使用int来表示bool值  
					printf("sw: %d\n", sw_value); // 输出sw的值,1代表true,0代表false 
					//控制命令发送
					if(sw_value == 1)
					{
						//开灯
					}else{
						//关灯
					}
					//设备响应
					Mqtt_Cmd_Resp(1,request_id);
				}else
				{  
					printf("Failed to get 'sw' value or it's not a boolean.\n");  
				}  
			}					
		}
	}
	// 释放资源 
	cJSON_Delete(root);  
	cJSON_free(json_data);  
	return 1;  
}

解析命令时需要从云端下发的数据中提取出一个完整的json对象来,这里通过观察下发的消息后采用从第一个“{”开始截取,通过函数判断json是否完整。

int is_json_complete(const char *json_data)
{  
    // 检查是否有明确的开始和结束标志  
    if (json_data == NULL || json_data[0] != '{' || json_data[strlen(json_data) - 1] != '}')
	{  
		printf("\r\n incomplete \r\n");
        return 0; // JSON不完整  
    }  
      
    // 尝试解析JSON数据,如果解析失败,则可能不完整  
    cJSON *root = cJSON_Parse(json_data);  
    if (root == NULL)
	{  
		printf("Parse error before: %s\n", cJSON_GetErrorPtr()); 
		printf("\r\n malformed \r\n");
        return 0; // JSON解析失败,可能不完整  
    }  
      
    cJSON_Delete(root); // 不论完整与否,都需要释放cJSON对象  
    return 1; // JSON完整  
}  
上报响应函数封装

上报响应需要先解析下发命令的request_id,在调用解析之前进行处理

void Mqtt_Cmd_Resp(char * request_id)
{
	// 创建一个根对象  
    cJSON *root = cJSON_CreateObject();  
    // 添加result_code字段  
    cJSON_AddNumberToObject(root, "result_code", 0);  
    // 创建一个嵌套的paras对象  
    cJSON *paras = cJSON_CreateObject();  
    cJSON_AddStringToObject(paras, "result", "success");  
    // 将paras对象添加到根对象  
    cJSON_AddItemToObject(root, "paras", paras);  
    // 打印JSON内容  
    char *json_string = cJSON_Print(root);  
    printf("%s\n", json_string);  
	memset(mqtt_topic,0,TOPIC_LENGTH);
    //组装上报反馈主题
	sprintf(mqtt_topic,Topic_CMD_RESP,request_id);
	printf("mqtt_topic:%s\r\n", mqtt_topic);  

	Mqtt_Publish(mqtt_topic,(unsigned char *)json_string);
	
    // 释放分配的内存  
    cJSON_free(json_string);  
    cJSON_Delete(root);  
}

2. 主函数调用

在主函数调用之前需要了解上一篇文章

/*此代码只为样式调用部分,其他相关初始化为显示*/

int main(void)
{
    ...//其他初始化
    
    //初始化4G模块
    LTE_Init();

    while(1)
    {
        /*采用串口3作为与4G模块的通信串口*/
        /*串口3采用DMA+空闲中断进行接收*/
        /*中断回调函数等见其他文章*/
        /*usart3--接收cat1数据接收*/
    	if(g_USART3_Recv_Flag)
    	{
    		g_USART3_Recv_Flag=0;
            /*串口数据解析开始*/
            //mqtt未连接
            if(is_mqtt_connected == 0)
            {
                LTE_Mqtt_Connect(data);
            }else{
                //mqtt连接成功
                
                //判断返回值,返回为发布应答
                if(data[0]>>4 == PUBACK)
                {
                    //发布消息成功
                    //发布消息计数值减1,发布是进行了自加见上一篇文章
                    mqtt_publish_count--;
                    if(mqtt_publish_count < 0) mqtt_publish_count = 0;
                    publish_overtime = 0;
                }
        		printf("------%d\r\n",data[0]>>4);
        		//判断返回值,返回值为服务器发布订阅消息
        		if(data[0]>>4 == PUBLISH)//解析
        		{	
        			//查找订阅消息的中json数据的开头{以及查找request_id
        			for(int i=0;i<len;i++)
        			{
                        /*request_id开始前为“=”,长度为36,截取request_id*/
        				if(data[i]==0x3D)
        				{
        					memset(request_id,0,strlen(request_id));
        					memcpy(request_id,&data[i+1],36);
        					printf("Extracted request_id: %s\n", request_id);
        				}		
        				if(data[i]==0x7b)//判断为“{”
        				{
        					memset(mqtt_cmd,0,strlen(mqtt_cmd));
        					sprintf(mqtt_cmd, "%s", data+i); 
                            /*调用解析函数*/
        					Parse_MqttCmd((uint8_t *)mqtt_cmd,request_id);
        					break;
        				}
        			}
        		}
		
        		//判断返回值,返回值为ping消息
        		if(data[0]>>4 == PINGRESP)
        		{
        			//发布消息成功
        			mqtt_ping_count--;
        			if(mqtt_ping_count < 0) mqtt_ping_count = 0;
        			ping_overtime = 0;
        		}
        	}
            /*串口数据解析结束*/
    		__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
    		HAL_UART_Receive_DMA(&huart3, (uint8_t*)g_USART3_Recv_Data, RECV_DATA_MAX_LEN);
    	}

        if(上报属性条件)
        {
            Mqtt_Json_Publish("属性名",内容);
        }
    }
}

至此已经能够通过stm32+有人4G模块实现了与华为云的上传下达,后续将介绍上位机APP接口调用,最终形成一个完整demo

标签:IoTDA,cJSON,上传下达,JSON,data,STM32,json,root,id
From: https://blog.csdn.net/LJ_96/article/details/141225575

相关文章

  • 零基础STM32单片机编程入门(三十四) JDY-31蓝牙模块实战含源码
    文章目录一.概要二.JDY-31蓝牙模块主要性能参数三.JDY-31蓝牙模块主芯片BK3432内部框图四.BK3432参考设计五.JDY-31蓝牙模块与单片机通讯方法1.与STM32F103板子硬件连接2.JDY-31蓝牙模块AT指令介绍六.STM32单片机与JDY-31蓝牙模块通讯实验1.硬件准备2.软件工程3.软......
  • 基于STM32设计的自动充放电测试系统(局域网)(208)
    文章目录一、前言1.1项目介绍【1】项目功能介绍【2】设计实现的功能【3】项目硬件模块组成1.2设计思路【1】整体设计思路【2】ESP8266模块配置【3】上位机开发思路1.3项目开发背景【1】选题的意义【2】可行性分析【3】参考文献【4】项目背......
  • 基于STM32的边缘计算实时数据处理可视化系统:嵌入式C++、 FreeRTOS、Kafka、Spring Bo
    一、项目概述本项目旨在设计并实现一个基于STM32的边缘计算实时数据处理系统。该系统能够在边缘设备端进行数据采集、预处理,并将处理后的数据实时传输到后端服务器进行进一步分析和存储。本项目主要解决以下问题:减轻后端服务器的数据处理负担,提高系统整体效率降低......
  • 基于STM32单片机智能浇花系统/大鹏灌溉/土壤湿度控制系统/自动灌溉/ 手机显示控制/环
    ⭐项目概述设计了一个基于STM32单片机智能浇花系统,该系统集成了多种环境传感器,包括土壤湿度、温度、光照强度和水位/雨水传感器,实现对环境参数的实时监测。通过OLED显示模块,系统能够直观展示环境数据,而ESP8266WiFi模块的加入则实现了数据的远程传输和监控,极大提升了管理者与系......
  • STM32&低功耗与备用备份区域
    STM的备份备用区域其实就是两个区块:BKP和RTC。低功耗则其实是STM32四种模式中的三种耗能很低的模式。目录一:备用区域1.BKP2.RTC二:低功耗模式1.睡眠模式:2.停机模式:3.待机模式:一:备用区域1.BKPBKP就是一个备份寄存器,大小不是一定的。但基本单位都是16位。所谓的的备......
  • STM32&IIC与SPI详解
    单片机里的通信协议其实蛮多的,IIC;SPI;MQTT;CAN;包括串口也是一种通信协议。而串口通信虽然实现了全双工,但需要至少三根线,为了节省这一根线的成本,于是IIC诞生了。目录一.IIC协议1.IIC的结构2.IIC的特点3.IIC的通信时序4.具体配置(32HAL库版)二.SPI协议1.SPI的结构2.SPI的特......
  • freeRTOS入门学习-基于STM32F103C8T6最小系统板-创建任务_声光色彩
    首先重温一下任务的三大要素:        ·做何事(函数)    ·栈(每个任务都应该有自己独享的栈)    ·优先级(非必要的因素,但是有了优先级可以处理更多的任务)一、如何创建任务:    当一个任务被切换出去之后,要想再找到他,应该去到某个链表里边......
  • STM32 H7系列 全中文HAL&LL库使用手册 中英双语对照 GPT机翻 共4020页、约152万字
    STM32H7系列全中文HAL_LL库使用手册,中英文双语对照阅读。内容、格式对照官方原文,含标签导航及目录跳转。全文GPT机翻,除人工翻译外,相对更加贴合原文原意,双语版防止翻译错误方便对照。全文:4020页,约152万字,2022年12月版本,当前官网最新版。 *******下有更多展示图片********......
  • [STM32]如何正确的安装和配置keil?(详细)
    一、我们为什么需要keil?    对于嵌入式开发的硬件来讲STM32可以说有着不可撼动的地位,它可能是很多人入门嵌入式开发接触到的第一款芯片,其强大的生态和大量开放的源代码也深受开发者的喜爱。对于嵌入式开发的软件来讲,keil绝对是在一届软件中脱颖而出的,它是一款集成了......
  • STM32在Keil5中DeBug界面可以正常运行但是正常运行就失败
    项目场景:提示:这里简述项目相关背景:使用STM32CubeMX建立项目生成基础代码在Keil5MDK中编写STM32F03RCT6程序问题描述提示:这里描述项目中遇到的问题:在DeBug界面可以正常运行但是一旦进行重启自启动芯片就不会正常运行在Debug运行是会卡在一下但是后续运行可继续运行......