查看: 263|回复: 4

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

[复制链接]
微信图片_20200208112032.png


国际惯例,先来看一下演示效果:



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

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

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

# nCoV API 解读

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

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

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

API接口细节.png

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

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

浙江省数据.png

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

vscode整理数据.png

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

整理后的数据.png

既然是 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.png

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

开发板选择.png


# 代码编写

## 连接网络

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

[C++] 纯文本查看 复制代码
#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.png


## 设置定时器

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

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

[C++] 纯文本查看 复制代码
#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.h。HTTPClient.h 库是用来联网获取上述 API 接口的数据的,ArduinoJson.h 库可以解析返回的 JSON 数据,获得我们关心的数据。

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

[C++] 纯文本查看 复制代码
#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 递归顺序层层解析数据即可。

[C++] 纯文本查看 复制代码
DynamicJsonBuffer jsonBuffer(512);
JsonObject& resultsJson = jsonBuffer.parseObject(results);


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

串口查看数据.png


## 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 号字体。

[C++] 纯文本查看 复制代码
#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 屏幕)等开发板上查看实时新型肺炎疫情的数据了。

掌控版显示数据.jpg



NodeMCU-32S显示数据.jpg


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

# 代码下载

关注公众号“铁熊玩创客”,回复“疫情”获取完整代码。

公众号二维码.jpg




youjingisland  见习技师

发表于 2020-2-11 23:14:25

值得学习
回复 支持 反对

使用道具 举报

铁熊  版主
 楼主|

发表于 2020-2-14 11:30:36


谢谢,多多交流
回复 支持 反对

使用道具 举报

且歌且行  中级技师

发表于 2020-2-15 20:00:43

太厉害了,大神老师
回复 支持 反对

使用道具 举报

kylinpoet  初级技匠

发表于 2020-2-18 02:26:53

多谢分享,学习了。
回复 支持 反对

使用道具 举报

高级模式
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2020 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail