ESP32 S3 基于IDF WIFI实现HTTP请求天气预报案例-2

514 阅读3分钟

1 天气服务

  • image.png
    https://www.seniverse.com/products?iid=4a8a4c16-71a3-4e94-8a49-b1680a3cb358
    SVaEt7yji_44YDi3_
  • 秘钥非常关键 image.png

1.1 JSON数据结构

image.png

https://api.seniverse.com/v3/weather/daily.json?key=SVaEt7yji_44YDi3_&location=beijing&language=zh-Hans&unit=c&start=0&days=5

{
	"results": [
		{
			"location": {
				"id": "WX4FBXXFKE4F",
				"name": "北京",
				"country": "CN",
				"path": "北京,北京,中国",
				"timezone": "Asia/Shanghai",
				"timezone_offset": "+08:00"
			},
			"daily": [
				{
					"date": "2023-03-18",
					"text_day": "多云",
					"code_day": "4",
					"text_night": "晴",
					"code_night": "1",
					"high": "17",
					"low": "1",
					"rainfall": "0.00",
					"precip": "0.00",
					"wind_direction": "北",
					"wind_direction_degree": "0",
					"wind_speed": "23.4",
					"wind_scale": "4",
					"humidity": "29"
				},
				{
					"date": "2023-03-19",
					"text_day": "多云",
					"code_day": "4",
					"text_night": "多云",
					"code_night": "4",
					"high": "14",
					"low": "1",
					"rainfall": "0.00",
					"precip": "0.00",
					"wind_direction": "南",
					"wind_direction_degree": "180",
					"wind_speed": "8.4",
					"wind_scale": "2",
					"humidity": "64"
				},
				{
					"date": "2023-03-20",
					"text_day": "多云",
					"code_day": "4",
					"text_night": "多云",
					"code_night": "4",
					"high": "18",
					"low": "7",
					"rainfall": "0.00",
					"precip": "0.00",
					"wind_direction": "南",
					"wind_direction_degree": "180",
					"wind_speed": "3.0",
					"wind_scale": "1",
					"humidity": "76"
				}
			],
			"last_update": "2023-03-18T08:00:00+08:00"
		}
	]
}

2 详细代码

#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
//#include "protocol_examples_common.h"
//#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
//#include "protocol_examples_common.h"
//#include "ssd1306.h"
//#include "font8x8_basic.h"

#include "esp_tls.h"
#include "esp_http_client.h"
#include "cJSON.h"

/*parameter for oled*/
// SSD1306_t oledDev;
// #define SSD1306_SDA_GPIO 4
// #define SSD1306_SCL_GPIO 5
// static const char *OLED_TAG = "oled_Task";
// #define OLED_ADDR 0x3C

/*WiFi config parameter*/
#define WiFi_STA_SSID "qkxhome"
#define WiFi_STA_PASSWORD "qkx2021&&"
#define WiFi_MAX_RETRY 15

/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1
static const char *WiFi_TAG = "WIFI基站20220925:获取天气信息 -> ";
static uint8_t WiFi_retry_num = 0;

/* store the station info */
static bool gl_sta_connected = false;
static uint8_t gl_sta_bssid[6];
static uint8_t gl_sta_ssid[32];
static int gl_sta_ssid_len = 0;
static bool gl_got_ip = false;

static const char *HTTP_TAG = "HTTP获取天气信息20220925 -> ";
#define MAX_HTTP_OUTPUT_BUFFER 1300
#define HOST "api.seniverse.com"
#define UserKey "SVaEt7yji_44YDi3_"
#define Location "zhengzhou"
#define Language "zh-Hans"
#define Strat "0"
#define Days "5"


static const char *RESULT_TAG = "天气信息解析数据";

// static void freeBuffer(char* buf, int bufSize){
// 	for( int i = 0; i < bufSize; i ++ )
// 		buf[i] = NULL;	
// }

