ESP32 实现获取天气情况

按照小安派AiPi-Eyes天气站思路,在ESP32 S3上实现获取天气情况。

一、在ESP32 S3实现

1、main.c

建立2个TASK

void app_main(void)
{
    //lvgl初始化
    xTaskCreate(guiTask, "guiTask", 1024 * 6, NULL, 5, NULL);
    //wifi初始化、socket、json处理task
    custom_init();
}

2、guiTask()

lVGL初始化


void guiTask(void *pvParameter)
{
    xGuiSemaphore = xSemaphoreCreateMutex();
    static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
    static lv_disp_drv_t disp_drv;      // contains callback functions

    ESP_LOGI(TAG, "Turn off LCD backlight");

    gpio_set_direction(PIN_NUM_RD, GPIO_MODE_OUTPUT);
    gpio_set_level(PIN_NUM_RD, 1);

    backlight_ledc_init();

    ESP_LOGI(TAG, "Initialize Intel 8080 bus");
    esp_lcd_i80_bus_handle_t i80_bus = NULL;
    esp_lcd_i80_bus_config_t bus_config = {
        .clk_src = LCD_CLK_SRC_DEFAULT,
        .dc_gpio_num = PIN_NUM_DC,
        .wr_gpio_num = PIN_NUM_PCLK,
        .data_gpio_nums = {
            PIN_NUM_DATA0,
            PIN_NUM_DATA1,
            PIN_NUM_DATA2,
            PIN_NUM_DATA3,
            PIN_NUM_DATA4,
            PIN_NUM_DATA5,
            PIN_NUM_DATA6,
            PIN_NUM_DATA7,
        },
        .bus_width = 8,
        .max_transfer_bytes = LCD_V_RES * 220 * sizeof(uint16_t),
        .psram_trans_align = PSRAM_DATA_ALIGNMENT,
        .sram_trans_align = 4,
    };
    ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus));
    esp_lcd_panel_io_handle_t io_handle = NULL;
    esp_lcd_panel_io_i80_config_t io_config = {
        .cs_gpio_num = PIN_NUM_CS,
        .pclk_hz = LCD_PIXEL_CLOCK_HZ,
        .trans_queue_depth = 10,
        .dc_levels = {
            .dc_idle_level = 0,
            .dc_cmd_level = 0,
            .dc_dummy_level = 0,
            .dc_data_level = 1,
        },
        .on_color_trans_done = notify_lvgl_flush_ready,
        .user_ctx = &disp_drv,
        .lcd_cmd_bits = LCD_CMD_BITS,
        .lcd_param_bits = LCD_PARAM_BITS,
        .flags.swap_color_bytes = true,
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle));

    esp_lcd_panel_handle_t panel_handle = NULL;

    ESP_LOGI(TAG, "Install LCD driver of ili9225");
    esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = PIN_NUM_RST,
        .color_space = ESP_LCD_COLOR_SPACE_RGB,
        .bits_per_pixel = 16,
    };
    ESP_ERROR_CHECK(esp_lcd_new_panel_ili9225(io_handle, &panel_config, &panel_handle));

    esp_lcd_panel_reset(panel_handle);
    esp_lcd_panel_init(panel_handle);


      ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));

    ESP_LOGI(TAG, "Initialize LVGL library");
    lv_init();
    // alloc draw buffers used by LVGL
    // it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized
    lv_color_t *buf1 = NULL;
    lv_color_t *buf2 = NULL;
  
    buf1 = heap_caps_malloc(LCD_V_RES * 50 * sizeof(lv_color_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
    buf2 = heap_caps_malloc(LCD_V_RES * 50 * sizeof(lv_color_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);

    assert(buf1);
    assert(buf2);
    ESP_LOGI(TAG, "buf1@%p, buf2@%p", buf1, buf2);
    // initialize LVGL draw buffers
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, LCD_V_RES * 50);

    ESP_LOGI(TAG, "Register display driver to LVGL");
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = LCD_H_RES;
    disp_drv.ver_res = LCD_V_RES;
    disp_drv.flush_cb = lvgl_flush_cb;
    disp_drv.draw_buf = &disp_buf;
    disp_drv.user_data = panel_handle;
    // lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
    lv_disp_drv_register(&disp_drv);

    ESP_LOGI(TAG, "Install LVGL tick timer");
    // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &increase_lvgl_tick,
        .name = "lvgl_tick"};
    esp_timer_handle_t lvgl_tick_timer = NULL;
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000));

   
    ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8191));
    // Update duty to apply the new value
    ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));

    // First to print one frame
     lv_timer_handler();

  

    lv_obj_t *screen = lv_obj_create(NULL);
    lv_obj_set_style_bg_color(screen,lv_color_hex(0x000000),LV_PART_MAIN);

    lv_scr_load(screen);


    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_bg_color(&style, lv_color_make(0xFF, 0xff, 0xff));
    lv_style_set_bg_opa(&style,LV_OPA_10);
    lv_style_set_border_color(&style, lv_color_make(0xFF, 0x00, 0xff));
    lv_style_set_text_color(&style,lv_color_make(0x00, 0x00, 0xff));


     label1=lv_label_create(lv_scr_act());
     lv_obj_add_style(label1, &style, 0);
     lv_obj_set_pos(label1, 10, 10);                                    /*Set its position*/
     lv_obj_set_size(label1, 160, 32);                                  /*Set its size*/

     lv_label_set_text(label1, "Weather");
     lv_obj_set_style_text_color(label1, lv_color_make(0xff, 0x00, 0x00), LV_PART_MAIN|LV_STATE_DEFAULT);



    //城市
    label_city=lv_label_create(lv_scr_act());
    lv_obj_set_pos(label_city, 10, 50);                                    /*Set its position*/
     lv_obj_set_size(label_city, 100, 32);                                  /*Set its size*/
     lv_obj_set_style_text_color(label_city, lv_color_make(0xff, 0xff, 0xff), LV_PART_MAIN|LV_STATE_DEFAULT);


    //温度
     label_tem=lv_label_create(lv_scr_act());
     lv_obj_set_pos(label_tem, 120, 50);                                    /*Set its position*/
     lv_obj_set_size(label_tem, 60, 32);                                  /*Set its size*/
     lv_obj_set_style_text_color(label_tem, lv_color_make(0xff, 0xff, 0xff), LV_PART_MAIN|LV_STATE_DEFAULT);

    //天气
     label_wea_img=lv_label_create(lv_scr_act());
     lv_obj_set_pos(label_wea_img, 10, 90);                                    /*Set its position*/
     lv_obj_set_size(label_wea_img, 160, 32);                                  /*Set its size*/
     lv_obj_set_style_text_color(label_wea_img, lv_color_make(0xff, 0xff, 0xff), LV_PART_MAIN|LV_STATE_DEFAULT);

    //湿度
    label_humidity=lv_label_create(lv_scr_act());

    lv_obj_set_pos(label_humidity, 80, 90);                                    /*Set its position*/
    lv_obj_set_size(label_humidity, 60, 32);                                  /*Set its size*/
    lv_obj_set_style_text_color(label_humidity, lv_color_make(0x00, 0xff, 0x00), LV_PART_MAIN|LV_STATE_DEFAULT);

    //日期
    label_date=lv_label_create(lv_scr_act());
    lv_obj_set_pos(label_date, 20, 130);                                    /*Set its position*/
    lv_obj_set_size(label_date, 170, 32);                                  /*Set its size*/
    lv_obj_set_style_text_color(label_date, lv_color_make(0x00, 0x00, 0xff), LV_PART_MAIN|LV_STATE_DEFAULT);


    ESP_LOGI(TAG, "LVGL interface init OK!");

   
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(1000));
        if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY))
        {
            lv_timer_handler();
            xSemaphoreGive(xGuiSemaphore);
        }
    }
    free(buf1);
    free(buf2);
    vTaskDelete(NULL);
}

