掌控版教程 | 用 ESP32 和 Arduino DIY 制作一个“疫情数据实时显示器”

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

前两天在群里聊天看到一个全国新型肺炎疫情的实时数据接口: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(格式化文件)命令。

VS Code整理数据格式

经过格式化之后,我们就可以看到排列有序、层次清晰的数据了。这是一堆 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 的话,可以选择 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 屏幕)等开发板上查看实时新型肺炎疫情的数据了。

在掌控板查看数据
在NodeMCU-32S开发板上查看数据

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

# 代码下载

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

# 相关推荐

## 51maker 微信公众号

51maker 是由一群教师团队维护的微信公众号,他们来自全国五湖四海,致力于 Scratch 编程及创客入门课程开发,为一线教师提供微视频、课件、教学设计等相关资源,为普及编程 & 创客教育尽一份力量。

## 知识星球创客教育能量站

激活课程制作分享,实现知识变现!能量站是一个付费知识社群,聚集了一大批优秀的老师,几乎每天都有优质的创客教育内容分享。可以扫描下方二维码付费加入,这是我的推荐码,你可以获得一定的优惠,当然我也会有一定的回报。


*欢迎转发朋友圈。如需转载,请注明出处和原作者。

扫描二维码

关注铁熊吧

往期精彩内容

创客项目缺少高颜值电路图?看这里就对了

小白也能学会的激光切割创意盒子设计方法

还在羡慕大疆 RoboMaster S1?教你自制麦轮战车

不会垃圾分类?教你制作一个瓦力机器人来帮你!

萌宠 Pando 机器人,不但能卖萌,还能跳太空步

萌宠 Pandy 机器人,谁说智能车不能卖萌!

学生获奖作品:戒烟帽

学生获奖作品:体感转向安全帽

掌控+麦轮,Mixly+Blynk,让你的麦轮战甲嗨炸全场

搞定掌控板+Siri语音控制,只要半小时

我知道你在看

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值