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