基于ESP32-S2模块的物联网/音频信号处理平台制作的本地气象站
利用该板子上所带的ESP32S2模块制作一个连接网络获取时间和天气信息的本地气象台。
标签
ESP32
WiFi
esp32s2
2022寒假在家练
0.96OLED
显示时间
获取天气
cardry
更新2022-03-01
中国矿业大学(北京)
1008

硬件介绍:

该平台使用了乐鑫公司的ESP32-S2-Mini-1模块,ESP32-S2-MINI-1是一颗通用型Wi-Fi MCU模组,功能强大,具有丰富的外设接口;还带有0.96寸OLED显示屏,与开发板采用SPI总线连接,可以用来显示信息参数等;以及4个独立按键。

 

项目要求:

制作一个本地气象站/温度计,利用OLED显示当前本地的时间、温度和气象信息。

 

设计思路:

首先初始化WiFi并设置WiFi模式为STA模式;成功连接WiFi后进入时间显示界面,按下按键2后进入天气显示界面,再次按下按键1后即可返回时间显示界面。时间显示功能利用“pool.ntp.org”网站进行网络校准时钟;天气显示功能利用“console.amap.com”高德开放平台上的天气查询api接口获取实时天气信息。下图为本系统程序流程图:

FgYB7I9bujKK8L3047AWsxf-UONG

 

时间校准功能介绍:

NTP(Network Time Protocol)是指网络时间协议,用来同步网络中各个计算机的时间的协议,可以给计算机和其它网络设备授时。

NTP提供准确时间,首先要有准确的时间来源,这一时间应该是国际标准时间UTC,UTC时间是使用多种不同的方法得到的,包括无线电和卫星系统。我国的北京时间为世界东八时区,与国际标准时间相比增加8小时,配置网络时钟代码如下所示:

const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 8*60*60;       //北京时间为东八时区,与世界时间相比增加8小时
const int daylightOffset_sec = 0;         //不使用夏令时

配置好网络时钟后,定义一个tm结构体的timeinfo即可,tm结构体中的变量具体如下所示:

struct tm 
{  
  int tm_sec;            //秒,取值区间为[0,59]
  int tm_min;            //分,取值区间为[0,59]
  int tm_hour; 	         //时,取值区间为[0,23]
  int tm_mday;           //一个月中的日期,取值区间为[1,31]
  int tm_mon;            //月份,取值区间为[0,11]
  int tm_year; 	         //年份,其值从1900开始
  int tm_wday; 	         //星期,取值区间为[0,6]
  int tm_yday; 	         //从每年的1月1日开始的天数,取值区间为[0,365]
  int tm_isdst;          //夏令时标识符  
  long int tm_gmtoff;    //指定了日期变更线东面时区中UTC东部时区正秒数或UTC西部时区的负秒数 
  const char *tm_zone;   //当前时区的名字(与环境变量TZ有关) 
}; 

 

 

获取天气介绍:

网上有很多免费的天气查询api接口,这里我选择的是高德开放平台的天气查询api,如下图所示:

FiFwQpxoYyrO5jt4EELz4De9sNH3

申请一个key后就可以进行天气信息查询,高德开放平台给出了有服务示例,我们将所要查询的地区的区域代码以及key替换后,再把这行网址输入网页地址栏中按下回车,就可以得到所在地区的天气信息,如下图所示:

Fi8fTjEjBNvwpGEX_dtVqm8abZVT

上图中“restapi.amap.com”即为要填入代码中的host信息,“/v3/weather/weatherInfo?city=410103&key=e765135a5fd92ba0d7b0d7247f0efa57”为代码中的url信息。

得到如上图所示的两行信息后还不够,我们需要将其转化为arduino的json格式的代码。百度搜索“arduinojson”打开ArduinoJson官网,点击首页上方“Assistant”,将“Processor”选为“ESP32”,再将网页中的两行信息填入“Input”中即可得到所需的json代码,最后将结果复制粘贴至我们自己的代码中即可。

