esp32-天气时钟项目(五)

前言

本文基于PlatformIO,使用freeRTOS写的简化版天气时钟代码如下,使用了3个task,分别为天气滚动栏目,时间栏目,动画栏目。使用互斥锁避免多个task同时对spi总线读写导致程序崩溃问题。代码如下

#include <TFT_eSPI.h>
#include <SPI.h>
#include <TJpg_Decoder.h>
#include <TimeLib.h>               //配置文件
#include "weatherNum/weatherNum.h" //天气图库
#define timeY 82
#include "Animate/img/hutao.h"
#include<thread>
/* *****************************************************************
 *  字库、图片库
 * *****************************************************************/
#include "font/ZdyLwFont_20.h"  //字体库
#include "font/timeClockFont.h" //字体库
#include "font/Chancery_L_20.h" //字体库
#include "img/temperature.h"    //温度图标
#include "img/humidity.h"       //湿度图标
#include "font/KT_20.h"
#include "soc/rtc_wdt.h"
/* *****************************************************************
 *  函数声明
 * *****************************************************************/
SemaphoreHandle_t xMutex; // 互斥锁句柄
void digitalClockDisplay(int *Hour_sign, int *Minute_sign, int* Second_sign);
//void refresh_AnimatedImage(int Animate_key,int Amimate_reflash_Time);
void scrollBanner(int currentIndex,String * str );
void weaterData(); // 天气信息写到屏幕上
//void imgAnim(int Animate_key,const uint8_t **Animate_value, uint32_t *Animate_size);
//----------------------------------------------------

// LCD屏幕相关设置
TFT_eSPI tft = TFT_eSPI(); // 引脚请自行配置tft_espi库中的 User_Setup.h文件
TFT_eSprite clk = TFT_eSprite(&tft);
#define LCD_BL_PIN 2 // LCD背光引脚
uint16_t bgColor = 0x0000;
uint16_t pinkColor = tft.color565(255, 174, 201);
uint16_t zongseColor = tft.color565(128, 64, 64);
uint16_t whiteColor = tft.color565(245, 246, 247);
uint16_t fhColor = tft.color565(253, 99, 139);
uint16_t blueColor = tft.color565(175, 221, 224);
// 其余状态标志位
int LCD_Rotation = 0; // LCD屏幕方向
int LCD_BL_PWM = 50;   // 屏幕亮度0-100,默认50

/*** Component objects ***/
WeatherNum wrat;

volatile int tempnum = 0;       // 温度百分比
volatile int huminum = 0;        // 湿度百分比
volatile int tempcol = 0xffff;    // 温度显示颜色
volatile int humicol = 0xffff;     // 湿度显示颜色


// 显示页面

/* *****************************************************************
 *  函数
 * *****************************************************************/

// TFT屏幕输出函数
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)
{
    if (y >= tft.height())
        return 0;
    tft.pushImage(x, y, w, h, bitmap);
    // Return 1 to decode next block
    return 1;
}

// 进度条函数
volatile byte loadNum = 6;
void loading(byte delayTime) // 绘制进度条
{
    clk.setColorDepth(8);

    clk.createSprite(200, 100); // 创建窗口
    clk.fillSprite(0x0000);     // 填充率

    clk.drawRoundRect(0, 0, 200, 16, 8, 0xFFFF);     // 空心圆角矩形
    clk.fillRoundRect(3, 3, loadNum, 10, 5, 0xFFFF); // 实心圆角矩形
    clk.setTextDatum(CC_DATUM);                      // 设置文本数据
    clk.setTextColor(TFT_GREEN, 0x0000);
    clk.drawString("Connecting to WiFi......", 100, 40, 2);
    clk.setTextColor(TFT_WHITE, 0x0000);
    clk.drawRightString("v1", 180, 60, 2);
    clk.pushSprite(20, 120); // 窗口位置
    clk.deleteSprite();
    loadNum += 1;
    delay(delayTime);
}

// 湿度图标显示函数
void humidityWin()
{
    clk.setColorDepth(8);

    huminum = huminum / 2;
    clk.createSprite(52, 6);                         // 创建窗口
    clk.fillSprite(0x0000);                          // 填充率
    clk.drawRoundRect(0, 0, 52, 6, 3, 0xFFFF);       // 空心圆角矩形  起始位x,y,长度,宽度,圆弧半径,颜色
    clk.fillRoundRect(1, 1, huminum, 4, 2, humicol); // 实心圆角矩形
    clk.pushSprite(45, 222);                         // 窗口位置
    clk.deleteSprite();
}

