[SOLVED] byte(s) to string; sporadic nonsense string

Hello,

after implementing a conversion from byte Mez[3] which contains the time { h, m, s } to a string like "hh:mm:ss" it works fine "most of the time". But sometimes (~10%) after power on the print of the string contains arbitrary sequence of digits and only one of the ":" is there. This persists for some time, up to a minute or two, and thereafter it's fine. All other functions of the project are running without issues (at the same time while the string contains nonsense).

It's made sure that Mez[] is initialized properly before the first call.

String * GetTime_PtrMezString(byte *Mez){
  static String timeString;
  timeString = "";
  timeString += String(Mez[0]);
  if (Mez[1] < 10){
    timeString += ':0';  // adds '0' as a leading digits for minutes if < 10
  }
  else {
    timeString += ':';
  }
  timeString += String(Mez[1]);
  timeString += ':';
  timeString += String(Mez[2]);
  return &timeString;  
}

// this is the call:
//...
ComTel_client.println(*GetTime_PtrMezString(GetTime_Mez)); 
//...

As I am not too familiar with the string lib I would like to know if someone could help me out with a direction for debugging.

Thanks!

Thomas

thomas_wz:
It's made sure that Mez[] is initialized properly before the first call.

Why not post the entire sketch so that helpers can decide that?

Hello aarg,

well, because the instruction of the forum reccommends to post focus areas. But, no problem, here is the module:

// ===================== about this module===============================================
// The module provides time as global variable GetTime_Mez and a function to convert it into a string;  GetTime_Mez will update every 10 seconds; in case the time is invalid hh, will assume 99; 
// Implementation: time is obtained by sending a request over UDP to the time server in the local network (here: Fritz Box)
// it builds on the basic ethernet functions of module ComCommon
// the core code for UDP handling and time calculations was copied from the arduino UDP example (created 4 Sep 2010 by Michael Margolis modified 9 Apr 2012 by Tom Igoe) and then modified here 
//----------------------------------------------------------------------------------------------------

// ============================= basic definitions ================================================
IPAddress GetTime_TimeServer(192, 168, 178, 1); // Fritz Box im LAN
const byte GetTime_offsetMezUtz = 2; // MEZ = (UTZ + GetTime_offsetMezUtz) % 24
unsigned int localPort = 2390;      // local port to listen for UDP packets

//===================== public global variables provided by GetTime ==============================================

byte GetTime_Mez[3]; // { h, m, s }; contains { 99, 99, 99 } in case of invalid time

//===================== global variables used from other modules===============================

// extern type xyz; none


//================================ internal definitions ====================================================
const int GetTime_NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message

