Created
March 20, 2023 09:02
-
-
Save simonjgreen/ddfb79169a28beeb83827a35590e3475 to your computer and use it in GitHub Desktop.
M5Stick C Plus World Clock with M5 Digiclocks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <M5StickCPlus.h> | |
#include <M5UNIT_DIGI_CLOCK.h> | |
#include <Wire.h> | |
#include <WiFi.h> | |
#include "time.h" | |
#include <NTPClient.h> | |
// Wireless details | |
const char* ssid = "SSID"; | |
const char* password = "PSK"; | |
// Screen settings | |
uint8_t clockBrightness = 5; // 3 is good | |
uint8_t tftBrightness = 11; // 8 is good | |
// Define time parameters | |
const char* ntpServer = "pool.ntp.org"; | |
const long gmtOffset_sec = 0; | |
const int daylightOffset_sec = 3600; | |
const int ntpSyncPeriod_min = 120; | |
unsigned long lastNtpSyncTime = 0; | |
unsigned long now = 0; | |
// Create UDP instance for NTP | |
WiFiUDP ntpUDP; | |
NTPClient timeClient(ntpUDP, ntpServer, gmtOffset_sec, daylightOffset_sec); | |
RTC_TimeTypeDef RTC_TimeStruct; | |
RTC_DateTypeDef RTC_DateStruct; | |
// Pins for SPI | |
#define SDA 32 | |
#define SCL 33 | |
// Clock addresses and timezones | |
const uint8_t clockAddresses[] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35}; | |
const int32_t timezoneOffsets[] = {-7, -6, -5, 0, 1, 10}; | |
const size_t clockCount = sizeof(clockAddresses) / sizeof(clockAddresses[0]); | |
static_assert(clockCount == sizeof(timezoneOffsets) / sizeof(timezoneOffsets[0]), "Timezone offsets count must match clock count"); | |
M5UNIT_DIGI_CLOCK Digiclocks[clockCount]; | |
void setup() { | |
M5.begin(); | |
M5.Lcd.setRotation(3); | |
M5.Lcd.fillScreen(BLACK); | |
M5.Axp.EnableCoulombcounter(); | |
M5.Lcd.println("Entering boot"); | |
// Initial sync | |
ntpsync(); | |
now = millis(); | |
lastNtpSyncTime = now; | |
// Init the SPI | |
delay(2000); | |
Wire.begin(SDA, SCL); | |
// Clocks init | |
M5.Lcd.fillScreen(BLACK); | |
M5.Lcd.setCursor(0,0); | |
M5.Lcd.println("Initialising Clocks"); | |
for (size_t i = 0; i < clockCount; i++) { | |
if (Digiclocks[i].begin(&Wire, SDA, SCL, clockAddresses[i])) { | |
M5.Lcd.printf("Clock %d init successful\n", i + 1); | |
} else { | |
M5.Lcd.printf("Clock %d init error\n", i + 1); | |
while (1); | |
} | |
char offsetString[6]; | |
sprintf(offsetString, " T:%d", timezoneOffsets[i]); | |
Digiclocks[i].setString(offsetString); | |
Digiclocks[i].setBrightness(clockBrightness); | |
} | |
M5.Lcd.fillScreen(BLACK); | |
} | |
void ntpsync() { | |
M5.Lcd.fillScreen(BLACK); | |
M5.Lcd.setCursor(0, 0); | |
// Connect to WiFi | |
M5.Lcd.printf("\nConnecting to %s", ssid); | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(ssid, password); // Connect wifi and return connection status. | |
while (WiFi.status() != WL_CONNECTED) { // If the wifi connection fails. | |
delay(500); // delay 0.5s. | |
M5.Lcd.print("."); | |
} | |
M5.Lcd.println("\nCONNECTED!"); | |
// Get time from NTP | |
M5.Lcd.print("Synchronizing time..."); | |
timeClient.begin(); | |
while (timeClient.getEpochTime() < 1000) { | |
M5.Lcd.print("."); | |
timeClient.update(); | |
} | |
time_t epoch_time = timeClient.getEpochTime(); | |
M5.Lcd.printf("\nTime fetched: %u\n", epoch_time); | |
delay(1000); | |
// Set RTC to NTP | |
struct tm timeinfo; | |
gmtime_r(&epoch_time, &timeinfo); | |
RTC_TimeTypeDef rtc_time = { | |
timeinfo.tm_hour, | |
timeinfo.tm_min, | |
timeinfo.tm_sec | |
}; | |
M5.Rtc.SetTime(&rtc_time); | |
M5.Lcd.println("Time synchronized"); | |
RTC_DateTypeDef rtc_date = { | |
timeinfo.tm_year + 1901, // tm_year is years since 1900 | |
timeinfo.tm_mon + 1, // tm_mon is months since January (0 to 11) | |
timeinfo.tm_mday, | |
timeinfo.tm_wday + 1 // tm_wday is days since Sunday (0 to 6) | |
}; | |
M5.Rtc.SetData(&rtc_date); | |
M5.Lcd.println("Date synchronized"); | |
// Drop WiFi once in sync | |
WiFi.disconnect(true); // Disconnect wifi. | |
WiFi.mode(WIFI_OFF); // Set the wifi mode to off. | |
M5.Lcd.println("Wifi Disconnected"); | |
// Pause a moment so the user can see feedback | |
delay (2000); | |
M5.Lcd.fillScreen(BLACK); | |
} | |
void cycleBrightness() { | |
clockBrightness = (clockBrightness % 8) + 1; | |
for (size_t i = 0; i < clockCount; i++) { | |
Digiclocks[i].setBrightness(clockBrightness); | |
} | |
} | |
void showDebug() { | |
// debug display | |
M5.Lcd.setTextSize(1); | |
M5.Lcd.setCursor(65, 0, 2); | |
M5.Axp.ScreenBreath(tftBrightness); | |
M5.Lcd.println("RTC World Clock"); | |
M5.Rtc.GetTime(&RTC_TimeStruct); | |
M5.Rtc.GetData(&RTC_DateStruct); | |
float voltage = M5.Axp.GetBatVoltage(); | |
float percentage = ((voltage - 3.3) / 0.9) * 100; // assuming a 3.3V minimum and 4.2V maximum voltage | |
float chargingCurrent = M5.Axp.GetBatChargeCurrent(); | |
bool charging = chargingCurrent > 0; | |
M5.Lcd.setCursor(0, 15); | |
M5.Lcd.printf("Date: %04d-%02d-%02d\n",RTC_DateStruct.Year, RTC_DateStruct.Month,RTC_DateStruct.Date); | |
M5.Lcd.printf("Time: %02d : %02d : %02d\n",RTC_TimeStruct.Hours, RTC_TimeStruct.Minutes, RTC_TimeStruct.Seconds); | |
M5.Lcd.printf("Battery: %.2fV %.2f%% \n", voltage, percentage); | |
M5.Lcd.println(charging ? "Charging: Yes" : "Charging: No "); | |
M5.Lcd.printf("Clock Brightness: %u\n", clockBrightness); | |
M5.Lcd.print("Offsets: "); | |
for (size_t i = 0; i < clockCount; i++) { | |
M5.Lcd.printf("%+d", timezoneOffsets[i]); | |
if (i < clockCount - 1) { | |
M5.Lcd.print(" "); | |
} | |
} | |
} | |
void loop() { | |
static unsigned long lastUpdateTime = 0; | |
static char clockBuffers[clockCount][6] = {"88:88"}; | |
static char prevClockBuffers[clockCount][6] = {"88:88"}; | |
now = millis(); | |
M5.Rtc.GetTime(&RTC_TimeStruct); | |
// Actions on buttons | |
M5.update(); | |
M5.BtnA.wasPressed() ? cycleBrightness() : (void)0; | |
M5.BtnB.wasPressed() ? ntpsync() : (void)0; | |
// If the requisite number of minutes have passed resync NTP | |
(now - lastNtpSyncTime >= ntpSyncPeriod_min * 60000) ? ntpsync() : (void)0; | |
if (now - lastUpdateTime >= 1000) { | |
lastUpdateTime = now; | |
showDebug(); | |
// Set each clock buffer against the RTC and push if they have changed | |
for (size_t i = 0; i < clockCount; i++) { | |
int adjusted_hour = (RTC_TimeStruct.Hours + timezoneOffsets[i]) % 24; | |
int adjusted_min = RTC_TimeStruct.Minutes; | |
sprintf(clockBuffers[i], "%02d:%02d", adjusted_hour, adjusted_min); | |
if (strcmp(clockBuffers[i], prevClockBuffers[i]) != 0) { | |
Digiclocks[i].setString(clockBuffers[i]); | |
strcpy(prevClockBuffers[i], clockBuffers[i]); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment