esp32 桌面小电视项目(基于freertos)(六)

目录

前言

主要功能

使用方法

代码

显示效果


前言

本文基于platformio,使用esp32和freertos修改了一个开源作品。

主要功能

主要功能为实时显示当前时间和日期和天气状况。主要分为weather更新任务,button任务,scroller任务,timer任务,Animate任务。

weather任务:每隔5分钟更新天气情况

button任务:检测是否有按键按下,按下就触发重置任务

scroller任务:实时滚动显示当前天气情况,温度等情况。

timer任务:实时显示当前时间

Animate任务:实时右下角刷新动图

使用方法

tft显示屏SLC接D18,SDA接D23,RES接D26,DC接D25,CS接D27,BLK接D22。插上电源开机等待提示手机连接esp32的AP,联入之后填写地区等要素然后结束,esp32自动重启开始联网。

代码

代码如下。

/* *****************************************************************
 *
 *
 *    小型桌面显示器
 *
 * 现作者:大益
 * 原  作  者:指针阿飞
 *
 * 创 建 日 期:2024.4.22
 *
 *
 *
 *
 * *****************************************************************/

/* *****************************************************************
 *  库文件、头文件
 * *****************************************************************/
#include <ArduinoJson.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <WebServer.h>
#include <WiFiUdp.h>
#include <TFT_eSPI.h>
#include <SPI.h>
#include <TJpg_Decoder.h>
#include <EEPROM.h>  //内存
#include <Button2.h> //按钮库
#include <TimeLib.h>
#include <PubSubClient.h>
#include "config.h"                  //配置文件
#include "weatherNum/weatherNum.h"   //天气图库
#include "Animate/Animate.h"         //动画模块
#include "wifiReFlash/wifiReFlash.h" //WIFI功能模块
#include "Animate/img/hutao.h"
#define Version "v1.0.0"
/* *****************************************************************
 *  配置使能位
 * *****************************************************************/

#if WM_EN
#include <WiFiManager.h>
// WiFiManager 参数
WiFiManager wm; // global wm instance
// WiFiManagerParameter custom_field; // global param ( for non blocking w params )
#endif

// 定义按钮引脚
Button2 Button_sw1 = Button2(15);
/* *****************************************************************
 *  字库、图片库
 * *****************************************************************/
#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"
SemaphoreHandle_t xMutex; // 互斥锁句柄
// struct tm timeinfo;
// const long gmtOffset_sec = 8 * 3600;
// const int daylightOffset_sec = 0;
/* *****************************************************************
 *  函数声明
 * *****************************************************************/
void sendNTPpacket(IPAddress &address); // 向NTP服务器发送请求
time_t getNtpTime();                    // 从NTP获取时间
void digitalClockDisplay(int reflash_en);
void printDigits(int digits);
void LCD_reflash();
void clear_screen();
void savewificonfig();         // wifi ssid,psw保存到eeprom
void readwificonfig();         // 从eeprom读取WiFi信息ssid,psw
void deletewificonfig();       // 删除原有eeprom中的信息
void getCityCode();            // 发送HTTP请求并且将服务器响应通过串口输出
void getCityWeater();          // 获取城市天气
void wifi_reset(Button2 &btn); // WIFI重设
void saveParamCallback();
void esp_reset(Button2 &btn);
void scrollBanner();
void weaterData(String *cityDZ, String *dataSK, String *dataFC); // 天气信息写到屏幕上

/* *****************************************************************
 *  参数设置
 * *****************************************************************/
struct config_type
{
  char stassid[32]; // 定义配网得到的WIFI名长度(最大32字节)
  char stapsw[64];  // 定义配网得到的WIFI密码长度(最大64字节)
};
//---------------修改此处""内的信息--------------------
// 如开启WEB配网则可不用设置这里的参数,前一个为wifi ssid,后一个为密码
config_type wificonf = {{"WiFi名"}, {"密码"}};

// 天气更新时间  X 分钟(默认3分钟)
unsigned int updateweater_time = WEATHER_UPDATE_TIME;
// 天气信息
struct Weather_Msg
{
  String cityDZ;
  String dataSK;
  String dataFC;
};

Weather_Msg weather_msg = {{""}, {""}, {""}};
//----------------------------------------------------

// LCD屏幕相关设置
TFT_eSPI tft = TFT_eSPI(); // 引脚请自行配置tft_espi库中的 User_Setup.h文件
TFT_eSprite clk = TFT_eSprite(&tft);
#define LCD_BL_PIN 5 // 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
uint8_t Wifi_en = 1;         // WIFI模块启动  1:打开    0:关闭
uint8_t UpdateWeater_en = 0; // 更新时间标志位
int prevTime = 0;            // 滚动显示更新标志位
int DHT_img_flag = 0;        // DHT传感器使用标志位

