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