首页 > 编程语言 >SmallDesktopDisplay V1.4.3 学习记录 程序基本流程

SmallDesktopDisplay V1.4.3 学习记录 程序基本流程

时间:2023-02-25 06:46:41浏览次数:65  
标签:clk int 流程 SmallDesktopDisplay 线程 V1.4 Serial include 该库

SmallDesktopDisplay V1.4.3 学习记录


声明:原作者:Misaka;修改:微车游;再次修改:丘山鹤
项目地址:https://github.com/SmallDesktopDisplay-team/SmallDesktopDisplay
本文引用上述作者代码仅作为学习使用

1. 文件认知

库文件

点击查看代码
#include <ArduinoJson.h>
/*
ArduinoJson.h 是一种用于 Arduino 平台的 C++ 库,用于处理 JSON(JavaScript 对象表示法)数据格式。该库提供了一种简单且灵活的方式来解析、生成和操作 JSON 数据,使得在 Arduino 上处理 JSON 数据变得更加容易。

以下是该库的一些特点和功能:

该库具有极低的内存占用,使得 Arduino 上的 JSON 处理更加高效。库使用动态内存分配(Dynamic Memory Allocation)来减少内存占用,同时还提供了一种 StaticJsonBuffer 类,用于在编译时分配内存,从而更加有效地利用 RAM。
该库支持标准的 JSON 数据格式,并提供了多种方式来读取和写入 JSON 数据,包括对象(Object)、数组(Array)、布尔值(Boolean)、整数(Integer)、浮点数(Float)和字符串(String)。
该库支持 JSON 数据的嵌套,允许在 JSON 对象中包含 JSON 对象或 JSON 数组,使得处理更加灵活。
该库提供了多种方式来访问 JSON 数据,包括使用点号(.)访问对象的属性,使用索引访问数组元素,使用迭代器访问数组等等。
该库提供了一种方便的方式来将 JSON 数据打印为字符串,并支持多种格式选项,包括缩进、空格和换行符等。
*/

#include <TimeLib.h>
/*
TimeLib.h 是 Arduino 平台上的一个 C++ 库,用于处理日期和时间。该库提供了一组函数和结构体,用于管理日期和时间,可以方便地获取当前日期和时间、设置日期和时间,以及进行日期和时间的计算和比较。

以下是该库的一些特点和功能:

该库支持日期和时间的多种表示方式,包括 Unix 时间戳、年月日时分秒、星期几等。
该库提供了一组函数来获取当前日期和时间,包括 now()、hour()、minute()、second() 等。
该库提供了一组函数来设置日期和时间,包括 setTime()、setDate()、setDay() 等。
该库提供了一组函数来进行日期和时间的计算和比较,包括 timeDiff()、dayOfWeek()、daysInMonth() 等。
该库还提供了一些常量和枚举类型,用于表示月份、星期几等。
使用 TimeLib.h 库可以使得 Arduino 平台上的日期和时间处理更加方便和高效。如果你需要在 Arduino 上进行日期和时间的计算和比较,该库值得一试。
*/

#include <ESP8266WiFi.h>
/*
ESP8266WiFi.h 是一个用于 ESP8266 芯片的 C++ 库,提供了一组函数和类,用于在 Arduino IDE 中进行 Wi-Fi 连接。使用该库可以轻松地连接到 Wi-Fi 网络,实现无线通信和互联网接入。

以下是该库的一些特点和功能:

该库提供了一组函数和类来管理 Wi-Fi 连接,包括连接、断开连接、获取连接状态等。
该库支持多种 Wi-Fi 加密方式,包括 WPA、WPA2、WEP 等。
该库支持静态 IP 和 DHCP IP 分配方式,可以根据需要进行选择。
该库提供了一些辅助函数,例如 WiFi.localIP()、WiFi.SSID()、WiFi.RSSI() 等,可以方便地获取本地 IP、SSID 和信号强度等信息。
该库还支持 mDNS(多播 DNS)协议,可以通过局域网的主机名进行访问,而不需要 IP 地址。
使用 ESP8266WiFi.h 库可以使得在 ESP8266 上进行 Wi-Fi 连接变得更加方便和高效。如果你需要在 ESP8266 上实现无线通信和互联网接入,该库值得一试。
*/

