Truncated Serial.println Output in Serial Monitor in ESP32

Hi,
I want to simply put out some data in serial monitor in Arduino IDE 2.2.1 from my ESP32.
The project get date and time value from NTP-Server in my getTime() function:

void getTime() {
  struct tm timeinfo;
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  //Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
  char timeStringBuff[8];
  strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M:%S", &timeinfo);
  String myTime(timeStringBuff);
  String myMonth(timeinfo.tm_mon+1);
  String myDay;
  if (timeinfo.tm_mday < 10){
    String myDay("0"+timeinfo.tm_mday);
  }
  else
  {
    String myDay(timeinfo.tm_mday);
  }
  String myYear(timeinfo.tm_year+1900);
  Date = (myYear + "-" + myMonth + "-"+ myDay);

  Time = (&timeinfo, "%H:%M:%S");
  char TestChar[64];
  Date.toCharArray(TestChar, 64);
  Serial.println(Date);
  Serial.flush();
  Serial.println(timeinfo.tm_mday); 
  Serial.println(timeStringBuff);
  Serial.flush(); 
  return;
}

The output of the serial.println is

16:42:46.047 -> 2023-11-
16:42:46.047 -> 16
16:42:46.047 -> 16:42:4

This seems to me that some data gets truncated. In first row it should be 2023-11-16 and in third row it should be like 16:42:46 . In the second line I output the mDay variable just to be sure it is the correct value.

My setup() looks like:

void setup() {
  Serial.begin(115200);
  #if 1
    delay (1000);
  #else
    while (! Serial);
  #endif

}

Any suggestions? What I tried so far:

  1. putting the string in a char array and send this instead of the date string
  2. using other seriel monitor apps
  3. tried other baud rates.
    Nothing of this worked.

what's that?


have you looked at tutorials ?

void showLocalTime(){
  struct tm timeinfo;
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }

  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
  Serial.print("Day of week: ");
  Serial.println(&timeinfo, "%A");
  Serial.print("Month: ");
  Serial.println(&timeinfo, "%B");
  Serial.print("Day of Month: ");
  Serial.println(&timeinfo, "%d");
  Serial.print("Year: ");
  Serial.println(&timeinfo, "%Y");
  Serial.print("Hour: ");
  Serial.println(&timeinfo, "%H");
  Serial.print("Hour (12 hour format): ");
  Serial.println(&timeinfo, "%I");
  Serial.print("Minute: ");
  Serial.println(&timeinfo, "%M");
  Serial.print("Second: ");
  Serial.println(&timeinfo, "%S");

  Serial.println("Time variables");
  char timeHour[3];
  strftime(timeHour,3, "%H", &timeinfo);
  Serial.println(timeHour);
  char timeWeekDay[10];
  strftime(timeWeekDay,10, "%A", &timeinfo);
  Serial.println(timeWeekDay);
  Serial.println();
}

(from ESP32 NTP Client-Server: Get Date and Time (Arduino IDE) | Random Nerd Tutorials)

/* Example Demo-Code for ESP8266 and ESP32
  Showing how to
  - organise code well structured into functions
  - connecting to a WiFi
  - synchronise time with an NTP-server
  - if connecting fails how to scan and print available WiFi-networks
  - how to use minuteOfDay and secondOfDay which make it easier to compare
  - the actual time with a certain point of time
  - non-blocking timing based on function millis() with a helper-function
  - how to print a timestamp when the code was compiled
  written by user StefanL38
*/

#if defined(ESP32)
#include <WiFi.h>
char deviceType[] = "ESP32";
#else
#include <ESP8266WiFi.h>
char deviceType[] = "ESP8266";
#endif

unsigned long MyTestTimer = 0;                   // variables MUST be of type unsigned long
const byte    OnBoard_LED = 2;
int connectCounter = 0;
int NoOfWiFis;
long minuteOfDay;
long secondOfDay;

const   char *ssid     = "your SSID";
const char *password = "your password";

const char dayOfWeek[][16] = {"sunday",
                              "monday",
                              "tuesday",
                              "wednesday",
                              "thursday",
                              "friday",
                              "saturday"
                             };

// a lot of home-wlan-routers can be used as a NTP-server too
const char* ntpServer = "fritz.box";
const long  gmtOffset_sec = 0;
const int   daylightOffset_sec = 7200;