// EEPROM参数存储地址位
int BL_addr = 1;    // 被写入数据的EEPROM地址编号  1亮度
int Ro_addr = 2;    // 被写入数据的EEPROM地址编号  2 旋转方向
int DHT_addr = 3;   // 3 DHT使能标志位
int CC_addr = 10;   // 被写入数据的EEPROM地址编号  10城市
int wifi_addr = 30; // 被写入数据的EEPROM地址编号  20wifi-ssid-psw

time_t prevDisplay = 0;       // 显示时间显示记录
int Amimate_reflash_Time = 0; // 更新时间记录
int Amilove_reflash_Time = 0; // 更新时间记录

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

uint32_t targetTime = 0;
String cityCode = "101190402"; // 天气城市代码
int tempnum = 0;               // 温度百分比
int huminum = 0;               // 湿度百分比
int tempcol = 0xffff;          // 温度显示颜色
int humicol = 0xffff;          // 湿度显示颜色

// NTP服务器参数
static const char ntpServerName[] = "ntp3.aliyun.com";
const int timeZone = 8; // 东八区

// wifi连接UDP设置参数
WiFiUDP Udp;
WiFiClient wificlient;
unsigned int localPort = 8000;
float duty = 0;

// 星期
String week()
{
  String wk[7] = {"日", "一", "二", "三", "四", "五", "六"};
  String s = "周" + wk[weekday() - 1];
  return s;
}

// 月日
String monthDay()
{
  String s = String(month());
  s = s + "月" + day() + "日";
  return s;
}

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

// wifi ssid,psw保存到eeprom
void savewificonfig()
{
  // 开始写入
  uint8_t *p = (uint8_t *)(&wificonf);
  for (unsigned int i = 0; i < sizeof(wificonf); i++)
  {
    EEPROM.write(i + wifi_addr, *(p + i)); // 在闪存内模拟写入
  }
  delay(10);
  EEPROM.commit(); // 执行写入ROM
  delay(10);
}

// 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;
}

// 进度条函数
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(Version, 180, 60, 2);
  clk.pushSprite(20, 120); // 窗口位置

  // clk.setTextDatum(CC_DATUM);
  // clk.setTextColor(TFT_WHITE, 0x0000);
  // clk.pushSprite(130,180);

  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();
}

#if WM_EN
// WEB配网LCD显示函数
void Web_win()
{
  clk.setColorDepth(8);

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

  clk.setTextDatum(CC_DATUM); // 设置文本数据
  clk.setTextColor(TFT_GREEN, 0x0000);
  clk.drawString("WiFi Connect Fail!", 100, 10, 2);
  clk.drawString("SSID:", 45, 40, 2);
  clk.setTextColor(TFT_WHITE, 0x0000);
  clk.drawString("AutoConnectAP", 125, 40, 2);
  clk.pushSprite(20, 50); // 窗口位置

  clk.deleteSprite();

}

