Wio Terminal 天气预报仪 Funpack第12期
上电后自动连接WiFi, 并通过心知天气API, 获取天气数据, 并在屏幕上显示
标签
嵌入式系统
willcome0
更新2022-01-10
846

Wio Terminal 天气预报仪

Wio Terminal 介绍

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAd2lsbGNvbWUwMA==,size_20,color_FFFFFF,t_70,g_se,x_16

产品特性
  • 高度集成的设计
    • MCU, LCD, WIFI, BT, IMU, 麦克风, 蜂鸣器, microSD Card, 光传感器, 五向开关, 光传感器和红外发射器 (IR 940nm), 加密验证
  • Microchip ATSAMD51P19 提供支持
    • ARM Cortex-M4F运行速度 120MHz (最高可达200MHz)
    • 4 MB 外置闪存, 192 KB RAM
  • 全面的协议支持
    • SPI, I2C, I2S, ADC, DAC, PWM, UART(Serial)
  • 强大的 无线连接
    • Realtek RTL8720DN 提供支持
    • 双频 2.4Ghz / 5Ghz Wi-Fi (802.11 a/b/g/n)
    • BLE / BLE 5.0
  • USB OTG支持
    • USB 主机
    • USB 客户端
  • 软件支持
    • Arduino
    • MicroPython
    • ArduPy
    • AT Firmware
实现功能介绍

1. 连接WiFi;

2. HTTP访问心知天气API, 获取天气信息;

3. JSON代码解析天气信息;

4. LCD上显示天气;

5. 按键切换显示城市;

 

开发流程

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAd2lsbGNvbWUwMA==,size_20,color_FFFFFF,t_70,g_se,x_16

 

代码解析

1. main中创建任务

void setup() 
{
    Serial.begin(115200);
    LvglInit();

    xTaskCreate(LvglTask,       "Lvgl Task",      1024, NULL, tskIDLE_PRIORITY + 1, &Handle_lvglTask);
    xTaskCreate(GetWeatherTask, "Weather Task",   1024, NULL, tskIDLE_PRIORITY + 2, &Handle_weatherTask);
    xTaskCreate(ScanKeyTask,    "key Task",       1024, NULL, tskIDLE_PRIORITY + 0, &Handle_keyTask);
}

void loop() 
{
    delay(1000);
}

2. 各任务处理内容

void GetWeatherTask(void* pvParameters) 
{
    xSemaphore = xSemaphoreCreateBinary();
    assert(xSemaphore != NULL);
    WifiConncet();
    delay(1000);
    WeatherUi();
    
    for (;;) {
        xSemaphoreTake(xSemaphore, 3000);
        GetWeatherJsonData(CityList[CitySelect]);
        SetWeatherShow(TodayWeather, FutureWeather);
        xSemaphoreGive(xSemaphore);
        delay(5000);
    }
}

void ScanKeyTask(void* pvParameters)
{
    pinMode(WIO_5S_PRESS, INPUT);
    for (;;) {
        if (digitalRead(WIO_5S_PRESS) == LOW) {
            delay(10);
            if (digitalRead(WIO_5S_PRESS) == LOW) {
                xSemaphoreTake(xSemaphore, 3000);
                CitySelect++;
                CitySelect %= sizeof(CityList) / sizeof(CityList[0]);
                GetWeatherJsonData(CityList[CitySelect]);
                SetWeatherShow(TodayWeather, FutureWeather);
                xSemaphoreGive(xSemaphore);
            }
        }
        delay(50);
    }
}

void LvglTask(void* pvParameters) 
{
    for (;;) {
        lv_tick_handler();
        lv_task_handler(); /* let the GUI do its work */
        delay(LVGL_TICK_PERIOD);
    }
}

3. 天气获取

void GetWeatherJsonData(String city)
{
    String today_weather_json  = GetHttpsClinetJsonData(URL[0] + city);
    String air_info_json       = GetHttpsClinetJsonData(URL[1] + city);
    String future_weather_json = GetHttpsClinetJsonData(URL[2] + city);

    if (today_weather_json) {
        deserializeJson(doc, today_weather_json);
        TodayWeather.city        = doc["results"][0]["location"]["name"].as<String>();
        TodayWeather.date.year   = doc["results"][0]["last_update"].as<String>().substring(0, 4).toInt();
        TodayWeather.date.mon    = doc["results"][0]["last_update"].as<String>().substring(5, 7).toInt();
        TodayWeather.date.day    = doc["results"][0]["last_update"].as<String>().substring(8, 10).toInt();
        TodayWeather.weather     = doc["results"][0]["now"]["text"].as<String>();
        TodayWeather.temperature = doc["results"][0]["now"]["temperature"].as<int8_t>();
        TodayWeather.humidity    = doc["results"][0]["now"]["humidity"].as<uint8_t>();
    }

    if (air_info_json) {
        deserializeJson(doc, air_info_json);
        TodayWeather.airQuality  = doc["results"][0]["air"]["city"]["aqi"].as<uint8_t>();
    }
    
    if (future_weather_json) {
        deserializeJson(doc, future_weather_json);
        for (uint8_t i = 0; i < sizeof(FutureWeather) / sizeof(FutureWeatherType); i++) {
            FutureWeather[i].date.year    = doc["results"][0]["daily"][i]["date"].as<String>().substring(0, 4).toInt();
            FutureWeather[i].date.mon     = doc["results"][0]["daily"][i]["date"].as<String>().substring(5, 7).toInt();
            FutureWeather[i].date.day     = doc["results"][0]["daily"][i]["date"].as<String>().substring(8, 10).toInt();
            FutureWeather[i].weatherDay   = doc["results"][0]["daily"][i]["text_day"].as<String>();
            FutureWeather[i].weatherNight = doc["results"][0]["daily"][i]["text_night"].as<String>();
            FutureWeather[i].tempMax      = doc["results"][0]["daily"][i]["high"].as<int8_t>();
            FutureWeather[i].tempMin      = doc["results"][0]["daily"][i]["low"].as<int8_t>();
        }
    }
    PrintWeatherData();
}