下图为天气查询中所能返回的天气现象表:

FoH6sJKr9L2PyFZQYI7VG-Z_8blh

下图为天气查询中所能返回的风向表:

FptaAoRGpT3SXXTb-2zCyR7Qhwps

下图为天气查询中所能返回的风力级别表:

FqCOJ_Q660HccP3eHuOSYjflHBR-

 

代码介绍:

本代码所需头文件如下所示:

#include <ETH.h>
#include <WiFi.h>
#include <WiFiAP.h>
#include <WiFiClient.h>
#include <WiFiGeneric.h>
#include <WiFiMulti.h>
#include <WiFiScan.h>
#include <WiFiServer.h>
#include <WiFiSTA.h>
#include <WiFiType.h>
#include <WiFiUdp.h>
#include <SPI.h>
#include <U8g2lib.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>

其中u8g2库和arduinojson库均可以直接在库管理器中找到,如下图所示:

Fpb9ETJhfcl1ebu-IevHvvdTLJu-

FnDXBbf5P3mXeTNpaZCSvint0Hc_

起始画面显示代码:

void Display_begin() {                     //显示起始画面
  u8g2.enableUTF8Print();
  u8g2.firstPage();
  u8g2.clearBuffer();                      //清除内部缓冲区
  u8g2.setFontDirection(0);                //设置字体方向
  u8g2.setFont(u8g2_font_wqy16_t_gb2312a); //设置字体
  u8g2.setCursor(0, 15);
  u8g2.print("正在连接至WiFi:");
  u8g2.setCursor(0, 35);
  u8g2.print(ssid);
  u8g2.sendBuffer();
}

setup()部分代码:

void setup() {
  Serial.begin(115200);                   //初始化串口
  u8g2.begin();                           //显示器初始化
  Display_begin();                        //显示起始画面
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while(WiFi.status() != WL_CONNECTED)    //未连接上
  {
    delay(400);
    Serial.print(".");
  }
  u8g2.setCursor(0, 55);
  u8g2.print("连接成功");
  u8g2.sendBuffer();  
  IP_address = WiFi.localIP().toString();
  Serial.println(IP_address);
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  num = 1;
}

按键判断部分代码:

  if(digitalRead(KEY1) == LOW)
  {
    delay(100);
    if(digitalRead(KEY1) == LOW)
    {
      num = 1;
      Serial.println("1");
    }
  }
  if(digitalRead(KEY2) == LOW)
  {
    delay(100);
    if(digitalRead(KEY2) == LOW)
    {
      num = 2;
      Serial.println("2");
    }
  }

在loop()循环中每次循环首先判断是否有按键被按下,然后执行switch语句,变量为num,num=1为时间显示界面,num=2为天气显示界面。开机后默认num=1;当按键1被按下后,num=1;当按键2被按下后,num=2。

时间显示部分代码如下所示:

      struct tm timeinfo;
      char str[64] = {0};
      if(!getLocalTime(&timeinfo))
        return;
      if(oldsec == timeinfo.tm_sec)
        return;
      oldsec = timeinfo.tm_sec;
      u8g2.setFont(u8g2_font_ncenB18_tr);
      u8g2.setCursor(25, 24);
      u8g2.print(&timeinfo, "%H:%M");
      u8g2.setFont(u8g2_font_ncenB12_tr);
      u8g2.setCursor(95, 24);
      u8g2.print(&timeinfo, "%S");      
      u8g2.setFont(u8g2_font_wqy12_t_gb2312);
      u8g2.setCursor(5, 42);
      u8g2.print(&timeinfo, "%Y年%m月%d日");
      u8g2.setCursor(95, 42);
      switch(timeinfo.tm_wday)
      {
        case 1 : u8g2.print("周一"); break;
        case 2 : u8g2.print("周二"); break;
        case 3 : u8g2.print("周三"); break;
        case 4 : u8g2.print("周四"); break;
        case 5 : u8g2.print("周五"); break;
        case 6 : u8g2.print("周六"); break;
        case 0 : u8g2.print("周日"); break;
        default : break;
      }

