昨天发了一篇:用STM32做个东京奥运奖牌榜,有很多朋友留言说要搞一个疫情数据的监控平台,这不今天安排上了!

目 录

使用到的软件包

0.RT-Thread Studio的下载和安装

1.硬件准备

2.新建工程

3.添加LED闪烁功能

4.添加ESP8266软件包

5.疫情数据的获取

6.疫情数据的解析

7.疫情数据的显示

8.开源地址

最终的显示效果:这是2020年写的一篇文章,所以数据是去年的!大家别误会了!

显示效果


有效文件就这9个,其他的就全是图形化配置:

有效文件

整个流程下来,如果顺利的话,可以在2个小时内完成。

使用到的软件包

  • at device:用于ESP8266配网

  • webclient:用于发送HTTPS请求

  • mbdetls:用于HTTPS加密

  • cJSON:用于JSON数据解析

0.RT-Thread Studio的下载和安装

一站式的 RT-Thread 开发工具,通过简单易用的图形化配置系统以及丰富的软件包和组件资源,让物联网开发变得简单和高效。

RT-Thread Studio
  • 支持多种芯片,STM32全系列

  • 支持创建裸机工程、RT-Thread Nano和Master工程

  • 强大的代码编辑功能,基于Eclipse框架

  • 免费无版权限制,基于开源Eclipse和ARM-GCC编译器。

  • 支持多种仿真器,J-Link,ST-link等,支持在线调试,变量观察。

  • SDK管理器,图形化配置RT-Thread软件包,同步RT-Thread最新版本。

  • 集成Putty串口终端工具

更多的使用教程:

https://www.rt-thread.org/page/studio.html

目前最新版本为1.1.3版本,支持3种下载方式,我们选择最后一个下载方式,从RT-Thread 官网服务器上下载。

下载地址:

http://117.143.63.254:9012/www/studio/download/RT-Thread%20Studio-v1.1.3-setup-x86_64_20200731-2100.exe

下载链接

安装过程和常用的软件安装方法一样,选择安装路径,然后Next就行了。

1.硬件准备

开发板用的是我在大四时自己设计的STM32开发板——NiceDay,基于STM32F103RET主控。

这是我设计的第二块板子(第一块是毕业设计两轮平衡车主板),是在大四快毕业时,毕设实物和论文完成之后还有点时间,就设计了这款板子,最开始是准备做桌面天气时钟的。

开发板

2.新建工程

RT-Thread Studio支持创建裸机工程包含RT-Thread Nano版本的工程包含Master版本的工程。这里,我们选择创建RT-Thread 项目,即包含完整版RT-Thread的工程。

新建项目

工程支持基于芯片创建工程,或者基于已有的BSP创建,这里使用的是我自己设计的开发板,所以选择基于芯片,选择芯片型号:STM32F103RE,调试串口选择串口1,调试器选择J-Link,SWD接口。

新建项目

创建完成之后,直接按Ctrl+B编译整个工程,第一次编译时间会长一点,如果修改很少,下次再进行编译就会很快了,可以看到无警告无错误。

编译结果

使用SWD接口连接JLink调试器和开发板,开发板上电,直接点击下载按钮,也可以使用快捷键Ctrl+Alt+D下载

下载程序

底部可以看到下载信息,从LOG来看,下载的程序文件是Bin文件,比较,擦除,编程,验证,复位整个流程耗时13s左右。

下载LOG

RT-Thread Studio是自带Putty串口终端的,点击终端图标:

终端按钮

选择串口号、波特率、文字编码方式等。

配置终端

底部切换到终端窗口,可以看到串口终端输出信息:

串口终端

这样,不到5分钟,一个基于STM32F103RET6的工程模板就创建好了,包含RT-Thread完整版操作系统,整个过程不需要写一行代码,完全图形化配置。

3.添加LED闪烁功能

作为单片机点灯小能手,RT-Thread下如何点灯是必须掌握的。打开RT-Thread组件图形化配置界面,可以看到默认开启了PIN和串口设备驱动的。

图形化配置界面

在main.c文件中添加LED闪烁功能。包含头文件和添加宏定义