4. WiFi连接

static void WifiConncet(void)
{
    Serial.printf("WiFi Firmware Version: %s\n", rpc_system_version());
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(50);

    ui.connect = lv_label_create(lv_scr_act(), NULL);
    lv_obj_align(ui.connect, NULL, LV_ALIGN_CENTER, 0, -60);
    lv_label_set_align(ui.connect, LV_LABEL_ALIGN_CENTER);
    lv_obj_set_style_local_text_font(ui.connect, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &font24);

    if(!WiFi.isConnected()) {
        lv_label_set_text_fmt(ui.connect, "\"%s\" 连接中...", WIFI_SSID);
        lv_obj_align(ui.connect, NULL, LV_ALIGN_CENTER, 0, 0);
        WiFi.begin(WIFI_SSID, WIFI_PWD);
        if (WiFi.isConnected()) {
            lv_label_set_text_fmt(ui.connect, "\"%s\" 连接成功. \nIP: %s",
            WIFI_SSID, WiFi.localIP().toString().c_str());
        }
        else {
            lv_label_set_text_fmt(ui.connect, "\"%s\" 连接失败!", WIFI_SSID);
        }
        lv_obj_align(ui.connect, NULL, LV_ALIGN_CENTER, 0, 0);
    }
}

5. 界面绘制