// 温度图标显示函数
void tempWin()
{
    clk.setColorDepth(8);

    clk.createSprite(52, 6);                         // 创建窗口
    clk.fillSprite(0x0000);                          // 填充率
    clk.drawRoundRect(0, 0, 52, 6, 3, 0xFFFF);       // 空心圆角矩形  起始位x,y,长度,宽度,圆弧半径,颜色
    clk.fillRoundRect(1, 1, tempnum, 4, 2, tempcol); // 实心圆角矩形
    clk.pushSprite(45, 192);                         // 窗口位置
    clk.deleteSprite();
}


// 天气信息写到屏幕上
void weaterData()
{


    /***绘制相关文字***/
    clk.setColorDepth(8);
    clk.loadFont(ZdyLwFont_20);

    // 温度
    clk.createSprite(58, 24);
    clk.fillSprite(bgColor);
    clk.setTextDatum(CC_DATUM);
    clk.setTextColor(TFT_WHITE, bgColor);
    clk.drawString("21℃", 28, 13);
    clk.pushSprite(100, 184);
    clk.deleteSprite();
    tempnum = 21.4;
    tempnum = tempnum + 10;
    if (tempnum < 10)
        tempcol = 0x00FF;
    else if (tempnum < 28)
        tempcol = 0x0AFF;
    else if (tempnum < 34)
        tempcol = 0x0F0F;
    else if (tempnum < 41)
        tempcol = 0xFF0F;
    else if (tempnum < 49)
        tempcol = 0xF00F;
    else
    {
        tempcol = 0xF00F;
        tempnum = 50;
    }
    tempWin();

    // 湿度
    clk.createSprite(58, 24);
    clk.fillSprite(bgColor);
    clk.setTextDatum(CC_DATUM);
    clk.setTextColor(TFT_WHITE, bgColor);
    clk.drawString("73%", 28, 13);
    // clk.drawString("100%",28,13);
    clk.pushSprite(100, 214);
    clk.deleteSprite();
    // String A = sk["SD"].as<String>();
    huminum = 82;

    if (huminum > 90)
        humicol = 0x00FF;
    else if (huminum > 70)
        humicol = 0x0AFF;
    else if (huminum > 40)
        humicol = 0x0F0F;
    else if (huminum > 20)
        humicol = 0xFF0F;
    else
        humicol = 0xF00F;
    humidityWin();

    // 城市名称
    clk.createSprite(94, 30);
    clk.fillSprite(bgColor);
    clk.setTextDatum(CC_DATUM);
    clk.setTextColor(TFT_WHITE, bgColor);
    clk.drawString("苏州", 44, 16);
    clk.pushSprite(15, 15);
    clk.deleteSprite();

    // PM2.5空气指数
    uint16_t pm25BgColor = tft.color565(156, 202, 127); // 优
    String aqiTxt = "优";
    int pm25V = 53;
    pm25BgColor = tft.color565(247, 219, 100); // 良
    aqiTxt = "良";
    clk.createSprite(56, 24);
    clk.fillSprite(bgColor);
    clk.fillRoundRect(0, 0, 50, 24, 4, pm25BgColor);
    clk.setTextDatum(CC_DATUM);
    clk.setTextColor(0x0000);
    clk.drawString(aqiTxt, 25, 13);
    clk.pushSprite(104, 18);
    clk.deleteSprite();
    // 天气图标
    wrat.printfweather(170, 15, 2);
    clk.unloadFont();
}
TFT_eSprite clkb = TFT_eSprite(&tft);
void scrollBanner(int currentIndex, String *scrollText)
{
    if (xSemaphoreTake(xMutex, portMAX_DELAY))
    {
        clkb.setColorDepth(8);
        clkb.loadFont(ZdyLwFont_20);
        clkb.createSprite(150, 30);
        clkb.fillSprite(bgColor);
        clkb.setTextWrap(false);
        clkb.setTextDatum(CC_DATUM);
        clkb.setTextColor(TFT_WHITE, bgColor);
        clkb.drawString(scrollText[currentIndex], 74, 16);
        clkb.pushSprite(10, 45);
        clkb.deleteSprite();
        clkb.unloadFont();
        xSemaphoreGive(xMutex);
    }
}
// 用快速线方法绘制数字
void drawLineFont(uint32_t _x, uint32_t _y, uint32_t _num, uint32_t _size, uint32_t _color)
{
    uint32_t fontSize;
    const LineAtom *fontOne;
    // 小号(9*14)
    if (_size == 1)
    {
        fontOne = smallLineFont[_num];
        fontSize = smallLineFont_size[_num];
        // 绘制前清理字体绘制区域
        tft.fillRect(_x, _y, 9, 14, TFT_BLACK);
    }
    // 中号(18*30)
    else if (_size == 2)
    {
        fontOne = middleLineFont[_num];
        fontSize = middleLineFont_size[_num];
        // 绘制前清理字体绘制区域
        tft.fillRect(_x, _y, 18, 30, TFT_BLACK);
    }
    // 大号(36*90)
    else if (_size == 3)
    {
        fontOne = largeLineFont[_num];
        fontSize = largeLineFont_size[_num];
        // 绘制前清理字体绘制区域
        tft.fillRect(_x, _y, 36, 90, TFT_BLACK);
    }
    else
        return;

    for (uint32_t i = 0; i < fontSize; i++)
    {
        tft.drawFastHLine(fontOne[i].xValue + _x, fontOne[i].yValue + _y, fontOne[i].lValue, _color);
    }
}