#include <board.h>
#include <rtdevice.h>#define LED_RED_PIN     GET_PIN(A, 7)
#define LED_BLUE_PIN    GET_PIN(A,6)int main(void)
{int count = 1;rt_pin_mode(LED_RED_PIN, PIN_MODE_OUTPUT);rt_pin_mode(LED_BLUE_PIN, PIN_MODE_OUTPUT);while (count++){rt_pin_write(LED_BLUE_PIN, PIN_LOW);rt_pin_write(LED_RED_PIN, PIN_LOW);rt_thread_mdelay(100);rt_pin_write(LED_BLUE_PIN, PIN_HIGH);rt_pin_write(LED_RED_PIN, PIN_HIGH);rt_thread_mdelay(100);}return RT_EOK;
}

重新编译,下载。可以看到LED闪烁起来了。工程默认是使用内部RC作为输入时钟,所以无论你的板子是8M还是12M,都可以正常闪烁。我的开发板是8M晶体,这里我们配置使用外部HSE作为输入时钟。

打开drivers->stm32f1xx_hal_conf.h文件,修改HSE_VALUE宏定义为8M。

晶体频率修改

打开drivers->drv_clk.c文件:

时钟源修改

配置PLL时钟源为HSE,并设置倍频系数为9。

时钟源修改

倍频系数

这里根据实际板子晶体频率来设置,如果是12M晶体,倍频系数应该设置为6,如果是16M,需要参考时钟树,先2倍分频,然后9倍倍频。

#include <rtdbg.h>void system_clock_config(int target_freq_Mhz)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};/** Initializes the CPU, AHB and APB busses clocks*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;........   //9倍频RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;   //8*9=72M........
}

这样就修改为外部8M晶体作为PLL时钟源,再次编译下载,和之前的现象是一样的。

4.添加ESP8266软件包

联网设备,我们选择的是ESP8266-01S,如果看过上一篇疫情监控三部曲——在STM32F103 MCU上实现(裸机版),里面介绍了如何配置ESP8266 GET HTTPS请求, 配置工作模式 > 连接WiFi > 与服务器建立SSL连接 > 发送GET请求获取数据等等,整个流程固定而繁琐,那么能不能封装成一个模块,直接拿来使用呢?

esp8266

这里就要介绍RT-Thread的AT Device软件包了,

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 命令章节 。
https://www.rt-thread.org/document/site/programming-manual/at/at/

简单的说,就是我只需要调用这个软件包,然后修改WiFi账号和密码,就可以直接配置ESP8266联网了。

由于AT Device依赖于libc组件,所以在添加AT Device软件包之前,先开启libc。

RT-Thread Settings中点击libc灰色图标,变成彩色说明已经开启。

组件配置

添加AT Device软件包,点击立即添加

软件包

在弹出的软件包中心,搜索at_device,然后点击添加,添加到当前工程。

软件包

在at_device软件包上右键,选择详细配置:

软件包

在弹出的页面,选择我们使用的WiFi模块类型,乐鑫的ESP8266系列,并配置WiFi账号和密码,WiFi模块所连接的串口号。

WiFi配置

点击保存之后,工程会重新进行配置,添加相应的软件包文件到当前工程,重新生成Makefile文件,rtconfig文件等等。

虽然我们在at_device配置中选择了uart2作为at_device设备连接的串口。但此时串口2并没有开启,还需要我们手动使能。

打开drivers->board.h文件,通过宏定义的方式使能串口2。

#define BSP_USING_UART2
#define BSP_UART2_TX_PIN       "PA2"
#define BSP_UART2_RX_PIN       "PA3"

这样就开启了UART2的片上外设,Ctrl + B重新进行编译,时间会有些长,编译完成之后,可以看到flash文件大小明显比之前大了。

编译结果

Ctrl + Alt + D重新下载运行,打开串口终端:

终端

可以看到,UART2初始化成功,WiFi连接成功。说明我们的串口模块已经可以正常工作了。提示[E/at.clnt] execute command (AT+CIPDNS_CUR?) failed!失败信息,是因为当前ESP8266的固件版本不支持AT+CIPDNS_CUR?这条命令,把固件升级到最新版本就好了。这个不影响后面的操作,所以就不用在意这个了。