#include <ESP8266HTTPClient.h>
/*
ESP8266HTTPClient.h 是一个用于 ESP8266 芯片的 C++ 库,提供了一组函数和类,用于在 Arduino IDE 中进行 HTTP 客户端请求。使用该库可以轻松地进行 HTTP 请求和响应,实现数据的传输和通信。

以下是该库的一些特点和功能:

该库提供了一组函数和类来进行 HTTP 请求,包括 GET、POST、PUT、DELETE 等请求方法。
该库支持 HTTP 和 HTTPS 协议,可以进行安全通信。
该库支持基本认证和摘要认证方式,可以进行身份验证。
该库支持重定向和重试机制,可以自动处理请求过程中出现的问题。
该库提供了一些辅助函数,例如 available()、read()、write() 等,可以方便地读写 HTTP 响应数据。
使用 ESP8266HTTPClient.h 库可以使得在 ESP8266 上进行 HTTP 请求变得更加方便和高效。如果你需要在 ESP8266 上进行数据传输和通信,该库值得一试。
*/

#include <ESP8266WebServer.h>
/*
ESP8266WebServer.h 是一个用于 ESP8266 芯片的 C++ 库,提供了一组函数和类,用于在 Arduino IDE 中实现 Web 服务器。使用该库可以轻松地创建 Web 服务器,实现网络通信和控制。

以下是该库的一些特点和功能:

该库提供了一组函数和类来创建 Web 服务器,包括 ESP8266WebServer 类和一些成员函数。
该库支持多种 HTTP 请求方法,包括 GET、POST、PUT、DELETE 等方法。
该库支持多种 HTTP 响应类型,包括 HTML、CSS、JavaScript、JSON 等类型。
该库支持请求参数和表单数据的处理,可以进行参数解析和处理。
该库支持基本认证和摘要认证方式,可以进行身份验证。
该库提供了一些辅助函数,例如 send()、send_P()、serveStatic() 等,可以方便地处理 HTTP 请求和响应。
使用 ESP8266WebServer.h 库可以使得在 ESP8266 上创建 Web 服务器变得更加方便和高效。如果你需要在 ESP8266 上实现网络通信和控制,该库值得一试。
*/

#include <WiFiUdp.h>
/*
WiFiUdp.h 是一个用于 ESP8266 芯片的 C++ 库,提供了一组函数和类,用于在 Arduino IDE 中进行 UDP 通信。使用该库可以轻松地实现 ESP8266 的 UDP 数据传输和接收。

以下是该库的一些特点和功能:

该库提供了一组函数和类来进行 UDP 数据传输和接收,包括 WiFiUDP 类和一些成员函数。
该库支持 UDP 协议,可以进行数据传输和接收。
该库支持广播和单播方式,可以根据需要进行选择。
该库提供了一些辅助函数,例如 begin()、endPacket()、parsePacket() 等,可以方便地进行 UDP 数据传输和接收。
使用 WiFiUdp.h 库可以使得在 ESP8266 上进行 UDP 数据传输和接收变得更加方便和高效。如果你需要在 ESP8266 上实现 UDP 通信,该库值得一试。
*/

#include <TFT_eSPI.h>
/*
TFT_eSPI.h 是一个 C++ 库,可以用于在 ESP8266 和 ESP32 微控制器上控制 TFT 显示器。它提供了许多函数,可以实现绘制图形和文本,以及其他一些高级功能。

以下是该库的一些主要特点和功能:

该库支持许多常见的 TFT 显示器类型,包括 ILI9341、ST7735、SSD1306 等等,这些类型的 TFT 显示器具有不同的尺寸和分辨率。
该库支持多种颜色模式,包括 RGB565、RGB888 等等,可以根据需要进行选择。
该库支持在 TFT 显示器上绘制文本、图形、图像和动画等等。
该库支持触摸屏输入,可以处理触摸事件并对其做出响应。
该库提供了许多调试工具,可以帮助开发人员调试代码和排除错误。
使用 TFT_eSPI.h 库可以轻松地控制 TFT 显示器,实现各种图形和文本的绘制。如果你需要在 ESP8266 或 ESP32 上使用 TFT 显示器,该库值得一试。
*/

