基于STM32+RT-Thread的新冠肺炎疫情监控平台
% w" S- F) d8 G) q1 D8 F, O& L% L7 {% b# w7 S6 ^! Y* ?" ^
上周末加班,这周末休息,有时间整理一篇之前做的基于RT-Thread的疫情监控平台。上一篇文章我们使用STM32F103 MCU裸机开发的方式实现了疫情监控平台。这次我们玩点高端的,使用RT-Thread Studio来实现同样的功能,一起来看看吧!; l: o1 C1 Z5 X% ?/ m: Q
+ |$ C7 o1 d# P) p% \* r
* _) ~; w+ a8 B5 ^4 w; v) ~·文章目录1 K% A# I1 w0 \7 w' {
·使用到的软件包8 T! j# a5 ?0 i. \' v6 E
·0.RT-Thread Studio的下载和安装0 ]/ r5 @ h g% _9 f
: r; m3 Q9 N( @5 t) ^" t R& h
! k% y9 [ P4 V2 {& S1.硬件准备7 G( }/ O) X+ E% A% z
2.新建工程
9 V: p4 @) n& d- e+ R3.添加LED闪烁功能
8 H: T/ }" w" N4.添加ESP8266软件包
3 M3 Y/ Z2 r6 B' _) P5.疫情数据的获取5 h+ h$ C3 Z/ R- @# X
6.疫情数据的解析% m! T( r9 i: O9 t; l! A
7.疫情数据的显示0 J5 ?, I0 `% U5 U6 n
开源地址
+ x6 D) W' z) S5 x) |7 I) J: T
, g6 s% ~) l2 L
+ P. F( j5 Y: k o# L Z最终的显示效果:' s3 a; m) M2 Z' y' V
显示效果 有效文件就这9个,其他的就全是图形化配置:
+ ^' u% C9 m8 C- Z2 [! ?有效文件 整个流程下来,如果顺利的话,可以在2个小时内完成。+ Q0 W( N* p( d8 o
& F D; t- r! l" v" E: H使用到的软件包
* U$ l8 y- N% b s1 \, A·at device:用于ESP8266配网: g f, `, e. E' n8 { z- T9 d. q, _
·webclient:用于发送HTTPS请求
# a+ j, F5 @% s" n; \1 h: J! A·mbdetls:用于HTTPS加密
9 n. Q% J/ l4 K. U- E# v·cJSON:用于JSON数据解析; G" k. h" q2 q9 }5 f) d. w
1 I3 A% G1 j/ L' J
0.RT-Thread Studio的下载和安装
* ^2 I/ q. Q$ e) O一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效 RT-Thread Studio ·支持多种芯片,STM32全系列& w0 Z6 c, y& z9 I& _- u+ }) p
·支持创建裸机工程、RT-Thread Nano和Master工程
2 u1 O: r! V- ?5 w% Y# v' f K3 a·强大的代码编辑功能,基于Eclipse框架
2 y0 G; y: O! J7 {1 r·免费无版权限制,基于开源Eclipse和ARM-GCC编译器。, Z1 n, g5 Y; u7 b: w5 M& u
·支持多种仿真器,J-Link,ST-link等,支持在线调试,变量观察。
; L8 _2 t1 N, B# p. O·SDK管理器,图形化配置RT-Thread软件包,同步RT-Thread最新版本。( V: a0 I O, N: ]; ]. M$ j; C* q
·集成Putty串口终端工具
+ M1 ]$ V0 h* ^- P$ d0 J: l& `% m7 W. L, m1 u4 e/ E- G
; j7 x+ F+ C5 x! K
更多的使用教程:& e5 i9 p9 f; V+ ^
http://www.rt-thread.org/page/studio.html; w$ |$ x, l7 F! N8 y
$ G: |* f. h& e& k. ^1 Q
. q% Z$ S$ [( ]5 @
目前最新版本为1.1.3版本,支持3种下载方式,我们选择最后一个下载方式,从RT-Thread 官网服务器上下载。
* Q2 F. M: u# n+ [% H0 O& B" G& `
/ x) |, W* Z( ^) p" f: H4 ^
$ m. Y5 B M) e& X下载地址:0 x- J6 G8 t5 B% b7 d! w, q
D" g4 _3 @1 ]. C0 U
" ^( f9 E% U: s+ M4 M5 L
http://117.143.63.254:9012/www/studio/download/RT-Thread%20Studio-v1.1.3-setup-x86_64_20200731-2100.exe9 E4 W0 Y6 k" C! s# ^. y
% Q% `/ w5 N# t, r* u" F( r8 h
; u) ~3 ~$ W7 V! J# q2 C
下载链接 安装过程和常用的软件安装方法一样,选择安装路径,然后Next就行了。4 f. r5 J0 h# Q7 h. }9 @
, \! N% p4 G; `
- b% H) k) D2 X5 ^; O: q7 q# I0 p1.硬件准备/ D5 G( M4 J5 v0 Z
开发板用的是我在大四时自己设计的STM32开发板——NiceDay,基于STM32F103RET主控。 这是我设计的第二块板子(第一块是毕业设计两轮平衡车主板),是在大四快毕业时,毕设实物和论文完成之后还有点时间,就设计了这款板子,最开始是准备做桌面天气时钟的。
- z5 @" W# [+ d( f' F( f+ {. R: x
$ B5 t( A9 C6 Z V$ z
开发板 2.新建工程7 O3 K, g! R* g/ t
RT-Thread Studio支持创建裸机工程、包含RT-Thread Nano版本的工程和包含Master版本的工程。这里,我们选择创建RT-Thread 项目,即包含完整版RT-Thread的工程。 u5 @# h6 k& s- t3 A" W3 d
新建项目 工程支持基于芯片创建工程,或者基于已有的BSP创建,这里使用的是我自己设计的开发板,所以选择基于芯片,选择芯片型号:STM32F103RE,调试串口选择串口1,调试器选择J-Link,SWD接口。, S* }& Z' N: A/ D7 L
新建项目 创建完成之后,直接按Ctrl+B编译整个工程,第一次编译时间会长一点,如果修改很少,下次再进行编译就会很快了,可以看到无警告无错误。
2 T% O. m d; r/ f编译结果 使用SWD接口连接JLink调试器和开发板,开发板上电,直接点击下载按钮,也可以使用快捷键Ctrl+Alt+D下载. F9 f* @ X4 E3 v; W- `; t
下载程序 底部可以看到下载信息,从LOG来看,下载的程序文件是Bin文件,比较,擦除,编程,验证,复位整个流程耗时13s左右。6 M* r t$ t2 o ?
下载LOG RT-Thread Studio是自带Putty串口终端的,点击终端图标:
9 L3 V0 p) I" }6 Q0 W% [+ _终端按钮 选择串口号、波特率、文字编码方式等。9 _# }& I" \/ ^+ p1 Q
配置终端 底部切换到终端窗口,可以看到串口终端输出信息:
4 l; e/ Z$ p3 l$ {" ?% ^串口终端 这样,不到5分钟,一个基于STM32F103RET6的工程模板就创建好了,包含RT-Thread完整版操作系统,整个过程不需要写一行代码,完全图形化配置。4 e/ U' g6 y/ h7 l7 I% i& p7 P% `
" r2 \' w7 g' R1 a
) ^8 y: J' D1 D, T5 F1 |3.添加LED闪烁功能( r- a; Q' H+ r2 C4 N. |* B+ F
作为单片机点灯小能手,RT-Thread下如何点灯是必须掌握的。打开RT-Thread组件图形化配置界面,可以看到默认开启了PIN和串口设备驱动的。
& u# w( v5 w2 {" [图形化配置界面 在main.c文件中添加LED闪烁功能。包含头文件和添加宏定义
, G$ T" s# J$ O* F9 E. R- #include <board.h>9 j- U5 R8 A/ B9 z( v# l7 R
- #include <rtdevice.h>4 e% M1 p2 R4 q8 \4 f
- * \/ C9 G, A/ U$ ^+ G! V
- #define LED_RED_PIN GET_PIN(A, 7)' h- K* Q% Y/ [- E; P
- #define LED_BLUE_PIN GET_PIN(A,6)
% c3 p3 u( D3 i a. h9 K, N
7 V! y8 B! |+ t0 Y% f/ R- int main(void) c0 W$ `' t; I( h8 l( A
- {
) B$ c9 e, d6 t* J* u% G- Q# e - int count = 1;2 r" [; x6 n; N6 }
- rt_pin_mode(LED_RED_PIN, PIN_MODE_OUTPUT);5 I8 `9 Q6 B1 Z
- rt_pin_mode(LED_BLUE_PIN, PIN_MODE_OUTPUT);
. ?& J7 M9 V8 r0 f9 Q& f. |( B$ d - / K5 G! V0 W; ?! }9 {4 I0 D m3 k2 t/ k
- while (count++)
/ t6 ?; r3 i6 n0 Q* [, U" @ - {
2 [+ q4 X/ D0 g4 h& k& R - rt_pin_write(LED_BLUE_PIN, PIN_LOW);( z# D% M9 _. s( y8 l P( \3 ^0 }
- rt_pin_write(LED_RED_PIN, PIN_LOW);$ d' c% }: ~3 L
- rt_thread_mdelay(100);* a7 t$ q& I3 _0 o$ {2 n$ x; R7 d# z
- rt_pin_write(LED_BLUE_PIN, PIN_HIGH);
6 e$ d$ m) i3 v, Y. S, O - rt_pin_write(LED_RED_PIN, PIN_HIGH);
# ~0 H% X$ \5 g2 v, R3 m - rt_thread_mdelay(100);" y$ B$ O. r6 _# D4 k5 G
- }2 O+ `( E3 h; L
- return RT_EOK;
& g0 U. v2 Z0 g - }
复制代码 重新编译,下载。可以看到LED闪烁起来了。工程默认是使用内部RC作为输入时钟,所以无论你的板子是8M还是12M,都可以正常闪烁。我的开发板是8M晶体,这里我们配置使用外部HSE作为输入时钟。2 Z& o. ^. S! E2 j
/ k+ C2 I" p7 o# x2 ^; Y! e: I$ ]9 w$ y2 d, ~
打开drivers->stm32f1xx_hal_conf.h文件,修改HSE_VALUE宏定义为8M。
0 i) f' R: e( I6 |5 N) c: b0 f晶体频率修改 打开drivers->drv_clk.c文件:
- L& ~3 y. n: N0 \3 K% H时钟源修改 配置PLL时钟源为HSE,并设置倍频系数为9。0 s9 m) s4 B( D+ G/ `0 g2 B
时钟源修改 倍频系数 这里根据实际板子晶体频率来设置,如果是12M晶体,倍频系数应该设置为6,如果是16M,需要参考时钟树,先2倍分频,然后9倍倍频。
3 B+ e9 ~$ ~; Y+ V# a2 c- #include <rtdbg.h># x$ m7 f) Z2 }" e
$ {" a' [2 K9 ?' z5 b- void system_clock_config(int target_freq_Mhz)
2 h* U! F& x8 u7 t* z7 D* a - {
9 }: T- ]+ D8 d* d5 @) i - RCC_OscInitTypeDef RCC_OscInitStruct = {0};* Y+ R! S# F. r" b% X
- 7 ~; ]) l; Q& |, K
- /** Initializes the CPU, AHB and APB busses clocks" F3 Q+ a2 q6 E: W7 Q# J
- */) g% H3 \- G# P& e( i
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
( D2 \% R: L; N7 ?' m - RCC_OscInitStruct.HSEState = RCC_HSE_ON;$ j0 q. ]2 t, E. j3 {- c
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;# @* p. K) _, _5 d. ^$ e
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;$ n2 ~2 E, k: ]# J+ V5 Y
- ........
: V0 w/ h/ U/ |8 `5 L2 Q - //9倍频
5 I$ z$ ^& L+ R. ~. r - RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; //8*9=72M% t) N/ h- C4 j3 v3 v, J8 c! p
- ......../ v: [' M, M. a3 o
- }
复制代码 这样就修改为外部8M晶体作为PLL时钟源,再次编译下载,和之前的现象是一样的。
# A, P1 \% \! e7 @7 P. b3 X5 u9 M# y
# @, ~( h- @0 {2 p% `, D
4.添加ESP8266软件包: r' y- o( D, S9 C& [) s
联网设备,我们选择的是ESP8266-01S,如果看过上一篇疫情监控三部曲——在STM32F103 MCU上实现(裸机版),里面介绍了如何配置ESP8266 GET HTTPS请求, 配置工作模式 > 连接WiFi > 与服务器建立SSL连接 > 发送GET请求获取数据等等,整个流程固定而繁琐,那么能不能封装成一个模块,直接拿来使用呢?
4 b4 q/ p" m( s: ^; O% I$ gesp8266 这里就要介绍RT-Thread的AT Device软件包了,
+ g* J! o6 E% t/ h7 ?1 ^. j5 J# H! Q, B( S1 o& a3 r
4 i. S% ]( O0 o, ^$ E6 P3 y/ UAT device 软件包是由 RT-Thread AT 组件针对不同 AT 设备的移植文件和示例代码组成,目前支持的 AT 设备有:ESP8266、ESP32、M26、MC20、RW007、MW31、SIM800C、W60X 、SIM76XX、A9/A9G、BC26 、AIR720、ME3616、M6315、BC28、EC200X、M5311系列设备等,目前上述设备都完成对 AT socket 功能的移植,及设备通过 AT 命令实现标准 socket 编程接口,完成 socket 通讯的功能,具体功能介绍可参考 《RT-Thread 编程指南》AT 命令章节 。
' ?* F- B$ I& k9 I0 ^/ |. H# ohttp://www.rt-thread.org/document/site/programming-manual/at/at/
4 Y0 _2 K! h: d$ Z2 Q) m4 c2 E ]/ }1 p' C- T# }0 b4 C) f
2 i. d1 p, v$ U8 G) ?5 p( n简单的说,就是我只需要调用这个软件包,然后修改WiFi账号和密码,就可以直接配置ESP8266联网了。
/ S2 R2 P0 A+ `
# f: U' X: |( a8 z
6 s) c* c2 Z3 d! T$ p" b由于AT Device依赖于libc组件,所以在添加AT Device软件包之前,先开启libc。1 _; O- |. v0 q; z# l2 I0 m
0 {( p6 r0 T, X* V6 `0 M$ L8 ~3 j0 [' T2 ^% u/ {
在RT-Thread Settings中点击libc灰色图标,变成彩色说明已经开启。( |- h% t' B. v; K1 B+ y
组件配置 添加AT Device软件包,点击立即添加
/ [" q% I: y4 n( ~! n软件包 在弹出的软件包中心,搜索at_device,然后点击添加,添加到当前工程。2 ?1 y G9 W: L _
软件包 在at_device软件包上右键,选择详细配置:4 i7 s8 T% N, l5 i7 Y
软件包 在弹出的页面,选择我们使用的WiFi模块类型,乐鑫的ESP8266系列,并配置WiFi账号和密码,WiFi模块所连接的串口号。
" P; |9 J ?4 Z3 M& k! E; vWiFi配置 点击保存之后,工程会重新进行配置,添加相应的软件包文件到当前工程,重新生成Makefile文件,rtconfig文件等等。' C5 m& S) `# W# H
. \8 \# S' F* ]0 d0 i6 W$ S
7 m* p- ~( f# a: D. v虽然我们在at_device配置中选择了uart2作为at_device设备连接的串口。但此时串口2并没有开启,还需要我们手动使能。! g! q" x% C( Q. I5 E* E
! q% Z# `3 d; D/ U& h# F) R; M" |' x4 Z S, L* A
打开drivers->board.h文件,通过宏定义的方式使能串口2。
! Z% F1 ^- p+ j. X9 Z" E) s; ]- #define BSP_USING_UART2
2 D: V; J( z0 N( E! ? - #define BSP_UART2_TX_PIN "PA2"& r3 Z1 y) }9 _
- #define BSP_UART2_RX_PIN "PA3"
复制代码 这样就开启了UART2的片上外设,Ctrl + B重新进行编译,时间会有些长,编译完成之后,可以看到flash文件大小明显比之前大了。) l8 E2 [0 e3 h% S
编译结果 Ctrl + Alt + D重新下载运行,打开串口终端:$ j3 U% P, ]4 c# Y& _" ]
终端 可以看到,UART2初始化成功,WiFi连接成功。说明我们的串口模块已经可以正常工作了。提示[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!失败信息,是因为当前ESP8266的固件版本不支持AT+CIPDNS_CUR?这条命令,把固件升级到最新版本就好了。这个不影响后面的操作,所以就不用在意这个了。
2 b9 Y, p2 U! B4 N
+ r1 B0 h* g K6 {, X7 |2 ? P" ]/ x, l! O. |% ]
测试一下ifconfig和ping命令,都是正常的。0 _% h: B4 f5 `! T( k- M6 y
终端 在RT-Thread Studio中配置ESP8266模块联网,整个流程只写了3行代码,可以说是非常的快速方便。# }# q' z- O+ x8 a1 _0 ^
5 R7 B# S1 V6 Q5 C, a( |
( n" W( Y, i& u; g |9 s5.疫情数据的获取% B" [4 q8 Z* u
WiFi模块连接上互联网之后,就可以连接GET疫情数据的API接口http://lab.isaaclin.cn/nCoV/api/overall,然后读取返回的疫情数据。在上一篇的裸机工程中,是通过先和服务器建立SSL连接,然后发送GET HTTPS请求,获取到的返回数据,那RT-Thread有没有这样功能的软件包呢?这里就需要添加另一个软件包webclient。
! o1 `( l, k D7 J) F5 r: |, @# a$ @. W' r
$ t$ _% Z- ~% @; z0 T3 U/ o) xWebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。) {! Q9 m# P% |5 g5 l O0 ]
WebClient 软件包功能特点如下:% |: u. j+ z) a9 i
. `, G) N/ a! |' c7 @; T
g) n1 e% l& v+ Z: _·支持 IPV4/IPV6 地址;
z# w; x5 j& s: X0 Y H·支持 GET/POST 请求方法;
4 _9 k# |9 p5 g·支持文件的上传和下载功能;
- L, {7 b$ W6 P$ t: U·支持 HTTPS 加密传输;
) ^! ^( [1 M( z' \$ J·完善的头部数据添加和处理方式。
% G- f' \8 m6 g( B& c% H% \ t; @& v
P% Q( Q$ P' V
和添加at_device一样,在软件包中心中搜索webclient,
# v2 y0 ], ]1 g; j( v1 I软件包 然后添加到当前工程,右键进行配置,由于我们的http://lab.isaaclin.cn/nCoV/api/overall这个疫情数据接口是HTTPS类型的,根据软件包使用手册,我们需要选择TLS模式中的 MbedTLS。勾选添加GET和POST示例。; [1 J' ?* k+ Z: [# Z& S( w
软件包配置 保存配置,看一下当前已经添加了哪些功能,可以看到有一些组件我们并没有去打开,但是已经被开启了,这是因为有些软件包是会依赖一些组件的,当使能软件包时,一些依赖的组件也被同时使能。* T; m1 V6 E. d) S$ L3 ^, V
软件包 Ctrl + B编译,Ctrl + Alt + D下载运行。在终端输入web_get_test测试GET请求功能。
: n- [: ]1 V7 pGET示例 可以看到,执行get命令之后,会返回一个字符串,那么GET的是哪个地址呢?打开packages->webclient-v2.1.2->samples->webclient_get_sample.c文件,$ y5 h3 w' t f& u& W8 v& W. {: X+ X6 f. J
示例代码 可以看到GET的是这个地址:http://www.rt-thread.com/service/rt-thread.txt,我们用电脑上的浏览器访问一下:2 j: P& V: D) d8 j4 c; x
浏览器访问 经过实际测试发现,GET HTTPS请求,还需要使能软件模拟RTC这个组件,否则会报assertion failed at function:gettimeofday, line number:19错误。
' [/ D; P; U8 H2 q2 \& a( ^, t8 E使能RTC 我们重新写一个获取疫情数据的函数,并导出到MSH。! P. D- o2 l- i" z' F) n% i
0 O2 r: ]( g+ d& w. Y
s( r- t/ m* K; ]. A
usr_ncov.c文件内容- V* G# d/ g& s0 s5 b
- //usr_ncov.c! |! ~3 w/ i( f B
- #include "usr_ncov.h"- a" Y* f( ] L% M" P$ V
/ Q4 {" U/ @# T: \3 g+ y. ^: l- int get_NCOV_Data(void)
" g) U% `- v k; f/ {4 s - {
. }3 C% s) \. D# A- A3 x - char *uri = RT_NULL;3 B1 W; Z! F0 W2 t' v$ Q( e# |% I
- struct webclient_session* session = RT_NULL;
. ]+ Q, O& U$ Y/ O: p$ | - uint8_t *buffer = RT_NULL;
! k5 _5 o1 a! j - int index, ret = 0;
* i5 ]3 H, b% B8 i% t* r - int bytes_read, resp_status;% L# ~3 n. p4 O; {8 [: [( ?# e e
- int content_length = -1;
' n( V T5 X& L* Z% x4 U) i5 C) M - int buffer_size = 1600;8 c) Y P% X$ `! u
- uri = web_strdup(API_NCOV);
# U0 J9 @4 v% p0 V ~0 h* @ - rt_kprintf("start get api: %s\r\n", API_NCOV); O: @4 V8 F$ Z, e! ]0 D" D4 z
- if(uri != RT_NULL)' I7 Q( b& v" H6 Q: a
- {
+ K& g( O2 A; J/ D - buffer = (unsigned char *) web_malloc(buffer_size);
! f9 K, n0 X+ E4 j: p - if (buffer == RT_NULL)
9 t% }: V h/ |2 _9 J - {
1 `9 n+ ?# ^* D; Q- T7 L - rt_kprintf("no memory for receive buffer.\n");
. P( ]! E. |( `. x$ g: D. \1 X - ret = -RT_ENOMEM;$ K: p8 e1 p! L6 M
- goto __exit; G5 d3 q% _' u, B' D q
- }" j' ]9 W( G5 f! P9 Q. N/ T
- $ U8 b1 V$ z7 ~- _# G
- /* create webclient session and set header response size */( o& O( V7 _& r" I, r
- session = webclient_session_create(buffer_size);# O6 o) i4 G/ a& L; h0 x1 H G1 l
- if (session == RT_NULL)* t- n* t! e+ }- S4 U+ [: S
- {
|0 T- ^0 r ~+ n2 N0 `0 I4 B - ret = -RT_ENOMEM;8 r( G. |: F, d# U
- goto __exit;
( H# |# e4 y g! E/ L: I _- m - }0 `( \6 g( }' J2 b9 c
8 ?, H R9 g: A. b6 V( N" x- /* send GET request by default header *// Z# V' i3 l8 ^% K+ P! o1 u9 ]$ ]0 h
- if ((resp_status = webclient_get(session, uri)) != 200)# H" ?! i! k2 e4 t7 U) ]
- {0 G# h6 r4 G: T! Z# g& K& P
- rt_kprintf("webclient GET request failed, response(%d) error.\n", resp_status);
; C# L. ]7 ^' F" ] Q. Q& x5 O& s - ret = -RT_ERROR;
9 [/ }% M: d0 R, C9 b - goto __exit;1 ^# z6 S/ Q2 p1 o) Y1 w
- }6 a+ t+ G. Z$ ]& @1 b6 ^" I7 O
- 8 Z1 n. p$ a9 Q3 u1 _9 v5 b: h. e# D
- rt_kprintf("webclient get response data: \n");
X4 v: ~8 g7 L- s* @, j
% _4 U7 u( X+ D- content_length = webclient_content_length_get(session);7 y) z7 L B2 A) b
- if (content_length < 0), U& I/ C+ Q1 G- A0 Y6 [0 X
- {
2 {4 S) V1 k/ }& w4 ^. c& t" J7 M - rt_kprintf("webclient GET request type is chunked.\n");6 X: d R# q6 P2 C
7 n3 u% `% `( ]6 Y/ l" _- do
/ W) X9 a5 j( P& w5 V* g7 O - {& P& L% Q$ C! z
- bytes_read = webclient_read(session, buffer, buffer_size);
# B. ^* @2 C) i3 i7 T; E8 k; [ - if (bytes_read <= 0)0 Z8 J2 Z5 D4 I& }3 w7 r
- break;
0 o5 Y" X5 I: y0 f8 |9 O7 K
O$ F( e' \) ]! `( q* m. m0 q6 }- for (index = 0; index < bytes_read; index++)
. R6 y0 j6 Y8 k" C, O2 Y1 ] - {0 I4 w) g$ U( h9 x, ^+ ~0 e
- rt_kprintf("%c", buffer[index]);1 P- t0 w) P2 ]2 c
- }
! w' b/ n! x: D; _0 c6 @ - } while (1);. |! }$ z8 J. h) C& U, f; N
- 4 C9 \% i1 h5 Z1 ]2 m
- rt_kprintf("\n");
! `( R' k. v# i& ^ - }) g7 |) R& e; S' e0 h5 W
- else
! P9 K. X! U8 K - {
( c- g' J7 D7 H) v! S - /* 读取服务器响应的数据 */8 w" z* X9 D0 h* o) J( {" _
- bytes_read = webclient_read(session, buffer, content_length);
! @- _: G3 h# W9 V& d - rt_kprintf("data length:%d\n", bytes_read); Z# q9 F3 p0 s4 c9 ]& }. x
- ( F! V: T# @6 k* t9 P/ c
- buffer[bytes_read] = '\0';
% o& U. A; Z' A/ Q9 \) m - rt_kprintf("\n\n %s \n\n", buffer);4 D+ \% A" r: Y; b8 s ] n: I: j' N
- // rt_kprintf("parse data\r\n");' N. \( c% |- |3 a+ L* T4 u: d. k
- // parseData(buffer); //解析函数
( W1 z7 P: o9 T: n4 x - rt_kprintf("\n");
: o" U1 ~$ n2 Z4 f4 y7 ~' R - }
! { S+ c1 m: Z1 `$ f! L/ r7 b - - W7 t3 Z/ r K4 I5 O1 u8 v* M
- __exit:
3 V0 f/ K2 D( @$ ^ - if (session)
. p, {! ?) Z/ h, b. I# D - webclient_close(session);
, m8 p, C4 [6 [% w9 A
, S0 I$ d5 p+ N' x' v! ~7 B- L- if (buffer)
" Q" ~& {1 e5 \' V - web_free(buffer);
" ?! N+ v3 T0 l* M0 t - }
7 g7 c* p' Z8 V' S- j - else
0 W, `* r& ?- e1 q/ d; m* r" Y( s - rt_kprintf("api error: %s\n", API_NCOV);. {6 ?0 d e7 k8 I$ A+ b
- ) i! W ?$ R# |" ?! C5 c& g
- return ret;
b" U0 I9 N0 X8 B - }, s& I2 r9 k$ n( Y0 d
- MSH_CMD_EXPORT(get_NCOV_Data, get api ncov);
复制代码 usr_ncov.h文件内容( e, W, u, J8 {8 L% C
- #ifndef APPLICATIONS_USR_NCOV_H_
5 ]( k; j1 O8 |' e. I' T - #define APPLICATIONS_USR_NCOV_H_
0 I) u& C2 V7 M) e - #include <webclient.h>4 a* ~# w( t1 o" l+ _8 s
- #include <rtdevice.h>
- Q% _& e4 P- w9 { x$ f - #include <rtthread.h>
' H9 F$ _; M( c( f - #define API_NCOV "http://lab.isaaclin.cn/nCoV/api/overall"
, T) ]7 u: g4 i4 r7 A( z2 R" D - int get_NCOV_Data(void);/ _" d; t U; C- `* l' l
- #endif /* APPLICATIONS_USR_NCOV_H_
复制代码 重新编译,下载,运行。在终端运行这个命令:
' ^% S/ m7 K3 Z! J. A8 J命令获取疫情数据 可以看到获取到了返回的数据,长度1366个字节。下一步就是对这个JSON数据进行解析,获取到我们想要的疫情数据。
T h9 S/ W' A5 G
% ?! J, S; \5 ?+ y! n
- ^( z. p4 w! p. o+ Y6.疫情数据的解析
$ x0 Y" I3 e; |, G5 ^API返回的数据是JSON格式的,关于JSON的介绍和解析,可以查看使用cJSON库解析和构建JSON字符串。数据的解析使用的开源小巧的cJSON解析库,我们可以在软件包管理中心直接添加:
( T8 L1 ^& k# l5 E0 Y3 t ?添加cJSON 在进行解析之前,先来分析一下JSON原始数据的格式:results键的值是一个数组,数组只有一个JSON对象,获取这个对象对应键的值可以获取到国内现存和新增确诊人数、累计和新增死亡人数,累计和新增治愈人数等数据。
5 Q2 g9 | \6 J, f
: `0 E0 H, n0 c" S/ A E- a1 }$ x9 d: `- m% _% a/ y
全球疫情数据保存在globalStatistics键里,它的值是一个JSON对象,对象仅包含简单的键值对,这些键的值,就是全球疫情数据,其中updateTime键的值是更新时间,这是毫秒级UNIX时间戳,可以转换为标准北京时间。# [+ I* R5 |) y$ X/ r7 y7 t" W
- {, a0 |# V: U; S% \7 ?* B* P
- "results": [{
4 F( e, J; f8 o5 F - "currentConfirmedCount": 509,
3 T7 O D4 S1 x2 ?9 g - "currentConfirmedIncr": 16,
1 N0 e* @' ^, q0 z; d. b - "confirmedCount": 85172,* \, w0 k1 V& C" m9 S" n
- "confirmedIncr": 24,. I7 D; E/ q/ v/ y
- "suspectedCount": 1899,' s+ V3 L$ X$ H
- "suspectedIncr": 4,
, D u2 B: l" b - "curedCount": 80015,2 [3 }; C2 @/ f' {; _
- "curedIncr": 8,' {! E1 J% y5 @' @) B1 V. m! p
- "deadCount": 4648,
, l* _' ]; h& c3 t- }8 ^ - "deadIncr": 0,
% V _6 j$ R7 w% O! S+ s( N - "seriousCount": 106,
" M( Q4 d# X. @. _ - "seriousIncr": 9,
3 I4 K; Z1 L( o/ F - "globalStatistics": {
) @( Z/ E7 l5 T" Q - "currentConfirmedCount": 4589839,; _) ^/ }' D, J. Y; u: }
- "confirmedCount": 9746927,
) O) n5 i, B0 U+ F; V' q9 O/ G - "curedCount": 4663778," ~9 N1 P+ k5 d' R& E! c! R
- "deadCount": 493310,
1 L' {5 }2 X, U) c# m6 O# ^# p - "currentConfirmedIncr": 281,
! T3 b7 y7 F+ W. f - "confirmedIncr": 711,& q% J/ Z7 q/ D4 v" e
- "curedIncr": 424,8 J4 [) m% \' Z( `' j
- "deadIncr": 6
+ b( n4 Q, a& H% w5 i# T% G' x - },+ N7 a- e6 S( B' Q J4 p: m3 Y! W
- "updateTime": 1593227489355; _ c% V0 s, {6 ?) J- K
- }],
) ^! E" S T- q& Q( Y$ V3 \4 D - "success": true4 r' n7 n) t5 c9 h' a
- }
复制代码 先定义了结构体NCOV_DATA,用于存储国内和全球疫情数据:
) p( B6 t' B$ m$ J8 {- struct NCOV_DATA{( f$ p) d% K s; x1 y% p# ~
- int currentConfirmedCount;
. l/ y1 [' |, z: i - int currentConfirmedIncr;
; G1 V+ O1 ]9 ?7 w9 g- c, P8 m - int confirmedCount;/ u) M' ?7 l0 R' E& m$ @
- int confirmedIncr;4 K5 j8 s( ^. l; l0 I& Z
- int curedCount;
3 H. ]: H r( J9 ~! F - int curedIncr;
& j1 @2 U! ]4 o) V - int deadCount; J% X3 z# {0 W3 w1 q4 _
- int deadIncr;
" \8 N6 A( L' ?3 K/ P4 Z - int seriousCount;3 Y8 O4 y+ q6 F# l' _2 f V
- int seriousIncr;
8 p8 X$ M/ d9 ]* f7 O) x
0 e3 t+ W# a+ R& G+ m# Z$ o- char updateTime[20];
& C- |5 c5 m9 {/ T4 I7 L" n - };
复制代码 对应的解析函数:
5 E! w# s7 y2 u) ]- #include <cJSON.h>9 q) N: O' f( q& N
- ; t# x" K* p( _ K( w1 N0 Z
- struct NCOV_DATA dataChina = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "06-13 16:22"};;
# ^. K+ p3 |7 F* m) `: R - struct NCOV_DATA dataGlobal = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};
0 H9 S# ^* L& u4 w9 L- W
4 k" m, v) Z0 g; I# R+ E3 Z- int parseData(uint8_t *str)4 N* ^/ e/ f$ D( B L$ \4 V
- {! a$ V6 J+ }% r0 i( X3 }
- int ret = 0;! X& E: q7 D- m+ L
- cJSON *root, *result_arr;
m3 B, [: _) T0 Z - cJSON *result, *global;% h3 N1 M. R; o. ?
- time_t updateTime;
$ p/ f2 |( B4 y2 l6 k$ R4 x - struct tm *time;
3 b/ U) Z* j; x" u* x% [, D. p - 6 a: d1 p* C/ f1 W
- root = cJSON_Parse((const char *)str); //创建JSON解析对象,返回JSON格式是否正确
8 |% c! U+ `( [" Z* \" Z
- Z! l# f$ Q# p% @2 z! l) k0 T4 o- if (root != 0)0 t$ x6 V8 T P) Z$ I; `
- {2 v+ z0 {$ s$ {
- rt_kprintf("JSON format ok, start parse!!!\n");+ |3 @- I. z o9 L8 l& P
- result_arr = cJSON_GetObjectItem(root, "results");
e+ A) b9 {! R, R - if(result_arr->type == cJSON_Array)
( T- H. x. K$ p. q3 X2 a - {
" T2 ]; g( }& t - // rt_kprintf("result is array\n");
4 b& I; T. z8 P. J8 P& O, t) a - result = cJSON_GetArrayItem(result_arr, 0);
# e& S% L, x. `" J | - if(result->type == cJSON_Object)6 `5 b) m. {; G( k
- {4 f# v V9 q+ @2 ?) m% N8 C" [
- // rt_kprintf("result_arr[0] is object\n");
, T: Y! x* e/ ?$ e& J
* X3 {; Y" ]6 m/ s1 |: ~- /* china data parse */
% {; i- w6 R, L. Z - dataChina.currentConfirmedCount = cJSON_GetObjectItem(result, "currentConfirmedCount")->valueint;( B) c, k, M' ^5 v* w+ E
- dataChina.currentConfirmedIncr = cJSON_GetObjectItem(result, "currentConfirmedIncr")->valueint;! I! _* T8 |6 A+ S- ?
- dataChina.confirmedCount = cJSON_GetObjectItem(result, "confirmedCount")->valueint;
9 C7 T, G6 n& Q$ _3 d! c# ?6 b - dataChina.confirmedIncr = cJSON_GetObjectItem(result, "confirmedIncr")->valueint;9 _+ \- Y, }0 q: k5 }# r5 P7 _
- dataChina.curedCount = cJSON_GetObjectItem(result, "curedCount")->valueint;# e( i7 ?( c1 r) ]
- dataChina.curedIncr = cJSON_GetObjectItem(result, "curedIncr")->valueint;) V; |; ~& ]- c) E$ N2 ~( f
- dataChina.deadCount = cJSON_GetObjectItem(result, "deadCount")->valueint;
% {! u# U7 `& J) M2 D4 W9 Y) N) V - dataChina.deadIncr = cJSON_GetObjectItem(result, "deadIncr")->valueint;7 b: M3 l- A7 X+ h0 f% C
- 2 \9 C) x5 O3 t
- rt_kprintf("**********china ncov data**********\n");: g) v+ @8 q. c, \, T5 h
- rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataChina.currentConfirmedCount, "currentConfirmedIncr", dataChina.currentConfirmedIncr);
: x' K/ `5 l/ p" s$ S# z* R - rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataChina.confirmedCount, "confirmedIncr", dataChina.confirmedIncr);
7 n8 U; S- G! C. t0 i - rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataChina.curedCount, "curedIncr", dataChina.curedIncr);) ~: G7 l" ], x! K3 N5 V7 i. ^5 K
- rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataChina.deadCount, "deadIncr", dataChina.deadIncr);
: m! S% K2 o L% S
7 `) X! \5 ]4 ~; A- /* global data parse */. e; p+ _* Y- x- A6 Y+ \
- global = cJSON_GetObjectItem(result, "globalStatistics");
1 l8 v* A* M; {5 i1 H6 \2 v9 T" O - if(global->type == cJSON_Object)
6 A( x( G3 B& Y3 C0 h5 f8 m# y$ G9 m. E - {
: i! J$ ?9 F, x( t - dataGlobal.currentConfirmedCount = cJSON_GetObjectItem(global, "currentConfirmedCount")->valueint; v2 G% \+ i; U9 ~* C, o: M) q% P0 ~
- dataGlobal.currentConfirmedIncr = cJSON_GetObjectItem(global, "currentConfirmedIncr")->valueint;! U$ ]1 G" \& R. U
- dataGlobal.confirmedCount = cJSON_GetObjectItem(global, "confirmedCount")->valueint;. j8 x- Q2 i% _7 H; ?0 Q
- dataGlobal.confirmedIncr = cJSON_GetObjectItem(global, "confirmedIncr")->valueint;/ H8 s, K+ N2 c8 u
- dataGlobal.curedCount = cJSON_GetObjectItem(global, "curedCount")->valueint;+ ^7 h: I3 h' X% y) @
- dataGlobal.curedIncr = cJSON_GetObjectItem(global, "curedIncr")->valueint;
/ M% S6 y7 \/ n* A4 d7 J; Q - dataGlobal.deadCount = cJSON_GetObjectItem(global, "deadCount")->valueint;
. Y1 f6 I1 H. E2 @$ a - dataGlobal.deadIncr = cJSON_GetObjectItem(global, "deadIncr")->valueint;; F4 j5 J O) |9 i; m
0 c* w# H# ~1 w; n/ D3 y; e- rt_kprintf("\n**********global ncov data**********\n");
: o2 j9 \) _7 K# @ A - rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataGlobal.currentConfirmedCount, "currentConfirmedIncr", dataGlobal.currentConfirmedIncr);$ `5 }& I4 e# Q) l
- rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataGlobal.confirmedCount, "confirmedIncr", dataGlobal.confirmedIncr);8 Q3 d c% ^5 \! S, D( q, S
- rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataGlobal.curedCount, "curedIncr", dataGlobal.curedIncr);
. E& o- e. C. B - rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataGlobal.deadCount, "deadIncr", dataGlobal.deadIncr);
9 _8 o$ u0 P5 l$ b: V
, @" o9 R, r" R" J- } else return 1;% K0 C; e" E* r4 j
) y) v# X8 v8 P; n- /* 毫秒级时间戳转字符串 */
5 I# Y3 m. L- v( y. ^- u - updateTime = (time_t )(cJSON_GetObjectItem(result, "updateTime")->valuedouble / 1000);
+ V1 q7 c' e1 O" c _ - updateTime += 8 * 60 * 60; /* UTC8校正 */6 B+ S( g+ X& b1 [" q8 ]' P; _
- time = localtime(&updateTime);% d& i; T. @" @& y1 A
- /* 格式化时间 */% }: C* _7 T8 p/ \. y1 A4 ~
- strftime(dataChina.updateTime, 20, "%m-%d %H:%M", time);! U; [9 a7 G+ y
- rt_kprintf("update: %s\r\n", dataChina.updateTime);/* 06-24 11:21 */* D3 B! R" @5 v
- //数据在LCD显示
4 N' m+ z. ?, ?0 K( [5 ~4 m* K: n& Q - //gui_show_ncov_data(dataChina, dataGlobal);, }! N; Z- @3 L- S; I: P* v
- } else return 1;
: z/ t9 p6 r- E: q - } else return 1;
# i; Y% U9 n: G - rt_kprintf("\nparse complete \n");8 p6 j" M, l% w, S
- }
. y L! {2 A4 @5 t; e X - else
+ E1 n( E) v$ t( _% I - {
( r" d0 I1 Z: d1 F - rt_kprintf("JSON format error:%s\n", cJSON_GetErrorPtr()); //输出json格式错误信息
, w/ {. I8 W. a! w5 d. \& {8 s - return 1;
' R& e& I3 [. v" |& k - }4 m( T6 A: X9 w6 g8 w4 T
- cJSON_Delete(root);- P* ^% r% x. {% }% v
' q% U- V7 p! `( I3 I) _- return ret;$ N+ ~, O; d& _$ j3 H! S3 H+ \
- }
复制代码 在数据接收完成之后,对JSON数据进行解析。
3 `* ~- m3 `# i0 i) q! H8 L9 J解析结果 7.疫情数据的显示* U) t1 i- j$ t6 @
数据解析出来之后,剩下的就简单了,把上一篇文章中9341的驱动文件移植过来就好了。8 P- `3 |' f$ o5 }: E6 _
$ s* q( }3 X; k2 i; x$ F l- V& T8 {1 E
液晶屏使用的是3.2寸 LCD,IL9341驱动芯片,320*240分辨率,16位并口。由于屏幕分辨率比较低,可显示的内容有限,所以只是显示了最基本的几个疫情数据。为了减小程序大小,GUI只实现了基本的画点,画线函数,字符的显示,采用的是部分字符取模,只对程序中用到的汉字和字符进行取模。为了增强可移植性,程序中并没有使用外置SPI Flash存储整个字库。
' k7 L# a( p( G0 g) O( a. y5 ^
# l1 c0 h3 n$ s% \5 ~
, ~9 y& Y/ V7 j- n由于RT-Thread Studio使用的HAL库,所以LCD的GPIO初始化函数需要修改一下:. j2 t( q$ S1 b! {" h i4 s
- void lcd_gpio_init(void)
- _2 { }, ]( @! G3 n - {5 W* M; ~; T5 C
- GPIO_InitTypeDef GPIO_InitStructure;
3 A* |# }; [8 F4 f, f `/ @: y
. z* F$ i S9 V: D2 B; }! j- __HAL_RCC_GPIOA_CLK_ENABLE();- k& ?1 a& W& p. x1 Y) R
- __HAL_RCC_GPIOC_CLK_ENABLE();
5 t5 U8 v' F7 d s" n - __HAL_RCC_GPIOB_CLK_ENABLE();1 j& j9 w/ l7 |, o( X/ S6 n! v+ s
- __HAL_RCC_AFIO_CLK_ENABLE();
* Z/ i; a2 o6 E W) G - __HAL_AFIO_REMAP_SWJ_NOJTAG();. [# q' K1 c, @/ E
- " S& I; B& I4 V7 Q# k2 g* H& q
- GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
+ a, H+ _6 G! V% L - GPIO_InitStructure.Pull = GPIO_PULLUP;
$ ]: g& o, o. ~$ C - GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
( c+ }5 C& G4 ^& I; R. f - GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6;! P% Z" A& c5 m7 G& g
- HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOC3 `; V/ `4 y* L( T: g* R) q8 t2 D
- " K& Y" P+ B: h5 O0 j/ I
- GPIO_InitStructure.Pin = GPIO_PIN_8; //背光引脚PA83 B* p# U5 _. U( ^9 Y! t, @& r. L
- HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIOC
0 \6 [ C9 ]* f* s* m/ E* C - & f5 c5 J7 e( ~
- GPIO_InitStructure.Pin = GPIO_PIN_All;1 y. V* S- C2 M$ |- a+ y
- HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
0 }1 j8 z0 I6 y( [0 S4 C
: a: Y, k# `* u: Q- HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6, GPIO_PIN_SET);
- I4 L% e* ~- } C0 t7 _( o9 H - HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);- r* ^" D& J1 X. D
- HAL_GPIO_WritePin(GPIOB, GPIO_PIN_All, GPIO_PIN_SET);6 m; u2 j- @7 W: [$ p/ [% @
- }
复制代码 延时函数换成:( X+ f# w% e% P4 ~% R7 z
还有一点,在Keil中,文字编码选择GBK编码,1个汉字占用2个字节,而RT-Thread Studio为UTF-8编码,1个汉字占用3个字节,汉字显示函数需要调整:
1 ~' C- d+ B/ n6 d- void gui_show_chn(uint16_t x0, uint16_t y0, char *chn)8 d0 d: N7 [7 R9 j
- {
3 D. y5 A$ c, S# I/ M - uint8_t idx = 0;
/ P0 @1 f- {, d% B - uint8_t* code[3]; //UTF-8:国=E59BBD" }) C' ^+ L/ _( _; x( Y
# X" x) A! M' ?/ Z5 R8 m' h- uint8_t size = sizeof(FONT_16X16_TABLE) / sizeof(FONT_16X16_TABLE[0]);# X4 ] P; X. B9 I8 S
- /* 遍历汉字,获取索引 */& c0 Q0 ]( {* O5 ?
- for(idx = 0; idx < size; idx++)/ V% P/ g& ]) K3 U* p
- {
) w8 ^! g; G: A8 [ - code[0] = FONT_16X16_TABLE[idx].chn;
. p! _! K1 f- u$ ?" e9 a0 @' h - code[1] = FONT_16X16_TABLE[idx].chn + 1;5 l1 r& n' @& o% f( D
- code[2] = FONT_16X16_TABLE[idx].chn + 2;% Z4 g& w0 O1 u# | ?& }
- //汉字内码一致
2 K' d" L! ^8 h - if(!(strcmp(code[0], chn) || strcmp(code[1], chn+1) || strcmp(code[2], chn+2)))$ |. S4 J/ Y4 W3 A, C
- {
4 \2 S. r* f9 h$ i7 f. c) | u - gui_show_F16X16_Char(x0, y0, idx, WHITE);
% J* T- }' d( u2 p - return;
! C8 w3 M: e! U: N6 b | - // break;
" E9 E* Q/ R- | o9 q - }+ t; u, r7 o( P; Y2 z
- }
; d( v! j4 M" d - }
复制代码 疫情数据显示函数:
1 X: a2 S6 T$ J9 G; M& Q( y- void gui_show_ncov_data(struct NCOV_DATA china, struct NCOV_DATA global)
- T) v! [: ^( ^) z+ B6 c9 _ - {
4 A% f; T5 E/ L' x. ?" Y' v( _ - uint8_t y0 = 20;4 g; b& h# X7 v
# J) e% R$ G# ]# I2 b- lcd_clear(BLACK);
2 C: Z$ P8 R3 \* T% ` - gui_show_bar();8 \" d( p& k, o3 s0 E# C
- 1 X6 J( }7 T3 x( p( u- D
- gui_drawLine(0, 18, 320, DIR_X, WHITE);: k. U7 l* ]& t. ?1 p3 m
- gui_drawLine(0, 38, 320, DIR_X, WHITE);: q' C+ e2 Y5 U6 I( a& s3 b! k
- gui_drawLine(0, 138, 320, DIR_X, WHITE);! u O+ k5 f( k4 [' S# z9 ]* C
- gui_drawLine(0, 158, 320, DIR_X, WHITE);
) k4 X2 B, h: ~; f - gui_drawLine(0, 220, 320, DIR_X, WHITE);( @8 D8 \" E1 v6 l9 I9 i
3 u' k3 o+ I0 Y) A# ^1 t- /* "国内疫情" */4 m7 U K7 X$ U% m
- gui_show_chn_string(128, y0, "国内疫情");: P. e8 s' x6 G( E% I) R* g5 N
- gui_show_line_data(40, "现存确诊:", china.currentConfirmedCount, "较昨日:", china.currentConfirmedIncr);
, g( ]+ E/ a n& m& x - gui_show_line_data(60, "累计确诊:", china.confirmedCount, "较昨日:", china.confirmedIncr);2 f2 d; x) V5 S2 X3 l0 |
- gui_show_line_data(80, "累计治愈:", china.curedCount, "较昨日:", china.curedIncr);
7 {: s9 e8 K9 b. W# M - gui_show_line_data(100, "现存重症:", china.seriousCount, "较昨日:", china.seriousIncr);- r. M: t! q% e K5 ~' C) G2 i* |' E
- gui_show_line_data(120, "累计死亡:", china.deadCount, "较昨日:", china.deadIncr);+ A6 m) C" K8 Z6 t0 s8 q3 L
- / ]. V$ @& i$ j
- /* 全球疫情 */
9 R- A/ |: i0 Y& t! u - gui_show_chn_string(128, 140, "全球疫情");" C. u( Y' \3 i9 ]6 h1 L
- gui_show_line_data(160, "现存确诊:", global.currentConfirmedCount, "较昨日:", global.currentConfirmedIncr);
2 O+ B9 G' l0 `+ q5 h% L2 A2 M& W - gui_show_line_data(180, "累计治愈:", global.curedCount, "较昨日:", global.curedIncr);
% m; E, H; I! ?- j0 l4 [ - gui_show_line_data(200, "累计死亡:", global.deadCount, "较昨日:", global.deadIncr);
. ^" c# [2 l) [ - ; |6 K9 O" q# q: z3 T1 A! @6 C7 p
- gui_show_chn_string(160, 222, "更新于:");
n, l/ }. Z* K6 B9 v$ R' c - gui_show_F8X16_String(230, 222, china.updateTime, GREEN);
复制代码 最终显示效果
' e1 X! l" |% `5 r2 B最终效果 开源地址6 q4 p" j+ T/ o' q% M8 `0 E' F# P
代码已经开源,地址在文末,欢迎大家参与,丰富这个小项目的功能!; ?, U7 ~; Y+ b6 e
. {% G; X) \ Z$ }0 j$ D% p m: S5 N1 j+ A6 X, M# B3 [" k
基于STM32+RT-Thread的疫情监控平台
+ A& F/ W3 a' X0 Xhttp://github.com/whik/rtt_2019_ncov: ~+ E- @6 J5 h) u, d) e
" z, n7 n! ^ D5 L7 Z
6 F2 k7 @& h, \( {" z基于STM32F103的疫情监控平台(裸机版)4 P- O5 J9 u6 m! n U
http://github.com/whik/stm32_2019_ncov
8 y- M8 S/ f9 E5 J Z
$ N, ]; x# ]: l+ [' n! j2 }4 @( K* V# A$ s5 B1 Q
; T% ^/ d3 N: `
文章出处:[color=var(--weui-LINK)][url=]RTThread物联网操作系统[/url]
1 i% x7 G, S+ y7 F: S( Y4 l2 W, I" j+ [; ^ h4 j3 D
|