#include <Arduino.h> //包含头文件,便于在后面使用Arduino软件框架提供的各类常量和函数等
#include <WiFi.h> //启用wifi功能必须引用的库
#include <esp_sntp.h> //用于设定NTP成功回调函数
#include <FastLED.h> //点亮LED灯珠需要的库,控制LED灯带的发光,有许多预定义好的发光模式,是点亮LED矩阵的基础
#include <FastLED_NeoMatrix.h> //点亮LED矩阵需要的库
#include "my_font.h" //引用文字库头文件
#include <Wire.h> //Arduino自带的I2C库,它提供了一系列函数,使得用户可以很方便地读取和写入I2C设备
#include "RTCDS1307.h" //计时模块RTC (Real Time Clock) module所需库
#include <DHT.h> //DHT22温湿度传感器库文件
//计时模块DS1307相关定义
#define DS1307_ADDR 0x68 //DS1307 I2C 地址
#define DS1307_SDA 18 //DS1307 SDA 引脚连接18
#define DS1307_SCK 19 //DS1307 SCK 引脚连接19
RTCDS1307 rtc(DS1307_ADDR); //主要用于DS1307写入
//DHT22温湿度传感器相关定义
#define DHTPIN 5 //传感器引脚连接5
#define DHTTYPE DHT22
#define TIMEDHT 1000 //每秒钟更新一次温湿度
DHT dht(DHTPIN, DHTTYPE); //初始化温湿度传感器
uint32_t timerDHT = TIMEDHT; //
float humidity, temperature;
//像素阵列定义
#define kMatrixWidth 32 //宽度
#define kMatrixHeight 8 //高度
#define BRIGHTNESS 255 //默认亮度
#define LED_PIN 12 //像素阵列引脚
#define COLOR_ORDER GRB //像素颜色顺序
#define CHIPSET WS2812B //像素控制器型号
#define NUM_LEDS ((kMatrixWidth) * (kMatrixHeight)) //LED灯珠总数
CRGB leds[NUM_LEDS]; ///定义一个LED灯带,用来存每个灯的颜色
FastLED_NeoMatrix *matrix; //LED矩阵需要用这个初始化,点一个灯不需要
//NTP相关定义
#define NTP_SERVER "pool.ntp.org" //使用ntp.org作为NTP服务器,用以实现自动对时
#define UTC_OFFSET 3600 * 8 // 确定时区UTC+8
#define UTC_OFFSET_DST 0 // 用于设置夏时制
//WIFI连接重试数,减少以测试离线模式
#define WIFI_RETRY_COUNT 20
//时间相关定义
uint8_t hour, minute, second, week, month, day;
uint16_t year;
bool period; //上午还是下午
bool need_set_time = false; //是否需要重新设定时间,用于NTP同步成功时触发
struct tm tm_info; //NTP设定时间值
int display_index = 0; //0:时间 1:日期 温度 湿度
int anim_index = 0; //用于动画显示
//NTP时间同步函数,在NTP同步时间时会被调用
void on_time_sync(struct timeval *tv)
{
localtime_r(&tv->tv_sec, &tm_info);
need_set_time = true;
}
//寄存器为BCD格式,需要转换为10进制
uint8_t bcdToDec(uint8_t value)
{
return ((value / 16 * 10) + (value % 16));
}
//通过I2C读取DS1307 RTC寄存器,获取准确时间
void readRtc()
{
Wire.beginTransmission(DS1307_ADDR);
Wire.write(0);
Wire.endTransmission();
Serial.print("I2C device found at address 0x68");
Wire.requestFrom(DS1307_ADDR, 0x07);
second = bcdToDec(Wire.read());
minute = bcdToDec(Wire.read());
hour = bcdToDec(Wire.read() & 0xff);
week = bcdToDec(Wire.read());
day = bcdToDec(Wire.read());
month = bcdToDec(Wire.read());
year = bcdToDec(Wire.read());
year += 2000;
}
void readDHT() //温湿度传感器数值读取
{
// Wait for a time between measurements
if ((millis() - timerDHT) > TIMEDHT) {
// Update the timer
timerDHT = millis();
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
humidity = dht.readHumidity();
// Read temperature as Celsius (the default)
temperature = dht.readTemperature();
// Check if any reads failed and exit early (to try again)
if (isnan(humidity) || isnan(temperature)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
}
}
//像素矩阵初始化
void InitLED_Matrix()
{
// 要配置一共点多少灯,类型是什么,接哪根线上
FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setCorrection(UncorrectedColor); //颜色修正方式,可以不写
FastLED.setTemperature(UncorrectedTemperature); //温度修正方式,可以不写
FastLED.setDither(DISABLE_DITHER); //抖动设置,可以不写
//矩阵的设置
matrix = new FastLED_NeoMatrix(leds, 32, 8, NEO_MATRIX_TOP );
matrix->setTextWrap(false); //设置文字是否自动换行
matrix->setFont(&Picopixel);
matrix->clear();
matrix->setBrightness(BRIGHTNESS);
}
//显示信息
void ShowMessage(char *message, int len, uint32_t color)
{
matrix->clear();
matrix->setTextColor(color);
if (len < 1 || len > 8)
return;
matrix->setCursor(1 + (8 - len) * 2, 5); //设置鼠标光标的位置用以确定在哪里显示文字
matrix->print(message);
matrix->show();
}
//显示Wifi搜索动画
void WifiSearchAnim(int x, int y)
{
for (int i = 0; i < 4; i++)
{
ShowMessage("WiFi", 5, matrix->Color(255, 255, 255));
switch (i)
{
case 3:
matrix->drawPixel(x, y, (uint32_t)0x22ff);
matrix->drawPixel(x + 1, y + 1, (uint32_t)0x22ff);
matrix->drawPixel(x + 2, y + 2, (uint32_t)0x22ff);
matrix->drawPixel(x + 3, y + 3, (uint32_t)0x22ff);
matrix->drawPixel(x + 2, y + 4, (uint32_t)0x22ff);
matrix->drawPixel(x + 1, y + 5, (uint32_t)0x22ff);
matrix->drawPixel(x, y + 6, (uint32_t)0x22ff);
case 2:
matrix->drawPixel(x - 1, y + 2, (uint32_t)0x22ff);
matrix->drawPixel(x, y + 3, (uint32_t)0x22ff);
matrix->drawPixel(x - 1, y + 4, (uint32_t)0x22ff);
case 1:
matrix->drawPixel(x - 3, y + 3, (uint32_t)0x22ff);
case 0:
break;
}
matrix->show();
delay(100);
matrix->clear();
}
}
//显示Wifi连接成功动画
void WifiCheckAnim(int x, int y)
{
int wifiCheckTime = millis();
int wifiCheckPoints = 0;
matrix->setTextColor(matrix->Color(255, 255, 255));
while (millis() - wifiCheckTime < 2000)
{
while (wifiCheckPoints < 7)
{
ShowMessage("WiFi", 5, matrix->Color(255, 255, 255));
switch (wifiCheckPoints)
{
case 6:
matrix->drawPixel(x, y, matrix->Color(0, 0, 255));
case 5:
matrix->drawPixel(x - 1, y + 1, matrix->Color(0, 0, 255));
case 4:
matrix->drawPixel(x - 2, y + 2, matrix->Color(0, 0, 255));
case 3:
matrix->drawPixel(x - 3, y + 3, matrix->Color(0, 0, 255));
case 2:
matrix->drawPixel(x - 4, y + 4, matrix->Color(0, 0, 255));
case 1:
matrix->drawPixel(x - 5, y + 3, matrix->Color(0, 0, 255));
case 0:
matrix->drawPixel(x - 6, y + 2, matrix->Color(0, 0, 255));
break;
}
wifiCheckPoints++;
matrix->show();
// my_Matrix_show();
delay(100);
}
}
}
void weekBarDisplay(int week) //日期
{
week -= 2;
for (int i = 0; i < 7; i++)
{
if (i == 6)
matrix->drawFastHLine(3 + 4 * i, 7, 3, matrix->Color(127, 0, 0));
else
matrix->drawFastHLine(3 + 4 * i, 7, 3, matrix->Color(127, 127, 127));
}
if (week == 6) //周日显示为红色
matrix->drawFastHLine(3 + 4 * week, 7, 3, matrix->Color(255, 0, 0));
else
matrix->drawFastHLine(3 + 4 * week, 7, 3, matrix->Color(255, 255, 255));
}
bool InitDS1307()
{
Wire.begin(DS1307_SDA, DS1307_SCK); //初始化I2C
Wire.beginTransmission(DS1307_ADDR); //向DS1307的I2C地址发送请求
uint8_t retval = Wire.endTransmission(); //如果得到正确回应,则说明在I2C总线上找到DS1307
if (retval == 0)
return true;
else
return false;
}
void setup() {
int count = 0;
Serial.begin(115200);
InitLED_Matrix(); //初始化LED矩阵显示
ShowMessage("BOOT", 4, matrix->Color(255, 0, 255));
InitDS1307();
dht.begin();
WiFi.begin("Wokwi-GUEST", "", 6); //连接到Wokwi默认Wifi
delay(500);
//尝试连接直至超时
while (WiFi.status() != WL_CONNECTED) {
count++;
if (count > WIFI_RETRY_COUNT)
break;
WifiSearchAnim(24, 0);
}
//如果WIFI正确连接,则从NTP服务器尝试自动对时
if (WiFi.status() == WL_CONNECTED)
{
configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
sntp_set_time_sync_notification_cb(on_time_sync); //自动对时成功的话,我们将这个时间更新到DS1307
WifiCheckAnim(27, 1);
ShowMessage("ONLINE", 6, matrix->Color(255, 255, 255));
}
else
{
ShowMessage("OFFLINE", 7, matrix->Color(255, 255, 255));
}
}
void loop() {
char disp_buf[40];
if (need_set_time) //如果需要对时
{
need_set_time = false; //反转对时状态,并从ntp取回来时间并更新ds1307的时间
rtc.setDate(tm_info.tm_year - 100, tm_info.tm_mon + 1, tm_info.tm_mday);
rtc.setTime(tm_info.tm_hour, tm_info.tm_min, tm_info.tm_sec);
}
readRtc(); //将时间从DS1307中取出并显示
readDHT(); //将温湿度参数取出并显示
switch (display_index)
{
case 0:
matrix->clear();
matrix->setTextColor(matrix->Color(0, 255, 0));
matrix->setCursor(3, 5);
sprintf(disp_buf, "%02d:%02d:%02d\0", hour, minute, second);
matrix->print(disp_buf);
weekBarDisplay(week);
matrix->show();
if (second == 30) { //间隔30s后显示时间、温度、湿度
display_index++;
anim_index = 0;
}
break;
case 1:
matrix->clear();
matrix->setTextColor(matrix->Color(0, 255, 0));
matrix->setCursor(3 - anim_index++, 5);
sprintf(disp_buf, "%02d:%02d %02d %04d-%02d-%02d %0.2f'C %0.1f%%\0", hour, minute, second, year, month, day, temperature, humidity);
matrix->print(disp_buf);
weekBarDisplay(week);
matrix->show();
if (anim_index == 89)
{
delay(1000);
display_index = 0;
}
break;
}
delay(50);
}