void WeatherUi(void)
{
    ui.screen = lv_scr_act();
    lv_obj_clean(ui.screen);
    ui.city = lv_label_create(ui.screen, NULL);
    lv_label_set_text(ui.city, "深圳·晴");
    lv_obj_set_style_local_text_font(ui.city, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &font24);
    lv_obj_align(ui.city, NULL, LV_ALIGN_IN_TOP_MID, 0,  15);

    ui.temp = lv_label_create(ui.screen, NULL);
    lv_label_set_align(ui.temp, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(ui.temp, LV_LABEL_LONG_BREAK);
    lv_obj_set_width(ui.temp, 192);
    lv_label_set_text(ui.temp, "99°");
    lv_obj_set_style_local_text_font(ui.temp, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &font48);
    lv_obj_align(ui.temp, NULL, LV_ALIGN_IN_TOP_MID, 10, 50);

    ui.hum = lv_label_create(ui.screen, NULL);
    lv_label_set_text(ui.hum, "湿度: 99");
    lv_obj_set_style_local_text_font(ui.hum, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &font24);
    lv_obj_align(ui.hum, NULL, LV_ALIGN_CENTER, 0, -44);

    ui.aqi = lv_label_create(ui.screen, NULL);
    lv_label_set_text(ui.aqi, "空气质量: 99");
    lv_obj_set_style_local_text_font(ui.aqi, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &font24);
    lv_obj_align(ui.aqi, NULL, LV_ALIGN_CENTER, 0, -8);

    // 画框
    static lv_point_t line_points[4][2] = {
        { {0,  70}, {LV_HOR_RES_MAX,  70} },
        { {0, 100}, {LV_HOR_RES_MAX, 100} },
        { {0, 130}, {LV_HOR_RES_MAX, 130} },
        { {0, 160}, {LV_HOR_RES_MAX, 160} }
    };
    
    static lv_style_t style_line;
    lv_style_init(&style_line);
    lv_style_set_line_width(&style_line, LV_STATE_DEFAULT, 1);
    lv_style_set_line_color(&style_line, LV_STATE_DEFAULT, LV_COLOR_BLACK);
    //lv_style_set_line_rounded(&style_line, LV_STATE_DEFAULT, true);

    lv_obj_t* line[4];
    line[0] = lv_line_create(ui.screen, NULL);
    lv_line_set_points(line[0], line_points[0], 2);     /*Set the points*/
    lv_obj_add_style(line[0], LV_LINE_PART_MAIN, &style_line);     /*Set the points*/
    lv_obj_align(line[0], NULL, LV_ALIGN_CENTER, 0, 0);

    for (uint8_t i = 1; i < sizeof(line) / sizeof(line[0]); i++) {
        line[i] = lv_line_create(ui.screen, line[0]);
        lv_line_set_points(line[i], line_points[i], 2);
    }

    lv_obj_t* future_date_title_l[FUTURE_DAY_COUNT];
    future_date_title_l[0] = lv_label_create(ui.screen, NULL);
    lv_label_set_align(future_date_title_l[0], LV_LABEL_ALIGN_CENTER);
    lv_obj_set_size(future_date_title_l[0], 64, 16);
    lv_obj_set_style_local_text_font(future_date_title_l[0], LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &font16);
    future_date_title_l[1] = lv_label_create(ui.screen, future_date_title_l[0]);
    future_date_title_l[2] = lv_label_create(ui.screen, future_date_title_l[0]);

    // 框中内容
    lv_label_set_text(future_date_title_l[0], "未来三天");
    lv_obj_align(future_date_title_l[0], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 6, -100);

    ui.furDate[0] = lv_label_create(ui.screen, future_date_title_l[0]);
    ui.furDate[1] = lv_label_create(ui.screen, future_date_title_l[0]);
    ui.furDate[2] = lv_label_create(ui.screen, future_date_title_l[0]);

    lv_label_set_text(ui.furDate[0], "11月11日");
    lv_obj_align(ui.furDate[0], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 4, -70);

    lv_label_set_text(ui.furDate[1], "22月22日");
    lv_obj_align(ui.furDate[1], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 4, -40);

    lv_label_set_text(ui.furDate[2], "33月33日");
    lv_obj_align(ui.furDate[2], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 4, -10);
    

    lv_label_set_text(future_date_title_l[1], "温度范围");
    lv_obj_align(future_date_title_l[1], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 90, -100);

    ui.furTemp[0] = lv_label_create(ui.screen, future_date_title_l[0]);
    lv_label_set_long_mode(ui.furTemp[0], LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(ui.furTemp[0], 64);
    ui.furTemp[1] = lv_label_create(ui.screen, ui.furTemp[0]);
    ui.furTemp[2] = lv_label_create(ui.screen, ui.furTemp[0]);

    lv_label_set_text(ui.furTemp[0], "22~11°");
    lv_obj_align(ui.furTemp[0], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 84, -70);

    lv_label_set_text(ui.furTemp[1], " 1~10°");
    lv_obj_align(ui.furTemp[1], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 84, -40);

    lv_label_set_text(ui.furTemp[2], "33~33°");
    lv_obj_align(ui.furTemp[2], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 84, -10);

    lv_label_set_text(future_date_title_l[2], "天气");
    lv_obj_align(future_date_title_l[2], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 180, -100);

    ui.furWeather[0] = lv_label_create(ui.screen, future_date_title_l[0]);
    lv_label_set_long_mode(ui.furWeather[0], LV_LABEL_LONG_SROLL_CIRC);
    lv_obj_set_width(ui.furWeather[0], 64);
    ui.furWeather[1] = lv_label_create(ui.screen, ui.furWeather[0]);
    ui.furWeather[2] = lv_label_create(ui.screen, ui.furWeather[0]);


    lv_label_set_text(ui.furWeather[0], "特大暴雨");
    lv_obj_align(ui.furWeather[0], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 160, -70);

    lv_label_set_text(ui.furWeather[1], "雷阵雨");
    lv_obj_align(ui.furWeather[1], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 160, -40);

    lv_label_set_text(ui.furWeather[2], "晴间多云");
    lv_obj_align(ui.furWeather[2], NULL, LV_ALIGN_IN_BOTTOM_LEFT, 160, -10);
}

6. 天气信息刷新

void SetWeatherShow(TodayWeatherType today, FutureWeatherType future[FUTURE_DAY_COUNT])
{
    lv_label_set_text_fmt(ui.city, "%s·%s", today.city.c_str(), today.weather.c_str());
    lv_label_set_text_fmt(ui.temp, "%d°", today.temperature);
    lv_label_set_text_fmt(ui.hum, "湿度: %d", today.humidity);
    lv_label_set_text_fmt(ui.aqi, "空气质量: %d", today.airQuality);
    for (uint8_t i = 0; i < FUTURE_DAY_COUNT; i++) {
        lv_label_set_text_fmt(ui.furDate[i], "%2d月%02d日", future[i].date.mon, future[i].date.day);
        lv_label_set_text_fmt(ui.furTemp[i], "%d~%d°", future[i].tempMin, future[i].tempMax);
        lv_label_set_text_fmt(ui.furWeather[i], "%s", future[i].weatherDay.c_str());
    }
}

实物展示:

WiFi连接界面:

请添加图片描述

在这里插入图片描述

城市天气显示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

总结

   每次参加funpack活动都能带来不小的收获,这次也不例外。描述页的编辑确实有些难用,希望能有所改进。

 

 

 

 

 

附件下载
firmware.bin
烧录固件
WioTerminal.rar
源码
团队介绍
一位普通的嵌入式软件工程师
团队成员
willcome0
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号