用RP2040游戏机制作简易气象台
用RP2040游戏机制作简易气象台,能够实时播放5个城市的气象信息
标签
嵌入式系统
ESP32
RP2040
2022暑假在家练
xiaojaingjiang
更新2022-09-06
太原理工大学
712

一、任务要求

1、RP2040 Game Kit板通过提供的ESP32- S2的WiFi模块连接网络
2、在RP2040 Game Kit上显示某一 个城市的气象信息-时间、温度、湿度、气压等
3、通过RP2040 Game Kit. 上的按键能够切换显示不同城市的信息

二、总体思路

采用WIFI模块SP32- S2使用NTP协议获取北京时间,使用http协议获取气象信息;串口接收来自RP2040 Game Kit传来的城市名称,从网络上获取到该城市的气象信息后通过串口发送给RP2040 Game Kit;每隔1S获取一次时间,然后通过串口发送时间信息给RP2040 Game Kit。RP2040 Game Kit作为主控制器,一是检测按键,每按一次,发送不同城市的名称给ESP32-S2;二是将接收到的时间和气象信息显示到屏幕上,供人查看。

Foysd8eszFT6mkqiWifUiBm34658

三、实现方案

使用arduino对ESP32-S2进行二次开发,使其能够连接到网络,从而通过ntp和http获取时间和气象信息,通过解析json出相关信息,后通过串口发送给RP2040 Game Kit。

使用Thonny开发RP2040 Game Kit,能够实现按键按键和消抖,对按键来计数,发送不同的城市名称给ESP32,让其通过网络获取信息,串口接收,LCD显示。

四、开发环境配置

1、下载并安装安装arduino

2、在开发板管理器中安装ESP32,这样就可以对ESP32进行二次开发了

3、下载并安装Thonny

4、从官网下载UF2文件,通过以下步骤使RP2040 Game Kit进入到烧录模式

(1)、按住RESET按钮和KEY-B按钮

(2)、释放RESET按钮

(3)、释放KEY-B按钮

(4)、拖动文件更新固件

然后就可以通过micropython进行开发了

五、软件设计

1、ESP32-S2程序部分

首先定义相关变量

// WiFi相关
const char *ssid = "ce-shi";
const char *password = "qwer1234@";
 
//心知天气 相关
String API = "S-rZRzBd2Of2tCQ6s";
String CITY = "luoyang";
 
// 定时器相关
u32_t cnt = 0;
char flag = 0;
char i; 

//串口相关
String comdata = "";//声明字符串变量

然后初始化串口,使其波特率为115200;初始化WIFI,等待其连接成功;初始化定时器,使其1S产生一次中断,用于定时获取时间。

void setup()
{
  Serial.begin(115200);
    
  WiFi.begin(ssid,password);  
  while(WiFi.status()!=WL_CONNECTED)
  {
    delay(500);
  } 
  
  hw_timer_t *tim0 = timerBegin(0, 8000, true); //建立定时器  //80MHZ 进行 8000预分频后 每秒 计数10000次
  timerAlarmWrite(tim0, 10000-1, true);//设置 自动重装载值 10000-1 
  timerAttachInterrupt(tim0, Tim0_Handle, true);//绑定定时器
  timerAlarmEnable(tim0);
 
}

串口接收来自RP2040的城市名称,接收完成后,作为获取气象信息函数中的城市;每隔1S,从网络上获取一次时间,每隔1分钟从网络上获取一次气象信息。

void loop()
{
  while (Serial.available() > 0)  
  {
    comdata += char(Serial.read());
    delay(2);
  }
    
  if (comdata.length() > 0)
  {
    CITY = "";
    for(i=0;i<comdata.length();i++)
    {
      CITY += comdata[i];  
    }  
    delay(50);
    Get_Today_Weather(API, CITY);
    Get_Rencent_Weather(API, CITY);
    cnt = 0;
    comdata = "";
  }
  if(flag)
  {
    Get_Now_Time();
    flag = 0;
  }
  if(cnt >= 60) //一分钟一个循环
  {
    Get_Today_Weather(API, CITY);
    Get_Rencent_Weather(API, CITY);
    cnt = 0;
  }

}

获取网络时间,并打印相关信息

