一、任务要求
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;二是将接收到的时间和气象信息显示到屏幕上,供人查看。
三、实现方案
使用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 = ''
至此,项目的软件部分就介绍完成了
六、项目总结
本项目经历了项目规划,查阅资料,软件实现,实物测试和项目总结,覆盖了嵌入式开发的整个过程。中途也遇到了一些问题,如我将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