#include <SPI.h>
/*
SPI.h 是一个 C++ 库,可以用于在 Arduino 微控制器上实现 SPI(串行外设接口)通信。SPI 是一种串行通信协议,它可以用于连接多个外设,并在它们之间传输数据。

以下是该库的一些主要特点和功能:

该库提供了一组函数和类来进行 SPI 通信,包括 SPI 类和一些成员函数。
该库支持多种 SPI 模式,可以根据需要进行选择。
该库支持主从模式,可以作为 SPI 主设备或从设备使用。
该库支持多种数据传输方式,包括 MSB(最高位优先)和 LSB(最低位优先)。
该库提供了一些辅助函数,例如 begin()、beginTransaction()、transfer() 等,可以方便地进行 SPI 通信。
使用 SPI.h 库可以使得在 Arduino 微控制器上进行 SPI 通信变得更加方便和高效。如果你需要在 Arduino 上连接多个外设并进行数据传输,该库值得一试。
*/

#include <TJpg_Decoder.h>
/*
TJpg_Decoder.h 是一个 C++ 库,可以用于在 Arduino 微控制器上解码 JPEG 图像。JPEG 是一种广泛使用的图像压缩格式,可以大幅度减小图像文件的大小。

以下是该库的一些主要特点和功能:

该库可以从 SD 卡或其他存储设备中读取 JPEG 图像,并将其解码为 RGB 像素数组。
该库可以处理多种 JPEG 图像格式,包括 Baseline(基线)、Progressive(渐进)和 Lossless(无损)。
该库可以处理 JPEG 图像的多种颜色空间,包括 YCbCr、RGB 和灰度等等。
该库提供了一些调节选项,例如图像缩放、旋转、镜像等等。
该库可以将解码后的 RGB 像素数组显示在 TFT 显示器上。
使用 TJpg_Decoder.h 库可以轻松地在 Arduino 微控制器上解码 JPEG 图像,并将其显示在 TFT 显示器上。如果你需要在 Arduino 上显示 JPEG 图像,该库值得一试。
*/

#include <EEPROM.h>                 //内存
/*
EEPROM.h 是一个 C++ 库,可以用于在 Arduino 微控制器上读写 EEPROM 存储器。EEPROM(电可擦可编程只读存储器)是一种非易失性存储器,可以存储少量数据,并且可以在需要时读取和写入数据。

以下是该库的一些主要特点和功能:

该库提供了一组函数来读写 EEPROM 存储器中的数据,包括 read()、write()、update() 等等。
该库支持存储器地址范围从 0 到 EEPROM 存储器大小的范围。
该库可以以字节、整数、浮点数等不同的格式读写数据。
该库支持对 EEPROM 存储器进行批量擦除和写入操作。
使用 EEPROM.h 库可以方便地读写 EEPROM 存储器中的数据,这对于许多嵌入式项目来说是非常有用的。如果你需要在 Arduino 上存储一些配置数据或状态信息,该库值得一试。注意,在某些 Arduino 微控制器上,EEPROM 存储器的大小是有限制的,因此在使用该库时应该注意存储器的大小限制。
*/

#include <Button2.h>                //按钮库
/*
Button2.h 是一个 C++ 库,可以用于在 Arduino 微控制器上实现按钮的检测和处理。该库可以检测按钮的按下、释放和长按等事件,并且可以通过回调函数或事件来处理这些事件。

以下是该库的一些主要特点和功能:

该库支持多种按钮类型,包括常闭(NC)、常开(NO)和反转(FLIP)等。
该库支持设置按钮的电平极性,可以根据需要进行选择。
该库支持检测按钮的按下、释放和长按等事件,并且可以设置长按时间和重复间隔等参数。
该库可以通过回调函数或事件来处理按钮事件,可以灵活地进行处理。
该库可以处理多个按钮,并且可以在一个回调函数中处理多个按钮事件。
使用 Button2.h 库可以方便地检测和处理按钮事件,这对于许多嵌入式项目来说是非常有用的。如果你需要在 Arduino 上实现按钮功能,该库值得一试。
*/

