基于ESP32-S2模块的物联网/音频信号处理平台制作一个本地气象台/温度计
本此次寒假在家练使用的是乐鑫的ESP32-S2-MINI-1模块。该项目从心知天气上获取当地日期时间和天气状况,通过json解析最后显示到屏幕上。
标签
嵌入式系统
显示
网络与通信
2022寒假在家练
AnswerFRX
更新2022-03-01
北京工业大学
799

1.模块简介

   本平台使用了乐鑫公司的ESP32-S2-Mini-1模块。它采用PCB板载天线,模组配置了4MB SPI flash,采用的是 ESP32-S2FN4 芯片。该芯片搭载了Xtensa® 32 位LX7 单核处理器,工作频率高达 240 MHz,同时,它还集成了丰富的外设接口。可用于多种场景。

模块构成:

FmgyblJD-JJ7ffyIdYqX2bPGQ4IS

该模块可以广泛应用于下面的一些领域:

  • 通用低功耗 IoT 传感器 Hub
  • 通用低功耗 IoT 数据记录器
  • USB 设备
  • 语音识别
  • 图像识别
  • Mesh 网络
  • 家庭自动化
  • 智慧楼宇
  • 工业自动化
  • 健康/医疗/看护
  • Wi-Fi 玩具
  • 可穿戴电子产品
  • 零售 & 餐饮
  • 智能 POS 应用

该模块的性能:

  • Core: Xtensa® single-core 32-bit LX7 CPU, frequency up to 240MHz
  • Memories:
    • 128 KB of ROM
    • 320 KB of SRAM
    • 16 KB of RTCSRAM
    • 4 MB of Flash memory
  • Working Voltage: 3 V to 3.6 V
  • Up to 37 GPIOs
  • 2*12-bit ADC (up to 20 channels)
  • Communication interfaces
    • 2 I2C interfaces
    • 1 I2S interface
    • 4 SPI interfaces
    • 2 UART interfaces
    • 1 USB OTG interface
  • Security:
    • 4096 bit OTP
    • AES, SHA, RSA, ECC, RNG
    • Secure Boot, Flash Encryption, Digital signature, HMAC module
  • Extended temperature range: -40 to 85 °C

关于该模块更详细的信息,参见ESP32-S2-Mini-1的选型页面

2.项目要求实现

   (1).项目要求  

         利用OLED显示显示当前本地的时间、温度和气象信息。

   (2).流程图
       Fp_jv1YM2B7uPZTwzyk9Qe5sw-fx
   (3).项目环境
 
         本项目使用的开发环境是Arduino IDE,使用Adafruit_SSD1306库驱动屏幕。
 
   (4)具体实现方法
        
         (1. WIFI连接
               使用库函数就可以简单的实现WIFI连接
#include <WiFi.h>    
//============WiFi名称和密码================//
const char* ssid     = "";
const char* password = "";

    display.printf("connecting to %s\n",ssid);//在OLED上打印信息
    display.display();
  
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
  
    while (WiFi.status() != WL_CONNECTED) //等待连接
    {
        delay(500);
        display.print(".");
        display.display();
    }
    display.println();
    display.println("WiFi connected");
    display.print("IP: ");
    display.println(WiFi.localIP());
    display.print("Please wait...");
    display.display();​
               将手机热点或WIFI的名称和密码分别输入ssid和password就可以连接WIFI。
             
           (2. OLED驱动 
                我是用的是Adafruit_SSD1306库函数驱动OLED,该库简单明了,且可以通过字模提取显示中文和图像。通过原理图可以知道OLED各引脚的连接。
#include <Adafruit_SSD1306.h>
//===============OLED引脚===================//
#define SCREEN_WIDTH 128  // OLED显示宽度,以像素为单位
#define SCREEN_HEIGHT 64  // OLED显示高度,以像素为单位
#define OLED_MOSI  35   
#define OLED_CLK   36     
#define OLED_DC    33     
#define OLED_CS    37     
#define OLED_RESET 34 
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
//检测OLED
if(!display.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));//SSD1306分配失败
    for(;;); // 不要继续,永远循环
}

