这是我对大型数字时钟的看法
基于 Brian Lough 的俄罗斯方块图书馆:
https://github.com/witnessmenow/WiFi-Tetris-Clock
https://www.youtube.com/watch?v=ey2mjZ-UQNM
添加了一个 MP3 / WAV 播放器,为这个时钟提供声音
添加了一个 Web 界面以动态更改设置
添加了两个文本代码来显示您的滚动消息
设计了包含音频硬件的 PCB
基于 ESP32,开源固件和硬件
用品
PCB在这里购买: 会说话的时钟主板
I2S音板:Maz98357 I2S;
ESP32 DEVKIT V1 30pin 版本
强烈建议:单行标头 2.54 用作 ESP32 的套接字
HUB75 显示器 64*64(优选)或 32*64 使用 1 或 2(优选)显示器应该是库支持的显示器,请在此处查看要使用的面板
微型 SD 卡
议长
一些电线和一根 USB 电缆用于对 ESP32 进行编程
您还需要安装软件(Arduino IDE)或使用 web界面上传预编译版本
第一次设置时钟。
当您第一次启动时钟时,它不知道您的 WIFI 凭据,它将启动到接入点模式。
它将充当 WIFI 点。使用您的手机、平板电脑或其他支持 WIFI 的设备连接到该接入点。接入点的名称是 GeniusWeb,密码是 Genius
连接到该 Wifi 点后,时钟将启动您可以访问的网络服务器。
如果您在连接串行监视器时启动,它会显示您需要访问的 IP 号码。如果没有,您必须检查您的路由器以找出要使用的 IP 地址。
使用 Web 界面更改设置
您应该更改的第一件事是显示设置,选择您拥有的硬件并按“确认显示类型”您可能必须反复试验地更改它,因为显示有很多变化,而且这些变化不是很一致
接下来,您应该更改 WIFI 凭据以允许时钟访问您的 wifi 网络。更改时钟后,您将不得不重新启动时钟。
如果重新启动使时钟成功访问您的 WIFI 网络,时间和日期将自动更新。但是,请确保将其设置为正确的时区。
时区是一种文本格式,更多信息请参见:时区
哦,不......没有wifi?
如果您没有 WIFI 或您的 WIFI 政策不允许时钟建立连接,那就不理想了。但你仍然可以使用时钟。作为接入点启动后,转到 web界面并手动设置时间。在此版本中,日期不会更新,而是设置为我的生日。
那还有什么呢?
顶部有按钮,每个按钮都用声音编程。按下它,您的声音就会从扬声器中发出。
有滑块可以更改音量、亮度、动画速度、每个(新闻)行情的速度。
有两个代码用于显示滚动消息。每个股票代码最多可包含 100 个字符。