#include <Thread.h>                 //协程
/*
Thread.h 是一个 C++ 库,可以用于在 Arduino 微控制器上实现多线程编程。该库可以创建和管理多个线程,每个线程都可以在独立的上下文中运行,并且可以通过互斥量和信号量等机制进行线程间通信和同步。

以下是该库的一些主要特点和功能:

该库可以创建和管理多个线程,并且可以指定线程的优先级和堆栈大小等参数。
该库可以通过互斥量和信号量等机制进行线程间通信和同步。
该库可以通过回调函数或继承 Thread 类来实现线程任务。
该库可以在主循环中调度线程任务,并且可以指定每个线程任务的运行时间和间隔。
该库可以监视和处理线程任务的异常,以确保线程的稳定运行。
使用 Thread.h 库可以方便地实现多线程编程,在某些场景下可以大大提高程序的响应性和并发性。如果你需要在 Arduino 上实现多线程功能,该库值得一试。注意,在某些资源受限的微控制器上,多线程编程可能会带来一些性能和资源方面的限制,因此在使用该库时应该注意这些限制。
*/

#include <StaticThreadController.h> //协程控制
/*
StaticThreadController.h 是一个 C++ 库,是 Thread.h 库的一个补充,用于在 Arduino 微控制器上实现静态线程管理。该库可以创建和管理多个静态线程,静态线程的数量和参数都在编译时确定,并且可以通过互斥量和信号量等机制进行线程间通信和同步。

以下是该库的一些主要特点和功能:

该库可以创建和管理多个静态线程,静态线程的数量和参数都在编译时确定。
该库可以指定每个静态线程的优先级、堆栈大小和任务函数等参数。
该库可以通过互斥量和信号量等机制进行线程间通信和同步。
该库可以在主循环中调度静态线程任务,并且可以指定每个线程任务的运行时间和间隔。
该库可以监视和处理静态线程任务的异常,以确保线程的稳定运行。
使用 StaticThreadController.h 库可以方便地实现静态线程管理,在一些资源受限的微控制器上可以更加高效和可靠。如果你需要在 Arduino 上实现静态线程管理功能,该库值得一试。注意,在使用该库时应该根据实际情况调整静态线程数量和参数等设置,以充分利用微控制器资源并避免资源浪费。
*/
/*
两库异同:
StaticThreadController.h 和 Thread.h 都是用于在 Arduino 微控制器上实现多线程编程的 C++ 库。它们的主要异同如下:

相同点:

都是用于在 Arduino 微控制器上实现多线程编程的 C++ 库。
都支持创建和管理多个线程,并且可以通过互斥量和信号量等机制进行线程间通信和同步。
都可以在主循环中调度线程任务,并且可以指定每个线程任务的运行时间和间隔。
都可以监视和处理线程任务的异常,以确保线程的稳定运行。
不同点:

Thread.h 库支持动态线程管理,可以在运行时创建和销毁线程,而 StaticThreadController.h 则是支持静态线程管理,线程数量和参数都在编译时确定。
Thread.h 库可以通过回调函数或继承 Thread 类来实现线程任务,而 StaticThreadController.h 则是通过指定静态线程的任务函数来实现线程任务。
Thread.h 库在使用上更加灵活,适用于需要动态管理线程的场景,而 StaticThreadController.h 则是更加高效和可靠,适用于需要静态管理线程的场景。
综上,Thread.h 库和 StaticThreadController.h 库都是用于实现多线程编程的工具,具体使用取决于实际需求和场景。如果需要在运行时动态创建和销毁线程,或者需要更加灵活的线程管理方式,可以使用 Thread.h 库;如果需要更加高效和可靠的静态线程管理方式,可以考虑使用 StaticThreadController.h 库。
*/

头文件