// WEB配网函数
void Webconfig()
{
  WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP

  delay(3000);
  wm.resetSettings(); // wipe settings

  // add a custom input field
  // int customFieldLength = 40;

  // new (&custom_field) WiFiManagerParameter("customfieldid", "Custom Field Label", "Custom Field Value", customFieldLength,"placeholder=\"Custom Field Placeholder\"");

  // test custom html input type(checkbox)
  //  new (&custom_field) WiFiManagerParameter("customfieldid", "Custom Field Label", "Custom Field Value", customFieldLength,"placeholder=\"Custom Field Placeholder\" type=\"checkbox\""); // custom html type

  // test custom html(radio)
  // const char* custom_radio_str = "<br/><label for='customfieldid'>Custom Field Label</label><input type='radio' name='customfieldid' value='1' checked> One<br><input type='radio' name='customfieldid' value='2'> Two<br><input type='radio' name='customfieldid' value='3'> Three";
  // new (&custom_field) WiFiManagerParameter(custom_radio_str); // custom html input

  const char *set_rotation = "<br/><label for='set_rotation'>显示方向设置</label>\
                              <input type='radio' name='set_rotation' value='0' checked> USB接口朝下<br>\
                              <input type='radio' name='set_rotation' value='1'> USB接口朝右<br>\
                              <input type='radio' name='set_rotation' value='2'> USB接口朝上<br>\
                              <input type='radio' name='set_rotation' value='3'> USB接口朝左<br>";
  WiFiManagerParameter custom_rot(set_rotation); // custom html input
  WiFiManagerParameter custom_bl("LCDBL", "屏幕亮度(1-100)", "10", 3);
#if DHT_EN
  WiFiManagerParameter custom_DHT11_en("DHT11_en", "Enable DHT11 sensor", "0", 1);
#endif
  WiFiManagerParameter custom_weatertime("WeaterUpdateTime", "天气刷新时间(分钟)", "10", 3);
  WiFiManagerParameter custom_cc("CityCode", "城市代码", "0", 9);
  WiFiManagerParameter custom_bir("birthday", "生日", "0", 8);
  WiFiManagerParameter p_lineBreak_notext("<p></p>");

  // wm.addParameter(&p_lineBreak_notext);
  // wm.addParameter(&custom_field);
  wm.addParameter(&p_lineBreak_notext);
  wm.addParameter(&custom_cc);
  wm.addParameter(&p_lineBreak_notext);
  wm.addParameter(&custom_bl);
  wm.addParameter(&p_lineBreak_notext);
  wm.addParameter(&custom_weatertime);
  wm.addParameter(&p_lineBreak_notext);
  wm.addParameter(&custom_rot);
  wm.addParameter(&p_lineBreak_notext);
  wm.addParameter(&custom_bir);
#if DHT_EN
  wm.addParameter(&p_lineBreak_notext);
  wm.addParameter(&custom_DHT11_en);
#endif
  wm.setSaveParamsCallback(saveParamCallback);

  // custom menu via array or vector
  //
  // menu tokens, "wifi","wifinoscan","info","param","close","sep","erase","restart","exit" (sep is seperator) (if param is in menu, params will not show up in wifi page!)
  // const char* menu[] = {"wifi","info","param","sep","restart","exit"};
  // wm.setMenu(menu,6);
  std::vector<const char *> menu = {"wifi", "restart"};
  wm.setMenu(menu);

  // set dark theme
  wm.setClass("invert");

  // set static ip
  //  wm.setSTAStaticIPConfig(IPAddress(10,0,1,99), IPAddress(10,0,1,1), IPAddress(255,255,255,0)); // set static ip,gw,sn
  //  wm.setShowStaticFields(true); // force show static ip fields
  //  wm.setShowDnsFields(true);    // force show dns field always

  // wm.setConnectTimeout(20); // how long to try to connect for before continuing
  //  wm.setConfigPortalTimeout(30); // auto close configportal after n seconds
  // wm.setCaptivePortalEnable(false); // disable captive portal redirection
  // wm.setAPClientCheck(true); // avoid timeout if client connected to softap

  // wifi scan settings
  // wm.setRemoveDuplicateAPs(false); // do not remove duplicate ap names (true)
  wm.setMinimumSignalQuality(20); // set min RSSI (percentage) to show in scans, null = 8%
  // wm.setShowInfoErase(false);      // do not show erase button on info page
  // wm.setScanDispPerc(true);       // show RSSI as percentage not graph icons

  // wm.setBreakAfterConfig(true);   // always exit configportal even if wifi save fails

  bool res;
  // res = wm.autoConnect(); // auto generated AP name from chipid
  res = wm.autoConnect("AutoConnectAP"); // anonymous ap
  //  res = wm.autoConnect("AutoConnectAP","password"); // password protected ap

  while (!res)
    ;
}

String getParam(String name)
{
  // read parameter from server, for customhmtl input
  String value;
  if (wm.server->hasArg(name))
  {
    value = wm.server->arg(name);
  }
  return value;
}

// 删除原有eeprom中的信息
void deletewificonfig()
{
  config_type deletewifi = {{""}, {""}};
  uint8_t *p = (uint8_t *)(&deletewifi);
  for (unsigned int i = 0; i < sizeof(deletewifi); i++)
  {
    EEPROM.write(i + wifi_addr, *(p + i)); // 在闪存内模拟写入
  }
  delay(10);
  EEPROM.commit(); // 执行写入ROM
  delay(10);
}

// 从eeprom读取WiFi信息ssid,psw
void readwificonfig()
{
  uint8_t *p = (uint8_t *)(&wificonf);
  for (unsigned int i = 0; i < sizeof(wificonf); i++)
  {
    *(p + i) = EEPROM.read(i + wifi_addr);
  }
  // EEPROM.commit();
  // ssid = wificonf.stassid;
  // pass = wificonf.stapsw;
  Serial.printf("Read WiFi Config.....\r\n");
  Serial.printf("SSID:%s\r\n", wificonf.stassid);
  Serial.printf("PSW:%s\r\n", wificonf.stapsw);
  Serial.printf("Connecting.....\r\n");
}