// JSON解析函数
static void cJSON_parse_task(char* text){
	char oledText[16];
	char *date,*Temp_High = NULL,*Temp_Low,*humidity,*Rainfall;

	cJSON *root,*arrayItem,*subArray;
	cJSON *arr_item,*sub_array_item;
    // 
	cJSON *JsonDate,*JsonTemp_High,*JsonTemp_Low,*JsonHumidity,*JsonRainfall;
	root = cJSON_Parse(text);
	if(root!=NULL)
	{
		arrayItem = cJSON_GetObjectItem(root,"results")
		int arr_size = cJSON_GetArraySize(arrayItem);
             
		ESP_LOGI(HTTP_TAG, "root_arr_size: %d \n", arr_size);
                
		arr_item = arrayItem->child;
                
		for(int i = 0; i < arr_size; i ++){
                
			subArray = cJSON_GetObjectItem(arr_item, "daily");
			int sub_array_size = cJSON_GetArraySize(subArray);
			sub_array_item = subArray->child;
                        
			ESP_LOGI(HTTP_TAG, "sub_arr_size: %d \n", sub_array_size);
			for(int j = 0; j < sub_array_size; j ++){

                        printf("\t 一组数据解析开始 \n");
                        //ESP_LOGE(HTTP_TAG, "Data IS json");
				if(sub_array_item->type == cJSON_Object){
					//ssd1306_clear_screen(&oledDev, false);
					//ssd1306_display_text(&oledDev, 1, "Weather Report", 14, false);
					JsonDate =  cJSON_GetObjectItem(sub_array_item, "date");
					if(cJSON_IsString(JsonDate)){
						date = JsonDate->valuestring;
						sprintf(oledText, "date: %s", date);                        
						//ssd1306_display_text(&oledDev, 2, oledText, 16, false);
						//freeBuffer(oledText, 16);
                                                ESP_LOGI(RESULT_TAG, "%s", oledText);     
					}
					
					JsonTemp_High =  cJSON_GetObjectItem(sub_array_item, "high");
					if(cJSON_IsString(JsonTemp_High))
						Temp_High = JsonTemp_High->valuestring;
					
					JsonTemp_Low =  cJSON_GetObjectItem(sub_array_item, "low");
					if(cJSON_IsString(JsonTemp_Low)){
						Temp_Low=JsonTemp_Low->valuestring;
						sprintf(oledText, "Temp: %s to %s", Temp_High, Temp_Low);
						//ssd1306_display_text(&oledDev, 3, oledText, 16, false);
						//freeBuffer(oledText, 16);
                                                   ESP_LOGI(RESULT_TAG, "%s", oledText);
					}
					
					JsonHumidity =  cJSON_GetObjectItem(sub_array_item, "humidity");
					if(cJSON_IsString(JsonHumidity)){
						humidity=JsonHumidity->valuestring;
						sprintf(oledText, "Humi: %s", humidity);
						//ssd1306_display_text(&oledDev, 4, oledText, 16, false);
						//freeBuffer(oledText, 16);
                                                ESP_LOGI(RESULT_TAG, "%s", oledText);
					}

					JsonRainfall =  cJSON_GetObjectItem(sub_array_item, "rainfall");
					if(cJSON_IsString(JsonRainfall)){
						Rainfall=JsonRainfall->valuestring;
						sprintf(oledText, "Rain: %s", Rainfall);
                                                ESP_LOGI(RESULT_TAG, "%s", oledText);
						//ssd1306_display_text(&oledDev, 5, oledText, 16, false);
						//freeBuffer(oledText, 16);
					}
					vTaskDelay(2000 / portTICK_PERIOD_MS);
				}
				sub_array_item = sub_array_item->next;
			}
			arr_item = arr_item -> next;
		}
                printf("\n");
		ESP_LOGI(HTTP_TAG, "Finish");
	}
	cJSON_Delete(root);
}

/** HTTP functions **/
static void http_client_task(void *pvParameters){
	char output_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};   // Buffer to store response of http request
    int content_length = 0;
	static const char *URL = "http://"HOST"/v3/weather/daily.json?"	\
							 "key="UserKey"&location="Location		\
							 "&language="Language					\
							 "&unit=c&start="Strat"&days="Days;

    esp_http_client_config_t config = {
        .url = URL,
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);

    // GET Request
    esp_http_client_set_method(client, HTTP_METHOD_GET);

    esp_err_t err = esp_http_client_open(client, 0);
    if (err != ESP_OK) {
        ESP_LOGE(HTTP_TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
    } else {
        content_length = esp_http_client_fetch_headers(client);
        if (content_length < 0) {
            ESP_LOGE(HTTP_TAG, "HTTP client fetch headers failed");
        } else {

            int data_read = esp_http_client_read_response(client, output_buffer, MAX_HTTP_OUTPUT_BUFFER);

            if (data_read >= 0) {
                ESP_LOGI(HTTP_TAG, "HTTP GET Status = %d, content_length = %d",
                esp_http_client_get_status_code(client),
                esp_http_client_get_content_length(client));
                // 打印天气信息
                ESP_LOGI(WiFi_TAG, "\n\n\n %s \n", output_buffer);
                //JSON解析
				cJSON_parse_task(output_buffer);
            } else {
                ESP_LOGE(HTTP_TAG, "Failed to read response");
            }
        }
    }
    esp_http_client_close(client);
	vTaskDelete(NULL);
}

