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

粉丝分享 | 使用STM32获取东京奥运奖牌榜

[复制链接]
STMCU-管管 发布时间:2021-8-23 13:43
东京奥运会可以说是奥运会历史上最特殊的一届,推迟一年、没有观众、环保奥运等。8 O* S, g( x* G! B  ]+ g: j

& b; M& }0 j9 G

. X- f* u. A: }/ D5 U, n+ r为了能实时关注奖牌榜,有开发者周末在家使用STM32+ESP8266做了一个东京奥运会奖牌榜桌面小摆件,最终效果如下:
# L. u, w1 @2 s& A
12.png
▲ 桌面效果1
13.png
▲ 桌面效果2
14.png
▲ 桌面效果3

+ o" V% W- ?, u5 i* M! _) @
+ S' H1 M( ?" @) U0 Y0 O. V. v
原理是通过STM32驱动ESP8266连接网络,然后GET接口,读取到接口返回的JSON数据,然后进行JSON解析,LCD显示,使用不同的接口就是不同的功能。$ Q* l9 P4 [& j: I( u

) x' Q# W( ^- P* S  b' ]$ z, z8 C
6 U' u) X* J* i
以上两个API接口返回的数据都非常简短,只有几百字节,而奖牌榜接口返回的数据有20KB之多,使用STM32不能一次处理这么多的数据量,本文采用了一种简单的方法来进行数据截取,从而减少数据量。
+ T* s, Y, f: o: X' g
3 _6 A. t. {; E

8 j( e$ {5 M% l1 a4 p& y+ d6 J都有哪些内容?
+ P! d  I$ P: f( r( ?! `7 b* \' m, q9 T& a& e  i$ h* w8 u+ C" s

% G8 ?9 g1 `$ o# G7 S; KAPI接口获取
6 g* Q3 @+ ~& N9 EJSON数据预处理
" s8 m* o1 G* ]0 r, J3 HJSON数据解析
! b! D- j3 ~$ q* w显示效果% ?( E" _: N, M" J  s
开源地址
8 J1 I# H5 l) G1 ~! z) b2 @( `; G
* g3 \- k! \' S7 h9 d* p, i
API接口获取
1 C# S0 U0 ~6 B( V
在进行开发之前,首先要找到一个API接口,请求方式最好是GET,返回数据格式JSON格式的。先上网搜一下,发现已经有很多网友实现奖牌榜数据获取的功能,使用的大多是:Python、Java、PHP,使用的语言无所谓,重要的是API接口是否是我们想要的,最终找到了两个API接口。
8 h/ ^; Q" M7 b第一个接口是央视网官方的东京奥运专题页面:1 ~& U0 ^  }- N8 Z
15.png
▲ 图:2020.cctv.com
网址如下:# B" n. }/ b* _
http://2020.cctv.com/medal_list/index.shtml
7 M8 v* o6 z' _! i9 ZF12打开开发者模式,可以找到页面请求的API地址:
, z5 B9 Z+ m. H" Z! J7 a% u6 f
16.png
▲ cntv_api
这个接口返回的数据量有11KB,格式化后有24KB,JSON数据格式(部分):
% i3 T2 V/ I  \- E, e( E+ g; I
17.png
▲ cntv_json
可以看到数据比较齐全,包括排名,金、银、铜、合计奖牌总数,国家ID,国家名称为UTF-16BE编码方式,而开发板上的字库为GBK编码,这一点不好处理。看看还有没有其他的接口。
( g4 s2 c. {* Q, b3 g. @第二个接口是我的小米手机负一屏界面呈现的奥运奖牌榜,类似于这种:
6 ^; E) g1 f; o, o. E5 B6 S  _
18.png
▲  手机界面
可以跳转到浏览器打开:
8 v5 C; Y( W( a- }http://act.e.mi.com/olympic/index.html
. l- H* S6 b8 n4 X5 Y3 H5 E( ?F12打开开发者模式,可以找到请求的API地址:
( {, `* ~: A5 [) O. e
19.png
▲ mi_api
这个接口返回的数据有21KB,格式化后有33KB,JSON数据格式(部分):
& ~; m7 V, s, R/ x
20.png
▲ mi_json
这个接口返回数据比较丰富,同样数据量相比于央视网的要大一些,除了奖牌数量排名等信息,还包括每个国家的国旗图片地址,更新时间等。国家名称为UTF-8编码,要在我们的开发板上进行使用需要进行UTF8-GBK的转换。
$ O- w+ \1 ^9 a: @7 C综上,我们就获取到了两个API接口的信息:
2 ?' j4 d6 v# n9 h$ z8 o) Y奥运奖牌榜央视API接口:
1 j& a; M1 j0 m  ]' ]: w( ~: r; e
  1. TYPE: "TCP"
    % V, @- B  `1 h# r# e. O
  2. PORT: "80"" _9 S2 s& [# b  U; Q4 |; t
  3. IP  : "111.206.176.78"% G/ s% G2 y5 {3 y
  4. API : "http://api.cntv.cn/olympic/getOlyMedals?serviceId=pcocean&itemcode=GEN-------------------------------"
复制代码
奥运奖牌榜小米手机API接口# I/ i1 d) x# z& Y
  1. TYPE: "TCP"" Z) l. A3 E) \) A$ ]8 @! t" i
  2. PORT: "80"4 y6 F+ g* A6 u7 \' ~7 u  O6 d
  3. IP  : "111.206.101.253"
    $ K9 Q9 f- c! ]
  4. API : "http://act.e.mi.com/olympic/medal_rank"
复制代码
为了方便在开发板上直接显示国家名称,我们采用小米手机的API接口。
, _5 e9 U- |, h3 c. N& D2 P! }5 w2 _1 l2 k# c% @
, v" ], ~$ }6 ^- J  u2 M
JSON 数据预处理

& k  |9 x1 [( N  H- _4 b& q1 L通过分析API接口返回的数据,共包括前90名的奖牌数据,数据长度为21KB:
. I; v! d7 \9 I0 n% b, w/ W
21.png
▲ mi_json
但是我们只需要前10名的数据即可。串口接口缓存长度设置为2500个字节,即返回的21KB字节数据,只接收前2500个字节,然后处理成JSON标准格式就可以了。
! a1 F! j; L1 I6 N6 E- p处理方法为,倒序查找最后一个}号的位置,将{之后的数据丢弃掉,然后添加上]},这样就将截取的2500字节数据修改为了标准JSON格式数据:
* y" `5 a' ]4 X& p
22.png
▲ mi_json_deal
1 B7 `4 T  d; }7 R  P
JSON数据解析
0 g1 f0 ?/ h+ w  _
从上图也可以看出,JSON格式比较简单,使用cJSON可以很方便的进行解析。
/ [9 s+ T& s; O& f% n: D
+ E& J8 w7 l, w: b3 \+ Y* a

8 k: ?6 r; R+ S, u) H$ _1 L定义一个结构体:9 s* ]1 g8 e% D: ~  C% t
  1. typedef struct medal{
    9 c& q; Y  B: A+ Y. _9 q
  2.     char rank[5];. W, z4 z# ~0 M0 H1 ?1 }! ^8 R
  3.     char countryname[50];
    8 N% H6 R( O$ ~) ^8 I& {+ D
  4.     char count[5];) Y& A# F/ N" t; K2 r0 d& C
  5.     char gold[5];
    9 r% h4 j% b6 ~( h8 w6 v: A
  6.     char silver[5];
    0 J9 U) p' c$ M0 b$ i9 E
  7.     char bronze[5];
    # |: M; h3 z1 w& r; N9 `8 ]1 I
  8.     char update_time[50];
    % `. n, J' X9 i4 R- J, G
  9.     char countryid[10];$ `% K4 H$ e& O' m5 w. p  x
  10. }medalObj;
复制代码
解析函数,只读取前7名的奖牌数据:8 e% ~: {, ~6 [1 g2 X) y$ ^
  1. uint8_t parse_mi_data(void)
    0 _5 `6 T; g# B4 E; H' y( `
  2. {
    6 t  i4 r* O" R% l, d* R9 C
  3.     cJSON *root, *data_obj, *list_obj;) o9 D3 D- g! f$ L
  4.     char *str;
    0 O* T& k/ I& S/ v0 F, b, _
  5.     char dest[USART2_MAX_RECV_LEN];, e- M$ L5 D& i
  6.     char *loc;
    3 j  K. G# t4 m
  7.     char gbk[50];
    + U* _- P* K  t) [2 U
  8.     char utf8[50];
    , V1 E& d" k! n( |5 |8 b2 I

  9. 8 F% q' S4 ?  h! |6 d* _
  10.     medalObj *pobj;
    0 n! i" L! }8 y$ k9 S
  11.     medalObj obj;
    ; U* @( e% w% l3 z  b9 `" t
  12.     int idx;/ i9 L; f2 X; D- W1 t: k' u
  13. - C+ o6 K- x( M0 g2 m
  14.     pobj = &obj;$ _* L" g4 O; B2 s* R5 M8 I: v9 ?

  15. + D* D5 r+ _% N
  16.     str = (char *)USART2_RX_BUF;
    / Y+ ?4 G; W- [8 P
  17.     memset(dest, '\0', USART2_MAX_RECV_LEN);9 k' x! l' {9 E% q0 X; E
  18.     loc = strrchr(str, '}');
    % l; T- R+ j/ a$ t/ B# d+ @
  19.     strncpy(dest, str, loc-str+1);
    - ?/ i5 _/ ^( Q  X( \; m2 P6 G
  20.     strcat(dest, "]}");
    . J4 y  A# A* b$ }
  21.     printf("json data size: %d bytes\r\n", strlen(dest));
    % I2 R0 j; @/ Q/ V
  22. : I. ^/ Q. Z8 M- _8 U
  23.     root = cJSON_Parse((const char*)dest);+ T  g* [1 `3 z2 B% V* d# ]+ v! q
  24. & f& a5 ~1 l0 K1 N* R
  25.     if(root != 0)$ v9 H6 b$ m- o& {  u+ S4 _6 V
  26.     {) k4 C7 z- r: E8 w
  27.         printf("JSON format ok, start parse!!!\r\n");: J+ t! X3 s$ y2 l9 P! y
  28.         data_obj = cJSON_GetObjectItem(root, "data");0 O% j' A/ B! q
  29.         if(data_obj->type == cJSON_Array)
    * i, ?( g9 {: ^/ F
  30.         {
    * B7 n% K: L' C, G4 b/ H
  31.             int size = cJSON_GetArraySize(data_obj);
    ) \+ T$ X& x9 W: A1 o1 Z8 ]8 P, ^
  32.             for(idx = 0; idx < size; idx++)
    0 C, i& |( K. d4 k
  33.             {
    : a7 g8 i, a, X, U6 w+ {1 t
  34.                 if(size >= 7 && idx <= 7)! @: a7 P7 }7 Z- i
  35.                 {4 E! D$ j: ^: A- X
  36.                     list_obj = cJSON_GetArrayItem(data_obj, idx);
    * m! N+ Y" Q) m) C

  37. 9 [; m2 C, F" ?8 y: d
  38.                     strcpy(obj.bronze, cJSON_GetObjectItem(list_obj, "medal_bronze_count")->valuestring);% H" {4 W4 {+ q6 |4 m9 `
  39.                     strcpy(obj.rank, cJSON_GetObjectItem(list_obj, "rank")->valuestring);
    " {7 v' O/ P7 b1 b" \# I9 }
  40.                     strcpy(obj.count, cJSON_GetObjectItem(list_obj, "medal_sum_count")->valuestring);" x' O5 {7 w2 i+ _" q7 c
  41.                     strcpy(obj.silver, cJSON_GetObjectItem(list_obj, "medal_silver_count")->valuestring);
    ! x5 a6 k1 w4 J( ^# @
  42.                     //utf8->gbk
    " W) H/ O+ W% X$ n
  43.                     memset(utf8, '\0', sizeof(utf8));
    7 I0 \! W0 [9 U- P$ C+ x( I* j
  44.                     memset(gbk, '\0', sizeof(gbk));
    ( H. j( a2 P3 o( @* {
  45.                     strcpy(utf8, cJSON_GetObjectItem(list_obj, "country_name")->valuestring);, R- s. Y7 O$ a6 V* B' O% I" ~
  46.                     SwitchToGbk(utf8, gbk);
    9 M1 F4 u! R5 E  t( H
  47. % I3 s( _. Y* E, s
  48.                     strcpy(obj.countryname, gbk);
    + L5 T9 Y' t8 e# t  w6 q4 J

  49. ' R% [4 H( p. U6 V7 _' ^. W
  50.                     strcpy(obj.gold, cJSON_GetObjectItem(list_obj, "medal_gold_count")->valuestring);
    $ X: s( [3 [5 B3 Y5 X" a
  51.                     strcpy(obj.update_time, cJSON_GetObjectItem(list_obj, "update_time")->valuestring);- M9 X+ p4 p) W# O* l/ k; B6 [
  52.                     printf("%s: %s %10s %s:%s-%s-%s\r\n",
      C) H1 [3 J5 [9 M5 j7 Q3 K
  53.                            pobj->rank, pobj->update_time, pobj->countryname,
    / i- x8 @; U/ w7 \
  54.                            pobj->count, pobj->gold, pobj->silver, pobj->bronze);
    7 q7 R: n: P9 d" m
  55.                         gui_show_data(48+idx*20, pobj);: Y# L1 M: A/ J9 w1 R
  56.                 }% W. ~9 E5 e6 q  n# Q4 W
  57.             }6 ~5 v: W7 h' [
  58.             Show_Str_Mid(200, 225, (u8 *)pobj->update_time, 12, 120);$ |1 n# ?9 O! b5 ~* [
  59.         } 9 l. Y# T" U+ {: H9 x" R; C0 a
  60.     }   
    & z( A) N6 ^- O" p$ L
  61.     else& D: G1 f9 v& i0 y$ q
  62.     {
    * h, _+ z5 \% m- m9 f" e$ `% T
  63.         printf("JSON format error:%s\r\n", cJSON_GetErrorPtr()); //输出json格式错误信息
    9 S: I4 D% v: F
  64.     }
    ) w9 t7 w7 q+ J$ d5 p8 h
  65. # H  U5 W/ x3 A* x+ q
  66.     USART2_RX_STA = 0;0 u# F) b# K- b( C& ^, U3 d" ?0 H
  67.     memset(USART2_RX_BUF, 0, sizeof(USART2_RX_BUF));& ?# r* I4 z" u

  68. : |2 V+ ^# s& O
  69.     cJSON_Delete(root);& N" M4 N% ^) n  \2 A
  70. # s4 q$ L5 _4 w$ [, X
  71.     return 0;+ J3 F0 s4 g8 s
  72. }
复制代码
如果出现解析失败的情况,可能需要调整启动文件中的堆栈大小:
) D- W/ Z$ v  Q  g. Z4 P7 z
  1. //startup_stm32f10x_hd.s
    ' v1 s1 K8 }  I1 }+ Y  R
  2. ' r0 Y/ q) l7 N" ?( Z
  3. Stack_Size      EQU     0x00000C00
      q& G7 r! T( M# w- u2 m; R

  4.   I& q( i: V* C8 A. l2 I+ e
  5. Heap_Size       EQU     0x00000200, B9 H0 L+ L  S
复制代码
最终效果
23.png
▲ BMP显示效果
24.png
▲ 桌面效果4
25.png
▲ 桌面效果5
和手机端数据完全一致:, F, ^  _- o; M, L7 x3 O
26.png
▲ 手机界面
! x9 \( v* {+ |" x1 @/ _; O# i) j
) D" Q$ f& K1 |& C1 J
开源地址

' G( d& T; k( c# L7 R% @我的开发板已经预先写入了中文字库,没有字库的开发板可能不适用。+ @* h5 e  o$ X3 T+ z3 C
全部代码已经开源在Gitee码云:
' t/ P2 p5 H+ @https://gitee.com/whik/stm32_olympic_medals
$ F' k/ t2 n2 U, S7 P; W5 C. W0 ]3 ], C" j- Y
总结
* o2 @8 @2 L2 g. F+ F9 h2 k. |. t
北京时间2021年8月8日晚,17天的2020东京奥运会落下帷幕。本届奥运会闭幕式理念为“Worlds we share”(我们共享的世界),蕴含“铭记与不同性格、不同文化背景的人们共享的感动,携手共创未来”之意,同时也包含了人类共同抵抗新冠疫情的团结之意,虽然东京奥运会已和观众说再见,但即将到来的2022北京冬奥会将持续点燃广大体育迷的热情。
( `  t, S" E( Q; P  T( _$ Z) `" e8 Q% W/ w% j+ I# n0 k  H

$ H& w: `# n3 I; G/ l! q
文章出处:张巧龙
4 u2 L2 ]) U! v5 {$ ?. a2 l& @2 N% C  q# k5 a3 U
赞 收藏 评论0 发布时间:2021-8-23 13:43

举报

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