3、void custom_init(void)

建立queue处理TASK

建立定时,1个小时执行一次重新获取天气信息

执行exmple_connect(),连接WIFI

向queue模拟发送获得IP成功,触发queue下一步操作

void custom_init(void)
{
    ESP_ERROR_CHECK( nvs_flash_init() );
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    ESP_ERROR_CHECK(example_connect());


    queue = xQueueCreate(1, 1024*2);
    xTaskCreate(queue_task, "queue task", 1024*6, NULL, 2, NULL);


    http_timers = xTimerCreate("http_timers", pdMS_TO_TICKS(1000), pdTRUE, 0, http_hour_requst_time);

    vTaskDelay(4000 / portTICK_PERIOD_MS);

    static char* queue_buff;
    queue_buff = pvPortMalloc(128);
    memset(queue_buff, 0, 128);
    sprintf(queue_buff, "{\"ip\":{\"IP\":\"%s\"}}","11.11.11.11");

    //前面example_connect()不可控,运行到这里应该已经获得ip了,向QUEUE模拟发送一个ip已经获得的消息,触发执行后面的程序
    xQueueSend(queue, queue_buff, portMAX_DELAY);
    vPortFree(queue_buff);
}

 4、static void queue_task(void* arg)

根据进入QUEUE的信息,执行不同的任务,一个守护task