项目代码
/*******************************************************************
Tetris clock that fetches its time Using the EzTimeLibrary
For use with the ESP32 or TinyPICO
* *
Written by Brian Lough
YouTube: https://www.youtube.com/brianlough
Tindie: https://www.tindie.com/stores/brianlough/
Twitter: https://twitter.com/witnessmenow
*******************************************************************/
// ----------------------------
// Standard Libraries - Already Installed if you have ESP32 set up
// ----------------------------
#include <WiFi.h>
// ----------------------------
// Additional Libraries - each one of these will need to be installed.
// ----------------------------
// Enabling this is meant to have a performance
// improvement but its worse for me.
// https://github.com/2dom/PxMatrix/pull/103
//#define double_buffer
#include <PxMatrix.h>
// The library for controlling the LED Matrix
// At time of writing this my changes for the TinyPICO
// Have been merged into the main PxMatrix library,
// but have not been released, so you will need to install
// from Github
//
// If you are using a regular ESP32 you may be able to use
// the library manager version
// https://github.com/2dom/PxMatrix
// Adafruit GFX library is a dependancy for the PxMatrix Library
// Can be installed from the library manager
// https://github.com/adafruit/Adafruit-GFX-Library
#include <TetrisMatrixDraw.h>
// This library draws out characters using a tetris block
// amimation
// Can be installed from the library manager
// https://github.com/toblum/TetrisAnimation
#include <ezTime.h>
// Library used for getting the time and adjusting for DST
// Search for "ezTime" in the Arduino Library manager
// https://github.com/ropg/ezTime
// ---- Stuff to configure ----
// Initialize Wifi connection to the router
char ssid[] = "SSID"; // your network SSID (name)
char password[] = "password"; // your network key
// Set a timezone using the following list
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
#define MYTIMEZONE "Europe/Dublin"
// Sets whether the clock should be 12 hour format or not.
bool twelveHourFormat = true;
// If this is set to false, the number will only change if the value behind it changes
// e.g. the digit representing the least significant minute will be replaced every minute,
// but the most significant number will only be replaced every 10 minutes.
// When true, all digits will be replaced every minute.
bool forceRefresh = true;
// -----------------------------
// ----- Wiring -------
#define P_LAT 22
#define P_A 19
#define P_B 23
#define P_C 18
#define P_D 5
#define P_E 15
#define P_OE 26 //TinyPICO
//#define P_OE 21 //Huzzah32
//#define P_OE 2 // Generic ESP32
// ---------------------
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
hw_timer_t * timer = NULL;
hw_timer_t * animationTimer = NULL;
// PxMATRIX display(32,16,P_LAT, P_OE,P_A,P_B,P_C);
// PxMATRIX display(64,32,P_LAT, P_OE,P_A,P_B,P_C,P_D);
PxMATRIX display(64, 32, P_LAT, P_OE, P_A, P_B, P_C, P_D, P_E);
TetrisMatrixDraw tetris(display); // Main clock
TetrisMatrixDraw tetris2(display); // The "M" of AM/PM
TetrisMatrixDraw tetris3(display); // The "P" or "A" of AM/PM
Timezone myTZ;
unsigned long oneSecondLoopDue = 0;
bool showColon = true;
volatile bool finishedAnimating = false;
bool displayIntro = true;
String lastDisplayedTime = "";
String lastDisplayedAmPm = "";
// This method is needed for driving the display
void IRAM_ATTR display_updater() {
portENTER_CRITICAL_ISR(&timerMux);
display.display(10);
portEXIT_CRITICAL_ISR(&timerMux);
}
// This method is for controlling the tetris library draw calls
void animationHandler()
{
#ifndef double_buffer
portENTER_CRITICAL_ISR(&timerMux);
#endif
// Not clearing the display and redrawing it when you
// dont need to improves how the refresh rate appears
if (!finishedAnimating) {
#ifdef double_buffer
display.fillScreen(tetris.tetrisBLACK);
#else
display.clearDisplay();
#endif
//display.fillScreen(tetris.tetrisBLACK);
if (displayIntro) {
finishedAnimating = tetris.drawText(1, 21);
} else {
if (twelveHourFormat) {
// Place holders for checking are any of the tetris objects
// currently still animating.
bool tetris1Done = false;
bool tetris2Done = false;
bool tetris3Done = false;
tetris1Done = tetris.drawNumbers(-6, 26, showColon);
tetris2Done = tetris2.drawText(56, 25);
// Only draw the top letter once the bottom letter is finished.
if (tetris2Done) {
tetris3Done = tetris3.drawText(56, 15);
}
finishedAnimating = tetris1Done && tetris2Done && tetris3Done;
} else {
finishedAnimating = tetris.drawNumbers(2, 26, showColon);
}
}
#ifdef double_buffer
display.showBuffer();
#endif
}
#ifndef double_buffer
portEXIT_CRITICAL_ISR(&timerMux);
#endif
}
void drawIntro(int x = 0, int y = 0)
{
tetris.drawChar("P", x, y, tetris.tetrisCYAN);
tetris.drawChar("o", x + 5, y, tetris.tetrisMAGENTA);
tetris.drawChar("w", x + 11, y, tetris.tetrisYELLOW);
tetris.drawChar("e", x + 17, y, tetris.tetrisGREEN);
tetris.drawChar("r", x + 22, y, tetris.tetrisBLUE);
tetris.drawChar("e", x + 27, y, tetris.tetrisRED);
tetris.drawChar("d", x + 32, y, tetris.tetrisWHITE);
tetris.drawChar(" ", x + 37, y, tetris.tetrisMAGENTA);
tetris.drawChar("b", x + 42, y, tetris.tetrisYELLOW);
tetris.drawChar("y", x + 47, y, tetris.tetrisGREEN);
}
void drawConnecting(int x = 0, int y = 0)
{
tetris.drawChar("C", x, y, tetris.tetrisCYAN);
tetris.drawChar("o", x + 5, y, tetris.tetrisMAGENTA);
tetris.drawChar("n", x + 11, y, tetris.tetrisYELLOW);
tetris.drawChar("n", x + 17, y, tetris.tetrisGREEN);
tetris.drawChar("e", x + 22, y, tetris.tetrisBLUE);
tetris.drawChar("c", x + 27, y, tetris.tetrisRED);
tetris.drawChar("t", x + 32, y, tetris.tetrisWHITE);
tetris.drawChar("i", x + 37, y, tetris.tetrisMAGENTA);
tetris.drawChar("n", x + 42, y, tetris.tetrisYELLOW);
tetris.drawChar("g", x + 47, y, tetris.tetrisGREEN);
}
void setup() {
Serial.begin(115200);
// Attempt to connect to Wifi network:
Serial.print("Connecting Wifi: ");
Serial.println(ssid);
// Set WiFi to station mode and disconnect from an AP if it was Previously
// connected
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// Do not set up display before WiFi connection
// as it will crash!
// Intialise display library
display.begin(16, SPI_BUS_CLK, 27, SPI_BUS_MISO, SPI_BUS_SS); // TinyPICO
//display.begin(16); // Generic ESP32 including Huzzah
display.flushDisplay();
// Setup timer for driving display
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &display_updater, true);
timerAlarmWrite(timer, 2000, true);
timerAlarmEnable(timer);
yield();
#ifdef double_buffer
display.fillScreen(tetris.tetrisBLACK);
#else
display.clearDisplay();
#endif
// "connecting"
drawConnecting(5, 10);
#ifdef double_buffer
display.showBuffer();
#endif
// Setup EZ Time
setDebug(INFO);
waitForSync();
Serial.println();
Serial.println("UTC: " + UTC.dateTime());
myTZ.setLocation(F(MYTIMEZONE));
Serial.print(F("Time in your set timezone: "));
Serial.println(myTZ.dateTime());
#ifdef double_buffer
display.fillScreen(tetris.tetrisBLACK);
#else
display.clearDisplay();
#endif
// "Powered By"
drawIntro(6, 12);
#ifdef double_buffer
display.showBuffer();
#endif
delay(2000);
// Start the Animation Timer
tetris.setText("TINY PICO");
animationTimer = timerBegin(1, 80, true);
timerAttachInterrupt(animationTimer, &animationHandler, true);
timerAlarmWrite(animationTimer, 100000, true);
timerAlarmEnable(animationTimer);
// Wait for the animation to finish
while (!finishedAnimating)
{
delay(10); //waiting for intro to finish
}
delay(2000);
finishedAnimating = false;
displayIntro = false;
tetris.scale = 2;
}
void setMatrixTime() {
String timeString = "";
String AmPmString = "";
if (twelveHourFormat) {
// Get the time in format "1:15" or 11:15 (12 hour, no leading 0)
// Check the EZTime Github page for info on
// time formatting
timeString = myTZ.dateTime("g:i");
//If the length is only 4, pad it with
// a space at the beginning
if (timeString.length() == 4) {
timeString = " " + timeString;
}
//Get if its "AM" or "PM"
AmPmString = myTZ.dateTime("A");
if (lastDisplayedAmPm != AmPmString) {
Serial.println(AmPmString);
lastDisplayedAmPm = AmPmString;
// Second character is always "M"
// so need to parse it out
tetris2.setText("M", forceRefresh);
// Parse out first letter of String
tetris3.setText(AmPmString.substring(0, 1), forceRefresh);
}
} else {
// Get time in format "01:15" or "22:15"(24 hour with leading 0)
timeString = myTZ.dateTime("H:i");
}
// Only update Time if its different
if (lastDisplayedTime != timeString) {
Serial.println(timeString);
lastDisplayedTime = timeString;
tetris.setTime(timeString, forceRefresh);
// Must set this to false so animation knows
// to start again
finishedAnimating = false;
}
}
void handleColonAfterAnimation() {
// It will draw the colon every time, but when the colour is black it
// should look like its clearing it.
uint16_t colour = showColon ? tetris.tetrisWHITE : tetris.tetrisBLACK;
// The x position that you draw the tetris animation object
int x = twelveHourFormat ? -6 : 2;
// The y position adjusted for where the blocks will fall from
// (this could be better!)
int y = 26 - (TETRIS_Y_DROP_DEFAULT * tetris.scale);
tetris.drawColon(x, y, colour);
}
void loop() {
unsigned long now = millis();
if (now > oneSecondLoopDue) {
// We can call this often, but it will only
// update when it needs to
setMatrixTime();
showColon = !showColon;
// To reduce flicker on the screen we stop clearing the screen
// when the animation is finished, but we still need the colon to
// to blink
if (finishedAnimating) {
handleColonAfterAnimation();
}
oneSecondLoopDue = now + 1000;
}
}
【Arduino 动手做】WiFi-会说话的俄罗斯方块时钟
项目链接:https://www.instructables.com/Talking-Tetris-Clock/
项目作者:emdee401
项目视频:
https://www.youtube.com/watch?v=XYWxpT7TInU
https://www.youtube.com/watch?v=ey2mjZ-UQNM
https://content.instructables.com/FCS/8JVW/L27URWGB/FCS8JVWL27URWGB.gif?format=mp4
项目代码:https://github.com/witnessmenow/WiFi-Tetris-Clock
https://github.com/witnessmenow/WiFi-Tetris-Clock/blob/master/ESP32%20or%20TinyPICO/EzTimeTetrisClockESP32/EzTimeTetrisClockESP32.ino
https://github.com/donnersm/TalkingClock/tree/main/Arduino%20Sketch/V1.0
俄罗斯方块动画:https://github.com/toblum/TetrisAnimation




评论