简介
在嵌入式的学习中不同的设备之间通信会使用不同的通信协议,例如串口、IIC、SPI等。最常用的应该就是串口了,但是每次发送数据的时候就会发现会有错误或者无法解析发送的数据。这就需要我们自己创建或者使用现成的数据格式。笔者最长使用的就是自定义数据格式或者就是Jason,Jason格式最常用的就是在云平台了。
按位(自定义数据格式)
按位发送和按位解码是最原始的数据格式,例如需要STM32的板子发送二氧化碳数值、土壤湿度数值、烟雾值、空气温湿度的值,这时候该如何发送数据?如何确定本次发送数据的长度?如何解析发送的数据?
发送数据时需要有一个数据头,可以是一串字母,也可以是一个符号,数据结尾也需要一个数据尾和数据头一样。
实例:发送二氧化碳浓度:600ppm;土壤湿度:30%;烟雾值:30ppm;空气湿度:40%;空气温度:25%。
数据发送
//定义一个共用体,空间与一个数组对应使用
typedef union
{
struct{
u16 CO2; //定义二氧化氮浓度
u16 Soil_humi; //定义土壤湿度
u16 Smoke; //定义烟雾
u16 humi; //定义空气湿度
u16 temp; //定义空气温度
}sc;
u16 Data[5];
}Data_TX;
Data_TX data_tx;
//将数据映射到数组中
data_tx.sc.CO2=600;
data_tx.sc.Soil_humi=31;
data_tx.sc.humi=40;
data_tx.sc.Smoke=30;
data_tx.sc.temp=25;
sprintf(Data,"oxfeData:%d:%d:%d:%d:%d}",data_tx.sc.CO2,data_tx.sc.Soil_humi,data_tx.sc.Smoke,data_tx.sc.humi,data_tx.sc.temp);
//使用串口将数组一个一个发送
//略
数据解析
使用串口发送数据oxfeData:600:31:30:40:26}给STM32板子,其中每个数据使用:隔开,从左到右的数据分别表示二氧化氮浓度,土壤湿度,烟雾浓度,空气湿度,空气温度。
#define DATA_TX_HEAD 0xfe //定义数据发送头
#define DATA_TX_TAIL '}' //定义数据发送尾
#define DATA_HEAD "Data" //定义数据头
#define DATA_MIDDLE ":" //定义数据格式
#define DATA_TAIL "}" //定义数据尾
typedef union
{
struct{
u16 CO2; //定义二氧化氮浓度
u16 Soil_humi; //定义土壤湿度
u16 Smoke; //定义烟雾
u16 humi; //定义空气湿度
u16 temp; //定义空气温度
}sc;
u16 Data[5];
}Data_TX;
Data_TX data_TX;
//串口中断函数的内容
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
read_data[counts++] = Res;//储存每一个接收的字符
if(Res==DATA_TX_HEAD){
Read_Data_Clear(); //清除接收缓存
}
else if(Res==DATA_TX_TAIL){
counts=0;//标志位复位
Data_analysis(DATA_HEAD,DATA_MIDDLE,DATA_TAIL);//自定义数据解析函数
}
}
}
//函数Read_Data_Clear()
void Read_Data_Clear(void){
memset(read_data,0,sizeof(read_data)); //将数组的内容全部置0,也就是简单的清除空间
}
//函数Data_analysis()解析数据使用的函数
/**********************************************
//*data_head 表示数据头
//*dmiddle 表示分割数据的符号,也是一种简单的自定义数据格式
//*data_tail 表示数据尾
**********************************************/
void Data_analysis(char *data_head,char *dmiddle,char *data_tail){
u16 temp=0;
if(strstr(read_data,data_head)){//判断是否有数据头
char *p=strstr(read_data,data_head);//将数据头的当前地址复制给指针p
//将指针移动到
while(*p++!=*dmiddle);//将指针移动到分割符号的后面一位
for(;*p!=*dmiddle;p++){ //从第一个分割符号到第二个数据符号之间的数据进行解析
temp=temp*10+((*p-'0'));
}
data_TX.sc.CO2=temp;//存储二氧化碳浓度数据
temp=0;
p++;//将指针移动到第二个分割符号的后面一位
//后面按照前面的方案解析数据
for(;*p!=*dmiddle;p++){
temp=temp*10+((*p-'0'));
}
data_TX.sc.Soil_humi=temp;//存储土壤湿度数据
temp=0;
p++;
for(;*p!=*dmiddle;p++){
temp=temp*10+((*p-'0'));
}
data_TX.sc.Smoke=temp;//存储烟雾数据
temp=0;
p++;
for(;*p != *dmiddle;p++){
temp=temp*10+((*p-'0'));
}
data_TX.sc.humi=temp;//存储空气湿度数据
temp=0;
p++;
for(;*p != *data_tail;p++){
temp=temp*10+((*p-'0'));
}
data_TX.sc.temp=temp;//存储空气温度数据
// printf("Data:%d:%d:%d:%d:%d}\r\n",data_TX.Data[0],data_TX.Data[1],data_TX.Data[2],data_TX.Data[3],data_TX.Data[4]);
}
}
此方法不是最优解还有更多的优化方法
Jason格式
Jason格式采用key:value的方式记录数据,非常直观,比XML简洁,因此很受欢迎。Jason格式的举例:
{
"CO2":600,
"Soil_humi":31,
"smoke":30,
"humi":40,
"temp":26,
}
数据发送
Jason有可以使用的库函数,但是发送数据会很占用空间。
typedef struct{
u16 CO2;
u16 soil_humi;
u16 smoke;
u16 humi;
u16 temp;
}Data_TX;
Data_TX datatx;
cJSON *json;
json = cJSON_CreateObject();
cJSON_AddNumberToObject(json,"CO2_data",datatx.CO2);
cJSON_AddNumberToObject(json,"Soil_humi_data",datatx.soil_humi);
cJSON_AddNumberToObject(json,"smoke_data",datatx.smoke);
cJSON_AddNumberToObject(json,"humi_data",datatx.humi);
cJSON_AddNumberToObject(json,"temp_data",datatx.temp);
char *a = cJSON_Print(json);
//通过串口发送函数发送数据
//代码略
这就是最简单粗暴的也是最节约内存的方案,但是这种发送数据的方法会很容易出错,需要多次调试和修改才能保证正确。
char Buff_jsaon[200]
sprintf(Buff_jsaon," {\n "
"\"CO2\":%d,"
"\"soil_humi\":%d,"
"\"smoke\":%d,"
"\"humi\":%d,"
"\"temp\":%d"
"\n}",datatx.CO2,datatx.soil_humi,datatx.smoke,datatx.humi,datatx.temp);
数据解析
解析数据也可以使用前面的方法解析数据,但是需要修改一下基本的原理。因为第二个:的前面不止有数据而已还有其他的干扰,所以需要修改一下原来的代码。
或者使用Jason格式的库函数,就可以很方便的解析数据。
有问题评论或私信 讨论群985432073