static int cjson__analysis_type(char* json_data)
{
    cJSON* root = cJSON_Parse(json_data);

    //ESP_LOGI(TAG, "json_data:%s",json_data);
    if (root==NULL) {
        printf("[%s] is not json\r\n", __func__);

        return 0;
    }
    cJSON* wifi = cJSON_GetObjectItem(root, "WiFi");
    if (wifi) {
        cJSON_Delete(root);
        return 1;
    }
    cJSON* ip = cJSON_GetObjectItem(root, "ip");
    if (ip) {
        cJSON_Delete(root);
        return 2;
    }

    cJSON* weather = cJSON_GetObjectItem(root, "weather");
    if (weather) {
        cJSON_Delete(root);
        return 3;
    }

    cJSON_Delete(root);
    return 0;
}

static void queue_task(void* arg)
{
    char* queue_buff = NULL;
    queue_buff = pvPortMalloc(1024*2);
    while (1)
    {
        vTaskDelay(pdMS_TO_TICKS(100));
        memset(queue_buff, 0, 1024*2);
        xQueueReceive(queue, queue_buff, portMAX_DELAY);

        switch (cjson__analysis_type(queue_buff))
        {
            case 0:
                printf("queue_buff:%s\r\n",queue_buff);
                break;
            case 1: //wifi
                break;
            case 2: //ip
                if (https_Handle!=NULL) {
                    vTaskDelete(https_Handle);
                }

                xTaskCreate(&http_get_task, "http_get_task", 4096, NULL, 5, &https_Handle);
                break;
            case 3: //weather
                vTaskSuspend(https_Handle);
                cjson_get_weather(queue_buff);
                break;
            default:
                break;
        }
    }
    vPortFree(queue_buff);
}

5、static void http_hour_requst_time(TimerHandle_t timer),

每1个小时重新执行一次获取天气信息

static void http_hour_requst_time(TimerHandle_t timer)
{
    if (timers_http>=60*60) {
        //LOG_I("Timed to http update,start https request");
        vTaskResume(https_Handle);
        timers_http = 0;
    }
    else {
        timers_http++;
    }

}

6、static void http_get_task(void *pvParameters)

向网站发送API获取信息,返回天气信息入QUEUE。

#define WEB_SERVER "v1.yiketianqi.com"
#define WEB_PORT "80"
#define WEB_PATH "/api?unescape=1&version=v61&appid=替换成自己的ID&appsecret=替换成自己的KEY"

static const char *REQUEST = "GET " WEB_PATH " HTTP/1.0\r\n"
    "Host: "WEB_SERVER":"WEB_PORT"\r\n"
    "User-Agent: esp-idf/1.0 esp32\r\n"
    "\r\n";

static char* https_get_data(const char* https_request_data)
{
    char* request_data = https_request_data;

    printf("https_get_data:\r\n%s\r\n",request_data);
    static char* https_data;
    https_data = pvPortMalloc(1024*2);
    memset(https_data, 0, 1024*2);
    request_data += 2;
    char* date = pvPortMalloc(64);
    char* request_value = strtok(request_data, "\n");
    for (size_t i = 0; i < 13; i++)
    {
        printf("[%d]%s\r\n", i,request_value);
        if (i==2) strcpy(date, request_value);
        if(i==12) strcpy(https_data, request_value);
        memset(request_value, 0, strlen(request_value));
        request_value = strtok(NULL, "\n");
    }

    vPortFree(date);
    return https_data;
}