void saveParamCallback()
{
  int CCODE = 0, cc;

  Serial.println("[CALLBACK] saveParamCallback fired");
  // Serial.println("PARAM customfieldid = " + getParam("customfieldid"));
  // Serial.println("PARAM CityCode = " + getParam("CityCode"));
  // Serial.println("PARAM LCD BackLight = " + getParam("LCDBL"));
  // Serial.println("PARAM WeaterUpdateTime = " + getParam("WeaterUpdateTime"));
  // Serial.println("PARAM Rotation = " + getParam("set_rotation"));
  // Serial.println("PARAM DHT11_en = " + getParam("DHT11_en"));
  // 将从页面中获取的数据保存

  updateweater_time = getParam("WeaterUpdateTime").toInt();
  cc = getParam("CityCode").toInt();
  LCD_Rotation = getParam("set_rotation").toInt();
  LCD_BL_PWM = getParam("LCDBL").toInt();

  // 对获取的数据进行处理
  // 城市代码
  Serial.print("CityCode = ");
  Serial.println(cc);
  if (((cc >= 101000000) && (cc <= 102000000)) || (cc == 0))
  {
    for (int cnum = 0; cnum < 5; cnum++)
    {
      EEPROM.write(CC_addr + cnum, cc % 100); // 城市地址写入城市代码
      EEPROM.commit();                        // 保存更改的数据
      cc = cc / 100;
      delay(5);
    }
    for (int cnum = 5; cnum > 0; cnum--)
    {
      CCODE = CCODE * 100;
      CCODE += EEPROM.read(CC_addr + cnum - 1);
      delay(5);
    }
    cityCode = CCODE;
  }
  // 屏幕方向
  Serial.print("LCD_Rotation = ");
  Serial.println(LCD_Rotation);
  if (EEPROM.read(Ro_addr) != LCD_Rotation)
  {
    EEPROM.write(Ro_addr, LCD_Rotation);
    EEPROM.commit();
    delay(5);
  }
  tft.setRotation(LCD_Rotation);
  tft.fillScreen(0x0000);
  Web_win();
  loadNum--;
  loading(1);
  if (EEPROM.read(BL_addr) != LCD_BL_PWM)
  {
    EEPROM.write(BL_addr, LCD_BL_PWM);
    EEPROM.commit();
    delay(5);
  }
  // 屏幕亮度
  Serial.printf("亮度调整为:");
  analogWrite(LCD_BL_PIN, 1023 - (LCD_BL_PWM * 10));
  Serial.println(LCD_BL_PWM);
  // 天气更新时间
  Serial.printf("天气更新时间调整为:");
  Serial.println(updateweater_time);
}
#endif

// 发送HTTP请求并且将服务器响应通过串口输出
void getCityCode()
{
  String URL = "http://wgeo.weather.com.cn/ip/?_=" + String(now());
  // 创建 HTTPClient 对象
  HTTPClient httpClient;

  // 配置请求地址。此处也可以不使用端口号和PATH而单纯的
  httpClient.begin(wificlient, URL);

  // 设置请求头中的User-Agent
  httpClient.setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1");
  httpClient.addHeader("Referer", "http://www.weather.com.cn/");

  // 启动连接并发送HTTP请求
  int httpCode = httpClient.GET();
  Serial.print("Send GET request to URL: ");
  Serial.println(URL);

  // 如果服务器响应OK则从服务器获取响应体信息并通过串口输出
  if (httpCode == HTTP_CODE_OK)
  {
    String str = httpClient.getString();

    int aa = str.indexOf("id=");
    if (aa > -1)
    {
      // cityCode = str.substring(aa+4,aa+4+9).toInt();
      cityCode = str.substring(aa + 4, aa + 4 + 9);
      Serial.println(cityCode);
      getCityWeater();
    }
    else
    {
      Serial.println("获取城市代码失败");
    }
  }
  else
  {
    Serial.println("请求城市代码错误:");
    Serial.println(httpCode);
  }

  // 关闭ESP8266与服务器连接
  httpClient.end();
}

// 获取城市天气
void getCityWeater()
{
  // String URL = "http://d1.weather.com.cn/dingzhi/" + cityCode + ".html?_="+String(now());//新
  String URL = "http://d1.weather.com.cn/weather_index/" + cityCode + ".html?_=" + String(now()); // 原来
  // 创建 HTTPClient 对象
  HTTPClient httpClient;

  // httpClient.begin(URL);
  httpClient.begin(wificlient, URL); // 使用新方法

  // 设置请求头中的User-Agent
  httpClient.setUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1");
  httpClient.addHeader("Referer", "http://www.weather.com.cn/");

  // 启动连接并发送HTTP请求
  int httpCode = httpClient.GET();
  Serial.println("正在获取天气数据");
  Serial.println(URL);

  // 如果服务器响应OK则从服务器获取响应体信息并通过串口输出
  if (httpCode == HTTP_CODE_OK)
  {

    String str = httpClient.getString();
    // Serial.println("httpdata=" + str);
    int indexStart = str.indexOf("weatherinfo\":");
    int indexEnd = str.indexOf("};var alarmDZ");

    weather_msg.cityDZ = str.substring(indexStart + 13, indexEnd);
    // Serial.println(jsonCityDZ);

    indexStart = str.indexOf("dataSK =");
    indexEnd = str.indexOf(";var dataZS");
    weather_msg.dataSK = str.substring(indexStart + 8, indexEnd);
    // Serial.println(jsonDataSK);

    indexStart = str.indexOf("\"f\":[");
    indexEnd = str.indexOf(",{\"fa");
    weather_msg.dataFC = str.substring(indexStart + 5, indexEnd);
    // Serial.println(jsonFC);

    // weaterData(&jsonCityDZ, &jsonDataSK, &jsonFC);
    weaterData(&(weather_msg.cityDZ), &(weather_msg.dataSK), &(weather_msg.dataFC));
    Serial.println("获取成功");
  }
  else
  {
    Serial.println("请求城市天气错误:");
    Serial.print(httpCode);
  }

  // 关闭ESP8266与服务器连接
  httpClient.end();
}