void Get_Now_Time()
{
    configTime(3600 * 7, 3600, "us.pool.ntp.org");
 
    static struct tm timeinfo;
 
    if (!getLocalTime(&timeinfo))
    {
        if (!getLocalTime(&timeinfo))
            return;
    }
 
    Serial.println(&timeinfo, "t;%Y-%m-%d;%w;%H-%M-%S;");//我需要的 时间 年月日 + 星期几 + 时分秒
}

从心知天气获取某个城市当天的气象信息,如天气状况、温度、湿度、压力,并打印

void Get_Today_Weather(String api, String city)
{
    String url_xinzhi = "";
    url_xinzhi = "https://api.seniverse.com/v3/weather/now.json?key=";
    url_xinzhi += api;
    url_xinzhi += "&location=";
    url_xinzhi += city;
    url_xinzhi += "&language=en&unit=c";
 //Serial.println(url_xinzhi);  //打印接受到的消息
    DynamicJsonDocument doc(2048); //分配内存,动态
 
    HTTPClient http;
    char data[100] = {"w;"};
 
    http.begin(url_xinzhi);
 
    int httpGet = http.GET();
 
    if (httpGet > 0)
    {
        if (httpGet == HTTP_CODE_OK)
        {
            String json = http.getString();
            deserializeJson(doc, json);
            strcat(data, doc["results"][0]["now"]["text"].as<const char *>());
            strcat(data, ";");
            strcat(data, doc["results"][0]["now"]["temperature"].as<const char *>());
            strcat(data, ";");
            strcat(data, doc["results"][0]["now"]["humidity"].as<const char *>());
            strcat(data, ";");
            strcat(data, doc["results"][0]["now"]["pressure"].as<const char *>());
            strcat(data, ";");
            Serial.println(data);//我需要的天气数据
        }
    }
    http.end();
}

从新知天气获取最近三天的白天和夜间的天气状况

void Get_Rencent_Weather(String api, String city)
{
    String url_xinzhi = "";
    url_xinzhi = "https://api.seniverse.com/v3/weather/daily.json?key=";
    url_xinzhi += api;
    url_xinzhi += "&location=";
    url_xinzhi += city;
    url_xinzhi += "&language=en&unit=c";
    url_xinzhi += "&start=1&days=3";
 
    DynamicJsonDocument doc(2048); //分配内存,动态
 
    HTTPClient http;
    char data[100] = {"y;"};
 
    http.begin(url_xinzhi);
 
    int httpGet = http.GET();
 
    if (httpGet > 0)
    {
        if (httpGet == HTTP_CODE_OK)
        {
            String json = http.getString();
 
            //Serial.println(json); //打印接收到的消息
 
            deserializeJson(doc, json);

            strcat(data, doc["results"][0]["daily"][0]["text_day"].as<const char *>());
            strcat(data, "-");
            strcat(data, doc["results"][0]["daily"][0]["text_night"].as<const char *>());
            strcat(data, ";");
            strcat(data, doc["results"][0]["daily"][1]["text_day"].as<const char *>());
            strcat(data, "-");
            strcat(data, doc["results"][0]["daily"][1]["text_night"].as<const char *>());
            strcat(data, ";");
            strcat(data, doc["results"][0]["daily"][2]["text_day"].as<const char *>());
            strcat(data, "-");
            strcat(data, doc["results"][0]["daily"][2]["text_night"].as<const char *>());
            strcat(data, ";");
 
            Serial.println(data);
        }
    }
    http.end();
}

2、RP2040 Game Kit程序设计

首先导入相关库,如UART、Pin等

from machine import UART,Pin

然后对LCD进行初始化,这部分用了DEMO中的代码,就不附上了

之后对按键进行初始化,配置Pin为输入模式,上拉

#初始化按键
cnt = 0;
key = Pin(6, Pin.IN, Pin.PULL_UP)

之后对串口进行初始化,配置波特率和引脚

#初始化串口
receiveStr = ''
uart=UART(0, baudrate=115200, tx=Pin(12), rx=Pin(13)) #设置串口

串口初始化完成后,通过串口发送城市名称给ESP32-S2

uart.write('beijing')

然后LCD屏幕显示相关信息

display.text(font2, "beijing", 0, 64)

然后呢,就是不停的进行按键检测和串口接收

