查看: 16841|回复: 0

[8月赛] 【开源盛世】onenet实战篇(二)

[复制链接]

26

主题

256

帖子

873

积分

高级会员

Rank: 4

积分
873
发表于 2017-8-25 16:53:15 | 显示全部楼层 |阅读模式
本帖最后由 chenguang3312 于 2017-8-25 17:01 编辑
ONENET实战篇(二)







                           主讲内容:

                                      1. 使用esp8266进行http格式API的发送和服务器数据的获取

                                        2. 基于麒麟座第三方API接入演示

                                      3.  使用C库在设备层做json格式解析
                                      4. 使用cjson库对json格式解析
                                      5. 关于ONENET 麒麟座v2.4 EDP例程服务器命令接收与解、析故障分析





                                                       部分理论内容来自互联网,本教程只为方便广大初学者学习接入云平台,禁止用于一切商业用途,转载请标明作者及来源。








使用esp8266进行http格式API的发送和服务器数据的获取


首先使用8266连接上目标服务器(若目标服务器是第三方服务器参考上一篇教程《esp8266多连接模式设配置》)

以单链接模式为例,多连接略微不同详细内容参考8266模块说明书AT指令集
1.  AT+CWMODE=1
2.  AT+RST
3.  AT+CWJAP="zz","123456789"           //要连接无线名称和密码
//使用多连接时候加上{ AT+CIPMUX=1}
4.  AT+CIPSTART="TCP","116.62.81.138",80    //目标服务器的IP地址
使用多连接时候的连接格式//{AT+CIPSTART=1,"TCP","116.62.81.138",80}
5.  AT+CIPSEND=113                                     //113是发送http报文长度
多连接模式时发送格式//{ AT+CIPSEND=1,113 }