static void http_get_task(void *pvParameters)
{
    const struct addrinfo hints = {
        .ai_family = AF_INET,
        .ai_socktype = SOCK_STREAM,
    };
    struct addrinfo *res;
    struct in_addr *addr;
    int s, r;
    char recv_buf[64];
    static char* buff;
    char* queue_buff = NULL;
    buff = pvPortMalloc(2*1024);
    memset(buff, 0, 2*1024);

    while(1) {
        int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res);

        if(err != 0 || res == NULL) {
            ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }

        /* Code to print the resolved IP.

           Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
        addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
        ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));

        s = socket(res->ai_family, res->ai_socktype, 0);
        if(s < 0) {
            ESP_LOGE(TAG, "... Failed to allocate socket.");
            freeaddrinfo(res);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... allocated socket");

        if(connect(s, res->ai_addr, res->ai_addrlen) != 0) {
            ESP_LOGE(TAG, "... socket connect failed errno=%d", errno);
            close(s);
            freeaddrinfo(res);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }

        ESP_LOGI(TAG, "... connected");
        freeaddrinfo(res);

        if (write(s, REQUEST, strlen(REQUEST)) < 0) {
            ESP_LOGE(TAG, "... socket send failed");
            close(s);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... socket send success");

        struct timeval receiving_timeout;
        receiving_timeout.tv_sec = 5;
        receiving_timeout.tv_usec = 0;
        if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout,
                sizeof(receiving_timeout)) < 0) {
            ESP_LOGE(TAG, "... failed to set socket receiving timeout");
            close(s);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... set socket receiving timeout success");

        /* Read HTTP response */
        do {
            bzero(recv_buf, sizeof(recv_buf));
            r = read(s, recv_buf, sizeof(recv_buf)-1);
            strncat(buff, recv_buf, r);     //
            for(int i = 0; i < r; i++) {
                putchar(recv_buf[i]);
            }
        } while(r > 0);

        ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d.", r, errno);
        close(s);


        queue_buff = pvPortMalloc(1024*3);
        memset(queue_buff, 0, 1024*3);
        sprintf(queue_buff, "{\"weather\":%s}", https_get_data(buff));
        xQueueSend(queue, queue_buff, portMAX_DELAY);
        xTimerStart(http_timers, portMAX_DELAY);
        vPortFree(buff);

        vTaskDelay(50/portTICK_PERIOD_MS);


        for(int countdown = 10; countdown >= 0; countdown--) {
            ESP_LOGI(TAG, "%d... ", countdown);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        ESP_LOGI(TAG, "Starting again!");


    }

}

7、void cjson_get_weather(char* weather_data)

处理json,并在LCD显示。

void cjson_get_weather(char* weather_data)
{
	cJSON * item = NULL;//cjson对象

    cJSON* root = cJSON_Parse(weather_data );

    root=   cJSON_GetObjectItem(root, "weather");

	if (!root)
	{
		printf("Error before: [%s]\n",cJSON_GetErrorPtr());
	}
	else
	{

		item = cJSON_GetObjectItem(root, "cityEn");			//城市
         lv_label_set_text(label_city, item->valuestring);

	

		item = cJSON_GetObjectItem(root, "tem");			//温度
        lv_label_set_text(label_tem, item->valuestring);

        item = cJSON_GetObjectItem(root, "wea_img");			//wea
        lv_label_set_text(label_wea_img, item->valuestring);

        item = cJSON_GetObjectItem(root, "humidity");			//wea
        lv_label_set_text(label_humidity, item->valuestring);

        item = cJSON_GetObjectItem(root, "date");
        lv_label_set_text(label_date, item->valuestring);

	}

	cJSON_free(item);

}

二、说明

1、利用了example中的example_connect()函数实现wifi连接,需要做一下配置:

CMakeLists.txt增加:

set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)

 idf.py menuconfig 设置SSID和password

2、生成的BIN大于1M,Partition Table中选择“Single factory app(large)”,可以支持到1.5M

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MagicBox超多功能桌面小电视固件 基于esp32-arduino LVGL的天气时钟、相册、桌面投屏、软路由信息显示、bilibili粉丝等.zip嵌入式优质项目,资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目。 本人单片机开发经验充足,深耕嵌入式领域,有任何使用问题欢迎随时与我联系,我会及时为你解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明,项目具体内容可查看下方的资源详情。 【附带帮助】: 若还需要嵌入式物联网单片机相关领域开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步。 【本人专注嵌入式领域】: 有任何使用问题欢迎随时与我联系,我会及时解答,第一时间为你提供帮助,CSDN博客端可私信,为你解惑,欢迎交流。 【建议小白】: 在所有嵌入式开发中硬件部分若不会画PCB/电路,可选择根据引脚定义将其代替为面包板+杜邦线+外设模块的方式,只需轻松简单连线,下载源码烧录进去便可轻松复刻出一样的项目 【适合场景】: 相关项目设计中,皆可应用在项目开发、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面中 可借鉴此优质项目实现复刻,也可以基于此项目进行扩展来开发出更多功能

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值