点击查看代码
#include "config.h"                  //配置文件
/*
#define Animate_Choice 2      //动图选择:1,太空人图片 2,胡桃
#define TMS 1000              //一千毫秒
#define WM_EN 1               // WEB配网使能标志位----WEB配网打开后会默认关闭smartconfig功能
#define DHT_EN 0              //设定DHT11温湿度传感器使能标志
#define SD_FONT_YELLOW 0xD404 // 黄色字体颜色
#define SD_FONT_WHITE 0xFFFF  // 黄色字体颜色

#define timeY 82 // 定义高度
*/

#include "weatherNum/weatherNum.h"   //天气图库
/*
#ifndef WEATHERNUM_H
#define WEATHERNUM_H

#include <TFT_eSPI.h> 

#include "img/tianqi/t0.h"
#include "img/tianqi/t1.h"
#include "img/tianqi/t2.h"
#include "img/tianqi/t3.h"
#include "img/tianqi/t4.h"
#include "img/tianqi/t5.h"
#include "img/tianqi/t6.h"
#include "img/tianqi/t7.h"
#include "img/tianqi/t9.h"
#include "img/tianqi/t11.h"
#include "img/tianqi/t13.h"
#include "img/tianqi/t14.h"
#include "img/tianqi/t15.h"
#include "img/tianqi/t16.h"
#include "img/tianqi/t18.h"
#include "img/tianqi/t19.h"
#include "img/tianqi/t20.h"
#include "img/tianqi/t26.h"
#include "img/tianqi/t29.h"
#include "img/tianqi/t30.h"
#include "img/tianqi/t31.h"
#include "img/tianqi/t53.h"
#include "img/tianqi/t99.h"


class WeatherNum
{
private:


public:
  void printfweather(int numx,int numy,int numw);
};


#endif

*/

#include "Animate/Animate.h"         //动画模块
/*
#ifndef ANIMATE_h
#define ANIMATE_h

// extern int DHT_img_flag; // DHT传感器使用标志位
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;

void imgAnim(const uint8_t **Animate_value, uint32_t *Animate_size); //动画函数

#endif
*/

#include "wifiReFlash/wifiReFlash.h" //WIFI功能模块
/*

*/

硬件配置

使能位、引脚配置

点击查看代码
#define Version "SDD V1.4.3"
/* *****************************************************************
 *  配置使能位
 * *****************************************************************/

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

#if DHT_EN
#include "DHT.h"
#define DHTPIN 12
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
#endif

//定义按钮引脚
Button2 Button_sw1 = Button2(4);

字体、图片库

点击查看代码
/* *****************************************************************
 *  字库、图片库
 * *****************************************************************/
#include "font/ZdyLwFont_20.h"  //字体库
#include "font/timeClockFont.h" //字体库
#include "img/temperature.h"    //温度图标
#include "img/humidity.h"       //湿度图标

//函数声明
void sendNTPpacket(IPAddress &address); //向NTP服务器发送请求
time_t getNtpTime();                    //从NTP获取时间

// void digitalClockDisplay(int reflash_en);
void printDigits(int digits);
String num2str(int digits);
void LCD_reflash();
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); //天气信息写到屏幕上
void refresh_AnimatedImage();                                    //更新右下角

//创建时间更新函数线程
Thread reflash_time = Thread();
//创建副标题切换线程
Thread reflash_Banner = Thread();
//创建恢复WIFI链接
Thread reflash_openWifi = Thread();
//创建动画绘制线程
Thread reflash_Animate = Thread();

//创建协程池
StaticThreadController<4> controller(&reflash_time, &reflash_Banner, &reflash_openWifi, &reflash_Animate);

//联网后所有需要更新的数据
Thread WIFI_reflash = Thread();

参数设置

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

//天气更新时间  X 分钟
unsigned int updateweater_time = 1;

//----------------------------------------------------

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

//其余状态标志位
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; //更新时间记录

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

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

