用Arduino玩转掌控板(ESP32):新冠肺炎疫情数据实时显示器

image

可以先看一下演示视频,视频地址:https://v.qq.com/x/page/h306275od2a.html

前两天在群里聊天看到一个全国新型肺炎疫情的实时数据接口:https://lab.isaaclin.cn/nCoV/

nCoV数据接口网站

正好这几天被隔离在家闲着没事做,而且对新型肺炎的数据也是一直很关注,所以就用手里的掌控版(ESP32 开发板)做了一个“疫情数据实时显示器”,放在电脑边上,实时查看最新的肺炎数据。

注:手里没有掌控版的朋友,可以用其他 ESP32 或者 ESP8266 开发板来实现。

nCoV API 解读

打开网站:https://lab.isaaclin.cn/nCoV/,上面列举了好几种请求接口:

  • 1:/nCoV/api/overall:返回病毒研究情况以及全国疫情概览。
  • 2:/nCoV/api/provinceName:返回数据库内有数据条目的省份、地区、直辖市列表。
  • 3:/nCoV/api/area:返回中国所有省份、地区或直辖市及世界其他国家的所有疫情信息变化的时间序列数据(精确到市),能够追溯确诊/疑似感染/治愈/死亡人数的时间序列。
  • 4:/nCoV/api/news:返回所有与疫情有关的新闻信息,包含数据来源以及数据来源链接。
  • 5:/nCoV/api/rumors:返回与疫情有关的谣言以及丁香园的辟谣。

可以看到,疫情精确数据主要在第 3 条接口中,我们进一步查看一下第 3 条接口的细节:

API接口细节

根据示例,如果我们要获取“浙江省”的疫情最新数据的话,接口网址应该是:https://lab.isaaclin.cn/nCoV/api/area?latest=1&province=浙江省

我们将这个网址输入浏览器地址栏查看一下相应的数据,果然有数据返回。

浙江省数据

但是返回的数据,格式太乱了,不方便查看。所以我将这些数据复制到 VS Code 软件中,然后在右下角将数据格式设置为 JSON。然后在文件中右键,选择Format Document(格式化文件)命令。

vscode整理数据

经过格式化之后,我们就可以看到排列有序、层次清晰的数据了。这是一堆 JSON 格式的数据,我们可以看到浙江省确诊人数是 1006 人、治愈人数是 99 人、杭州确诊人数是 156 人等信息。

整理后的数据

既然是 JSON 格式的数据,我们就可以很轻松的用代码来读取了。

至于实时更新数据,那就更简单了,我们只要定时去访问这个接口,就可以随时获取最新数据了。什么?还要人为去访问这个网址?当然不是,掌控版(ESP32 开发板)可是有联网功能的,虽然我也闲着没事做,但是这种可以自动化的工作,还是交给程序去做吧。

接下来我们就来看一下具体的代码该怎么写。

软件选择

这里我们选用 Arduino 软件来进行代码的编写,为了减少 Arduino 中配置掌控版(ESP32) 开发环境的麻烦,我们直接使用 Mixly 软件内置的 Arduino 软件,已经帮我们配置好相应的 ESP32 开发环境了。

如果你想自己在 Arduino 中配置 ESP32 开发环境,也可以查看官方教程:
https://github.com/espressif/arduino-esp32

Mixly 软件下载地址:

https://mixly.readthedocs.io/zh_CN/latest/basic/02Installation-update.html

mixly_arduino

配置好相应的开发环境之后,就是选择开发板、编写程序、最后下载程序。如果你用的和我一样,是 Mixly 内置的 Arduino 的话,可以选择 Arduino ESP32(通用 ESP32 开发板)或 Arduino HandBit(掌控版)。如果你是自己配置 ESP32 开发环境的话,只要选择对应的开发板即可。

开发板选择

代码编写

连接网络

首先连接网络,并且初始化串口,方便后面调试。联网调试代码如下,使用 ESP32 自带的 WiFi 库即可,如果网络连接成功,则在串口打印出开发板的 IP 地址。

#include <WiFi.h>
#include <Wire.h>

const char *ssid = "wifi-name";
const char *password = "wifi-password";

void setup() {
  Serial.begin(115200);

  // 连接网络
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print("Local IP: ");
  Serial.println(WiFi.localIP());
}

void loop() {
}

上传程序,打开串口监视器,看到打印出来的串口,则表示联网成功。

显示IP

设置定时器

接下来我们再来设置一个定时器。为什么要设置定时器呢?一来是降低访问数据接口的频次,减少服务器的压力;二来可以不用 delay() 函数,可以让程序更加高效。这里我们使用了 SimpleTimer.h 库,它使用起来比较简单。程序如下:

篇幅原因,这里省略了联网功能相关的代码,我们只要把定时器相关的代码添加到相应位置即可。