天气显示部分代码如下所示:

      WiFiClient client;
      const int httpPort = 80;
      if(!client.connect(host, httpPort))
      {
        return;
      }
      client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
      delay(50);
      String answer;
      while(client.available())
      {
        String line = client.readStringUntil('\r');
        answer += line;
      }
      client.stop();
      String jsonAnswer;
      int jsonIndex = 0;
      int i = 0;
      for(i = 0; i < answer.length(); i++)
      {
        if (answer[i] == '{')
        {
          jsonIndex = i;
          break;
        }
      }
      jsonAnswer = answer.substring(jsonIndex);
      StaticJsonDocument<512> doc;
      DeserializationError error = deserializeJson(doc, jsonAnswer);
      if(error)
      {
        return;
      }
      const char* status = doc["status"];             //返回状态
      const char* count = doc["count"];               //返回结果总数目
      const char* info = doc["info"];                 //返回的状态信息
      const char* infocode = doc["infocode"];         //返回状态说明,10000代表正确
      JsonObject lives_0 = doc["lives"][0];
      const char* lives_0_weather = lives_0["weather"];         //天气现象(汉字描述)
      const char* lives_0_temperature = lives_0["temperature"]; //实时气温,单位:摄氏度
      const char* lives_0_winddirection = lives_0["winddirection"]; //风向描述
      const char* lives_0_windpower = lives_0["windpower"];     //风力级别,单位:级
      const char* lives_0_humidity = lives_0["humidity"];       //空气湿度
      u8g2.setFont(u8g2_font_wqy12_t_gb2312);
      u8g2.setCursor(10, 11); u8g2.print("天气现象:");
      u8g2.setCursor(80, 11); u8g2.print(lives_0_weather);
      u8g2.setCursor(10, 24); u8g2.print("实时气温:");
      u8g2.setCursor(80, 24); u8g2.print(lives_0_temperature);
      u8g2.setCursor(95, 24); u8g2.print("℃");
      u8g2.setCursor(10, 37); u8g2.print("风向描述:");
      u8g2.setCursor(80, 37); u8g2.print(lives_0_winddirection);
      u8g2.setCursor(10, 50); u8g2.print("风力级别:");
      u8g2.setCursor(80, 50); u8g2.print(lives_0_windpower);
      u8g2.setCursor(10, 63); u8g2.print("空气湿度:");
      u8g2.setCursor(80, 63); u8g2.print(lives_0_humidity);
      u8g2.setCursor(95, 63); u8g2.print("%");

 

功能展示:

上电后进入起始画面显示界面,显示WiFi名称,等待WiFi连接,如下图所示:

FqsGNsjO1evfTyKpKLrQMYWX06cT

成功连接WiFi后屏幕会跳出“连接成功”字样,如下图所示:

FsaC4AdKqGp7py3ejfUPF2NsFFNr

成功连接WiFi后默认进入的是时间显示界面,显示网络时钟信息,如下图所示:

FtU-OgkqxBOcBKYjcDSOBdBJe0Ty

按下按键2后,即可进入天气信息显示界面,如下图所示:

Fj3CsQVzBH2dNzRl_ik1N3I8H-OK

再次按下按键1后即可返回时间显示界面,两界面可随意切换。

 

存在不足:

1、还可以为该系统加入气象图标功能,使得气象站更生动形象;

2、不知为何,我这块板子的按键3和按键4有些问题,采用跟按键1和2一样的按键检测代码后,会检测到按键3和4处于一直被按下的状态,故本次我并未采用按键3和4;

3、或许下次我会采用汉字取模来显示汉字中文的方法,这种方法会很好地节约内存。

附件下载
Local_weather_station.zip
团队介绍
机电与信息工程学院电气专业
团队成员
cardry
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号