// 日期刷新
void digitalClockDisplay(int *Hour_sign,int *Minute_sign,int *Second_sign)
{
    // 时钟刷新,输入1强制刷新
    int now_hour = hour();     // 获取小时
    int now_minute = minute(); // 获取分钟
    int now_second = second(); // 获取秒针
    // 小时刷新
    if (now_hour != *Hour_sign)
    {
        if (xSemaphoreTake(xMutex, portMAX_DELAY))
        {
            drawLineFont(20, timeY, now_hour / 10, 3, SD_FONT_WHITE);
            drawLineFont(60, timeY, now_hour % 10, 3, SD_FONT_WHITE);
            *Hour_sign = now_hour;
            xSemaphoreGive(xMutex);
        }
    }
    // 分钟刷新
    if (now_minute != *Minute_sign) 
    {
        if (xSemaphoreTake(xMutex, portMAX_DELAY))
        {
            drawLineFont(101, timeY, now_minute / 10, 3, SD_FONT_YELLOW);
            drawLineFont(141, timeY, now_minute % 10, 3, SD_FONT_YELLOW);
            
            *Minute_sign = now_minute;
            xSemaphoreGive(xMutex);
        }
    }
    // 秒针刷新
    if (now_second != *Second_sign) 
    {
        if (xSemaphoreTake(xMutex, portMAX_DELAY))
        {
            drawLineFont(182, timeY + 30, now_second / 10, 2, SD_FONT_WHITE);
            drawLineFont(202, timeY + 30, now_second % 10, 2, SD_FONT_WHITE);
            
            *Second_sign = now_second;
            xSemaphoreGive(xMutex);
        }
    }
    /***日期****/
    if (xSemaphoreTake(xMutex, portMAX_DELAY))
    {
        clk.setColorDepth(8);
        clk.loadFont(ZdyLwFont_20);

        // 星期
        clk.createSprite(58, 30);
        clk.fillSprite(bgColor);
        clk.setTextDatum(CC_DATUM);
        clk.setTextColor(TFT_WHITE, bgColor);
        clk.drawString("周一", 29, 16);
        clk.pushSprite(102, 150);
        clk.deleteSprite();

        // 月日
        clk.createSprite(95, 30);
        clk.fillSprite(bgColor);
        clk.setTextDatum(CC_DATUM);
        clk.setTextColor(TFT_WHITE, bgColor);
        clk.drawString("12月12日", 49, 16);
        clk.pushSprite(5, 150);
        clk.deleteSprite();

        clk.unloadFont();
        xSemaphoreGive(xMutex);
        /***日期****/
    }
}
// 切换天气 or 空气质量
void reflashBanner(void *param)
{
    // UBaseType_t uxHighWaterMark;
    // uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
    int idx = 0;
    Serial.println("reflashBanner");
    Serial.println(xPortGetCoreID());
    String scrollText[7] = {"实时天气 阴", "空气质量 良",
                            "风向 东风2级",
                            "今日 阴",
                            "最低温度 12℃",
                            "最高温度 20℃"};
    while (1)
    {
        scrollBanner(idx++,scrollText);
        if (idx >= 5)
            idx = 0; // 回第一个
        Serial.println("reflashBanner");
        vTaskDelay(pdMS_TO_TICKS(5000));
        // uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
        // Serial.println("reflashBanner uxHighWaterMark:");
        // Serial.println(uxHighWaterMark);
    }
}
void refreshtime(void *param)
{
    // UBaseType_t uxHighWaterMark;
    // uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
    int Hour_sign = 60;
    int Minute_sign = 60;
    int Second_sign = 60;
    Serial.println("refresh time prev");
    Serial.println(xPortGetCoreID());
    while (1)
    {
        digitalClockDisplay(&Hour_sign,&Minute_sign,&Second_sign);
        vTaskDelay(pdMS_TO_TICKS(100));
        // uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
        // Serial.println("refreshtime uxHighWaterMark:");
        // Serial.println(uxHighWaterMark);
    }
}
void refreshAnimatedImage(void *param)
{
    int Amimate_reflash_Time = 1;
    int Animate_key = -1; // 初始化图标显示帧数
    const uint8_t *Animate_value; // 指向关键帧的指针
    uint32_t Animate_size;        // 指向关键帧大小的指针
    // UBaseType_t uxHighWaterMark;
    // uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
    Serial.println("refreshAnimatedImage prev");
    Serial.println(xPortGetCoreID());
    while (1)
    {
        //Serial.println(millis());
        //refresh_AnimatedImage(Animate_key++,Amimate_reflash_Time);
        if (millis() - Amimate_reflash_Time > 100) // x ms切换一次
        {
            Animate_key++;
            Amimate_reflash_Time = millis();
            // Serial.println(Amimate_reflash_Time);
            //imgAnim(Animate_key++, &Animate_value, &Animate_size);
            Animate_value = hutao[Animate_key];
            Animate_size = hutao_size[Animate_key];
            if (xSemaphoreTake(xMutex, portMAX_DELAY))
            {
                TJpgDec.drawJpg(160, 160, Animate_value, Animate_size);
                xSemaphoreGive(xMutex);
                // Serial.println("Animate_size");
            }
            if (Animate_key >= 31)
            {
                Animate_key = -1;
            }
            vTaskDelay(pdMS_TO_TICKS(100));
        // uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);
        // Serial.println("refreshAnimatedImage uxHighWaterMark:");
        // Serial.println(uxHighWaterMark);
        }
    }
}
void func(void *param){

    while(1){
        if (xSemaphoreTake(xMutex, portMAX_DELAY))
        {
            TJpgDec.drawJpg(160, 160, hutao[1], hutao_size[1]);
            vTaskDelay(100);
            xSemaphoreGive(xMutex);
        }
    }
}
void setup()
{
    xMutex = xSemaphoreCreateMutex();
    Serial.begin(115200);

    //disableCore0WDT();
    //hw_timer_t *t = timerBegin(2, 80, true);
    pinMode(LCD_BL_PIN, OUTPUT);
    analogWrite(LCD_BL_PIN, 1023 - (LCD_BL_PWM * 10));

    tft.begin();          /* TFT init */
    tft.invertDisplay(1); // 反转所有显示颜色:1反转,0正常
    tft.setRotation(LCD_Rotation);
    tft.fillScreen(0x0000);
    tft.setTextColor(TFT_BLACK, bgColor);

    TJpgDec.setJpgScale(1);
    TJpgDec.setSwapBytes(true);
    TJpgDec.setCallback(tft_output);
    delay(10);
    while (loadNum < 194) // 让动画走完
    {
        loading(1);
    }

    TJpgDec.setJpgScale(1);
    TJpgDec.setSwapBytes(true);
    TJpgDec.setCallback(tft_output);


    tft.fillScreen(TFT_BLACK);                                  // 清屏
    TJpgDec.drawJpg(15, 183, temperature, sizeof(temperature)); // 温度图标
    TJpgDec.drawJpg(15, 213, humidity, sizeof(humidity));       // 湿度图标
    weaterData();
    Serial.println("weaterData");
    xTaskCreatePinnedToCore(refreshAnimatedImage, "image", 2048, NULL, 1, NULL,1);
    xTaskCreatePinnedToCore(reflashBanner, "scroller", 2048, NULL, 1, NULL,1);
    xTaskCreatePinnedToCore(refreshtime, "time", 2048, NULL, 2, NULL,1);
    //xTaskCreatePinnedToCore(func, "func", 2048,  NULL, 1,NULL,1);
}