#include <time.h>                   // time() ctime()
time_t now;                         // this is the epoch
tm myTimeInfo;                      // the structure tm holds time information in a more convient way
int Year;
int Month;
int Day;
int Hour;
int Minute;
int Second;
int DayIndex;


void updateMyTimeVars() {
 Year   = myTimeInfo.tm_year + 1900; // years since 1900
 Month  = myTimeInfo.tm_mon + 1;     // January = 0 (!)
 Day    = myTimeInfo.tm_mday;
 Hour   = myTimeInfo.tm_hour;
 Minute = myTimeInfo.tm_min;
 Second = myTimeInfo.tm_sec; 
 DayIndex = myTimeInfo.tm_wday;
}


void clearSerialMonitor() {
  for (int i = 0; i < 50; i++) {
    Serial.println();  // print 50 empty lines
  }
}


void showTime() {
  time(&now);                       // read the current time
  localtime_r(&now, &myTimeInfo);   // update the structure tm with the current time
  updateMyTimeVars();
    
  Serial.print("year:");
  Serial.print(Year);  

  Serial.print(" month:");
  if (Month < 10) {
    Serial.print("0");
  }
  Serial.print(Month);      

  Serial.print(" day:");
  if (Day < 10) {
    Serial.print("0");
  }
  Serial.print(Day);         // day of month

  Serial.print(" hour:");
  if (Hour < 10) {
    Serial.print("0");
  }
  Serial.print(Hour);         // hours since midnight  0-23

  Serial.print(" min:");
  if (Minute < 10) {
    Serial.print("0");
  }
  Serial.print(Minute);          // minutes after the hour  0-59

  Serial.print(" sec:");
  if (Second < 10) {
    Serial.print("0");
  }
  Serial.print(Second);          // seconds after the minute  0-61*
  Serial.print(" DayIndex:");

  Serial.print(DayIndex);         // days since Sunday 0-6

  Serial.print(" name of day ");
  Serial.print(dayOfWeek[DayIndex]);

  minuteOfDay = Hour *   60 + Minute;
  secondOfDay = Hour * 3600 + Minute * 60 + Second;
  Serial.println();
  Serial.println();

  Serial.print("minuteOfDay ");
  Serial.print(minuteOfDay);

  Serial.print(" secondOfDay ");
  Serial.print(secondOfDay);

  //                 12 hours    4 minutes  59 seconds = 12:04:59
  if (secondOfDay < (12 * 3600 + 4 * 60   + 59) ) {
    Serial.println(" actual time is before 12:04:59");
  }
  else {
    Serial.println(" actual time is AFTER 12:04:59");
  }
  Serial.println();
  Serial.println();
}


void connectToWifi() {
  Serial.print("try connecting to ");
  Serial.println(ssid);

  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    yield(); // very important to have this function call to enable backround-processes
    BlinkHeartBeatLED(OnBoard_LED, 333);

    if ( TimePeriodIsOver(MyTestTimer, 500) ) {
      Serial.print(".");
      connectCounter++;
      if (connectCounter % 20 == 0) {
        Serial.println();
      }
    }

    if (connectCounter > 120) {
      break;
    }
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("\n connected.");
    Serial.println(WiFi.localIP() );
  }
  else {
    Serial.print("\n unable to connect to WiFi with name #");
    Serial.print(ssid);
    Serial.println("#");
    ScanWiFis();
    Serial.print("\n code stopped with while(true) yield() ");
    while (true) {
      yield();
    }
  }
}

void ScanWiFis() {
  int WiFiIdx;

  Serial.println("Startscanning for networks");
  NoOfWiFis = WiFi.scanNetworks();
  Serial.println("scanning done List of SSIDs");
  Serial.print("found number of networks ");
  Serial.println(NoOfWiFis);

  for (WiFiIdx = 0; WiFiIdx < NoOfWiFis; WiFiIdx++) {
    // Print SSID and RSSI for each network found
    Serial.print(WiFiIdx);
    Serial.print(" #");
    Serial.print( String( WiFi.SSID(WiFiIdx) ) );
    Serial.print("# RSSI ");
    Serial.print( String (WiFi.RSSI(WiFiIdx)) );
    Serial.print(" dB");
    Serial.println();
  }
}


