Closed spiff72 closed 4 years ago
Update: I unplugged from the computer and plugged into a charger, and I have already lost 15 minutes after just an hour or so. Something doesn't seem to work right. I might go back to your original code and try that again - maybe something I did to the code is causing this...
UPDATE: I loaded the unmodified code (other than the centering code for the 12hr) on a TTGO T-display last night, and this morning it was 15 minutes slow again. I didn't include any other pieces of my code modifications.
Sorry, as I said, life got in the way - This is troubling thou, so I'll conduct some tests here and see if I can pin point what is happen.
I really have no other thoughts about it right now
I am sure that the controller will loose time over a period of 24 hours, but with updates coming from the NTP I wouldn't think the loss would be that big - so troubling. I'll get mine hooked up and see what I can find out.
Again - no worries! I did some more experimenting and found that if I intentionally turn off wifi so the TTGO can't connect, it seems to get stuck in the reconnect() function and won't come back out, and since it can't seem to exit that function loop, it never reconnects to wifi. I am wondering if that was what was happening when I would find the serial monitor blank/unresponsive when coming back to it after a day.
I cludged together a change that lets it reconnect when wifi is available again, and I have been testing that iteration here. So far, it hasn't lost any time yet (but I may just be lucky and the condition that causes the lost time hasn't happened). It is just really weird that it is exactly 15 minutes slow every time i found it like this. I will flash the original code back again and see if I can get the lost time to occur again - it didn't take long last time i tried.
Here is the full code of what I am running now if it is useful. I made a function called wifi_reconnect() that checks for wifi, and IF available, it does the MQTT reconnect() function. Otherwise it goes into an attempt to reconnect to wifi. This also includes the centering of the 12hr clock and the fixes for the graphics glitch, and my backlight PWM dimmer.
/*
Cheerlights Clock v7.2
based on @martinbateman clock code
https://t.co/1gRc56wNOE
https://pastebin.com/4Ec6d4xY
and updated v6 test code
https://pastebin.com/N2bH9m50
added cheerlight code back in from v6 above. - LeRoy Miller July 27
added TTGO_T2 and M5SickC code to main code base using defines
ported to TTGO T2 Board by LeRoy Miller July 25, 2019
added Geolocate Timezone code July 25, 2019
ported to M5StickC LeRoy Miller July 26, 2019
added support for TTGO_TS_144 device Aug 1, 2019
July 27 - updated M5StickC display to display weather description and temp.
reformated screen for better display of time, and color and to make room for weather info.
reformated TTGO_T2 screen to fit brief weather and temps.
TO DO: add M5StackC Real Time Clock support
* Idea add SPIFFs support, OTA support (Added July 30, 2019 LeRoy Miller)
* add a Wifi Manager to update OTA
* Update API Key with no reprogramming (done OTA update SPIFFS see https://github.com/me-no-dev/arduino-esp32fs-plugin for info on how to install, use https://github.com/me-no-dev/arduino-esp32fs-plugin/releases to get the tool)
* Make for 24 hour (current) or 12 hour time (done July 30, 2019 LeRoy Miller)
* Make temperatures either C or F (done July 30, 2019)
Aug 1 - added TTGO_TS_144 board, changed how the weather and geolocation work (no longer update lat/lon on each loop), added some spaces after weather brief.
Aug 5 - added date to TTGO_T_DISPLAY
Aug 8/9 - Applied @vmensik's fix for artifacts on all boards.
see https://www.thingiverse.com/thing:3777859/comments for more info.
*/
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <PubSubClient.h>
#include <SPI.h>
#include <ESPmDNS.h>
#include <ArduinoOTA.h>
#include "FS.h"
#include "SPIFFS.h"
//Define your board type DEFAULT to TTGO_T_Display
//#define TTGO_T2 1
//#define M5Stick_C 1
//#define TTGO_TS_144 1
//Define your temperature units Default will be Celsius
#define Fahrenheit //comment out for Celsius
//Define 24 hour time (default) or 12 hour time
#define HOUR12 //comment out for 24 hour time
#ifdef TTGO_T2
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1331.h>
#define sclk 14
#define mosi 13
#define cs 15
#define rst 4
#define dc 16
#define TFT_BL -1 // Display backlight control pin
// Color definitions
#define TFT_BLACK 0x0000
#define TFT_BLUE 0x001F
#define TFT_RED 0xF800
#define TFT_GREEN 0x07E0
#define TFT_CYAN 0x07FF
#define TFT_MAGENTA 0xF81F
#define TFT_YELLOW 0xFFE0
#define TFT_WHITE 0xFFFF
Adafruit_SSD1331 tft = Adafruit_SSD1331(cs, dc, mosi, sclk, rst);
#elif M5Stick_C
#include <M5StickC.h>
#define TFT_BL -1 // Display backlight control pin
// Color definitions
#define TFT_BLACK 0x0000
#define TFT_BLUE 0x001F
#define TFT_RED 0xF800
#define TFT_GREEN 0x07E0
#define TFT_CYAN 0x07FF
#define TFT_MAGENTA 0xF81F
#define TFT_YELLOW 0xFFE0
#define TFT_WHITE 0xFFFF
#elif TTGO_TS_144
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#define TFT_CS 16
#define TFT_RST 9
#define TFT_DC 17
#define TFT_SCLK 5 // set these to be whatever pins you like!
#define TFT_MOSI 23 // set these to be whatever pins you like!
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
#define TFT_BL -1 // Display backlight control pin
#else
#include <TFT_eSPI.h>
#define TFT_BL 4 // Display backlight control pin
TFT_eSPI tft = TFT_eSPI();
#endif
// Replace with your network credentials
const char* ssid = "xxxx"; //home wifi credentials
const char* password = "xxxx"; //home wifi credentials
const char* mqtt_server = "simplesi.cloud";
// openweathermap.org key
String WEATHERKEY;
// temp in celcius
double temperature = 0.0;
//Weather Statement
String weatherStatement;
// offset in seconds
int gmtOffset_sec = 0;
//Original Code
int cheer_red = 0;
int cheer_green = 0;
int cheer_blue = 0;
unsigned int rgb565Decimal = 0x8410;
unsigned int newrgb565Decimal;
String colourString = "";
String newColourString;
String strData;
String topicStr;
const int freq = 5000;
const int ledChannel = 3;
const int resolution = 8;
const int BLDutyCycle = 92; //PWM duty cycle for display backlight
int UpCount = 0;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
//NTPClient timeClient(ntpUDP, "us.pool.ntp.org");
WiFiClient espClient;
PubSubClient client(espClient);
String payload;
double lat, lon;
SemaphoreHandle_t serialMutex = NULL;
void updateNTP (void *pvParams);
void updateScreen (void *pvParams);
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived in topic: ");
Serial.println(topic);
topicStr = topic;
Serial.print("Message:");
strData = "";
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
strData += (char)payload[i];
}
Serial.println();
Serial.println("-----------------------");
if (topicStr.endsWith("cheerlights/rgb565Decimal")) {
colourString = newColourString;
rgb565Decimal = strData.toInt();
Serial.println("*******");
Serial.println(rgb565Decimal);
}
if (topicStr.endsWith("cheerlights")) {
newColourString = "\n" + strData + " "; //newColourString = "Cheerlights:\n" + strData;
//sixteenBitHex = newSixteenBitHex;
Serial.println(strData);
}
} // end callback
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP32Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str())) {
Serial.println("connected");
client.subscribe("cheerlights",1);
client.subscribe("cheerlights/rgb565Decimal",1);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin (115200);
serialMutex = xSemaphoreCreateMutex ();
if (TFT_BL > 0) {
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
}
if (!SPIFFS.begin()) {
Serial.println("Failed to mount file system");
return;
}
if (!loadConfig()) {
Serial.println("Failed to load config");
} else {
Serial.println("Config loaded");
}
#ifdef TTGO_T2
tft.begin();
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW, TFT_BLACK); // Note: the new fonts do not draw the background colour
#elif M5Stick_C
M5.begin();
M5.Lcd.setRotation(3);
M5.Lcd.fillScreen(TFT_BLACK);
M5.Lcd.setTextColor(TFT_YELLOW, TFT_BLACK); // Note: the new fonts do not draw the background colour
#elif TTGO_TS_144
tft.initR(INITR_144GREENTAB);
tft.setRotation(3);
tft.fillScreen(ST7735_BLACK);
#else
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_YELLOW, TFT_BLACK); // Note: the new fonts do not draw the background colour
#endif
Serial.print("Configuring PWM for TFT backlight...");
ledcSetup(ledChannel, freq, resolution);
ledcAttachPin(TFT_BL, ledChannel);
Serial.println("DONE");
Serial.print("Setting PWM for TFT backlight to default intensity... ");
ledcWrite(ledChannel, BLDutyCycle);
Serial.println("DONE");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print (".");
}
Serial.println(WiFi.localIP());
//OTA Code
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
timeClient.begin();
geolocation(); // guess where I am
getWeather(); //because we want weather, and dont need to update or lat/lon or timezone each loop.
timeClient.setTimeOffset(gmtOffset_sec);
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
xTaskCreate (updateNTP, "NTP Client", 4096, NULL, 2, NULL);
xTaskCreate (updateScreen, "Screen", 4096, NULL, 1, NULL);
}
void wifi_reconnect() {
//check to see if wifi is still connected (and to connect if not currently connected)
if ( WiFi.status() == WL_CONNECTED )
{
// WiFi is UP, do what ever
reconnect(); //MQTT reconnect if needed
} else
{
// wifi down, reconnect here
WiFi.begin(ssid, password);
int WLcount = 0;
while (WiFi.status() != WL_CONNECTED && WLcount < 200 )
{
delay( 100 );
Serial.printf(".");
if (UpCount >= 60) // just keep terminal from scrolling sideways
{
UpCount = 0;
Serial.printf("\n");
}
++UpCount;
++WLcount;
}
}
}
void loop() {
if (!client.connected()) {
wifi_reconnect();
}
client.loop();
ArduinoOTA.handle();
}
void getJson(String url) {
if (WiFi.status() == WL_CONNECTED) { //Check WiFi connection status
HTTPClient http; //Declare an object of class HTTPClient
http.begin(url); //Specify request destination
int httpCode = http.GET(); //Send the request
if (httpCode > 0) { //Check the returning code
payload = http.getString(); //Get the request response payload
}
http.end(); //Close connection
}
}
/*
* This function will use the external IP to get a Latitude and Longitude
* which will be used for many different things for ISS tracker.
* The website http://ip-api.com is used for this.
* https://github.com/kd8bxp/M5Stack-ISS-Tracker-Updated/tree/master/M5Stack_ISS_Tracker_Updated
*/
int geolocation(){
String url = "http://ip-api.com/json";
getJson(url);
const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
DynamicJsonDocument jsonBuffer(1024);
DeserializationError error = deserializeJson (jsonBuffer, payload);
if (error){
Serial.println ("deserializeJson () failed");
}
lat = jsonBuffer["lat"];
lon = jsonBuffer["lon"];
}
void getWeather() {
String url = "http://api.openweathermap.org/data/2.5/weather?lat=" + String(lat) + "&lon=" + String(lon) + "&appid=" + WEATHERKEY;
Serial.println(url);
getJson(url);
const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
DynamicJsonDocument jsonBuffer(1024);
DeserializationError error = deserializeJson (jsonBuffer, payload);
error = deserializeJson (jsonBuffer, payload);
if (error){
Serial.println ("deserializeJson () failed");
}
gmtOffset_sec = jsonBuffer["timezone"]; //maybe causing a problem on new calls (?) may only need this one time (?)
temperature = jsonBuffer["main"]["temp"];
temperature = temperature - 273.0;
//JsonObject obj = jsonBuffer.as<JsonObject>();
weatherStatement = jsonBuffer["weather"][0]["description"].as<String>();
weatherStatement += " ";
Serial.println(temperature);
Serial.println(weatherStatement);
}
void updateNTP (void *pvParameters) {
(void) pvParameters;
for (;;) {
while(!timeClient.update()) {
timeClient.forceUpdate();
}
if (xSemaphoreTake (serialMutex, (TickType_t)10) == pdTRUE) {
setTime (timeClient.getEpochTime ());
xSemaphoreGive (serialMutex);
Serial.println("Time Updated");
Serial.println(timeClient.getFormattedTime()); //added for debugging
}
vTaskDelay ((1000/portTICK_PERIOD_MS) * 60 * 15); // update every 15 minutes.
getWeather(); //geolocation (); // update based on location
Serial.println("Weather Updated");
}
}
void updateScreen (void *pvParameters) {
(void) pvParameters;
for (;;) {
if (xSemaphoreTake (serialMutex, (TickType_t)10) == pdTRUE) {
char timeString[25];
char colourString2[25];
colourString.toCharArray(colourString2,25);
time_t t = now ();
#ifdef HOUR12
#if defined (TTGO_TS_144) || defined (M5Stick_C)
sprintf (timeString, "%02i:%02i:%02i", hourFormat12(), minute (t), second (t));
#else
sprintf(timeString, "%02i:%02i ", hourFormat12(), minute(t));
#endif
if (isAM()) {
String temp = "AM";
strcat (timeString , temp.c_str()); } else {
String temp = "PM";
strcat(timeString, temp.c_str());}
Serial.println(timeString);
#else
sprintf (timeString, "%02i:%02i:%02i", hour (t), minute (t), second (t));
Serial.println(timeString);
#endif
xSemaphoreGive (serialMutex);
char out[25];
#ifdef Fahrenheit
sprintf (out,"%2.0f`F", (temperature * 9/5)+32);
#else
sprintf (out, "%2.0f`C", temperature);
#endif
String dayArray[9] = {"","Sun","Mon","Tue","Wed","Thr","Fri","Sat"};
String monthArray[14] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
String dateString = dayArray[weekday()] + " " + monthArray[month()] + " " + day() + ", " + year() + " ";
//Serial.print("Date: "); Serial.print(dayArray[weekday()]); Serial.print(" "); Serial.print(monthArray[month()]); Serial.print(" ");Serial.print(day()); Serial.print(", "); Serial.println(year());
#ifdef TTGO_T2
//some screen flashing when updating (?)
tft.setTextSize(2);
tft.setTextColor(0x39C4, TFT_BLACK);
tft.setTextColor(rgb565Decimal, TFT_BLACK);
tft.setCursor(0,0);
tft.print(timeString);
tft.setTextSize(1);
tft.setCursor(0,17);
tft.print(dateString);
//tft.setCursor(0,4);
//tft.setTextSize(2);
//tft.print(colourString2);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(1);
tft.setCursor(0,42);
tft.print(weatherStatement);
tft.setCursor(40,52);
tft.print(out);
#elif M5Stick_C
M5.Lcd.setTextColor(0x39C4, TFT_BLACK);
#ifdef HOUR12
M5.Lcd.setCursor(0,0,4);
#else
M5.Lcd.setCursor(25,0,4);
#endif
M5.Lcd.setTextColor(rgb565Decimal, TFT_BLACK);
M5.Lcd.print(timeString); //tft.drawString (timeString, 10, 10, 7);
M5.Lcd.setCursor(20,25,2);
M5.Lcd.print(dateString);
//M5.Lcd.setCursor(0,1,4);
//M5.Lcd.print(colourString2);
M5.Lcd.setCursor(0,60,2);
M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Lcd.print(weatherStatement);
M5.Lcd.setCursor(118,60,2);
//M5.Lcd.setTextColor(TFT_WHITE);
M5.Lcd.print(out);
#elif TTGO_TS_144
tft.setTextSize(2);
tft.setTextColor(0x39C4, ST7735_BLACK);
tft.setTextColor(rgb565Decimal, ST7735_BLACK);
tft.setCursor(0,0);
tft.print(timeString);
tft.setTextSize(1);
tft.setCursor(12,17);
tft.print(dateString);
//tft.setCursor(0,4);
//tft.setTextSize(2);
//tft.print(colourString2);
tft.setTextColor(ST7735_WHITE, ST7735_BLACK);
tft.setTextSize(1);
tft.setCursor(0,42);
tft.print(weatherStatement);
tft.setCursor(40,72);
tft.print(out);
#else
//check to see if it is midnight, and if so, fill screen with black to eliminate any graphical
//glitches that may have occurred from the changes to day/date line (variable character widths)
if (hour(t)==0 && minute(t)==0 && (second(t)>0 && second(t)<3)) {
tft.fillScreen(TFT_BLACK);
}
tft.setTextColor(0x39C4, TFT_BLACK);
//tft.drawString("88:88:88",10,10,7);
tft.setTextColor(rgb565Decimal, TFT_BLACK);
#ifdef HOUR12
tft.drawString(timeString, 35, 0, 7);
if (isAM()) {
tft.drawString("AM", 175,0,4); } else {
tft.drawString("PM", 175,0,4); }
char temp[5];
sprintf (temp, ":%02i", second (t));
tft.drawString(temp /*":" + (String)second (t)*/, 175, 25, 4);
#else
tft.drawString (timeString, 0, 0, 7);
#endif
tft.drawString(dateString,25,63, 4);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setCursor(0,100,4);
tft.print(weatherStatement);
tft.setCursor(175, 100 ,4);
tft.println(out);
#endif
}
vTaskDelay (1000/portTICK_PERIOD_MS);
}
}
bool loadConfig() {
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return false;
}
size_t size = configFile.size();
if (size > 1024) {
Serial.println("Config file size is too large");
return false;
}
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
// We don't use String here because ArduinoJson library requires the input
// buffer to be mutable. If you don't use ArduinoJson, you may as well
// use configFile.readString instead.
configFile.readBytes(buf.get(), size);
Serial.println(buf.get());
const size_t capacity = JSON_OBJECT_SIZE(1) + 50;
DynamicJsonDocument jsonBuffer(capacity);
DeserializationError error = deserializeJson (jsonBuffer, buf.get());
if (error){
Serial.println ("deserializeJson () failed");
}
WEATHERKEY = jsonBuffer["WEATHERKEY"].as<String>();
return true;
}
I should also add that my home network has been giving me fits occasionally, even after trying two (different) new routers. I have to reboot it every few days lately, so loss of network connectivity could very well be my issue.
Looks like a good change to the code, I'll get that included as well (when I can).
My 1st thought was that your router was dropping out (or the board was), but without testing I couldn't say for sure.
Early in this project I was having issues with my router which was causing me problems, in that case the time never got set - and I don't think it was a disconnect/re-connect issue like you are talking about, I think mine just never connected the 1st time.
My other thought was maybe a hardware issue with your board, but that would be even harder to troubleshoot
And my last thought is maybe a power supply issue (I know that is remote, but what I'm thinking is if the power supply is a little weak, it might cause a small spike, causing the ESP32 to either freeze up or reboot) again pretty hard to troubleshoot - so let's go with testing mine for 24 or so hours and see what happens.
I've had mine running now for about 2 hours and so far so good.
OK - maybe if it doesn't happen to you, try turning off your wifi and turning it back on? I will try that too if I can't replicate the problem again leaving the clock running overnight. I tried this today while it was connected to my phone as a hotspot for a while, and couldn't get it to fail, but I didn't let it go very long with the MQTT reconnect loop running.
I am using this code as an example for gaining more experience on this new board, as I am not very familiar with the ESP32 (i am an amateur coder - no real training), so I am still trying to figure out the code you have in there for the multitasking. I think I have the gist of it though!
Thanks again!
Well, I let it run overnight and no loss of time (with the "original code" running - not my customized version).
I just disabled wifi on my router, let the MQTT reconnect() loop start, and then re-enabled the wifi. Oddly, this time it recovered and reconnected while the same code yesterday failed to exit that reconnect loop. I am stumped at this point.
I'm just over 24 hours at this point (I believe I started mine at 8:30pm yesterday, and it's 10pm now) So far, I haven't seen any loss of time, I did have to reset my router once (different problem) but I reconnected without any problem. I was watching it at midnight this morning just to see if the date would leave artifacts as well - and I didn't see that issue either (not to say that there isn't a problem thou, just catching it on the right day might take a little to catch it) I'm leaning toward a router issue on your side - but it isn't hurting anything to let this run a longer and see if anything happens.
I'm just going to let it run for a little bit - and I'll be able to get back to coding probably Monday (March 2)
P.S.S. MQTT is really fun! :-)
If you still have your clock running, you should be able to see a glitch in the date line at the end (since we went from 2 digits to 1 for the day. See green line next to the right of 2020...
Funny, I was about to post pretty much the same picture...(and ask if that is what you saw) Yes, I saw it, just after midnight - and I think it is because it went from a 2 digit date to a 1 digit date - not sure, but Yup, you found a glitch.
And sometime during the day, the little artifact went away, I'm not sure when but I looked and it was gone.
But on to other issues, I've had it running I've had mine running for more than 48 hours at this point - I did reset my router again, but didn't have any problem reconnecting.
I'm going to close this issue, and can reopen it if we can figure out how to duplicate it. I'm not sure what else to do thou.
Tomorrow (Monday) I'll get back into fixing the couple of minor issues you have pointed out and I'll let you know when that is done.
I don't think the line went away on mine, but yeah...should be easy fix...
Thanks
I am seeing pretty consistent "loss" of 15 minutes on my clock. I have been leaving it plugged into a computer with the serial port open to show the output of the device. I have added a serial print command to the section that updates the time and weather to state "time updated" and "weather updated", and I do see these in the serial output every 15 minutes.
I find it odd that the time always seems to be off by 15 minutes (or maybe an increment of 15).
My setup is that the device is plugged into a USB port of my monitor (which does turn off after inactivity), so this may play a role. I plan to try a test where the device is just plugged into a charger so the serial port isn't a factor. (Maybe the monitor shutoff causes some weirdness - I do find that when I come back to the computer after work, the serial monitor is blank and I need to close the serial monitor and re-open it to get content in it).
I know it is still connected to wifi because obviously it still gets the weather info updates, and the color updates are coming through also.
I also have a battery plugged into the connector on the TTGO T-display so power has not been interrupted. When I noticed this last night, a reset/reboot of the TTGO got the time updated correctly.