// NTP服务器参数
static const char ntpServerName[] = "ntp6.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 DHT_EN
//外接DHT11传感器,显示数据
void IndoorTem()
{
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  String s = "内温";
  /***绘制相关文字***/
  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(s, 29, 16);
  clk.pushSprite(172, 150);
  clk.deleteSprite();

  //温度
  clk.createSprite(60, 24);
  clk.fillSprite(bgColor);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(TFT_WHITE, bgColor);
  clk.drawFloat(t, 1, 20, 13);
  //  clk.drawString(sk["temp"].as<String>()+"℃",28,13);
  clk.drawString("℃", 50, 13);
  clk.pushSprite(170, 184);
  clk.deleteSprite();

  //湿度
  clk.createSprite(60, 24);
  clk.fillSprite(bgColor);
  clk.setTextDatum(CC_DATUM);
  clk.setTextColor(TFT_WHITE, bgColor);
  //  clk.drawString(sk["SD"].as<String>(),28,13);
  clk.drawFloat(h, 1, 20, 13);
  clk.drawString("%", 50, 13);
  // clk.drawString("100%",28,13);
  clk.pushSprite(170, 214);
  clk.deleteSprite();
}
#endif

#if !WM_EN
//微信配网函数
void SmartConfig(void)
{
  WiFi.mode(WIFI_STA); //设置STA模式
  // tft.pushImage(0, 0, 240, 240, qr);
  tft.pushImage(0, 0, 240, 240, qr);
  Serial.println("\r\nWait for Smartconfig..."); //打印log信息
  WiFi.beginSmartConfig();                       //开始SmartConfig,等待手机端发出用户名和密码
  while (1)
  {
    Serial.print(".");
    delay(100);                 // wait for a second
    if (WiFi.smartConfigDone()) //配网成功,接收到SSID和密码
    {
      Serial.println("SmartConfig Success");
      Serial.printf("SSID:%s\r\n", WiFi.SSID().c_str());
      Serial.printf("PSW:%s\r\n", WiFi.psk().c_str());
      break;
    }
  }
  loadNum = 194;
}
#endif

