你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32 FOTA 例程之 cJSON 使用

[复制链接]
STMCU小助手 发布时间:2022-3-4 18:36
前言4 v# h* d$ i, E3 |
在 STM32 OTA 例程中,设备端(stm32F769 探索版)与云端交换数据使用的是 json 格式。在本篇文章中,将对 json 格式以及 Cjson 的使用及注意事项进行说明。
5 F) Y  f, I" \& {
4 E' c7 G/ ]: _" H9 OJSON 格式
/ e3 r1 x8 m9 A$ J0 v5 m6 h3 \JSON(JavaScript Object Notation)是一个轻量级的数据交换格式。既便于开发者读写,也便于机器分析和构建。它独立于开发语言,是一种文本格式,很适用描述在各个系统间交换的数据。
; k& T8 P1 [/ wJSON 格式的数据看起来就像下面这个样子:
) w9 n1 E# s3 _: }{
# D2 {$ p9 g- Z7 e/ u& x1 T“Room1”: {
" h6 d0 d( H8 z" n' i6 z! f, [“LED”: “on”,- o5 p3 M" e0 n" I5 b
“Temperature”: “36”, m5 X9 J& q9 E! i; w! {
}
2 V; q0 D: n7 Y}9 @& e4 e; e! i8 }0 L+ D
这个 JSON 数据描述的是 Room1 的 LED 灯状态以及温度值。它由一组“名称(key)”以及对应的“ 值(value)”组成。“名称”和“ 值”间由“ :”分开。各组“名称:值”对之间由“,”符号进行分割。; b9 F, q* S, k( ~( p4 I3 X: V7 c% B
1 d$ E0 W$ T. E. F9 `. E
cJSON 的使用
2 {2 u( y3 F2 v3 ^# t% p' y3 q# P针对不同的开发语言,网上有很多 JSON 的实现, demo 里使用的是 Cjson,版本 1.6。7 K' b) x$ a  d; w8 H- y

  B3 H, \/ a& q4 k& g将 Cjson 添加到工程
" M5 c2 f: e% M* wCjson 只有一个 C 文件 Cjson.C 和一个头文件 Cjson.h。所以只需要将这两个文件拷贝到工程文件夹中,并将 Cjson.C 添加到工程中就可以了。
. Z) R2 p. v9 N) P
& {5 b% Q5 P6 t! h) C; e数据结构9 @& ^. J# M5 q/ b
Cjson 中使用下面的数据结构来表示 JSON 数据。
% r2 T; x) g/ P' T  f) }: d4 K0 Z- P4 _
}E$LN1AD)@5TAKGS[YQFWAP.png # G/ D9 Y+ M& ^2 Q2 C/ e
5 t; c- D  u# w: c8 E3 Y
*next 和*prev 指针可以用来遍历“矩阵”或者“对象”类型的 JSON 数据链表;这两种类型的 JSON 数据还会有一个子数据指针*child( n5 S* M" T8 j  n3 j
type : 表示该 json 数据的类型,比如数字,字符串、矩阵、对象等*valuestring, valueint, valuedouble 和*string 指针分别指向该 json 数据类型具体的值,视其类型而定。* M% U5 S6 ~' h; u; @
( B: h' ]1 P& I; r9 U7 R
使用 cjson 生成 json 数据
; y! N0 z  O: P# C+ [下面我们看看如何使用 cjson 来生成下面的数据:! ]. z. j; R. h1 }1 ?  `  m; V9 Y
{
4 H1 L2 E/ V1 E9 v! A4 v$ U“reported”: {
: j5 I8 ]/ q2 t) \6 z" o' N, A“LED”: “on”,
5 n0 Q( f0 V7 X# R# Q+ O1 }* A“status” : “normal”
( I- o& {3 ]; T6 a* M* r}
1 m5 a% H% [8 ?% m9 W}9 W4 Y- R4 Q( J9 f) A6 h
见下面的代码:1 q- Z' }' l8 p0 O' w
  1. cJSON * reported_obj;' Q" @$ e! {  x+ S; |5 r
  2. cJSON * device_obj;
    4 P7 z+ Y! f6 G" F
  3. 7 q& @' x4 }. Y
  4. reported_obj = cJSON_CreateObject();                                             <font color="#00ff00"> //创建空的对象:reproted_obj和 device_obj</font>
    ! U8 R( _& ]8 ~
  5. , O: q" U/ U& R8 v. B5 x0 d9 P, i
  6. device_obj = cJSON_CreateObject();
    0 {$ c! d8 U7 A

  7. + Q0 f9 \; I; u9 G5 R# E: }& g; V% }
  8. //add LED status
    - v) `, F& W, @
  9. if(flag_led)
    9 r! e* \: f2 {. T  G
  10. cJSON_AddStringToObject(reported_obj, "LED", "on");% ~: @0 a- r2 T/ J$ s3 }$ R
  11. else                                                                                           <font color="#00ff00">   //在 reproted_obj 中添加对象“LED”作为它的子集</font># u% Y1 a% p0 i3 H
  12. cJSON_AddStringToObject(reported_obj, "LED", "off");. H$ {! U$ t" t* E( l" M. S% i

  13. $ {2 x3 N+ t  W( K1 Z7 {" r
  14. //add device status
    & P+ ^" l5 [2 L
  15. cJSON_AddStringToObject(reported_obj,"status", "normal");              <font color="#00ff00">//在 reproted_obj 中添加对象“status”作为它的子集</font>
    . H6 S3 Y6 R( m: |1 H! _" F3 X
  16. cJSON_AddItemToObject(device_obj, "reported", reported_obj);      <font color="#00ff00" style=""> //reproted_obj 添加到device_obj,作为它的子集</font>
复制代码

$ \  k2 _% A7 z7 q5 X, V6 Z现在就已经在 cjson 中,构件好了和前面的数据对应的数据结构。但现在这个数据结构还不能发送出去,需要调用
( |6 w" ]+ o/ O5 s& A9 YcJSON_Print 将其打印成串行的数据,存放在 buffer 中,以便后面进行发送。
' @  D, M: k/ r$ s
  1. cjson_print_buf = cJSON_Print(device_obj);
复制代码

: t3 H. }" F: ~  ^4 x0 g; M" `cJSON_Print 执行的时候会向系统申请一段内存来保存串行化了的数据,并返回其指针。这里一定要注意的是,cJSON_Print中申请的内存,一定要记得释放(cjson 的代码中不会自动去做释放动作),否则就会出现内存泄漏。7 f) x( |  a- P& J. F4 j0 z3 H
通过 cJSON_CreateObject 创建的对象,也需要调用 cJSON_Delete 来进行删除并释放占用的内存。否则也会出现内存泄漏。见下面的代码:
; U- I+ g# Z; n1 U  O9 b. T0 m
  1. if(cjson_print_buf!=NULL)4 v1 f1 i1 {" ~% u! N
  2. {. C0 |* _0 x8 r& U' @- R- y5 p/ h
  3. snprintfreturn = snprintf(buf,bufsize,cjson_print_buf);; a7 r% i) Q$ q* ]( w0 _
  4. cJSON_free(cjson_print_buf);" P8 e& r- S+ j6 S) W
  5. }- z7 P3 Z) q6 K& d, x' x2 U
  6. cJSON_Delete(device_obj);
复制代码

9 S( }! Z! J7 g7 u% z* r" d6 D4 y仔细的同学可能会发现为什么调用了两次 cJSON_CreateObject,但只看到释放了其中的 device_obj。这里也是需要注意的一个地方,从前面的代码中,我们可以看到,reported_obj 最终是作为子对象添加到了 device_obj 中,所以在删除 device_obj时,cJSON_Delete 会自动删除 device_obj 中所有的子对像,故而不需要再调用 cJSON_Delete 对 reported_obj 进行删除。
2 @( a2 B5 W4 {7 q  M2 C$ _- t. y* \4 ^

) b0 b" y' {3 F; ?0 S: }+ F* W2 L! g# i使用 cjson 解析 json 数据8 H! g  G2 Y4 j8 E/ }) m8 E* @
可以通过 cJSON_Parse()函数来解析接收到的 json 数据,cJSON_Parse()函数会对数据进行解析,并申请一段内存保存解析后的 cjson 的数据结构,并返回指针。
* c4 I) y9 a& X, ]( _/ J( X1 m" P通过 cJSON_GetObjectItem()函数可以获取解析后的 cjson 数据结构中的第一级子对象。- k2 l4 {% a' u$ o
使用 cJSON_Parse()后,切记也一定要通过 cJSON_Delete 释放之前所申请的内存。
; v! k! X  c: z8 `下面代码是对收到的 json 数据的解析过程。收到的数据内容为:% f! R# U* W0 ^, N
{
5 l: @0 [) G- _! D5 D“desired”: {0 \1 t- P; t% T& y6 K( T0 m; {- c
“LED”: “on”,
, v6 K  p: s+ ^3 Y: H9 B“SW_Version” : “02010100”6 [2 G9 O* Q8 x2 L8 E! C
}3 y, |; k# b9 [# @' M- A( V5 j3 s9 O
}
. x- n% b" U, X; a( R2 m+ F. R0 v
- ?; ~* a; |  w' {5 y3 C# S
  1. receive_obj = cJSON_Parse((char*)message->payload);                 <font color="#00ff00">//解析收到的消息 message-> payload</font>( u2 |. L, O8 ~
  2. desired_obj = cJSON_GetObjectItem(receive_obj, "desired");         <font color="#00ff00">//提取 desired 对象</font>4 ^4 J0 y  X% C, r/ H$ H/ R6 V$ f
  3. item_obj = desired_obj->child;            
    4 c0 j! j/ {* U, F
  4. while(item_obj)                                                0 P" E8 ]3 @, r/ n- Z2 X
  5. {
    - M4 n* O/ o* u+ v1 E, J
  6. string = item_obj->string;
      r1 c9 U% _+ b
  7. if(!strcmp(string, "LED")){
    , ~, g+ A* U; b' S" I6 h: z
  8. if(!strcmp(item_obj->valuestring,"on"))        <font color="#00ff00">    //依次解析 desired 对象下的子对象值</font>
      F( q# ]! Y/ L  q' R7 }7 l
  9. {
    : |! O8 {; K$ m8 E( y3 i1 }. F0 V
  10. Led_On();  c% q! C$ l+ q
  11. flag_led = 1;1 c  ~; V5 e: b- B9 F! Z
  12. msg_info("LED On!\n");
    7 E1 u- K0 e9 P' q" T
  13. }/ {9 V: {2 v  U& w
  14. else
    , z) m% d; J  ~  L! }
  15. {" l$ g2 n( d. X6 u, U/ v
  16. Led_Off();4 k5 }# B# ?9 B
  17. flag_led =0;/ a* |0 l! ^5 F
  18. msg_info("LED Off!\n");5 j2 m: Y4 E0 y# P
  19. }
      T* S5 _. K4 ?' O
  20. }( x3 q4 O0 L2 y0 ]
  21. " E% {8 Z  d8 [9 ]0 j8 e
  22. item_obj = item_obj->next;
    ! T# M) c0 k; @3 h; [0 P
  23. }. T9 _& M# {# c- a

  24. . V8 u, Z& s; L& e' V
  25. cJSON_Delete(receive_obj);             <font color="#00ff00"> //释放 receieve_obj 及其子对象</font>
复制代码
- Y4 j  K7 M; g) q
总结
$ a7 k- C( _: Z' FcJSON 代码量不大,用起来也方便。使用的时候一定要注意前面提到对使用完的内存空间进行释放。否则会造成内存泄漏。8 G5 B) E1 x% c( |! x/ J+ `5 }
# C9 F! G4 ^% b9 r
* H4 v% N# h7 _7 i2 t0 l- Z
赞 收藏 评论0 发布时间:2022-3-4 18:36

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版