您现在的位置是:首页 >技术交流 >esp32获取天气数据(基于esp-idf-5.2.3)网站首页技术交流
esp32获取天气数据(基于esp-idf-5.2.3)
首先需要esp32连接wifi上网, 再通过向天气网站服务器发送GET请求获取天气数据,这里有两个选择:1. 发送http请求,2. 发送https请求。这两种有什么区别请自行百度,总的来说https涉及加密算法更安全。
这里我们选择方式2,发送https请求。因为HTTPS 是基于 TLS/SSL 的安全通信协议,而 TLS/SSL 依赖于设备的系统时间来进行证书验证,所以这里还需要获取sntp网络时间,并保存在nvs分区中。
1. esp32连接wifi、获取网络时间并备份
这里连接wifi、获取网络时间参考我的上一篇博客:esp32获取网络时间代码
由于上一篇博客没有备份时间,这里补充下时间备份的代码(将时间戳写入nvs分区保存)以及更新nvs时间的代码,这里将Ntp_time.c更改如下:
#include <stdio.h>
#include "incl/Ntp_time.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <string.h>
#include <sys/time.h>
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_sleep.h"
#include "esp_netif_sntp.h"
#include "esp_netif.h"
#include "lwip/ip_addr.h"
#include "esp_sntp.h"
#include "esp_mac.h"
static const char* TAG = "myNTP";
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 48
#endif
#define CONFIG_EXAMPLE_CONNECT_WIFI // 使用wifi连接网络
#define STORAGE_NAMESPACE "storage" // 自定义NVS命名空间
RTC_DATA_ATTR static int boot_count = 0;
// sntp初始化标志位
bool sntp_inited_flag = 0;
// 获取sntp时间完成回调指针
static mySntp_done_t f_mySntp_done = NULL;
static esp_err_t obtain_time(void);
void time_sync_notification_cb(struct timeval *tv)
{
ESP_LOGI(TAG, "Notification of a time synchronization event");
}
static void print_servers(void)
{
ESP_LOGI(TAG, "List of configured NTP servers:");
for (uint8_t i = 0; i < SNTP_MAX_SERVERS; ++i){
if (esp_sntp_getservername(i)){
ESP_LOGI(TAG, "server %d: %s", i, esp_sntp_getservername(i));
} else {
// we have either IPv4 or IPv6 address, let's print it
char buff[INET6_ADDRSTRLEN];
ip_addr_t const *ip = esp_sntp_getserver(i);
if (ipaddr_ntoa_r(ip, buff, INET6_ADDRSTRLEN) != NULL)
ESP_LOGI(TAG, "server %d: %s", i, buff);
}
}
}
static esp_err_t mySntp_init(void)
{
esp_err_t ret;
#if LWIP_DHCP_GET_NTP_SRV
ESP_LOGI(TAG, "Initializing SNTP");
esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(3, ESP_SNTP_SERVER_LIST("cn.pool.ntp.org", "time.windows.com", "ntp.sjtu.edu.cn"));
config.start = false; // start SNTP service explicitly (after connecting)
config.server_from_dhcp = true; // accept NTP offers from DHCP server, if any (need to enable *before* connecting)
config.renew_servers_after_new_IP = true; // let esp-netif update configured SNTP server(s) after receiving DHCP lease
config.index_of_first_server = 1; // updates from server num 1, leaving server 0 (from DHCP) intact
// configure the event on which we renew servers
#ifdef CONFIG_EXAMPLE_CONNECT_WIFI
config.ip_event_to_renew = IP_EVENT_STA_GOT_IP;
#else
config.ip_event_to_renew = IP_EVENT_ETH_GOT_IP;
#endif
config.sync_cb = time_sync_notification_cb; // only if we need the notification function
ret = esp_netif_sntp_init(&config);
#endif /* LWIP_DHCP_GET_NTP_SRV */
#if LWIP_DHCP_GET_NTP_SRV
ESP_LOGI(TAG, "Starting SNTP");
ret += esp_netif_sntp_start();
#endif
print_servers();
if(ret == ESP_OK)
return ret;
return ESP_FAIL;
}
void sntp_task(void* param)
{
++boot_count;
ESP_LOGI(TAG, "Boot count: %d", boot_count);
time_t now;
struct tm timeinfo;
time(&now);
localtime_r(&now, &timeinfo);
// Is time set? If not, tm_year will be (1970 - 1900).
if (timeinfo.tm_year < (2016 - 1900)) {
ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP.");
if(mySntp_init() == ESP_OK)
sntp_inited_flag = 1;
if(obtain_time() != ESP_OK)
{
ESP_LOGE(TAG, "obtain time failed!");
vTaskDelete(NULL);
}
// update 'now' variable with current time
time(&now);
}
char strftime_buf[64];
// Set timezone to China Standard Time
setenv("TZ", "CST-8", 1);
tzset();
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
ESP_LOGI(TAG, "The current date/time in BeiJing is: %s", strftime_buf);
f_mySntp_done(); // 成功获取sntp时间后调用
// 进入睡眠模式10s
// const int deep_sleep_sec = 10;
// ESP_LOGI(TAG, "Entering deep sleep for %d seconds", deep_sleep_sec);
// esp_deep_sleep(1000000LL * deep_sleep_sec);
vTaskDelete(NULL);
}
static esp_err_t obtain_time(void)
{
// wait for time to be set
time_t now = 0;
struct tm timeinfo = { 0 };
int retry = 0;
const int retry_count = 15;
while (esp_netif_sntp_sync_wait(2000 / portTICK_PERIOD_MS) == ESP_ERR_TIMEOUT && ++retry < retry_count) {
ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
}
time(&now);
localtime_r(&now, &timeinfo);
esp_netif_sntp_deinit();
// 将时间戳写入nvs
nvs_handle_t my_handle = 0;
if(tim_write_into_nvs(&my_handle, &now) == ESP_OK)
ESP_LOGI(TAG, "tim_write_into_nvs successful");
else ESP_LOGE(TAG, "tim_write_into_nvs failed");
if(retry == retry_count)
return ESP_ERR_TIMEOUT;
return ESP_OK;
}
esp_err_t fetch_and_store_time_in_nvs(void *args)
{
nvs_handle_t my_handle = 0;
esp_err_t err;
if(!sntp_inited_flag)
{
ESP_LOGE(TAG, "sntp don't init");
err = ESP_FAIL;
goto exit;
}
if (obtain_time() != ESP_OK) {
err = ESP_FAIL;
goto exit;
}
time_t now;
time(&now);
// 将时间戳写入nvs
err = tim_write_into_nvs(&my_handle, &now);
if(err != ESP_OK)
{
ESP_LOGE(TAG, "tim_write_into_nvs failed");
goto exit;
}
exit:
if (my_handle != 0) {
nvs_close(my_handle);
}
esp_netif_sntp_deinit();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error updating time in nvs");
} else {
ESP_LOGI(TAG, "Updated time in NVS");
}
return err;
}
esp_err_t update_time_from_nvs(void)
{
nvs_handle_t my_handle = 0;
esp_err_t err;
int64_t timestamp = 0;
// 从nvs中读取网络时间
err = tim_read_from_nvs(&my_handle, ×tamp);
if (err == ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGI(TAG, "Time not found in NVS. Syncing time from SNTP server.");
if (fetch_and_store_time_in_nvs(NULL) != ESP_OK) {
err = ESP_FAIL;
} else {
err = ESP_OK;
}
} else if (err == ESP_OK) {
struct timeval get_nvs_time;
get_nvs_time.tv_sec = timestamp;
settimeofday(&get_nvs_time, NULL);
}
// exit
if (my_handle != 0) {
nvs_close(my_handle);
}
return err;
}
void mySntp_start(mySntp_done_t f)
{
f_mySntp_done = f;
ESP_LOGI(TAG, "mySntp start");
xTaskCreatePinnedToCore(sntp_task, "sntp_task", 4096, NULL, 3, NULL, 1);
}
esp_err_t tim_write_into_nvs(nvs_handle_t* handle, time_t* t)
{
esp_err_t err;
//Open
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, handle);
if (err != ESP_OK) {
return err;
}
//Write
err = nvs_set_i64(*handle, "timestamp", *t);
if (err != ESP_OK) {
return err;
}
// commit
err = nvs_commit(*handle);
if (err != ESP_OK) {
return err;
}
return err;
}
esp_err_t tim_read_from_nvs(nvs_handle_t* handle, int64_t* timestamp)
{
esp_err_t err;
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error opening NVS");
return err;
}
return nvs_get_i64(*handle, "timestamp", timestamp);
}
以下是Ntp_time.h
#ifndef __NTP_TIME_H
#define __NTP_TIME_H
#include "esp_err.h"
#include "nvs_flash.h"
#include <time.h>
// sntp时间获取完成回调定义
typedef void (*mySntp_done_t)(void);
/**
* @description: 开始获取sntp网络时间
* @param {mySntp_done_t} f 回调函数
* @return {*}
*/
void mySntp_start(mySntp_done_t f);
/**
* @description: 将时间戳备份至nvs
* @param {nvs_handle_t*} handle nvs操作句柄
* @param {time_t*} t 时间戳指针
* @return {*} 成功: ESP_OK; 失败: others
*/
esp_err_t tim_write_into_nvs(nvs_handle_t* handle, time_t* t);
esp_err_t tim_read_from_nvs(nvs_handle_t* handle, int64_t* timestamp);
/**
* @description: Fetch the current time from SNTP and stores it in NVS.
* @param {void*}
* @return {*} Successful: ESP_OK; Failed: other flag
*/
esp_err_t fetch_and_store_time_in_nvs(void*);
/**
* @description: Update the system time from time stored in NVS.
* @return {*} Successful: ESP_OK; Failed: other flag
*/
esp_err_t update_time_from_nvs(void);
struct tm* get_time(void);
#endif
2. 获取例程源码
首先找到esp32的官方例程(自行上官网找,或者在esp-idf环境目录中查找一个example目录),我下载的SDK是保存在以下目录:
在example目录下找到https_request例程
打开https_request文件夹,将里面的main文件夹复制出来,删除time_sync.c文件。
3. 开启Use ESP-TLS with mbedTLS选项
这里我们在上一篇博客的代码基础上增加https_request功能。首先新建https_reqst.c和.h文件,然后将https_request_example_main.c中的代码全部复制进来。
这里我们需要先启用Use ESP-TLS with mbedTLS(使用 mbedTLS 作为 ESP-TLS 后端)选项,但是我们不是基于示例项目,而是在上一篇博客的代码基础上,所以需要手动添加 Kconfig
文件来启用 Example Configuration
菜单。按照以下步骤在命令行中执行相应指令(也可手动添加):
1.在项目的 main 目录下创建 Kconfig.projbuild 文件:
touch main/Kconfig.projbuild
2.编辑 Kconfig.projbuild 文件,添加以下内容:
menu "Example Configuration"
config EXAMPLE_USING_ESP_TLS_MBEDTLS
bool "Use ESP-TLS with mbedTLS"
default y
help
Enable this option to use mbedTLS as the backend for ESP-TLS.
endmenu
3.将Kconfig.projbuild 文件保存为UTF-8格式
4.重新运行 menuconfig:
idf.py menuconfig
在menuconfig --> Example Configuration中勾选[*] Use ESP-TLS with mbedTLS
4. 编写https_request功能代码
在https_reqst.c文件中我们将一些不必要的代码删除,这里我们为了方便直接使用证书包(https_request using crt bundle)发送https请求,所以将root_cert、local_cert证书部分以及server_supported_ciphersuites密码套件代码注释掉。
然后将app_main函数中的example_connect()连接wifi函数删除,并将app_main函数改名为https_request_start。再包含之前ntp代码的头文件,具体代码如下:
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <time.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "protocol_examples_common.h"
#include "esp_sntp.h"
#include "esp_netif.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "esp_tls.h"
#include "sdkconfig.h"
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE && CONFIG_EXAMPLE_USING_ESP_TLS_MBEDTLS
#include "esp_crt_bundle.h"
#endif
#include "incl/Ntp_time.h"
#include "incl/https_reqst.h"
/* Constants that aren't configurable in menuconfig */
#define WEB_SERVER "www.howsmyssl.com"
#define WEB_PORT "443"
#define WEB_URL "https://www.howsmyssl.com/a/check"
#define SERVER_URL_MAX_SZ 256
static const char *TAG = "example";
/* Timer interval once every day (24 Hours) */
#define TIME_PERIOD (86400000000ULL)
static const char HOWSMYSSL_REQUEST[] = "GET " WEB_URL " HTTP/1.1
"
"Host: "WEB_SERVER"
"
"User-Agent: esp-idf/1.0 esp32
"
"
";
//#ifdef CONFIG_EXAMPLE_CLIENT_SESSION_TICKETS
//static const char LOCAL_SRV_REQUEST[] = "GET " CONFIG_EXAMPLE_LOCAL_SERVER_URL " HTTP/1.1
"
// "Host: "WEB_SERVER"
"
// "User-Agent: esp-idf/1.0 esp32
"
// "
";
//#endif
/*
extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start");
extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end");
extern const uint8_t local_server_cert_pem_start[] asm("_binary_local_server_cert_pem_start");
extern const uint8_t local_server_cert_pem_end[] asm("_binary_local_server_cert_pem_end");
#if CONFIG_EXAMPLE_USING_ESP_TLS_MBEDTLS
static const int server_supported_ciphersuites[] = {MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 0};
static const int server_unsupported_ciphersuites[] = {MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, 0};
#endif
#ifdef CONFIG_EXAMPLE_CLIENT_SESSION_TICKETS
static esp_tls_client_session_t *tls_client_session = NULL;
static bool save_client_session = false;
#endif
*/
static void https_get_request(esp_tls_cfg_t cfg, const char *WEB_SERVER_URL, const char *REQUEST)
{
char buf[512];
int ret, len;
esp_tls_t *tls = esp_tls_init();
if (!tls) {
ESP_LOGE(TAG, "Failed to allocate esp_tls handle!");
goto exit;
}
if (esp_tls_conn_http_new_sync(WEB_SERVER_URL, &cfg, tls) == 1) {
ESP_LOGI(TAG, "Connection established...");
} else {
ESP_LOGE(TAG, "Connection failed...");
int esp_tls_code = 0, esp_tls_flags = 0;
esp_tls_error_handle_t tls_e = NULL;
esp_tls_get_error_handle(tls, &tls_e);
/* Try to get TLS stack level error and certificate failure flags, if any */
ret = esp_tls_get_and_clear_last_error(tls_e, &esp_tls_code, &esp_tls_flags);
if (ret == ESP_OK) {
ESP_LOGE(TAG, "TLS error = -0x%x, TLS flags = -0x%x", esp_tls_code, esp_tls_flags);
}
goto cleanup;
}
//#ifdef CONFIG_EXAMPLE_CLIENT_SESSION_TICKETS
// /* The TLS session is successfully established, now saving the session ctx for reuse */
// if (save_client_session) {
// esp_tls_free_client_session(tls_client_session);
// tls_client_session = esp_tls_get_client_session(tls);
// }
//#endif
size_t written_bytes = 0;
do {
ret = esp_tls_conn_write(tls,
REQUEST + written_bytes,
strlen(REQUEST) - written_bytes);
if (ret >= 0) {
ESP_LOGI(TAG, "%d bytes written", ret);
written_bytes += ret;
} else if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "esp_tls_conn_write returned: [0x%02X](%s)", ret, esp_err_to_name(ret));
goto cleanup;
}
} while (written_bytes < strlen(REQUEST));
ESP_LOGI(TAG, "Reading HTTP response...");
do {
len = sizeof(buf) - 1;
memset(buf, 0x00, sizeof(buf));
ret = esp_tls_conn_read(tls, (char *)buf, len);
if (ret == ESP_TLS_ERR_SSL_WANT_WRITE || ret == ESP_TLS_ERR_SSL_WANT_READ) {
continue;
} else if (ret < 0) {
ESP_LOGE(TAG, "esp_tls_conn_read returned [-0x%02X](%s)", -ret, esp_err_to_name(ret));
break;
} else if (ret == 0) {
ESP_LOGI(TAG, "connection closed");
break;
}
len = ret;
ESP_LOGD(TAG, "%d bytes read", len);
/* Print response directly to stdout as it is read */
for (int i = 0; i < len; i++) {
putchar(buf[i]);
}
putchar('
'); // JSON output doesn't have a newline at end
} while (1);
cleanup:
esp_tls_conn_destroy(tls);
exit:
for (int countdown = 10; countdown >= 0; countdown--) {
ESP_LOGI(TAG, "%d...", countdown);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE && CONFIG_EXAMPLE_USING_ESP_TLS_MBEDTLS
static void https_get_request_using_crt_bundle(void)
{
ESP_LOGI(TAG, "https_request using crt bundle");
esp_tls_cfg_t cfg = {
.crt_bundle_attach = esp_crt_bundle_attach,
};
https_get_request(cfg, WEB_URL, HOWSMYSSL_REQUEST);
}
#endif // CONFIG_MBEDTLS_CERTIFICATE_BUNDLE && CONFIG_EXAMPLE_USING_ESP_TLS_MBEDTLS
//static void https_get_request_using_cacert_buf(void)
//{
// ESP_LOGI(TAG, "https_request using cacert_buf");
// esp_tls_cfg_t cfg = {
// .cacert_buf = (const unsigned char *) server_root_cert_pem_start,
// .cacert_bytes = server_root_cert_pem_end - server_root_cert_pem_start,
// };
// https_get_request(cfg, WEB_URL, HOWSMYSSL_REQUEST);
//}
//static void https_get_request_using_specified_ciphersuites(void)
//{
//#if CONFIG_EXAMPLE_USING_ESP_TLS_MBEDTLS
//
// ESP_LOGI(TAG, "https_request using server supported ciphersuites");
// esp_tls_cfg_t cfg = {
// .cacert_buf = (const unsigned char *) server_root_cert_pem_start,
// .cacert_bytes = server_root_cert_pem_end - server_root_cert_pem_start,
// .ciphersuites_list = server_supported_ciphersuites,
// };
//
// https_get_request(cfg, WEB_URL, HOWSMYSSL_REQUEST);
//
// ESP_LOGI(TAG, "https_request using server unsupported ciphersuites");
//
// cfg.ciphersuites_list = server_unsupported_ciphersuites;
//
// https_get_request(cfg, WEB_URL, HOWSMYSSL_REQUEST);
//#endif
//}
//
//static void https_get_request_using_global_ca_store(void)
//{
// esp_err_t esp_ret = ESP_FAIL;
// ESP_LOGI(TAG, "https_request using global ca_store");
// esp_ret = esp_tls_set_global_ca_store(server_root_cert_pem_start, server_root_cert_pem_end - server_root_cert_pem_start);
// if (esp_ret != ESP_OK) {
// ESP_LOGE(TAG, "Error in setting the global ca store: [%02X] (%s),could not complete the https_request using global_ca_store", esp_ret, esp_err_to_name(esp_ret));
// return;
// }
// esp_tls_cfg_t cfg = {
// .use_global_ca_store = true,
// };
// https_get_request(cfg, WEB_URL, HOWSMYSSL_REQUEST);
// esp_tls_free_global_ca_store();
//}
//#ifdef CONFIG_EXAMPLE_CLIENT_SESSION_TICKETS
//static void https_get_request_to_local_server(const char* url)
//{
// ESP_LOGI(TAG, "https_request to local server");
// esp_tls_cfg_t cfg = {
// .cacert_buf = (const unsigned char *) local_server_cert_pem_start,
// .cacert_bytes = local_server_cert_pem_end - local_server_cert_pem_start,
// .skip_common_name = true,
// };
// save_client_session = true;
// https_get_request(cfg, url, LOCAL_SRV_REQUEST);
//}
//
//static void https_get_request_using_already_saved_session(const char *url)
//{
// ESP_LOGI(TAG, "https_request using saved client session");
// esp_tls_cfg_t cfg = {
// .client_session = tls_client_session,
// };
// https_get_request(cfg, url, LOCAL_SRV_REQUEST);
// esp_tls_free_client_session(tls_client_session);
// save_client_session = false;
// tls_client_session = NULL;
//}
//#endif
static void https_request_task(void *pvparameters)
{
ESP_LOGI(TAG, "Start https_request example");
//#ifdef CONFIG_EXAMPLE_CLIENT_SESSION_TICKETS
// char *server_url = NULL;
//#ifdef CONFIG_EXAMPLE_LOCAL_SERVER_URL_FROM_STDIN
// char url_buf[SERVER_URL_MAX_SZ];
// if (strcmp(CONFIG_EXAMPLE_LOCAL_SERVER_URL, "FROM_STDIN") == 0) {
// example_configure_stdin_stdout();
// fgets(url_buf, SERVER_URL_MAX_SZ, stdin);
// int len = strlen(url_buf);
// url_buf[len - 1] = '