测试一下ifconfig和ping命令,都是正常的。

终端

在RT-Thread Studio中配置ESP8266模块联网,整个流程只写了3行代码,可以说是非常的快速方便。

5.疫情数据的获取

WiFi模块连接上互联网之后,就可以连接GET疫情数据的API接口https://lab.isaaclin.cn/nCoV/api/overall,然后读取返回的疫情数据。

在上一篇的裸机工程中,是通过先和服务器建立SSL连接,然后发送GET HTTPS请求,获取到的返回数据,那RT-Thread有没有这样功能的软件包呢?这里就需要添加另一个软件包webclient

WebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。
WebClient 软件包功能特点如下:

  • 支持 IPV4/IPV6 地址;

  • 支持 GET/POST 请求方法;

  • 支持文件的上传和下载功能;

  • 支持 HTTPS 加密传输;

  • 完善的头部数据添加和处理方式。

和添加at_device一样,在软件包中心中搜索webclient

软件包

然后添加到当前工程,右键进行配置,由于我们的https://lab.isaaclin.cn/nCoV/api/overall这个疫情数据接口是HTTPS类型的,根据软件包使用手册,我们需要选择TLS模式中的 MbedTLS。勾选添加GET和POST示例。

软件包配置

保存配置,看一下当前已经添加了哪些功能,可以看到有一些组件我们并没有去打开,但是已经被开启了,这是因为有些软件包是会依赖一些组件的,当使能软件包时,一些依赖的组件也被同时使能。

软件包

Ctrl + B编译,Ctrl + Alt + D下载运行。在终端输入web_get_test测试GET请求功能。

GET示例

可以看到,执行get命令之后,会返回一个字符串,那么GET的是哪个地址呢?打开packages->webclient-v2.1.2->samples->webclient_get_sample.c文件,

示例代码

可以看到GET的是这个地址:http://www.rt-thread.com/service/rt-thread.txt,我们用电脑上的浏览器访问一下:

浏览器访问

经过实际测试发现,GET HTTPS请求,还需要使能软件模拟RTC这个组件,否则会报assertion failed at function:gettimeofday, line number:19错误。

使能RTC

我们重新写一个获取疫情数据的函数,并导出到MSH。

usr_ncov.c文件内容