#include <SimpleTimer.h>

// other codes ...

SimpleTimer timer;

void setup() {
  // setup codes ...
    
  // 设置定时器
  timer.setInterval(10000L, timerCallback);  // 10000ms = 10s
}

void loop() {
  timer.run();
}

// 定时器回调函数
void timerCallback() {
    // timer code here...
}

在程序最开头,我们引入 SimpleTimer.h 库函数,然后初始化一个定时器对象 timer,在 setup() 中初始化定时器,并设置定时器回调函数 timerCallback(),这里设置每隔 10 秒(10000ms)调用一次定时器,你也可以该修改为其他时间。

然后在定时器回调函数内,获取新型肺炎的实时数据。这里我们需要用到两个库:HTTPClient.hArduinoJson.hHTTPClient.h 库是用来联网获取上述 API 接口的数据的,ArduinoJson.h 库可以解析返回的 JSON 数据,获得我们关心的数据。

然后设置 API 接口地址 apiUrl,这里你可以修改为你想要的数据地址。再设置几个变量,用于存储我们关心的数据,其中 xx_confirmedCount 表示确诊人数,xx_suspectedCount 表示疑似人数,xx_curedCount 表示治愈人数,xx_deadCount 表示死亡人数,前面的 xx 是前缀,分别可以修改成 zj (浙江)、hangzhou (杭州)、ningbo(宁波)等。

#include <HTTPClient.h>
#include <ArduinoJson.h>

// other codes ...

String apiUrl = "https://lab.isaaclin.cn/nCoV/api/area?latest=1&province=浙江省";

// 浙江疫情数据变量
int zj_confirmedCount;
int zj_suspectedCount;
int zj_curedCount;
int zj_deadCount;

// 杭州疫情数据变量
int hangzhou_confirmedCount;
int hangzhou_suspectedCount;
int hangzhou_curedCount;
int hangzhou_deadCount;

 // 宁波疫情数据变量      
int ningbo_confirmedCount;
int ningbo_suspectedCount;
int ningbo_curedCount;
int ningbo_deadCount;  

void setup() {
  // setup codes ...
}

void loop() {
  // loop codes ...
}

// 定时器回调函数
void timerCallback() {

  HTTPClient http;
  http.begin(apiUrl);

  int httpCode = http.GET();
  if (httpCode == HTTP_CODE_OK) {
    String results = http.getString();
    //    Serial.println(results); // 网页内容

    DynamicJsonBuffer jsonBuffer(512);
    JsonObject& resultsJson = jsonBuffer.parseObject(results);

    if (!resultsJson.success()) {
      Serial.println("parseObject() failed");
      return;
    }

    // -------- 浙江省数据 -----------
    JsonObject& provinces = resultsJson["results"][0];

    const char* country = provinces["country"]; // "中国"
    const char* provinceName = provinces["provinceName"]; // "浙江省"
    const char* provinceShortName = provinces["provinceShortName"]; // "浙江"
    zj_confirmedCount = provinces["confirmedCount"];
    zj_suspectedCount = provinces["suspectedCount"];
    zj_curedCount = provinces["curedCount"];
    zj_deadCount = provinces["deadCount"];

    // -------- cities -----------
    JsonArray& cities = provinces["cities"];

    // -------- 杭州数据 -----------
    JsonObject& hangzhou = cities[1];
    const char* hangzhou_cityName = hangzhou["cityName"]; // "杭州"
    hangzhou_confirmedCount = hangzhou["confirmedCount"];
    hangzhou_suspectedCount = hangzhou["suspectedCount"];
    hangzhou_curedCount = hangzhou["curedCount"];
    hangzhou_deadCount = hangzhou["deadCount"];
    //    long hangzhou_locationId = hangzhou["locationId"]; // 330100

    // -------- 宁波数据 -----------
    JsonObject& ningbo = cities[2];
    const char* ningbo_cityName = ningbo["cityName"]; // "宁波"
    ningbo_confirmedCount = ningbo["confirmedCount"];
    ningbo_suspectedCount = ningbo["suspectedCount"];
    ningbo_curedCount = ningbo["curedCount"];
    ningbo_deadCount = ningbo["deadCount"];
    //    long ningbo_locationId = ningbo["locationId"]; // 330200

    // -------- 串口打印实时疫情信息 -----------
    Serial.println("浙江省新型肺炎疫情实时数据");

    Serial.println("-----------------------------------------");

    Serial.print("浙江:\t确诊:");
    Serial.print(zj_confirmedCount);
    Serial.print("\t治愈:");
    Serial.print(zj_curedCount);
    Serial.print("\t\t死亡:");
    Serial.println(zj_deadCount);

    Serial.print("杭州:\t确诊:");
    Serial.print(hangzhou_confirmedCount);
    Serial.print("\t治愈:");
    Serial.print(hangzhou_curedCount);
    Serial.print("\t\t死亡:");
    Serial.println(hangzhou_deadCount);

    Serial.print("宁波:\t确诊:");
    Serial.print(ningbo_confirmedCount);
    Serial.print("\t治愈:");
    Serial.print(ningbo_curedCount);
    Serial.print("\t\t死亡:");
    Serial.println(ningbo_deadCount);

    Serial.println();

  } else {
    Serial.println("GET Error.");
  }

  http.end();
}