String scrollText[7];
// int scrollTextWidth = 0;

// 天气信息写到屏幕上
void weaterData(String *cityDZ, String *dataSK, String *dataFC)
{
  // 解析第一段JSON
  DynamicJsonDocument doc(1024);
  deserializeJson(doc, *dataSK);
  JsonObject sk = doc.as<JsonObject>();

  // TFT_eSprite clkb = TFT_eSprite(&tft);
  String temperature = sk["temp"].as<String>();
  /***绘制相关文字***/
  // if (xSemaphoreTake(xMutex, portMAX_DELAY))
  // {
  //   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(sk["temp"].as<String>() + "℃", 28, 13);
  //   clk.pushSprite(100, 184);
  //   clk.deleteSprite();
  //   xSemaphoreGive(xMutex);
  // }
  tempnum = sk["temp"].as<int>();
  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;
  }
  // if (xSemaphoreTake(xMutex, portMAX_DELAY))
  // {
  //   tempWin();

  //   // 湿度
  //   clk.createSprite(58, 24);
  //   clk.fillSprite(bgColor);
  //   clk.setTextDatum(CC_DATUM);
  //   clk.setTextColor(TFT_WHITE, bgColor);
  //   clk.drawString(sk["SD"].as<String>(), 28, 13);

  //   // clk.drawString("100%",28,13);
  //   clk.pushSprite(100, 214);
  //   clk.deleteSprite();
  //   xSemaphoreGive(xMutex);
  // }
  // String A = sk["SD"].as<String>();
  String huminit = sk["SD"].as<String>();
  huminum = atoi((sk["SD"].as<String>()).substring(0, 2).c_str());

  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;
  // if (xSemaphoreTake(xMutex, portMAX_DELAY))
  // {
  //   humidityWin();

  //   // 城市名称
  //   clk.createSprite(94, 30);
  //   clk.fillSprite(bgColor);
  //   clk.setTextDatum(CC_DATUM);
  //   clk.setTextColor(TFT_WHITE, bgColor);
  //   clk.drawString(sk["cityname"].as<String>(), 44, 16);
  //   clk.pushSprite(15, 15);
  //   clk.deleteSprite();
  //   xSemaphoreGive(xMutex);
  // }
  // PM2.5空气指数
  String cityname = sk["cityname"].as<String>();
  uint16_t pm25BgColor = tft.color565(156, 202, 127); // 优
  String aqiTxt = "优";
  int pm25V = sk["aqi"];
  if (pm25V > 200)
  {
    pm25BgColor = tft.color565(136, 11, 32); // 重度
    aqiTxt = "重度";
  }
  else if (pm25V > 150)
  {
    pm25BgColor = tft.color565(186, 55, 121); // 中度
    aqiTxt = "中度";
  }
  else if (pm25V > 100)
  {
    pm25BgColor = tft.color565(242, 159, 57); // 轻
    aqiTxt = "轻度";
  }
  else if (pm25V > 50)
  {
    pm25BgColor = tft.color565(247, 219, 100); // 良
    aqiTxt = "良";
  }

  scrollText[0] = "实时天气 " + sk["weather"].as<String>();
  scrollText[1] = "空气质量 " + aqiTxt;
  scrollText[2] = "风向 " + sk["WD"].as<String>() + sk["WS"].as<String>();


  // 左上角滚动字幕
  // 解析第二段JSON
  deserializeJson(doc, *cityDZ);
  JsonObject dz = doc.as<JsonObject>();
  // Serial.println(sk["ws"].as<String>());
  // 横向滚动方式
  // String aa = "今日天气:" + dz["weather"].as<String>() + ",温度:最低" + dz["tempn"].as<String>() + ",最高" + dz["temp"].as<String>() + " 空气质量:" + aqiTxt + ",风向:" + dz["wd"].as<String>() + dz["ws"].as<String>();
  // scrollTextWidth = clk.textWidth(scrollText);
  // Serial.println(aa);
  scrollText[3] = "今日" + dz["weather"].as<String>();

  deserializeJson(doc, *dataFC);
  JsonObject fc = doc.as<JsonObject>();

  scrollText[4] = "最低温度" + fc["fd"].as<String>() + "℃";
  scrollText[5] = "最高温度" + fc["fc"].as<String>() + "℃";

  if (xSemaphoreTake(xMutex, portMAX_DELAY))
  {
    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(temperature + "℃", 28, 13);
    clk.pushSprite(100, 184);
    clk.deleteSprite();
    tempWin();
    // 湿度
    clk.createSprite(58, 24);
    clk.fillSprite(bgColor);
    clk.setTextDatum(CC_DATUM);
    clk.setTextColor(TFT_WHITE, bgColor);
    clk.drawString(huminit, 28, 13);
    // clk.drawString("100%",28,13);
    clk.pushSprite(100, 214);
    clk.deleteSprite();
    humidityWin();
    // 城市名称
    clk.createSprite(94, 30);
    clk.fillSprite(bgColor);
    clk.setTextDatum(CC_DATUM);
    clk.setTextColor(TFT_WHITE, bgColor);
    clk.drawString(cityname, 44, 16);
    clk.pushSprite(15, 15);
    clk.deleteSprite();

    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, atoi((sk["weathercode"].as<String>()).substring(1, 3).c_str()));
    clk.unloadFont();
    xSemaphoreGive(xMutex);
  }
}