String SMOD = ""; // 0亮度
//串口调试设置函数
void Serial_set()
{
  String incomingByte = "";
  if (Serial.available() > 0)
  {
    while (Serial.available() > 0) //监测串口缓存,当有数据输入时,循环赋值给incomingByte
    {
      incomingByte += char(Serial.read()); //读取单个字符值,转换为字符,并按顺序一个个赋值给incomingByte
      delay(2);                            //不能省略,因为读取缓冲区数据需要时间
    }
    if (SMOD == "0x01") //设置1亮度设置
    {
      int LCDBL = atoi(incomingByte.c_str()); // int n = atoi(xxx.c_str());//String转int
      if (LCDBL >= 0 && LCDBL <= 100)
      {
        EEPROM.write(BL_addr, LCDBL); //亮度地址写入亮度值
        EEPROM.commit();              //保存更改的数据
        delay(5);
        LCD_BL_PWM = EEPROM.read(BL_addr);
        delay(5);
        SMOD = "";
        Serial.printf("亮度调整为:");
        analogWrite(LCD_BL_PIN, 1023 - (LCD_BL_PWM * 10));
        Serial.println(LCD_BL_PWM);
        Serial.println("");
      }
      else
        Serial.println("亮度调整错误,请输入0-100");
    }
    if (SMOD == "0x02") //设置2地址设置
    {
      int CityCODE = 0;
      int CityC = atoi(incomingByte.c_str()); // int n = atoi(xxx.c_str());//String转int
      if (((CityC >= 101000000) && (CityC <= 102000000)) || (CityC == 0))
      {
        for (int cnum = 0; cnum < 5; cnum++)
        {
          EEPROM.write(CC_addr + cnum, CityC % 100); //城市地址写入城市代码
          EEPROM.commit();                           //保存更改的数据
          CityC = CityC / 100;
          delay(5);
        }
        for (int cnum = 5; cnum > 0; cnum--)
        {
          CityCODE = CityCODE * 100;
          CityCODE += EEPROM.read(CC_addr + cnum - 1);
          delay(5);
        }

        cityCode = CityCODE;

        if (cityCode == "0")
        {
          Serial.println("城市代码调整为:自动");
          getCityCode(); //获取城市代码
        }
        else
        {
          Serial.printf("城市代码调整为:");
          Serial.println(cityCode);
        }
        Serial.println("");
        getCityWeater(); //更新城市天气
        SMOD = "";
      }
      else
        Serial.println("城市调整错误,请输入9位城市代码,自动获取请输入0");
    }
    if (SMOD == "0x03") //设置3屏幕显示方向
    {
      int RoSet = atoi(incomingByte.c_str());
      if (RoSet >= 0 && RoSet <= 3)
      {
        EEPROM.write(Ro_addr, RoSet); //屏幕方向地址写入方向值
        EEPROM.commit();              //保存更改的数据
        SMOD = "";
        //设置屏幕方向后重新刷屏并显示
        tft.setRotation(RoSet);
        tft.fillScreen(0x0000);
        LCD_reflash(); //屏幕刷新程序
        UpdateWeater_en = 1;
        TJpgDec.drawJpg(15, 183, temperature, sizeof(temperature)); //温度图标
        TJpgDec.drawJpg(15, 213, humidity, sizeof(humidity));       //湿度图标

        Serial.print("屏幕方向设置为:");
        Serial.println(RoSet);
      }
      else
      {
        Serial.println("屏幕方向值错误,请输入0-3内的值");
      }
    }
    if (SMOD == "0x04") //设置天气更新时间
    {
      int wtup = atoi(incomingByte.c_str()); // int n = atoi(xxx.c_str());//String转int
      if (wtup >= 1 && wtup <= 60)
      {
        updateweater_time = wtup;
        SMOD = "";
        Serial.printf("天气更新时间更改为:");
        Serial.print(updateweater_time);
        Serial.println("分钟");
      }
      else
        Serial.println("更新时间太长,请重新设置(1-60)");
    }
    else
    {
      SMOD = incomingByte;
      delay(2);
      if (SMOD == "0x01")
        Serial.println("请输入亮度值,范围0-100");
      else if (SMOD == "0x02")
        Serial.println("请输入9位城市代码,自动获取请输入0");
      else if (SMOD == "0x03")
      {
        Serial.println("请输入屏幕方向值,");
        Serial.println("0-USB接口朝下");
        Serial.println("1-USB接口朝右");
        Serial.println("2-USB接口朝上");
        Serial.println("3-USB接口朝左");
      }
      else if (SMOD == "0x04")
      {
        Serial.print("当前天气更新时间:");
        Serial.print(updateweater_time);
        Serial.println("分钟");
        Serial.println("请输入天气更新时间(1-60)分钟");
      }
      else if (SMOD == "0x05")
      {
        Serial.println("重置WiFi设置中......");
        delay(10);
        wm.resetSettings();
        deletewificonfig();
        delay(10);
        Serial.println("重置WiFi成功");
        SMOD = "";
        ESP.restart();
      }
      else
      {
        Serial.println("");
        Serial.println("请输入需要修改的代码:");
        Serial.println("亮度设置输入        0x01");
        Serial.println("地址设置输入        0x02");
        Serial.println("屏幕方向设置输入    0x03");
        Serial.println("更改天气更新时间    0x04");
        Serial.println("重置WiFi(会重启)    0x05");
        Serial.println("");
      }
    }
  }
}

#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 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);
#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"));
//将从页面中获取的数据保存
#if DHT_EN
  DHT_img_flag = getParam("DHT11_en").toInt();
#endif
  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);

#if DHT_EN
  // 是否使用DHT11传感器
  Serial.printf("DHT11传感器:");
  EEPROM.write(DHT_addr, DHT_img_flag);
  EEPROM.commit(); //保存更改的数据
  Serial.println((DHT_img_flag ? "已启用" : "未启用"));