byte GetTime_packetBuffer[ GetTime_NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

EthernetUDP Udp; // why does the class identifier not appear in that nice orange colour?

//==============================================Init=================================================================
void GetTime_init() {                                 //attach it after ComCom_init()
  Udp.begin(localPort);
  GetTimeResultMez(false);                           // this writes invalid values to the time variable GetTime_Mez[3] as first valid time is available only after about 11s.
}

//============================================= Loop=================================================================
// public 
void GetTime_Loop() {             // reccommended to be put into the 1s task; it will trigger a NTP request every 10s and 1s after the trigger it listens whether a package has come in
  static byte GetTime_CountSchedScale = 0;
  static boolean GetTime_ReadUdpNow = false;

  GetTime_CountSchedScale ++;
  Serial.println(GetTime_CountSchedScale);
  if (GetTime_ReadUdpNow == true) {                                     // read UDP answer and interpret its content
    // wait to see if a reply is available
    if ( Udp.parsePacket() ) {
      Serial.println("packet received");
      // We've received a packet, read the data from it
      Udp.read(GetTime_packetBuffer, GetTime_NTP_PACKET_SIZE); // read the packet into the buffer
      GetTimeResultMez(true);
      GetTime_ReadUdpNow = false;
    }
    else {
      GetTimeResultMez(false);
    }
  }
  if (GetTime_CountSchedScale % 10 == 0) {                     // trigger a NTP request every 10th call
    GetTime_sendNTPpacket(GetTime_TimeServer); // send an NTP packet to a time server
    GetTime_ReadUdpNow = true;
    Serial.println("SendNTPp");

    // wait to see if a reply is available
  }
}

// =================================fcns==================================================================
//private                      interprets the received UDP package from time server; Writes time into the byte GetTime_Mez[3]: [h, m, s]; 
void GetTimeResultMez(boolean rawTimIsvalid) {
  if (rawTimIsvalid) {
    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:
    unsigned long highWord = word(GetTime_packetBuffer[40], GetTime_packetBuffer[41]);
    unsigned long lowWord = word(GetTime_packetBuffer[42], GetTime_packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);
    
    // now convert NTP time into everyday time:
    Serial.print("Unix time = ");
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;
    // print Unix time:
    Serial.println(epoch);
    // print the hour, minute and second:
    Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
    //Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
    //Serial.print(':');
    GetTime_Mez[0] = (byte)(((epoch  % 86400UL) / 3600 + GetTime_offsetMezUtz) % 24);  // write MEZ hh to global variable
    Serial.print(GetTime_Mez[0]); 
    Serial.print(':');
    if ( ((epoch % 3600) / 60) < 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      Serial.print('0');
    }
    //Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
    GetTime_Mez[1] = (byte)((epoch  % 3600UL) / 60);      
    Serial.print(GetTime_Mez[1]); 
    Serial.print(':');
    if ( (epoch % 60) < 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      Serial.print('0');
    }
    //Serial.println(epoch % 60); // print the second
    GetTime_Mez[2] = (byte)(epoch % 60UL); 
    Serial.println(GetTime_Mez[2]);              
  }
  else {                                                  // if UDP message from timeserver not received --> invalid
    GetTime_Mez[0] = 99;
    GetTime_Mez[1] = 99;
    GetTime_Mez[2] = 99; 
  }
}
  
//public **************** returns a pointer to a string with the time in hh:mm:ss ***********************            
String * GetTime_PtrMezString(byte *Mez){
  static String timeString;
  timeString = "";
  timeString += String(Mez[0]);
  if (Mez[1] < 10){
    timeString += ':0';  // adds '0' as a leading digits for minutes if < 10
  }
  else {
    timeString += ':';
  }
  timeString += String(Mez[1]);
  timeString += ':';
  timeString += String(Mez[2]);
  return &timeString;  
}

//private
unsigned long GetTime_sendNTPpacket(IPAddress& address)  //Send a standard NTP request
{
  //Serial.println("1");
  // set all bytes in the buffer to 0
  memset(GetTime_packetBuffer, 0, GetTime_NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  //Serial.println("2");
  GetTime_packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  GetTime_packetBuffer[1] = 0;     // Stratum, or type of clock
  GetTime_packetBuffer[2] = 6;     // Polling Interval
  GetTime_packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  GetTime_packetBuffer[12]  = 49;
  GetTime_packetBuffer[13]  = 0x4E;
  GetTime_packetBuffer[14]  = 49;
  GetTime_packetBuffer[15]  = 52;

  //Serial.println("3");

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  //Serial.println("4");
  Udp.write(GetTime_packetBuffer, GetTime_NTP_PACKET_SIZE);
  //Serial.println("5");
  Udp.endPacket();
  //Serial.println("6");
}

I'm not sure which guide you read, but in the sticky post entitled, "how to post a programming question", one of the first items is,

"Post your complete sketch (program code)! If you don't you waste time while people ask you to do that."

It is trivial to make a C-string with the formatted time as follows. Simple and extremely reliable.

void setup() {
Serial.begin(9600);
char Mez[]={3,4,54};
char buf[10];  //make sure to allocate space for the zero terminator

snprintf(buf,sizeof(buf),"%02d:%02d:%02d",Mez[0],Mez[1],Mez[2]);
Serial.println(buf);  //prints 03:04:54

}

void loop() {}

That looks much better and it avoids the string class!

Great!

Thanks for your advice!

If I had found the "solved" button I would have ... .

Regards
Thomas

thomas_wz:
If I had found the "solved" button I would have ... .

Edit your opening post and update the title.

thomas_wz:
That looks much better and it avoids the String class!

Fixed it for you