​
 
           (3. 图像制作
               用PCtoLCD2002软件制作可以用OLED显示的图像和文字。
           (4. 天气api接口
            https://api.seniverse.com/v3/weather/now.json?key=密钥&location=地区&language=zh-Hans&unit=c
            通过这个链接能从心知天气官网获取地区的天气,心知天气在全国范围内的天气状况获  取是免费的,注册就可以获得密钥,将密钥和地区的拼音替换可以得到地区的天气状况。例如将链接粘贴到浏览器:FiBYygLISRWO2g2asnlhXTDgEYrL
         代码:           
void GET_Weather()
{
    if((millis() - getTime > 5000))//10s
    {
        getTime = millis();
        //Serial.print("connecting to ");
        if (!client.connect(host,port)) 
        {
            Serial.println("服务器连接失败");
            return;
        }    
        //Serial.print("Requesting URL: ");
        // 发送请求报文
        client.print(String("GET ") + url + " HTTP/1.1\r\n" +  //请求行  请求方法 + 请求地址 + 协议版本
                     "Host: " + host + "\r\n" +                //请求头部
                     "Connection: close\r\n" +                //处理完成后断开连接
                     "\r\n" +                                 //空行
                     urlDat);                                 //请求数据            
        delay(100);  
        while(client.available()) //接收数据
        {          
            String line = client.readStringUntil('\r');
            inputString += line;
        }
       // Serial.println(inputString);
        client.stop();      //断开与服务器连接以节约资源
        DatFlag = true;
       // Serial.println(inputString);
    }
}​
       
            (5. json解析                  

                  用ArduinoJson库函数可以将上一步获得的信息提取出来,这一部分比较复杂。但是ArduinoJson库有一个官方支持的网站可以直接得到代码:

            https://arduinojson.org/v5/assistant/ 

            但可能根据实际需要更改。

void processMessage()
{
    const size_t capacity = JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(3) + JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(6) + 3*JSON_OBJECT_SIZE(13) + 760;
    DynamicJsonDocument doc(capacity);
    deserializeJson(doc, inputString);
    JsonObject results_0 = doc["results"][0];
    JsonObject results_0_location = results_0["location"];
    const char* results_0_location_name = results_0_location["name"]; // "城市"
    JsonArray results_0_daily = results_0["daily"];
    JsonObject results_0_daily_0 = results_0_daily[0];
    const char* results_0_daily_0_date = results_0_daily_0["date"]; //今天日期
    const char* results_0_daily_0_text_day = results_0_daily_0["text_day"]; //白天天气
    const char* results_0_daily_0_code_day = results_0_daily_0["code_day"]; //图标编号
    const char* results_0_daily_0_text_night = results_0_daily_0["text_night"]; //晚上天气
    const char* results_0_daily_0_code_night = results_0_daily_0["code_night"]; //图标编号
    const char* results_0_daily_0_high = results_0_daily_0["high"]; //最高气温
    const char* results_0_daily_0_low = results_0_daily_0["low"]; //最低气温
    const char* results_0_daily_0_wind_direction = results_0_daily_0["wind_direction"]; //风向
    const char* results_0_daily_0_wind_direction_degree = results_0_daily_0["wind_direction_degree"]; //度数
    const char* results_0_daily_0_wind_speed = results_0_daily_0["wind_speed"]; //速度
    const char* results_0_daily_0_wind_scale = results_0_daily_0["wind_scale"]; // 风标
    const char* results_0_daily_0_humidity = results_0_daily_0["humidity"]; //湿度
    
    JsonObject results_0_daily_1 = results_0_daily[1];
    const char* results_0_daily_1_date = results_0_daily_1["date"];
    const char* results_0_daily_1_text_day = results_0_daily_1["text_day"];
    const char* results_0_daily_1_code_day = results_0_daily_1["code_day"];
    const char* results_0_daily_1_text_night = results_0_daily_1["text_night"];
    const char* results_0_daily_1_code_night = results_0_daily_1["code_night"];
    const char* results_0_daily_1_high = results_0_daily_1["high"]; 
    const char* results_0_daily_1_low = results_0_daily_1["low"];
    const char* results_0_daily_1_precip = results_0_daily_1["precip"];
    const char* results_0_daily_1_wind_direction = results_0_daily_1["wind_direction"]; 
    const char* results_0_daily_1_wind_direction_degree = results_0_daily_1["wind_direction_degree"]; 
    const char* results_0_daily_1_wind_speed = results_0_daily_1["wind_speed"]; 
    const char* results_0_daily_1_wind_scale = results_0_daily_1["wind_scale"]; 
    const char* results_0_daily_1_humidity = results_0_daily_1["humidity"]; 
    
    JsonObject results_0_daily_2 = results_0_daily[2];
    const char* results_0_daily_2_date = results_0_daily_2["date"];
    const char* results_0_daily_2_text_day = results_0_daily_2["text_day"]; // "小雨"
    const char* results_0_daily_2_code_day = results_0_daily_2["code_day"]; // "13"
    const char* results_0_daily_2_text_night = results_0_daily_2["text_night"]; // "小雨"
    const char* results_0_daily_2_code_night = results_0_daily_2["code_night"]; // "13"
    const char* results_0_daily_2_high = results_0_daily_2["high"]; // "12"
    const char* results_0_daily_2_low = results_0_daily_2["low"]; // "11"
    const char* results_0_daily_2_precip = results_0_daily_2["precip"]; // ""
    const char* results_0_daily_2_wind_direction = results_0_daily_2["wind_direction"]; // "东北"
    const char* results_0_daily_2_wind_direction_degree = results_0_daily_2["wind_direction_degree"]; // "59"
    const char* results_0_daily_2_wind_speed = results_0_daily_2["wind_speed"]; // "34.20"
    const char* results_0_daily_2_wind_scale = results_0_daily_2["wind_scale"]; // "5"
    const char* results_0_daily_2_humidity = results_0_daily_2["humidity"]; // "86"
    
    const char* results_0_last_update = results_0["last_update"];
    
    String riqi = results_0_last_update;  //将日期取出处理
    int nian = riqi.lastIndexOf("T");
    hTime.Year = (riqi.substring(nian-10, nian-6)).toInt();
    hTime.Month = (riqi.substring(nian-5, nian-3)).toInt();
    hTime.Day = (riqi.substring(nian-2, nian)).toInt();
    
    OnTime++;   //显示标志位 0,1,2显示时间 3显示今天天气 4显示明天天气 5显示后天天气
    OnTime%=5;
    dTime.Hour = hTime.Hour+8;   
    dTime.Minute = hTime.Minute;
    dTime.Second = hTime.Second;
    dTime.Year = hTime.Year;
    dTime.Month = hTime.Month;
    dTime.Day = hTime.Day;

    day1.tianqitubiao = atoi(results_0_daily_0_code_day);//获取今天天气信息
    day1.zuigaowendu = atoi(results_0_daily_0_high);
    day1.zuidiwendu = atoi(results_0_daily_0_low);
    day1.shidu = atoi(results_0_daily_0_humidity);
    
    day2.tianqitubiao = atoi(results_0_daily_1_code_day);
    day2.zuigaowendu = atoi(results_0_daily_1_high);
    day2.zuidiwendu = atoi(results_0_daily_1_low);
    day2.shidu = atoi(results_0_daily_1_humidity);    
      
    day3.tianqitubiao = atoi(results_0_daily_2_code_day);
    day3.zuigaowendu = atoi(results_0_daily_2_high);
    day3.zuidiwendu = atoi(results_0_daily_2_low);
    day3.shidu = atoi(results_0_daily_2_humidity);
}

            代码前半段是从那个网站上解析到的代码,后半段是取出想要的数据,取出的方法比较暴力,就是一个一个提取,存到设置的结构体中。

            (6. 显示时间日期

void DisplayTime()
{
    if(OnTime ==0 or OnTime == 1)
    {
        display.clearDisplay();//清除缓冲区
        display.setTextSize(2);//设置字体大小
      
       //display.setCursor(0,0);//设置光标
        display.drawBitmap(0,0,liu,16,16,1);
        display.drawBitmap(16,0,zhou,16,16,1);
        //display.drawBitmap(0,0,liuzhou,16,16,1);//qin
        //display.print("LiuZhou");//显示地区  
        display.setCursor(0,30);//设置光标
        display.printf("%d:%d",dTime.Hour,dTime.Minute);//时间
        display.setCursor(0,50);//设置光标
        display.printf("%d.%d.%d",dTime.Year,dTime.Month,dTime.Day);//日期
        display.display();
    }
}

             (7.显示天气

void display_tq(int dday,int tq,int dw,int gw,int sd)
{
    tianqi_dat(dw,gw,sd);
    display.clearDisplay();
    switch(dday)
    {
        case 0:display.drawBitmap(64,0,jintian[0],16,16,1);//今
        break;  
        case 1:display.drawBitmap(64,0,jintian[1],16,16,1);//
        break;
        case 2:display.drawBitmap(64,0,jintian[2],16,16,1);//
        break;
    }  
    display.drawBitmap(80,0,jintian[3],16,16,1);//天
    switch(tq)
    {
        case 0:display.drawBitmap(64,16,tianqi[0],16,16,1);//qin
               display.drawBitmap(0,0,qintian,60,60,1);//图标
        break;  
        case 4:display.drawBitmap(64,16,tianqi[2],16,16,1);//dy
               display.drawBitmap(80,16,tianqi[3],16,16,1);//
               display.drawBitmap(0,0,duoyun,60,60,1);//图标
        break;  
        case 9:display.drawBitmap(64,16,tianqi[1],16,16,1);//yin
               display.drawBitmap(0,0,yintian,60,60,1);//图标
        break;
        case 13:display.drawBitmap(64,16,tianqi[4],16,16,1);//x
               display.drawBitmap(80,16,tianqi[7],16,16,1);//
               display.drawBitmap(0,0,xiaoyu,60,60,1);//图标
        break;  
        case 14:display.drawBitmap(64,16,tianqi[5],16,16,1);//z
               display.drawBitmap(80,16,tianqi[7],16,16,1);//
               display.drawBitmap(0,0,zhongyu,60,60,1);//图标
        break;  
        case 15:display.drawBitmap(64,16,tianqi[6],16,16,1);//d
               display.drawBitmap(80,16,tianqi[7],16,16,1);//
               display.drawBitmap(0,0,dayu,60,60,1);//图标
        break;  
    }
    display.drawBitmap(64,32,num[Nume.dws],8,16,1);//最低温
    display.drawBitmap(72,32,num[Nume.dwg],8,16,1);//最低温
    
    display.drawBitmap(80,32,num[10],8,16,1);//-
    
    display.drawBitmap(88,32,num[Nume.gws],8,16,1);//最g温
    display.drawBitmap(96,32,num[Nume.gwg],8,16,1);//最g温
    
    display.drawBitmap(108,32,sheshidu[0],16,16,1);//shehisdu
    display.drawBitmap(64,48,shidu[0],16,16,1);//湿
    display.drawBitmap(80,48,shidu[1],16,16,1);//度  
    
    display.drawBitmap(96,48,num[Nume.sds],8,16,1);//湿度
    display.drawBitmap(104,48,num[Nume.sdg],8,16,1);//
    
    display.drawBitmap(112,48,sheshidu[1],16,16,1);//baifenhao
    display.display();
}

             (8. 结果展示

              显示日期时间

FjftfqVgFsq3DQhOKq5mRB_cH-tt

            显示天气

FgIylub88JgSpYOKKoWWtORzvs2BFvnrOABQzKAfOdtVql3zZXNpuzBU

FqadWgls_yahPtSjG-2I5kmC-vMh

              (9. 已知问题

               WIFI连接有时候长时间连不上,只需要按下单片机的复位键即可。

               天气图像可能有显示BUG。

3.注意事项

   使用该代码时需要更改信息,例如WiFi,心知天气的密钥,地区的字模需要另外制作和替换。

附件下载
Weather.ino
源代码
PCtoLCD2002.zip
字模提取软件
团队介绍
北京工业大学信息学部
团队成员
AnswerFRX
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号