#endif
}
#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();
    int indexStart = str.indexOf("weatherinfo\":");
    int indexEnd = str.indexOf("};var alarmDZ");

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

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

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

    weaterData(&jsonCityDZ, &jsonDataSK, &jsonFC);
    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);

  /***绘制相关文字***/
  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();
  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;
  }
  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();
  // String A = 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;
  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();

  // PM2.5空气指数
  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 = "良";
  }
  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();

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

  // scrollText[6] = atoi((sk["weathercode"].as<String>()).substring(1,3).c_str()) ;

  //天气图标
  wrat.printfweather(170, 15, atoi((sk["weathercode"].as<String>()).substring(1, 3).c_str()));

  //左上角滚动字幕
  //解析第二段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>() + "℃";

  // Serial.println(scrollText[0]);

  clk.unloadFont();
}

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])
  {
    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();

    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))
  {
    drawLineFont(20, timeY, now_hour / 10, 3, SD_FONT_WHITE);
    drawLineFont(60, timeY, now_hour % 10, 3, SD_FONT_WHITE);
    Hour_sign = now_hour;
  }
  //分钟刷新
  if ((now_minute != Minute_sign) || (reflash_en == 1))
  {
    drawLineFont(101, timeY, now_minute / 10, 3, SD_FONT_YELLOW);
    drawLineFont(141, timeY, now_minute % 10, 3, SD_FONT_YELLOW);
    Minute_sign = now_minute;
  }
  //秒针刷新
  if ((now_second != Second_sign) || (reflash_en == 1)) //分钟刷新
  {
    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;
  }

  if (reflash_en == 1)
    reflash_en = 0;
  /***日期****/
  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();
  /***日期****/
}

/*-------- 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.reset();
}

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()
{
#if DHT_EN
  if (DHT_img_flag != 0)
    IndoorTem();
#endif
  scrollBanner();
}

//所有需要联网后更新的方法都放在这里
void WIFI_reflash_All()
{
  if (Wifi_en == 1)
  {
    if (WiFi.status() == WL_CONNECTED)
    {
      Serial.println("WIFI connected");

      // Serial.println("getCityWeater start");
      getCityWeater();
      // Serial.println("getCityWeater end");

      getNtpTime();
      //其他需要联网的方法写在后面

      WiFi.forceSleepBegin(); // Wifi Off
      Serial.println("WIFI sleep......");
      Wifi_en = 0;
    }
    else
    {
      // Serial.println("WIFI unconnected");
    }
  }
}

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

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

// 守护线程池
void Supervisor_controller()
{
  if (controller.shouldRun())
  {
    // Serial.println("controller 启动");
    controller.run();
  }
}

void setup()
{
  Button_sw1.setClickHandler(esp_reset);
  Button_sw1.setLongClickHandler(wifi_reset);
  Serial.begin(115200);
  EEPROM.begin(1024);
  // WiFi.forceSleepWake();
  // wm.resetSettings();    //在初始化中使wifi重置,需重新配置WiFi

#if DHT_EN
  dht.begin();
  //从eeprom读取DHT传感器使能标志
  DHT_img_flag = EEPROM.read(DHT_addr);
#endif
  //从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);

  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();
#if DHT_EN
  if (DHT_img_flag != 0)
    IndoorTem();
#endif

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

  reflash_time.setInterval(300); //设置所需间隔 100毫秒
  reflash_time.onRun(reflashTime);

  reflash_Banner.setInterval(2 * TMS); //设置所需间隔 2秒
  reflash_Banner.onRun(reflashBanner);

  reflash_openWifi.setInterval(updateweater_time * 60 * TMS); //设置所需间隔 10分钟
  reflash_openWifi.onRun(openWifi);

  reflash_Animate.setInterval(TMS / 10); //设置帧率
  reflash_openWifi.onRun(refresh_AnimatedImage);
  controller.run();
}

const uint8_t *Animate_value; //指向关键帧的指针
uint32_t Animate_size;        //指向关键帧大小的指针
void refresh_AnimatedImage()
{
#if Animate_Choice
  if (DHT_img_flag == 0)
  {
    if (millis() - Amimate_reflash_Time > 100) // x ms切换一次
    {
      Amimate_reflash_Time = millis();
      imgAnim(&Animate_value, &Animate_size);
      TJpgDec.drawJpg(160, 160, Animate_value, Animate_size);
    }
  }
#endif
}

``
</details>

标签:clk,int,流程,SmallDesktopDisplay,线程,V1.4,Serial,include,该库
From: https://www.cnblogs.com/withhoney/p/17153661.html

相关文章