int currentIndex = 0;
TFT_eSprite clkb = TFT_eSprite(&tft);

void scrollBanner()
{
  // if(millis() - prevTime > 2333) //3秒切换一次
  //  if(second()%2 ==0&& prevTime == 0)
  //  {
  if (scrollText[currentIndex])
  {

    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);
    }
    if (currentIndex >= 5)
      currentIndex = 0; // 回第一个
    else
      currentIndex += 1; // 准备切换到下一个
  }
  prevTime = 1;
  //  }
}
// 用快速线方法绘制数字
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);
  }
}

int Hour_sign = 60;
int Minute_sign = 60;
int Second_sign = 60;
// 日期刷新
void digitalClockDisplay(int reflash_en = 0)
{
  // 时钟刷新,输入1强制刷新
  int now_hour = hour();     // 获取小时
  int now_minute = minute(); // 获取分钟
  int now_second = second(); // 获取秒针
  // 小时刷新
  if ((now_hour != Hour_sign) || (reflash_en == 1))
  {
    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) || (reflash_en == 1))
  {
    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) || (reflash_en == 1)) // 分钟刷新
  {

    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 (reflash_en == 1)
    reflash_en = 0;
  /***日期****/
  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(week(), 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(monthDay(), 49, 16);
    clk.pushSprite(5, 150);
    clk.deleteSprite();

    clk.unloadFont();
    xSemaphoreGive(xMutex);
  }
}

  /*-------- NTP code ----------*/

  const int NTP_PACKET_SIZE = 48;     // NTP时间在消息的前48字节中
  byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming & outgoing packets

  time_t getNtpTime()
  {
    IPAddress ntpServerIP; // NTP server's ip address

    while (Udp.parsePacket() > 0)
      ;
    // discard any previously received packets
    // Serial.println("Transmit NTP Request");
    //  get a random server from the pool
    WiFi.hostByName(ntpServerName, ntpServerIP);
    // Serial.print(ntpServerName);
    // Serial.print(": ");
    // Serial.println(ntpServerIP);

      sendNTPpacket(ntpServerIP);

      uint32_t beginWait = millis();
      while (millis() - beginWait < 1500)
      {
        int size = Udp.parsePacket();

        if (size >= NTP_PACKET_SIZE)
        {
          Serial.println("Receive NTP Response");
          Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
          unsigned long secsSince1900;
          // convert four bytes starting at location 40 to a long integer
          secsSince1900 = (unsigned long)packetBuffer[40] << 24;
          secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
          secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
          secsSince1900 |= (unsigned long)packetBuffer[43];
          // Serial.println(secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR);
          return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
        }
      }
      Serial.println("No NTP Response :-(");
      return 0; // 无法获取时间时返回0
    }

  // 向NTP服务器发送请求
  void sendNTPpacket(IPAddress & address)
  {
    // set all bytes in the buffer to 0
    memset(packetBuffer, 0, NTP_PACKET_SIZE);
    // Initialize values needed to form NTP request
    // (see URL above for details on the packets)
    packetBuffer[0] = 0b11100011; // LI, Version, Mode
    packetBuffer[1] = 0;          // Stratum, or type of clock
    packetBuffer[2] = 6;          // Polling Interval
    packetBuffer[3] = 0xEC;       // Peer Clock Precision
    // 8 bytes of zero for Root Delay & Root Dispersion
    packetBuffer[12] = 49;
    packetBuffer[13] = 0x4E;
    packetBuffer[14] = 49;
    packetBuffer[15] = 52;
    // all NTP fields have been given values, now
    // you can send a packet requesting a timestamp:
    Udp.beginPacket(address, 123); // NTP requests are to port 123
    Udp.write(packetBuffer, NTP_PACKET_SIZE);
    Udp.endPacket();
  }

  void esp_reset(Button2 & btn)
  {
    ESP.restart();
  }

  void wifi_reset(Button2 & btn)
  {
    wm.resetSettings();
    deletewificonfig();
    delay(10);
    Serial.println("重置WiFi成功");
    ESP.restart();
  }

  // 更新时间
  void reflashTime()
  {
    prevDisplay = now();
    // timeClockDisplay(1);
    digitalClockDisplay();
    prevTime = 0;
  }

  // 切换天气 or 空气质量
  void reflashBanner()
  {
    scrollBanner();
  }

  // 打开WIFI
  void openWifi()
  {
    Serial.println("WIFI reset......");
    // WiFi.forceSleepWake(); // wifi on
    Wifi_en = 1;
  }

  // 强制屏幕刷新
  void LCD_reflash()
  {
    reflashTime();
    reflashBanner();
    openWifi();
  }

  // 清空屏幕
  void clear_screen()
  {
    tft.fillScreen(bgColor);
  }

  void currentWeather(void *param)
  {
    Serial.println("weather");
    while (1)
    {
      getCityWeater();
      vTaskDelay(1000 * 60 * 5);
    }
  }

  const uint8_t *Animate_value; // 指向关键帧的指针
  uint32_t Animate_size;        // 指向关键帧大小的指针
  int Animate_key = -1;
  void refresh_AnimatedImage(void *)
  {
    while (1)
    {
      if (millis() - Amimate_reflash_Time > 100) // x ms切换一次
      {
        Animate_key++;
        Amimate_reflash_Time = millis();
        Animate_value = hutao[Animate_key];
        Animate_size = hutao_size[Animate_key];
        if (xSemaphoreTake(xMutex, portMAX_DELAY))
        {
          // Serial.println("xSemaphoreTake in");
          TJpgDec.drawJpg(160, 160, Animate_value, Animate_size);
          xSemaphoreGive(xMutex);
          // Serial.println("xSemaphoreTake out");
        }
        if (Animate_key >= 31)
        {
          Animate_key = -1;
        }
        vTaskDelay(pdMS_TO_TICKS(100));
      }
    }
  }
  void buttonLoop(void *param)
  {
    Serial.println("button");
    while (1)
    {
      Button_sw1.loop();
      vTaskDelay(pdMS_TO_TICKS(500));
    }
  }
  // 切换天气 or 空气质量
  void reflashBanner(void *param)
  {
    Serial.println("scroller");
    while (1)
    {

      scrollBanner();
      vTaskDelay(pdMS_TO_TICKS(5000));
    }
  }
  void currentTime(void *pvParameters)
  {
    Serial.println("time");
    while (1)
    {
      digitalClockDisplay();
      vTaskDelay(pdMS_TO_TICKS(100));
    }
  }