//usr_ncov.c
#include "usr_ncov.h"int get_NCOV_Data(void)
{char *uri = RT_NULL;struct webclient_session* session = RT_NULL;uint8_t *buffer = RT_NULL;int index, ret = 0;int bytes_read, resp_status;int content_length = -1;int buffer_size = 1600;uri = web_strdup(API_NCOV);rt_kprintf("start get api: %s\r\n", API_NCOV);if(uri != RT_NULL){buffer = (unsigned char *) web_malloc(buffer_size);if (buffer == RT_NULL){rt_kprintf("no memory for receive buffer.\n");ret = -RT_ENOMEM;goto __exit;}/* create webclient session and set header response size */session = webclient_session_create(buffer_size);if (session == RT_NULL){ret = -RT_ENOMEM;goto __exit;}/* send GET request by default header */if ((resp_status = webclient_get(session, uri)) != 200){rt_kprintf("webclient GET request failed, response(%d) error.\n", resp_status);ret = -RT_ERROR;goto __exit;}rt_kprintf("webclient get response data: \n");content_length = webclient_content_length_get(session);if (content_length < 0){rt_kprintf("webclient GET request type is chunked.\n");do{bytes_read = webclient_read(session, buffer, buffer_size);if (bytes_read <= 0)break;for (index = 0; index < bytes_read; index++){rt_kprintf("%c", buffer[index]);}} while (1);rt_kprintf("\n");}else{/* 读取服务器响应的数据 */bytes_read = webclient_read(session, buffer, content_length);rt_kprintf("data length:%d\n", bytes_read);buffer[bytes_read] = '\0';rt_kprintf("\n\n %s \n\n", buffer);
//            rt_kprintf("parse data\r\n");// parseData(buffer);        //解析函数rt_kprintf("\n");}__exit:if (session)webclient_close(session);if (buffer)web_free(buffer);}elsert_kprintf("api error: %s\n", API_NCOV);return ret;
}
MSH_CMD_EXPORT(get_NCOV_Data, get api ncov);

usr_ncov.h文件内容

#ifndef APPLICATIONS_USR_NCOV_H_
#define APPLICATIONS_USR_NCOV_H_#include <webclient.h>
#include <rtdevice.h>
#include <rtthread.h>#define API_NCOV     "https://lab.isaaclin.cn/nCoV/api/overall"int get_NCOV_Data(void);#endif /* APPLICATIONS_USR_NCOV_H_

命令获取疫情数据

可以看到获取到了返回的数据,长度1366个字节。下一步就是对这个JSON数据进行解析,获取到我们想要的疫情数据。

6.疫情数据的解析

API返回的数据是JSON格式的,关于JSON的介绍和解析,可以查看使用cJSON库解析和构建JSON字符串。数据的解析使用的开源小巧的cJSON解析库,我们可以在软件包管理中心直接添加:

添加cJSON

在进行解析之前,先来分析一下JSON原始数据的格式:results键的值是一个数组,数组只有一个JSON对象,获取这个对象对应键的值可以获取到国内现存和新增确诊人数、累计和新增死亡人数,累计和新增治愈人数等数据。

全球疫情数据保存在globalStatistics键里,它的值是一个JSON对象,对象仅包含简单的键值对,这些键的值,就是全球疫情数据,其中updateTime键的值是更新时间,这是毫秒级UNIX时间戳,可以转换为标准北京时间。

{"results": [{"currentConfirmedCount": 509,"currentConfirmedIncr": 16,"confirmedCount": 85172,"confirmedIncr": 24,"suspectedCount": 1899,"suspectedIncr": 4,"curedCount": 80015,"curedIncr": 8,"deadCount": 4648,"deadIncr": 0,"seriousCount": 106,"seriousIncr": 9,"globalStatistics": {"currentConfirmedCount": 4589839,"confirmedCount": 9746927,"curedCount": 4663778,"deadCount": 493310,"currentConfirmedIncr": 281,"confirmedIncr": 711,"curedIncr": 424,"deadIncr": 6},"updateTime": 1593227489355}],"success": true
}

先定义了结构体NCOV_DATA,用于存储国内和全球疫情数据:

struct NCOV_DATA{int currentConfirmedCount;int currentConfirmedIncr;int confirmedCount;int confirmedIncr;int curedCount;int curedIncr;int deadCount;int deadIncr;int seriousCount;int seriousIncr;char updateTime[20];
};

对应的解析函数:

#include <cJSON.h>struct NCOV_DATA dataChina = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "06-13 16:22"};;
struct NCOV_DATA dataGlobal = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL};int parseData(uint8_t *str)
{int ret = 0;cJSON *root, *result_arr;cJSON *result, *global;time_t updateTime;struct tm *time;root = cJSON_Parse((const char *)str);   //创建JSON解析对象,返回JSON格式是否正确if (root != 0){rt_kprintf("JSON format ok, start parse!!!\n");result_arr = cJSON_GetObjectItem(root, "results");if(result_arr->type == cJSON_Array){
//            rt_kprintf("result is array\n");result = cJSON_GetArrayItem(result_arr, 0);if(result->type == cJSON_Object){
//                rt_kprintf("result_arr[0] is object\n");/* china data parse */dataChina.currentConfirmedCount = cJSON_GetObjectItem(result, "currentConfirmedCount")->valueint;dataChina.currentConfirmedIncr = cJSON_GetObjectItem(result, "currentConfirmedIncr")->valueint;dataChina.confirmedCount = cJSON_GetObjectItem(result, "confirmedCount")->valueint;dataChina.confirmedIncr = cJSON_GetObjectItem(result, "confirmedIncr")->valueint;dataChina.curedCount = cJSON_GetObjectItem(result, "curedCount")->valueint;dataChina.curedIncr = cJSON_GetObjectItem(result, "curedIncr")->valueint;dataChina.deadCount = cJSON_GetObjectItem(result, "deadCount")->valueint;dataChina.deadIncr = cJSON_GetObjectItem(result, "deadIncr")->valueint;rt_kprintf("**********china ncov data**********\n");rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataChina.currentConfirmedCount, "currentConfirmedIncr", dataChina.currentConfirmedIncr);rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataChina.confirmedCount, "confirmedIncr", dataChina.confirmedIncr);rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataChina.curedCount, "curedIncr", dataChina.curedIncr);rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataChina.deadCount, "deadIncr", dataChina.deadIncr);/* global data parse */global = cJSON_GetObjectItem(result, "globalStatistics");if(global->type == cJSON_Object){dataGlobal.currentConfirmedCount = cJSON_GetObjectItem(global, "currentConfirmedCount")->valueint;dataGlobal.currentConfirmedIncr = cJSON_GetObjectItem(global, "currentConfirmedIncr")->valueint;dataGlobal.confirmedCount = cJSON_GetObjectItem(global, "confirmedCount")->valueint;dataGlobal.confirmedIncr = cJSON_GetObjectItem(global, "confirmedIncr")->valueint;dataGlobal.curedCount = cJSON_GetObjectItem(global, "curedCount")->valueint;dataGlobal.curedIncr = cJSON_GetObjectItem(global, "curedIncr")->valueint;dataGlobal.deadCount = cJSON_GetObjectItem(global, "deadCount")->valueint;dataGlobal.deadIncr = cJSON_GetObjectItem(global, "deadIncr")->valueint;rt_kprintf("\n**********global ncov data**********\n");rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataGlobal.currentConfirmedCount, "currentConfirmedIncr", dataGlobal.currentConfirmedIncr);rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataGlobal.confirmedCount, "confirmedIncr", dataGlobal.confirmedIncr);rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataGlobal.curedCount, "curedIncr", dataGlobal.curedIncr);rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataGlobal.deadCount, "deadIncr", dataGlobal.deadIncr);} else return 1;/* 毫秒级时间戳转字符串 */updateTime = (time_t )(cJSON_GetObjectItem(result, "updateTime")->valuedouble / 1000);updateTime += 8 * 60 * 60; /* UTC8校正 */time = localtime(&updateTime);/* 格式化时间 */strftime(dataChina.updateTime, 20, "%m-%d %H:%M", time);rt_kprintf("update: %s\r\n", dataChina.updateTime);/* 06-24 11:21 *///数据在LCD显示//gui_show_ncov_data(dataChina, dataGlobal);} else return 1;} else return 1;rt_kprintf("\nparse complete \n");}else{rt_kprintf("JSON format error:%s\n", cJSON_GetErrorPtr()); //输出json格式错误信息return 1;}cJSON_Delete(root);return ret;
}

在数据接收完成之后,对JSON数据进行解析。

解析结果

7.疫情数据的显示

液晶屏使用的是3.2寸 LCD,IL9341驱动芯片,320*240分辨率,16位并口。由于屏幕分辨率比较低,可显示的内容有限,所以只是显示了最基本的几个疫情数据。为了减小程序大小,GUI只实现了基本的画点,画线函数,字符的显示,采用的是部分字符取模,只对程序中用到的汉字和字符进行取模。为了增强可移植性,程序中并没有使用外置SPI Flash存储整个字库。

由于RT-Thread Studio使用的HAL库,所以LCD的GPIO初始化函数需要修改一下:

void lcd_gpio_init(void)
{GPIO_InitTypeDef GPIO_InitStructure;__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_AFIO_CLK_ENABLE();__HAL_AFIO_REMAP_SWJ_NOJTAG();GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStructure.Pull = GPIO_PULLUP;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;GPIO_InitStructure.Pin = GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6;HAL_GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOCGPIO_InitStructure.Pin = GPIO_PIN_8;    //背光引脚PA8HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); //GPIOCGPIO_InitStructure.Pin = GPIO_PIN_All;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9 | GPIO_PIN_8 | GPIO_PIN_7 | GPIO_PIN_6, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, GPIO_PIN_All, GPIO_PIN_SET);
}

延时函数换成:

rt_thread_mdelay(nms);

还有一点,在Keil中,文字编码选择GBK编码,1个汉字占用2个字节,而RT-Thread Studio为UTF-8编码,1个汉字占用3个字节,汉字显示函数需要调整:

void gui_show_chn(uint16_t x0, uint16_t y0, char *chn)
{uint8_t idx = 0;uint8_t* code[3]; //UTF-8:国=E59BBDuint8_t size = sizeof(FONT_16X16_TABLE) / sizeof(FONT_16X16_TABLE[0]);/* 遍历汉字,获取索引 */for(idx = 0; idx < size; idx++){code[0] = FONT_16X16_TABLE[idx].chn;code[1] = FONT_16X16_TABLE[idx].chn + 1;code[2] = FONT_16X16_TABLE[idx].chn + 2;//汉字内码一致if(!(strcmp(code[0], chn) || strcmp(code[1], chn+1) || strcmp(code[2], chn+2))){gui_show_F16X16_Char(x0, y0, idx, WHITE);return;
//            break;}}
}

疫情数据显示函数:

void gui_show_ncov_data(struct NCOV_DATA china, struct NCOV_DATA global)
{uint8_t y0 = 20;lcd_clear(BLACK);gui_show_bar();gui_drawLine(0, 18, 320, DIR_X, WHITE);gui_drawLine(0, 38, 320, DIR_X, WHITE);gui_drawLine(0, 138, 320, DIR_X, WHITE);gui_drawLine(0, 158, 320, DIR_X, WHITE);gui_drawLine(0, 220, 320, DIR_X, WHITE);/* "国内疫情" */gui_show_chn_string(128, y0, "国内疫情");gui_show_line_data(40, "现存确诊:", china.currentConfirmedCount, "较昨日:", china.currentConfirmedIncr);gui_show_line_data(60, "累计确诊:", china.confirmedCount, "较昨日:", china.confirmedIncr);gui_show_line_data(80, "累计治愈:", china.curedCount, "较昨日:", china.curedIncr);gui_show_line_data(100, "现存重症:", china.seriousCount, "较昨日:", china.seriousIncr);gui_show_line_data(120, "累计死亡:", china.deadCount, "较昨日:", china.deadIncr);/* 全球疫情 */gui_show_chn_string(128, 140, "全球疫情");gui_show_line_data(160, "现存确诊:", global.currentConfirmedCount, "较昨日:", global.currentConfirmedIncr);gui_show_line_data(180, "累计治愈:", global.curedCount, "较昨日:", global.curedIncr);gui_show_line_data(200, "累计死亡:", global.deadCount, "较昨日:", global.deadIncr);gui_show_chn_string(160, 222, "更新于:");gui_show_F8X16_String(230, 222, china.updateTime, GREEN);
}

最终显示效果

最终效果

8. 开源地址

代码已经开源,地址在文末,欢迎大家参与,丰富这个小项目的功能!

  • 基于STM32+RT-Thread的疫情监控平台https://github.com/whik/rtt_2019_ncov

  • 基于STM32F103的疫情监控平台(裸机版)https://github.com/whik/stm32_2019_ncov

-END-

大家好,我是张巧龙,一名教电子的大学老师,欢迎关注!

基于STM32+RT-Thread的新冠肺炎疫情监控平台相关推荐

  1. 居家学习:新冠肺炎疫情下中国高校基于直播的远程教育体验的混合方法分析

    Learning from Home: A Mixed-Methods Analysis of Live Streaming Based Remote Education Experience in ...

  2. 【大数据平台】基于Spark的美国新冠肺炎疫情数据分析及预测

    (本实验系中国地质大学(武汉)2022年秋期大数据平台及应用课程设计) 一.选题背景 新型冠状病毒疫情是由严重急性呼吸系统综合征冠状病毒2(SARS-CoV-2)导致的2019冠状病毒病(COVID- ...

  3. 第四范式荣获“工业和信息化系统抗击新冠肺炎疫情先进集体”称号

    近日,在全国工业和信息化工作会议上,工信部党组为工业和信息化系统抗击新冠肺炎疫情工作中,对疫情防控.促进经济持续稳定恢复发挥重要作用的全国100家「先进集体」和300名「先进个人」进行了表彰,凭借在疫 ...

  4. 含最新数据! 使用Python检测新冠肺炎疫情拐点

    注:本文案例仅供技术学习,不代表研究性观点. 本文对应代码.数据及文献资料已上传至Github仓库https://github.com/CNFeffery/DataScienceStudyNotes ...

  5. 数据分享——EPS数据库-新冠肺炎疫情实时监控平台

    原文链接:https://www.lianxh.cn/news/af018044fad9e.html 与 EPS 数据库沟通后,与大家一起分享最新的舆情监控数据. 疫情数据,牵动人心!数据是我们在疫情 ...

  6. 新冠肺炎疫情引发空前应用需求,Dynatrace助企业轻松应对

    数字化团队充分利用持续自动化和人工智能辅助技术,实现以高效率.高性价比的方式交付必要的业务成果 2020 年 5 月 14日--软件智能公司Dynatrace(纽交所代码:DT)指出,在新冠肺炎疫情( ...

  7. 新冠肺炎疫情把科研推上“云端”

    来源:新华网 美国威斯康星国家灵长类动物研究中心的戴夫·奥康纳清晨收到在伦敦的一名合作伙伴发来的论文预印本.这项研究在中国完成,两人通过企业协同云端办公软件Slack讨论了一上午.下午2点,奥康纳打开 ...

  8. 每日一练:Python爬虫爬取全国新冠肺炎疫情数据实例详解,使用beautifulsoup4库实现

    Python 爬虫篇 - 爬取全国新冠肺炎疫情数据实例详解 效果图展示 第一章:疫情信息的下载与数据提取 ① 爬取页面数据到本地 ② json 字符串正则表达式分析 ③ 提取数据中的 json 字符串 ...

  9. 新冠肺炎疫情预测与防控策略评价

    新冠肺炎疫情预测与防控策略评价 新冠肺炎疫情在全球蔓延以来,各个国家采取了不同的防疫策略和措施, 导致新冠肺炎疫情感染人数各异,效果不尽相同. 问题1:请你们团队收集几个典型国家(如:美国.英国.印度 ...

最新文章

  1. RocEDU.课程设计2018 第二周进展 博客补交
  2. uclinux与linux的区别
  3. JavaScript DOM编程艺术第一章:JavaScript简史
  4. 一道常被人轻视的前端JS面试题
  5. 第二章:WebDriver 打开Firefox浏览器 和 Chrome 浏览器
  6. Android项目开发实战——简单备忘录
  7. 训练集,验证集,测试集分别是什么
  8. python怎么返回上一行代码_如何返回循环Python中的第一行代码
  9. 2D转换及相关案例实现
  10. phalapi做登录检测_欢迎使用PhalApi!
  11. matplotlib在一张图中画两条线
  12. JS—— 常用图片后缀正则校验
  13. matlab2018a课后答案,[2018年最新整理]matlab习题及答案.doc
  14. 计算机收藏夹无法添加,Win7系统收藏夹不能用怎么办?
  15. 警惕!微信诈骗花样多:点个赞都可能被骗
  16. 工具推荐 10款移动界面原型设计工具
  17. matlab实验-拉格朗日插值的龙格(Runge)现象
  18. C++ STL容器详解
  19. MVC设计模式及Sprint MVC设计模式
  20. cdn加速以及前后分离SpringBoot+Vue 配置https及SSL证书

热门文章

  1. 端口漏洞之21(FTP)
  2. 第12部分 交换机基本配置
  3. ABP VNext学习日记27
  4. 开源是基础设施最佳开发方式 | GOTC 全球开源技术峰会
  5. 2020小迪安全第六天笔记-加密算法
  6. 时间序列浊度neph画图 python pandas 折线图 大气科学 大气化学 论文作图
  7. 免费音乐节拍软件:My Metronome for Mac
  8. 关于iTunes随机播放和我所不知道的自己
  9. 分数小数计算机在线,循环小数化分数计算器
  10. 纯手写SpringFramework-完结版(原创)