void synchroniseWith_NTP_Time() {
  Serial.print("configTime uses ntpServer ");
  Serial.println(ntpServer);
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  Serial.print("synchronising time");

  while (myTimeInfo.tm_year + 1900 < 2000 ) {
    time(&now);                       // read the current time
    localtime_r(&now, &myTimeInfo);
    BlinkHeartBeatLED(OnBoard_LED, 100);
    delay(100);
    Serial.print(".");
  }
  Serial.print("\n time synchronsized \n");
  showTime();
}


void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println(__FILE__);
  Serial.print( F("  compiled ") );
  Serial.print(__DATE__);
  Serial.print( F(" ") );
  Serial.println(__TIME__);
}

boolean TimePeriodIsOver (unsigned long & periodStartTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - periodStartTime >= TimePeriod )
  {
    periodStartTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("\n Setup-Start \n");
  PrintFileNameDateTime();

  connectToWifi();
  synchroniseWith_NTP_Time();
}

void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 100);

  if ( TimePeriodIsOver(MyTestTimer, 1000) ) {
    showTime();
  }
}

Stefan,
I see synchronization only occurs for setup().

Does this keep 'track' of the time thereafter? Or is this pinging the ntpServer to collect the current time?

as long as the WiFi-connection is established the ESP32 keeps getting the time form the WiFi-connection.

pinging very often to NTP-servers has become a real problem because today there are billions of network-devices who want to do that.

That is the reason why I use my local WiFi-Router as the NTP-server.
The FritzBox-routers from manufacturer AVM are very versatile and very reliable devices with an outstanding WiFi-supply-perfomance.

Would it be fair to say that setting the ntpInterval then is the time requested for the wifi to go update from the NTP server?

Using your above sketch, what would happen to a loss in wifi connectivity? What it ouput zero's, or the last known time prior to loss of wifi signal?

I don't know never checked this.

easy to test for yourself.

User @J-M-L and user @noiasca know much more about these details

I'm about to start developing something that needs timestamps often about every second, nothing faster. Would it make sense then to add an RTC and update the RTC when wifi is available in the event the RTC has skewed over time?

Priority:

  1. Wifi
  2. RTC (if loss of Wifi)

Yes.
don't use a DS1307-RTC they are unprecise
use a DS3231-RTC

1 Like

the default NTP interval is 1 hour.
the ESP will keep track of the time between each NTP polling interval. So each time you call for a new timestamp (something like time(&now)) will give you an updated timestamp - but that will not cause an NTP request to the NTP server.

If you loose WIFI connection, the internal time will continue to run - just there are no NTP updates any more (until you reconnect to WIFI).

Sounds like I don't need to implement a RTC if the assumption that wifi will always be available? (Except for power outages). Accuracy is not necessary while no wifi, I just want to make sure timestamps continue 'incrementing'.

Your ESP can get an update from time to time from An NTP server to maintain its time system up to date

If you have wifi access that should probably meet your needs

Thanks, that's a nice example, but it doesn't solve my problem. I wasn't asking for useful methods to get the time from somewhere and synchronize it with my ESP32 system, I was presenting a problem with Serial.Println.

It doesn't matter where the data comes from, that's not the problem. The problem is that apparently a joined string of other strings cannot be transferred completely without further ado.

For 1000% sure you can transfer a joined string of integers completely.
The only thing is you have to obey the rules to join them right.

If the print-method can print it right the transferring will work.
another idea is to add

.c_str()

whenever a method is able to handle strings at all .c_str() should make it work

any answer to this?

Time is a string. But this command isn't working. Time will always be %H:%M:%S when outputted by Serial.Println. That's the reason why I use

  strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M:%S", &timeinfo);
  String myTime(timeStringBuff);

to get a time-string.

this command is working, it stores "%H:%M:%S" into your String... that's what you ask.

see C++ Comma Operator

it probably does not do what you want...

that's why I showed the example

First of all, I made a mistake in my last post. I meant it is a string of concatenated strings, not integers. I edited the post immediately afterwards, perhaps there was a misunderstanding.

Even with c_str(); there is no improvement.

I now suspect that it is due to my IF-Else branching. I have built this in so that a preceding 0 is appended in the event that the date is less than 10, for example 9 becomes 09:

UUMMMMM, I compare strings with integers. :exploding_head: :open_mouth:

 String myDay;
  if (timeinfo.tm_mday < 10){
    String myDay("0"+timeinfo.tm_mday);
  }
  else
  {
    String myDay(timeinfo.tm_mday);
  }

