本篇记录一个案例,功能为:连接Wi-Fi,网络授时,开启定时器(1s),每分钟(整分钟)串口输出一次时间,每5分钟输出一次实时天气信息。
1、请求天气
请求天气是通过“心知天气”站点,需要使用者自行注册并获取api_key。相关注册和使用过程,这里就不啰嗦了,不清楚的朋友可以自己到官网上查看(https://www.seniverse.com/)。
本例仅测试实时天气数据获取,天气相关数据只有“状态(晴朗之类)”和“气温”,请求接口地址如下:
https://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c
请求天气使用ESP32-Arduino的HTTPClient实例,获取的天气数据为JSON格式,因此案例加载了ArduinoJson库,装得比较早版本是6.18.5,看更新提示最新版是6.19.4了。
图7-1 库安装
图7-2 心知天气返回的JSON格式
2、天气案例代码
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
//-----SSID & password
const char *ssid = "yourssid"; //WIFI名称
const char *password = "yourpsw"; //密码
//-----NTP server & timezone offset
const char *ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 14400; //3600*4 for UTC+8
const int daylightOffset_sec = 14400;
struct tm t; //time struct
void printLocalTime(void);
//-----timer HW per second
#define INMIN 0x60 //in 60s flag
#define ONEMIN 0x61 //one minute flag
hw_timer_t *timer = NULL; //timer variable
volatile SemaphoreHandle_t timersem; //timer fire sem
void IRAM_ATTR onTimer(void); //callback func
void initTimer(void);
static uint8_t timestate = INMIN; //flag variable
//-----weather server URL & instance for json parsing
DynamicJsonDocument doc(1024); //json doc instance
const char *url = "https://api.thinkpage.cn/v3/weather/now.json?key=your_api_key&location=tianjin&language=en&unit=c";
void getWeather(void); //request now weather func
void setup() {
Serial.begin(115200);
//-----connect AP
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
//-----print IP address
Serial.println(" CONNECTED");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
//-----initialize and get the time then initialize timer(per sec)
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
if(!getLocalTime(&t)){
Serial.println("Failed to obtain time");
return;
} else {
Serial.println(&t, "%A, %B %d %Y %H:%M:%S");
initTimer(); //timer init
}
}
void loop() {
//-----update time state per second
if(xSemaphoreTake(timersem, 0) == pdTRUE) {
if((t.tm_sec)++ >= 59) timestate = ONEMIN;
}
//-----print localtime per minute & weather info per 5 minutes
if(timestate == ONEMIN) {
timestate = INMIN;
if(WiFi.status() == WL_CONNECTED) {
printLocalTime();
if(t.tm_min%5 == 0) getWeather();
}
}
}
//-----print local time function
void printLocalTime() {
if(!getLocalTime(&t)) {
Serial.println("Failed to obtain time");
return;
}
Serial.println(&t, "%F %T %A");
}
//-----second timer callback function
void IRAM_ATTR onTimer() {
xSemaphoreGiveFromISR(timersem, NULL);
}
//-----intialize timer function
void initTimer() {
timersem = xSemaphoreCreateBinary();
//ESP32 sysclk is 80MHz.
//frequency division factor to 80,
//then timer clock is 1MHz.
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 1000000, true); //1000000us timer
timerAlarmEnable(timer);
}
//-----request now weather function
void getWeather() {
if((WiFi.status() == WL_CONNECTED)) {
HTTPClient http; //create HTTPClient instance
http.begin(url); //begin HTTP request
int httpCode = http.GET(); //get response code - normal:200
if(httpCode > 0) {
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
if(httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
//parse response string to DynamicJsonDocument instance
deserializeJson(doc, payload);
//get top level Json-Object
JsonObject obj = doc.as<JsonObject>();
//get "results" property then parse the value to Json-Array
JsonVariant resultsvar = obj["results"];
JsonArray resultsarr = resultsvar.as<JsonArray>();
//get array index 0 which is now weather info
JsonVariant resultselementvar = resultsarr[0];
JsonObject resultselementobj = resultselementvar.as<JsonObject>();
//get city, temp, last_update time
JsonVariant namevar = resultselementobj["location"]["name"];
String namestr = namevar.as<String>();
Serial.println(namestr);
JsonVariant temperaturevar = resultselementobj["now"]["temperature"];
String temperaturestr = temperaturevar.as<String>();
Serial.println(temperaturestr);
JsonVariant last_updatevar = resultselementobj["last_update"];
String last_updatestr = last_updatevar.as<String>();
Serial.println(last_updatestr);
}
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n",
http.errorToString(httpCode).c_str());
}
http.end(); //end http connectiong
}
}