timerCallback() 回调函数中,初始化一个 HTTPClient 对象 http,并用 http.begin(apiUrl) 去访问 API 地址,然后用 http.GET() 方法去下载相应的数据,并用 http.getString() 函数,将数据保存为一个 String 数组。

然后用下面的代码将结果解析为 JSON 格式,然后按照 JSON 递归顺序层层解析数据即可。

DynamicJsonBuffer jsonBuffer(512);
JsonObject& resultsJson = jsonBuffer.parseObject(results);

上传程序,这时我们就可以在穿口中查看相应的数据了。

串口查看数据

OLED 屏数据显示

但是我们总不能一直在串口监视器中查看数据吧,所以为了查看方便,我们可以将数据显示到 OLED 屏幕上,这里我们选用 OLED 12864 显示屏来显示数据,这也是掌控版自带的显示屏。

首先在程序开头引入 U8g2lib.h 库,它是用来控制 OLED 屏幕显示内容的,然后初始化显示屏对象 u8g2,常用的 OLED 12864 有两种尺寸,分别为 1.3 寸和 0.96 寸,你可以根据你的屏幕尺寸和和相应的显示屏控制芯片来选择对应的初始化程序,掌控版对应的屏幕尺寸是 1.3 寸。

接着就是在 setup() 中初始化 u8g2 对象了,然后设置字体为 u8g2_font_wqy12_t_gb2312a,这是一个宋体字集,包含了 4000 多个常用字,字体大小可以设置为 12、13、14、15、16。这里要显示的内容比较多,我们设置为最小的 12 号字体。

#include <U8g2lib.h>

// other codes ...

// 根据选用的显示屏,注释相应的程序
// 1.3寸OLED12864显示屏
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);
// 0.96寸OLED12864显示屏
//U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

void setup() {
  // 初始化OLED显示屏
  u8g2.begin();
  u8g2.enableUTF8Print();
  u8g2.setFont(u8g2_font_wqy12_t_gb2312a);
  u8g2.setFontPosTop();
  u8g2.clearDisplay();
    
  // setup codes ...
}

void loop() {
  // loop codes ...
}

// 定时器回调函数
void timerCallback() {
  // other codes ...

  u8g2.firstPage();
  do
  {
    displayData();
  }
  while (u8g2.nextPage());
}


// 在OLED上显示实时疫情数据
void displayData() {
  u8g2.clearDisplay();

  // 绘制表格
  u8g2.drawFrame(0, 16, 128, 48);
  u8g2.drawLine(0, 32, 127, 32);
  u8g2.drawLine(0, 48, 127, 48);
  u8g2.drawLine(32, 16, 32, 63);
  u8g2.drawLine(64, 16, 64, 63);
  u8g2.drawLine(96, 16, 96, 63);

  // 标题
  u8g2.setCursor(18, 2);
  u8g2.print("浙江疫情实时信息");

  // 表格类目
  u8g2.setCursor(36, 20);
  u8g2.print("确诊");
  u8g2.setCursor(68, 20);
  u8g2.print("治愈");
  u8g2.setCursor(100, 20);
  u8g2.print("死亡");

  // 浙江情况
  u8g2.setCursor(4, 36);
  u8g2.print("浙江");
  u8g2.setCursor(38, 36);
  u8g2.print(zj_confirmedCount);
  u8g2.setCursor(72, 36);
  u8g2.print(zj_curedCount);
  u8g2.setCursor(104, 36);
  u8g2.print(zj_deadCount);

  // 杭州情况
  u8g2.setCursor(4, 52);
  u8g2.print("杭州");
  u8g2.setCursor(40, 52);
  u8g2.print(hangzhou_confirmedCount);
  u8g2.setCursor(72, 52);
  u8g2.print(hangzhou_curedCount);
  u8g2.setCursor(104, 52);
  u8g2.print(hangzhou_deadCount);
}

最后在 timerCallback() 回调函数中调用 OLED 屏的显示函数 displayData() 即可。

最后上传程序,就可以在掌控版(自带 OLED 屏幕的 ESP32 开发板)或 NodeMCU-32S (ESP32 开发板,须外接 OLED 屏幕)等开发板上查看实时新型肺炎疫情的数据了。

image

在这里插入图片描述

至此,“疫情数据实时显示器”就做完了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容