void loop()
{
}

实验结果如下

遇到的问题:在实验过程中主要遇到的问题为断言错误如下,通过添加互斥锁就能解决。

assert failed: xQueueGenericSend queue.c:832 (pxQueue->pcHead != ((void *)0) || pxQueue->u.xSemaphor

参考的文献如下: 乐鑫esp32官方文档freertos官网la_fe_ 的CSDN博客

百度网盘链接代码:https://pan.baidu.com/s/1WfqPu8D-bEKfIChEKYpBKw 
提取码:gl9t

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以提供一些建议,但最终的实现取决于您的需求和技术水平。首先,您需要搜集所需的组件,如esp32,Lvgl,WiFi模块等。其次,您需要确定网络天气时钟的功能,以及如何实现它们。最后,您需要开发和调试代码,以完成您的项目。 ### 回答2: ESP32是一个非常强大的开发板,具有Wi-Fi和蓝牙连接功能。Lvgl是一个开源跨平台的GUI库,可以用于制作各种应用界面。现在,我将用300字中文回答如何使用ESP32和Lvgl来制作一个网络天气时钟。 首先,我们需要搭建ESP32的开发环境。这包括安装Arduino IDE和ESP32的开发库。接下来,我们将安装并配置Lvgl库。此外,我们还需要安装Json库,用于解析天气数据。 然后,我们需要通过Wi-Fi连接到互联网。我们可以使用ESP32的内置Wi-Fi模块来连接到无线网络,并获取天气数据。我们需要预先获取一个天气API的密钥,并根据API文档中的要求获取天气数据。 接下来,我们需要设计和创建界面。我们可以使用Lvgl库中的各种控件来创建时钟和天气显示。可以创建一个时钟控件,并使用时间库获取当前时间。然后,我们可以创建一个文本框来显示天气数据。 最后,我们需要使用Json库解析天气数据,并将其显示在界面上。我们可以使用ESP32的HTTP客户端库来发送API请求,并使用Json库解析和获取所需的天气数据。 在代码编写完成后,我们可以将代码烧录到ESP32开发板中,并通过串口监视器来进行调试和查看输出。 总结起来,使用ESP32和Lvgl来制作一个网络天气时钟需要完成以下步骤:搭建开发环境、连接到Wi-Fi和获取天气数据、创建界面、解析天气数据、编写和烧录代码。通过这个过程,我们可以实现一个功能强大的网络天气时钟。 ### 回答3: 使用ESP32和LVGL编写网络天气时钟是一种将网络天气数据与时钟功能结合的创新方案。ESP32是一款功能强大的物联网开发板,而LVGL是一款开源的图形库,能够提供丰富的用户界面功能。 首先,我们可以通过ESP32连接到互联网,使用WiFi或者以太网连接,以获取实时的天气数据。通过与天气API进行通信,可以获取到当地的天气信息,例如温度、湿度、风速等。 然后,利用LVGL的图形库功能,我们可以在屏幕上创建一个美观的时钟界面,并实时显示当前的时间和日期。可以使用LVGL中的时钟小部件来显示时间,并使用合适的字体和样式来提高用户体验。 此外,我们可以利用LVGL的图表功能来显示天气趋势。通过将天气数据以图表的形式表示出来,用户可以直观地了解未来一段时间的天气变化。 另外,LVGL还提供了按钮、滑块等用户交互控件,我们可以利用这些控件来增加一些额外的功能,例如用户可以通过按钮切换不同城市的天气信息,或者调整屏幕亮度等。 最后,将ESP32和LVGL进行集成,在开发板上运行我们编写的代码,就可以实现一个功能齐全的网络天气时钟。用户可以通过屏幕直观地获得当前的时间和日期,同时还可以获取实时的天气信息,方便用户决策出门及日常生活。这样的天气时钟不仅提供了时钟的基本功能,还能够通过与网络的结合,提供更多的实用功能,使生活更加便捷。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值