返回内容如图所示【图为多连接模式下返回内容】
QQ截图20170818210416.png

       +IPD1,263{"results":[{"location":{"id":"WW8P3NH2TPDT","name":"太原","country":"CN","path":"太原,太原,山西,中国","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"now":{"text":"多      云","code":"4","temperature":"23"},"last_update":"2017-08-18T20:55:00+08:00"}]}  1,CLOSED


   这是esp8266返回的完整格式 +IPD,连接id,数据长度  花括号内是完整的json格式数据,如果是单连接时,返回格式没有连接ID这一位数据。  具体json格式解析教程请关注下一篇《使用c库对json格式解析》和《使用cjson库进行json格式解析》



麒麟座第三方API接入方案




   以麒麟座v2.4  裸机EDP协议为例,接入心知天气API,其他协议略作参考。测试开发板仍为麒麟座V2.4(其他开发板参考 EDP 协议非官方开发板上移植》)


   1.下载v2.4 裸机  EDP例程源码,并打开 找到平台命令解析部分,红色框内为stm32串口给8266模块发送第三方API接入指令。设置apitime的条件,使设备每隔一段时间获取一次心知天气网的气象数据。(apitime<30只为演示用,实际条件自定)
QQ截图20170819002948.png

2.Apitime 在定时器4中断中 +
QQ截图20170819003902.png



使用C库在设备层做json格式解析

首先要明白,什么是json格式,json格式用在什么场合,为什么物联网设备层要对             json格式进行解析以及c语言库对json格式解析的原理。
     JSON,全称是JavaScript Object Notation。它是基于JavaScript编程语言ECMA-262 3rdEdition-December 1999标准的一种轻量级的数据交换格式,主要用于跟服务器进行交换数据。
JSON建构有两种结构:
  1. “名称/对的集合,可以理解为对象
2.值的有序列表,可以理解为数组。

表示名称 / 值对
  最简单的形式,可以用下面这样的 JSON 表示"名称 / 值对":
  { "firstName": "Brett" }
  表示 firstName=Brett
  当将多个"名称 / 值对"串在一起时,创建包含多个"名称 / 值对"的 记录,例如:
{ "firstName": "Brett", "lastName":"hah", "data": "15646" }


表示数组
  当需要表示一组值时,JSON 不但能够提高可读性,而且可以减少复杂性。
  如果使用 JSON,就只需将多个带花括号的记录分组在一起:
  { "people": [
  { "firstName": "Brett", "lastName":"McLaughlin", "email": "aaaa" },
  { "firstName": "Jason", "lastName":"Hunter", "email": "bbbb"},
  { "firstName": "Elliotte", "lastName":"Harold", "email": "cccc" }
  ]}
       简单了解下json结构,回归正题(如若想深入接触,请上网查阅官方资料),既然json主要用于和服务器进行数据交互,那么物联网设备必然要完成对发送数据进行json格式的封装,和接收到json数据对其进行解析的任务。
      我们首先来看下本次要解析的json数据。如下:
{"results":[{"location":{"id":"WW8P3NH2TPDT","name":"太原","country":"CN","path":"太原,太原,山西,中国","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"now":{"text":"小雨","code":"13","temperature":"21"},"last_update":"2017-08-19T01:00:00+08:00"}]}


这是在心知天气网获取的太原地区的实时气象资料,我们来看下麒麟v2.4例程是如何做EDP服务器下发命令解析的(中移的 web应用端下发的控制指令好像并不是json结构,但我们试着看看能否得到启发)

QQ截图20170819100328.png

可以看出在硬件收到平台命令后,会执行一次NET_DEVICE_GetIPD(0);函数   并把返回值赋值给dataPtr dataPtr明显是个char型指针。所以NET_DEVICE_GetIPD(0);函数是命令解析的核心,打开此函数:
QQ截图20170819101358.png
函数开始定义了 char型数组sByte[],这个数组是用来存放C库解析出来的字符串数据的, *ptrIPD是一个数据指针,在调试时候可以通过打印这个指针内容来查看实际解析到的数据。  在源码467行,执行了一次搜索IPD 头的动作(因为8266接收到的数据 格式为 “+IPD,XX: XXXXXXXX ”  )首先在接收缓存区的字符串中寻找“IPD”字符 如果能找到 说明有正常接收的数据; 这里用C库的方式进行解析,主要用的是strstr()函数,它的头文件为   string.h
  函数原型strstr(str1,str2)

函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2str1中首次出现的地址;否则,返回NULL
QQ截图20170819105536.png

找到IPD 找“,”逗号和冒号“:”之间的XX就是接收到的数据长度(xx是字符型数据),将XX读取出来存入sByte【】数组中, 利用atoi函数 将字符转为数值形式,并赋值给byte,通过for循环(483行)将剩余数据(除去”+IPD,XX :”)重新写入缓存区Buf内,这时就去掉了8266 的“IPD”数据头.
同样我们在json解析的时候,对于8266接收到 的数据也需要做去  IPD  头处理,提取出json格式后,用C库中strstr()函数模仿上述操作,搜索关键字,读取有用数据的方式来进行json格式的解析。

QQ截图20170819111255.png
QQ截图20170819111514.png

模仿去 IPD头的方式,用C库进行 读取 nametextcodetemperature的值,存入自己定义的数组里面 num是用来演示而定义的结构)
typedefstruct
{
      
unsigned  char name[16];
unsigned       char code[16];
unsigned       char text[16];
unsigned       char temperature [8];
              
}NUM;

注:如果你在实际移植过程中 遇到有接收到的数据,但是提取完整,或者接收不到完整数据,请关注由本人编写的《关于ONENET 麒麟座v2.4 EDP例程 服务器命令接收与解、析故障分析》




   使用cjson库对json格式解析
     Json协议广泛的应用于服务器层数据交互,不仅是因为它的可读性好,编码难度低,代码易懂,可扩展性好,最重要的是解码也非常方便。 Cjson是一款很好用的json解析器,cjson源码在官网可以免费下载到。本次主要讲解如何使用cjsonjson格式解析,不会涉及到json格式创建和值对的修改。

     cJSON存储的时候是采用链表存储的,其访问方式很像一颗树。每一个节点可以有兄妹节点,通过next/prev指针来查找,它类似双向链表;每个节点也可以有孩子节点,通过child指针来访问,进入下一层。
   先来个例子,感受下cjson的解析步骤。
     {   
      "name": "Mr
chen",   
      "text":{   
       "weight":     200,   
       "height":    100,   
        "age":24   
                     }   
      }

解析出text weight的值存入person结构体中
typedef struct
{
unsigned  char name[16];
unsigned  char weigh[16];
unsigned  char age[16];
           
} person;
1.cJSON_Parse:json数据解析成json结构体
cJSON *root = cJSON_Parse(project);


2. cJSON_GetObjectItem:获取某个元素
cJSON *root1 = cJSON_GetObjectItem(root,"text");   


3. 对我们刚取出来的对象text,多次调用cJSON_GetObjectItem()函数,来获取对象的成员。
item=cJSON_GetObjectItem(root1,"weight");


4. memcpy()函数进行内存拷贝   strlen()函数获取拷贝数据长度,将weight对应的值(200)写入person.weight内。
memcpy(person.weight,item->valuestring,strlen(item->valuestring));


5. 通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。
          cJSON_Delete(root)






小结:使用cJSON_Parse()函数,解析JSON数据包。

使用cJSON_GetObjectItem:获取某个元素 .   
                                                      获取数组内的元素时时候用 cJSON_GetArrayItem(cJSON *array,intitem);
                                                                                                          使用memcpy()进行内存拷贝.
                                                                                                          使用cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间(注意必须执行次操作




        json解析大概有些感觉,回归正题,为了对比使用C库解析json,这次演示解析的仍然是心知天气API获取下来的json结构.8266记得对接收数据进行去IPD 头处理,参考《使用C库在设备层进行json结构解析》)


{"results":[{"location":{"id":"WW8P3NH2TPDT","name":"太原","country":"CN","path":"太原,太原,山西,中国","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"now":{"text":"小雨","code":"13","temperature":"21"},"last_update":"2017-08-19T01:00:00+08:00"}]}
      拿到这一串json结构的字符串时,是不是感觉无从下手,使用cjsonjson结构解析时,首先要明白谁是数组成员,谁是谁的子节点,谁又是谁的孩子。这里介绍一个简单的方法,能直观明了的看出整个json结构。

打开百度,搜索json结构格式化,将json字符串输入,网页自动生成,json结构框架
QQ图片20170819170426.png

现在看起来就直观多了,这个结构比先前都复杂,里面涉及到数组格式。目标:取出namecodetexttemperature 存入NUM结构中对应的位置。
typedef struct
{
      
unsigned  char name[16];
unsigned  char code[16];
unsigned  char text[16];
unsigned  char temperature [8];
           
}NUM;

NUM num;

QQ截图20170819171753.png
QQ截图20170819171836.png

注意 解析结束后一定得释放内

cjson解析器在stm32移植发生故障,参考《关于ONENET 麒麟座v2.4 EDP例程 服务器命令接收与解、析故障分析》









关于ONENET 麒麟座v2.4 EDP例程

服务器命令接收与解析故障分析
麒麟座v2.4 EDP 裸机 例程对服务器下发的数据,也有初步解析的部分也许设计时只考虑到onenet通信,所以获取ESP8266返回信息处理部分有些欠缺,导致接入第三方API时,会出去返回数据部分丢失。

这是原例程源码:(注意红色标记处)

QQ截图20170823172219.png
QQ截图20170823172445.png

这是esp8266获取服务器信息后,读取串口缓存区的数据,对数据进行去掉 +IPDxx:”处理。程序开头定义了unsingned char型变量 byte count,所以限制了变量取值不能大于256,当服务器传回数据大于256时经过这个函数就会使数据溢出,导致处理后的数据丢失。 所以需要重新定义数据类型。
QQ截图20170823173835.png
QQ截图20170823173857.png
扩大数据类型限制,保证能够完全处理全部串口缓存区的数据(前提串口缓存区数组能够全部存下服务器返回的数据,否则请更改netIOInfo.bufbuf数组长度)。

2.如果使用cjson库进行解析时,使用cJSON_Parse(dataPtr);函数时出现返回数据时钟是空字符,但是你打印串口缓存区去IPD处理后的内容时,发现有完整的json结构数据。出现这个问题应该是内存堆大小不合适,处理方式:打开stm’32启动部分startup_stm32f10x_hd.s ,找到Heap_Size 宏,更改其大小。
QQ截图20170823180441.png






回复

举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表