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>