static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
	if(event_base == WIFI_EVENT){
		wifi_event_sta_connected_t *wifi_sta_event = (wifi_event_sta_connected_t*) event_data;
		switch(event_id){
			case WIFI_EVENT_STA_START:
				ESP_LOGI(WiFi_TAG, "connect to AP:%s ", WiFi_STA_SSID);
				esp_wifi_connect();
				break;
			case WIFI_EVENT_STA_CONNECTED:
				ESP_LOGI(WiFi_TAG, "connected");
				WiFi_retry_num = 0;
				gl_sta_connected = true;
				wifi_sta_event = (wifi_event_sta_connected_t*) event_data;
				memcpy(gl_sta_bssid, wifi_sta_event->bssid, 6);
				memcpy(gl_sta_ssid, wifi_sta_event->ssid, wifi_sta_event->ssid_len);
				gl_sta_ssid_len = wifi_sta_event->ssid_len;
				break;
			case WIFI_EVENT_STA_DISCONNECTED:
				gl_sta_connected = false;
				WiFi_retry_num ++;
				if(WiFi_retry_num > WiFi_MAX_RETRY){
					ESP_LOGI(WiFi_TAG, "retry to connect to AP:%s ", WiFi_STA_SSID);
					esp_wifi_connect();
					xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
				}else{
					xEventGroupSetBits(wifi_event_group, WIFI_FAIL_BIT);
				}
				break;
			default:
				break;
		}
	}else if(event_base ==  IP_EVENT){
		ip_event_got_ip_t* ip_event = (ip_event_got_ip_t*) event_data;
		if(event_id == IP_EVENT_STA_GOT_IP){
			ESP_LOGI(WiFi_TAG, "got ip:" IPSTR, IP2STR(&ip_event->ip_info.ip));
			xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
			WiFi_retry_num = 0;
			gl_got_ip = true;
			//创建HTTP任务
			xTaskCreate(http_client_task, "http_client", 5120, NULL, 3, NULL);
		}
	}
	return;
}

static void initialise_wifi(void)
{
	wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();		//初始化并注册事件至event loop中
    assert(sta_netif);
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
	ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
    
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
	
	wifi_config_t wifi_config= {
        .sta = {
            .ssid = WiFi_STA_SSID,
			.password = WiFi_STA_PASSWORD,
			.bssid_set = 0,
		}
	};
	
	ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK( esp_wifi_start() );
	ESP_LOGI(WiFi_TAG, "wifi_init_sta finished.");

 /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(WiFi_TAG, "connected to ap SSID:%s password:%s",
                 WiFi_STA_SSID, WiFi_STA_PASSWORD);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(WiFi_TAG, "Failed to connect to SSID:%s, password:%s",
                 WiFi_STA_SSID, WiFi_STA_PASSWORD);
    } else {
        ESP_LOGE(WiFi_TAG, "UNEXPECTED EVENT");
    }
}

void app_main(void)
{
	//i2c_master_init(&oledDev, SSD1306_SDA_GPIO, SSD1306_SCL_GPIO, -1);
	//oledDev._flip = true;
	//oledDev._address = OLED_ADDR;
	//ssd1306_init(&oledDev, 128, 64);
	//ESP_LOGI(OLED_TAG,"init seccess");
	//ssd1306_clear_screen(&oledDev, false);
	//ssd1306_contrast(&oledDev, 0xff);
	//ssd1306_display_text(&oledDev, 0, "Weather Report", 14, false);
	ESP_ERROR_CHECK(nvs_flash_init());
	initialise_wifi();
}