If I output myDay directly, Serial.Println remains empty at this point:

  String myYear(timeinfo.tm_year+1900);
  Date = (myYear + myMonth + myDay);
  Date.c_str();
  Serial.println("MyDay-Variable: " + myDay);
  Serial.println(Date);
  Serial.flush();
   
  Serial.println(timeStringBuff);
  Serial.flush(); 
  return;

It returns:

12:46:46.292 -> MyDay-Variable:

12:46:46.292 -> 202311

12:46:46.292 -> 12:46:4

And Date also only contains an empty string at this point. Perhaps the problem is not with Serial.Println.

if you want to concantenate strings and not do pointer arithmetics (like you do with "0"+timeinfo.tm_mday which builds a pointer pointing timeinfo.tm_mday bytes further away in memory than the constant char pointer of "0" ) then take it one line at a time

String myDay;
if (timeinfo.tm_mday < 10) myDay = "0";
myDay += timeinfo.tm_mday;

of course I would recommend to use sprintf() rather than the String class for this...

On microcontrollers with small RAM the string-class can eat up all RAM over time and then your code can crash.

There is another library which does avoid this.
It is the SafeString-library.
The SafeString-library works this way
initially you declare a safeString-variable and define the maximum of characters that this variable can hold.

cSF(mySafeString,32);

Then you can assign any kind of variable in this way

int myInteger = - 1234;
mySafeString = "";
mySafeString += myInteger;

Here is a demo-code with a function that stores timestamps

#include <time.h>                   // time() ctime()
time_t now;                         // this is the epoch
tm myTimeInfo;                      // the structure tm holds time information in a more convient way


#include <SafeString.h>
createSafeString(myDemo_SS, 32);
createSafeString(mySecondDemo_SS, 32);
#define MaxTimeStampLength 64
createSafeString(TimeStamp_SS, MaxTimeStampLength);

unsigned long myCounter;


//useful function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();  
  if ( currentMillis - periodStartTime >= TimePeriod )
  {
    periodStartTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

unsigned long MyTestTimer = 0;                   // variables MUST be of type unsigned long
const byte    OnBoard_LED = 13; // Arduino-Uno Onboard-LED is IO-pin 13



void setup() {
  Serial.begin(115200);
  Serial.println( F("Setup-Start") );
  myCounter = 0;
  myDemo_SS = "Hello world!";
}


void loop() {
  myCounter++;
  
  // loop is running very fast counting up very fast
  // but only once every 1234 milliseconds print 
  if ( TimePeriodIsOver(MyTestTimer,1234) ) {
    mySecondDemo_SS = myDemo_SS;  // assigning a SafeString to another SafeString
    
    mySecondDemo_SS += " ";       // append a SPACE
    mySecondDemo_SS += myCounter; // append integer-number
    Serial.println(mySecondDemo_SS);   
    StoreTimeStampIntoSS(TimeStamp_SS);
    Serial.println(TimeStamp_SS);
    Serial.println(TimeStamp_SS.c_str() );     
  }  

}


void StoreTimeStampIntoSS(SafeString& p_RefToSS) {

  time(&now);                               // read the current time
  localtime_r(&now, &myTimeInfo);           // update the structure tm with the current time

  //p_RefToSS = " ";
  p_RefToSS  = myTimeInfo.tm_year + 1900;
  p_RefToSS += ".";
  if (myTimeInfo.tm_mon + 1 < 10) {
    p_RefToSS += "0";  
  }
  p_RefToSS += myTimeInfo.tm_mon + 1;
  p_RefToSS += ".";

  if (myTimeInfo.tm_mday < 10) {
    p_RefToSS += "0";  
  }
  p_RefToSS += myTimeInfo.tm_mday;

  p_RefToSS += "; ";

  if (myTimeInfo.tm_hour < 10) {
    p_RefToSS += "0";  
  }  
  p_RefToSS += myTimeInfo.tm_hour;
  p_RefToSS += ":";

  if (myTimeInfo.tm_min < 10) {
    p_RefToSS += "0";  
  }  
  p_RefToSS += myTimeInfo.tm_min;
  p_RefToSS += ":";

  if (myTimeInfo.tm_sec < 10) {
    p_RefToSS += "0";  
  }  
  p_RefToSS += myTimeInfo.tm_sec;
}

best regards Stefan