基于ESP32-S2-Mini-1模块的本地气象台
项目介绍:
本项目是使用硬禾学堂的音频信号板卡,通过WIFI-S2模块联网后调用心知天气API获取本地当天的天气、气温信息以及实时时间,数据经过解析后将其显示在板卡自带的OLED上。实现一个本地气象台。
项目要求:
利用OLED显示当前本地的时间、温度和气象信息
使用软件:
arduino
设计思路(框图):
本项目流程较简单,先使用ESP32 S2模块连接WiFi网络。之后通过访问阿里云时间服务器获去本地实时时间。关于获取天气信息,我们可以先注册一个心知天气的账户,申请免费的产品,获取免费的API并调用,得到JSON格式的数据,再使用ArduinoJson库函数进行解析,将获取的天气温度信息显示在OLED上,并以合适速率刷新。
简单的硬件介绍:
1、核心控制器介绍:
本平台使用了乐鑫公司的ESP32-s2-Mini-1模块,ESP32-s2-Mini-1是一颗通用型Wi-Fi MCU模组,功能强大,具有丰富的外设接口,可用于穿戴电子设备,智能家居等场景,ESP-S2-Mini-1采用PCB板载天线,模块配置了4MB SPI Flash,采用的是ESP32-S2FN4芯片。该芯片搭载了Xtensa R 32位LX7单核处理器,工作频率高达240MHz。用户可以关闭CPU的电源,利用低功耗处理器监测外设的状态变化或某些模拟量是否超出阈值。ESP32-S2-FH4还集成了丰富的外设接口。
ESP32-s2-MINI-1的构成
音频信号处理板卡构成:
基于ESP32-S2 WiFi核心模块;128*64 SPI接口OLED显示;4个按键用于参数控制或菜单选择;1路Mic音频输入-模拟电路;2路音频输出,并有功率放大,可以驱动喇叭和耳机插座;一个FM接收模块;一个用于切换来自ESP32产生的音频还是FM输出的音频到喇叭或者耳机。
板卡功能框图
板卡实物图
实现的功能及图片展示:
在本次项目中,我仅仅实现了简单的天气和时间显示功能,由于天气短时间内不会突变,我通过使用定时器中断,每小时刷新一次。关于时间信息,由于要实时显示,我将时间刷新设置为200ms,由于ESP32是第一次接触,有些问题也困扰了一些时间,但同时ESP32丰富的功能也令我眼前一亮,未来我会多使用相关产品,做出更多有意思的东西。
主要的代码片段及说明:
各变量的定义
const char * temperature ;
const char * currenttime ;
const char * weather;
const char * area;
unsigned long currentMillis;
DynamicJsonDocument doc(1400); //分配空间,用于保存JSON数据
WiFiServer server(80); //端口号
struct tm timeinfo;
hw_timer_t * timer = NULL;
volatile uint32_t isrCounter = 3600;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 36, /* data=*/ 35, /* cs=*/ 10, /* dc=*/ 33, /* reset=*/ 34); //为OLED设置引脚
中断函数
void ARDUINO_ISR_ATTR onTimer(){ //中断函数, 1s中断用于刷新天气信息
portENTER_CRITICAL_ISR(&timerMux);
isrCounter++; //记录中断次数 写入与中断共享的变量时禁用中断。这样我们就可以确保
//主代码和ISR之间不存在任何对它的并发访问。
portEXIT_CRITICAL_ISR(&timerMux);
}
以下代码是串口、OLED、定时器等初始化。
Serial.begin(115200);
u8g2.begin();
Serial.print("Connecting to ");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("Connected ");
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
if(!getLocalTime(&timeinfo)){
Serial.println("failde to obtain time");
return;
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
server.begin();
//定时器配置
timer = timerBegin(0, 80, true); //定时器0,80预分频
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 1000000, true); //1秒
timerAlarmEnable(timer); //定时器使能
}
以下代码是在循环中反复检查连接情况,如果WiFi意外断开会重新尝试连接,以防连接过程外界WiFi突然断开又恢复后WiFi模块无法自动重连。
if(WiFi.status() != WL_CONNECTED){
currentMillis = millis();
if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval)) {
Serial.print(millis());
Serial.println("Reconnecting to WiFi...");
WiFi.begin(ssid, password);
previousMillis = currentMillis;
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
}
}
以下代码是1s定时器计数累加值达到3600,既1小时,连接知心天气API重新获取天气信息,解析JSON数据,对相应变量进行刷新。
if (isrCounter > 3600){
portENTER_CRITICAL_ISR(&timerMux);
isrCounter = 0; //重新计时
portEXIT_CRITICAL_ISR(&timerMux);
HTTPClient https;
https.begin(Targethttp); //连接心知天气API
int httpCode = https.GET(); //获取连接状况
Serial.print(httpCode); //串口打印
if(httpCode == HTTP_CODE_OK){ //如果连接成功
String payload = https.getString(); //将获取的响应数据转换为字符串
deserializeJson(doc, payload); //反序列化JSON数据
JsonObject results_0 = doc["results"][0];
JsonObject results_0_now = results_0["now"];
JsonObject results_0_location = results_0["location"];
weather = results_0_now["text"]; //天气
temperature = results_0_now["temperature"]; //温度
area = results_0_location["name"]; //地理位置
}
}
getLocalTime(&timeinfo); //获取时间 每隔200ms
以下代码是负责OLED的显示。
u8g2.clearBuffer(); // 清除内部缓冲区
u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
u8g2.setCursor(0,10);
u8g2.print("weather:");
u8g2.setCursor(74,10);
u8g2.print(weather);
u8g2.drawLine(0, 22, 127, 22); //横线
u8g2.setCursor(0,23);
u8g2.print("area:");
u8g2.setCursor(74,23);
u8g2.print(area);
u8g2.drawLine(0, 25, 127, 25); //横线
u8g2.setCursor(0,36);
u8g2.print("temperature:");
u8g2.setCursor(74,36);
u8g2.print(temperature);
u8g2.drawLine(0, 38, 127, 38); //横线
u8g2.drawLine(72, 0, 72, 53); //横线
u8g2.drawLine(0, 53, 127, 53); //纵线
u8g2.setCursor(0,60);
u8g2.print(&timeinfo, "%F %T");
u8g2.sendBuffer();
delay(200);
}
遇到的主要难题及解决方法:
使用开发板在连接手机WiFi时发生重启,代码检查了好多次,最后发现是由于粗心使用OLED的显示函数时出现了问题。arduino查错是真不方便,最好还是使用原生的IDF。关于ESP32的联网方式我觉得可以做到更好,似乎可以通过AP模式让用户通过网页进行网络配置。这样就可以在不改变代码的情况下配网了,后面我会继续进行尝试。
未来的计划及建议等:
自己对相应的库及原理还是不够清楚,虽然这个项目的实现很简单,但其中涉及到的知识可不少如HTTP请求的过程、TCP和UDP协议等等,未来要多学习ESP32的各种功能,将这个模块的潜力发掘出来。我也正式成为esp32粉丝大军的一员了。感谢硬禾学堂给我这次学习的机会,希望硬禾学堂相关活动越办越好。