// void syncTime(void *param)
// {

//   while (1)
//   {
//     // if (!getLocalTime(&timeinfo))
//     // {
//     //   Serial.println("Failed to obtain time");
//     //   return;
//     // }
//     // setTime(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900);
//     //getNtpTime();
//     setSyncProvider(getNtpTime);
//     vTaskDelay(pdMS_TO_TICKS(1000 * 60 * 4));
//   }
// }
void setup()
{
    xMutex = xSemaphoreCreateMutex();
    Button_sw1.setLongClickHandler(wifi_reset);
    // 设置消抖时间

    Serial.begin(115200);
    EEPROM.begin(1024);

    // WiFi.forceSleepWake();
    // wm.resetSettings();    //在初始化中使wifi重置,需重新配置WiFi

    // 从eeprom读取背光亮度设置
    if (EEPROM.read(BL_addr) > 0 && EEPROM.read(BL_addr) < 100)
      LCD_BL_PWM = EEPROM.read(BL_addr);
    // 从eeprom读取屏幕方向设置
    if (EEPROM.read(Ro_addr) >= 0 && EEPROM.read(Ro_addr) <= 3)
      LCD_Rotation = EEPROM.read(Ro_addr);

    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);

    targetTime = millis() + 1000;
    readwificonfig(); // 读取存储的wifi信息
    Serial.print("正在连接WIFI ");
    Serial.println(wificonf.stassid);
    WiFi.begin(wificonf.stassid, wificonf.stapsw);

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

    while (WiFi.status() != WL_CONNECTED)
    {
      loading(30);

      if (loadNum >= 194)
      {
// 使能web配网后自动将smartconfig配网失效
#if WM_EN
        Web_win();
        Webconfig();
#endif

#if !WM_EN
        SmartConfig();
#endif
        break;
      }
    }
    delay(10);
    while (loadNum < 194) // 让动画走完
    {
      loading(1);
    }

    if (WiFi.status() == WL_CONNECTED)
    {
      Serial.print("SSID:");
      Serial.println(WiFi.SSID().c_str());
      Serial.print("PSW:");
      Serial.println(WiFi.psk().c_str());
      strcpy(wificonf.stassid, WiFi.SSID().c_str()); // 名称复制
      strcpy(wificonf.stapsw, WiFi.psk().c_str());   // 密码复制
      savewificonfig();
      readwificonfig();
    }

    Serial.print("本地IP: ");
    Serial.println(WiFi.localIP());
    Serial.println("启动UDP");
    Udp.begin(localPort);
    Serial.println("等待同步...");
    setSyncProvider(getNtpTime);
    setSyncInterval(300);
    //getNtpTime();
    //configTime(gmtOffset_sec, daylightOffset_sec, ntpServerName);
    TJpgDec.setJpgScale(1);
    TJpgDec.setSwapBytes(true);
    TJpgDec.setCallback(tft_output);

    int CityCODE = 0;
    for (int cnum = 5; cnum > 0; cnum--)
    {
      CityCODE = CityCODE * 100;
      CityCODE += EEPROM.read(CC_addr + cnum - 1);
      delay(5);
    }
    if (CityCODE >= 101000000 && CityCODE <= 102000000)
      cityCode = CityCODE;
    else
      getCityCode(); // 获取城市代码

    tft.fillScreen(TFT_BLACK);                                  // 清屏
    TJpgDec.drawJpg(15, 183, temperature, sizeof(temperature)); // 温度图标
    TJpgDec.drawJpg(15, 213, humidity, sizeof(humidity));       // 湿度图标

    getCityWeater();

    // WiFi.forceSleepBegin(); // wifi off
    Serial.println("WIFI休眠......");
    Wifi_en = 0;

    xTaskCreatePinnedToCore(currentWeather, "weather", 8192, NULL, 1, NULL, 1);
    xTaskCreatePinnedToCore(refresh_AnimatedImage, "Animate", 2048, NULL, 2, NULL, 1);
    xTaskCreatePinnedToCore(buttonLoop, "button", 2048, NULL, 1, NULL, 1);
    xTaskCreatePinnedToCore(reflashBanner, "scroller", 2048, NULL, 1, NULL, 1);
    xTaskCreatePinnedToCore(currentTime, "timer", 2048, NULL, 3, NULL, 1);
    //xTaskCreatePinnedToCore(syncTime, "syncTime", 2048, NULL, 1, NULL, 1);
  }