检测到按键按下时,进行20ms的消抖,然后计数器加1,根据计数器的值串口发送对应的城市名称,之后进行松手检测。

    #按键检测   
    if key.value() == 0:
        time.sleep_ms(20)
        if key.value() == 0:
            cnt = cnt + 1
            if cnt == 5:
                cnt = 0                
            if cnt == 0:
                uart.write('beijing')
                display.text(font2, "        ", 0, 64)
                display.text(font2, "beijing", 0, 64)
            if cnt == 1:
                uart.write('shanghai')
                display.text(font2, "        ", 0, 64)
                display.text(font2, "shanghai", 0, 64)
            if cnt == 2:
                uart.write('taiyuan')
                display.text(font2, "        ", 0, 64)
                display.text(font2, "taiyuan", 0, 64)
            if cnt == 3:
                uart.write('tianjin')
                display.text(font2, "        ", 0, 64)
                display.text(font2, "tianjin", 0, 64)
            if cnt == 4:
                uart.write('wuhan')
                display.text(font2, "        ", 0, 64)
                display.text(font2, "wuhan", 0, 64)
            print(cnt)
    
        while(key.value()==0):
            time.sleep_ms(1)

串口接收到数据时,等待100ms,读出一行数据来,然后将接收到的数据转为utf-8格式的数据,根据一个数据判断是时间、气象信息,近三天天气状况,然后通过split()函数,通过;分割字符串,最后通过display.text()函数将相关信息显示在LCD的对应位置

    #串口接收字符串
    if uart.any() > 0:
        time.sleep_ms(100)
        receiveStr = uart.readline()
        print(receiveStr.decode('utf-8'))
        
        if receiveStr.decode('utf-8')[0] == 't':
            list1 = receiveStr.decode('utf-8').split(';')
            display.text(font2, list1[1], 32, 0)
            display.text(font2, list1[3], 48, 32)
        if receiveStr.decode('utf-8')[0] == 'w':
            list2 = receiveStr.decode('utf-8').split(';')
            display.text(font2, '         ', 128, 64)
            display.text(font2, list2[1], 128, 64)
            display.text(font2, list2[2], 0, 96)
            display.text(font2, list2[3], 80, 96)
            display.text(font2, '          ', 160, 96)
            display.text(font2, list2[4], 160, 96)
            
        if receiveStr.decode('utf-8')[0] == 'y':
            list3 = receiveStr.decode('utf-8').split(';')
            display.text(font2, '               ', 0, 128)
            display.text(font2, list3[1], 0, 128)
            display.text(font2, '               ', 0, 160)
            display.text(font2, list3[2], 0, 160)
            display.text(font2, '               ', 0, 192)
            display.text(font2, list3[3], 0, 192)
        
        receiveStr = ''

至此,项目的软件部分就介绍完成了

六、项目总结

Fq-bj5BcR5RIah_LsZ60GKoDEExL

本项目经历了项目规划,查阅资料,软件实现,实物测试和项目总结,覆盖了嵌入式开发的整个过程。中途也遇到了一些问题,如我将ESP32-S2 AT固件参照教程通过USB烧录到ESP32后,发送AT指令进行测试,发现没有反应,后来改用arduino对esp32进行了二次开发,经过测试,esp32只能发送不能接收,后来确定是硬件有几个引脚连接在了一起,发生了短路,才出现了这么多问题,既然都对esp32二次开发了,后续就改用了这种方案,自定义数据格式,不在使用AT固件的方法了;还有一个问题是ESP32每次上电后,都需要手动按一次复位,这样才能工作。

历经一个半月的心酸,终于完成了本项目,这么多长时间的努力没有白费,还学到了使用C和micro python对模块进行开发,积累了项目经验。未来我将LCD上信息英文显示改成中文显示,ESP32做低功耗优化,系统采用电池供电。

七、参考资料

1、安信可ESP32-S2   AT指令使用:https://docs.ai-thinker.com/esp32s2

2、AT固件下载使用说明:https://docs.ai-thinker.com/_media/esp32/docs/nodemcu-32-s2_%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.pdf

3、ESP32接入WIFI:https://blog.csdn.net/qq_41650023/article/details/124663892

4、新知天气使用:https://seniverse.yuque.com/books/share/ded1e167-e35c-4669-8306-cf65c6e01dc0/wvfkgq

5、pi pico开发指南:https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html#raspberry-pi-pico

6、pi pico开发教程:https://datasheets.raspberrypi.com/pico/raspberry-pi-pico-python-sdk.pdf

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