void loop()
{

}

显示效果

显示效果如下

参考:指针阿飞

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如何使用ESP32-CAM或ESP32-EYE + Omnivision相机制作小型,低成本的监控摄像头 - 包括应用和设备源。 硬件组件: Seeed ESP-CAM× 1 Espressif ESP-EYE× 1 Espressif Generic ESP32 IC× 1 软件应用程序和在线服务: iOS P2P视频应用 Android P2P视频应用 简单安全的标准监控摄像头 一个简单的用例可能是您希望制作标准的低成本,简单(安全)监控摄像头。标准监控摄像机通常随附大量软件,然后必须通过更新,安全修复等来支持。因此,没有桌面/服务器规模操作系统和运行服务的简化环境因其小得多的攻击面而具有更高的安全性。但如果这还不够,低成本和小尺寸应该说服你。 其他应用中的远程视频输入 我们的许多客户将视频监控摄像头视为独立应用程序,即您安装它并在需要查看远程端发生的情况时将视频流式传输到手机。但是,我们看到越来越多的项目将流式视频作为另一个应用程序的一部分。例如,带有视频流的宠物喂食器,具有音频和视频功能的门铃,可以监控的3D打印机等。 我们开始研发M5Stack ESP32 Cam。它没有额外的外部RAM,而是有一个USB到板上的ESP32 UART,这使得编程更容易(你不必手动操作GPIO0等进入闪存编程模式) M5Stack的问题在于它缺少外部存储器,当你需要流式传输大量数据并快速完成时,你需要保留一个未经确认的数据包缓冲区从相机流向应用程序,如果数据包已准备好重新发送在运输过程中丢失了。此外,您需要从相机缓冲帧缓冲区。当然,这可以进行优化,因此所有内容都使用相同的缓冲区,但这会违反关注点分离原则,并使集成更加困难。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值