Problem receiving as char array and parsing with sscanf

I am trying to read a times string received from a server to use to adjust the time on the Arduino WiFi Rev2.

This is the code:

include <Wire.h>
#include <RTClib.h>
#include <SoftwareSerial.h>
#include <WiFiNINA.h>

// Declare global variables
// - Real-time clock object
RTC_DS1307 RT_Clock;
DateTime NowDtTm;
char TimeLine[25];
char SyncDate[12];
char SyncTime[9];

void setup () 
  {

  // Launch send diagnostic messages
  Serial.begin(9600);
  while (!Serial);
  Serial.println( "" );
  Serial.println( F( "Arduino ***** DIAGS RUNNING *****" ));

  // RTC launch
  Wire.begin();
  RT_Clock.begin();

  Serial.println( F( "RT_Clock running" ) );

  // Send time adjust trigger. and wait for response
  // Send2Srvr( "AdjT", "Initial time adjust request", true  );
  AdjRTC();
  
  }

void loop()
  {
  }

void AdjRTC()
  {

  int NrChar = myTcpClient.readBytesUntil( '\n', TimeLine, 100);
  Serial.println( TimeLine );

  sscanf(TimeLine, "%s,%s", SyncDate, SyncTime );
  Serial.println( SyncDate );
  Serial.println( SyncTime );

  // Note current date and time
  NowDtTm = RT_Clock.now();
  sprintf(ChgMsgFr, "%04d-%02d-%02d %02d:%02d:%02d", NowDtTm.year(), NowDtTm.month(), NowDtTm.day(), NowDtTm.hour(), NowDtTm.minute(), NowDtTm.second());
  
  // Adjust
  RT_Clock.adjust(DateTime(SyncDate, SyncTime));

  // Note New date and time
  NowDtTm = RT_Clock.now();
  sprintf(ChgMsgTo, "%04d-%02d-%02d %02d:%02d:%02d", NowDtTm.year(), NowDtTm.month(), NowDtTm.day(), NowDtTm.hour(), NowDtTm.minute(), NowDtTm.second());

//  Send2Srvr( "Ctrl", ChgMsgFr, false );
  delay(250);
//  Send2Srvr( "Ctrl", ChgMsgTo, false );

  }

I’ve left out the stuff about the TCP connection and other things but these are all working fine. I just wanted to try getting an adjustment string from the server and the sscanf doesn’t seem to work.

These are the three diagnostic lines when the message is received:
14:44:55.576 → Sep 24 2021,14:44:54
14:44:55.622 → Sep
14:44:55.622 →
The first is perfectly correct. I then want to break it into Date and Time based on the comma separator but it gate “Sep” as the date and nothing for the time. Like it is seeings the space after Sep as a field divider rather than the comma.

In a separate run, I printed NrChar and it was 20 - seems correct.
Also printing the TimeLine string between a leading and trailing pipe “|” also shows no extra spaces.

Any suggestions?

1 Like

Hi Brian,

I believe that this is related to the syntax of the function sscanf() I’ve attached a tutorials point on it below for you:

https://www.tutorialspoint.com/c_standard_library/c_function_sscanf.htm

I’d give this a go, to see whether reading each of the strings individually and then concatenating them will work. Here’s a modified version of the function for you to have a go with assuming that the structor of Sep 24 2021,14:44:54 is always the same:

void AdjRTC()
  {

  int NrChar = myTcpClient.readBytesUntil( '\n', TimeLine, 100);
  Serial.println( TimeLine );

  sscanf(TimeLine, "%s %s %s,%s", SyncMonth, SyncDay, SyncYear, SyncTime );


  Serial.println( SyncMonth+" "+SyncDay+" "+SyncYear );
  Serial.println( SyncTime );

  // Note current date and time
  NowDtTm = RT_Clock.now();
  sprintf(ChgMsgFr, "%04d-%02d-%02d %02d:%02d:%02d", NowDtTm.year(), NowDtTm.month(), NowDtTm.day(), NowDtTm.hour(), NowDtTm.minute(), NowDtTm.second());
  
  // Adjust
  RT_Clock.adjust(DateTime( SyncMonth+" "+SyncDay+" "+SyncYear, SyncTime));

  // Note New date and time
  NowDtTm = RT_Clock.now();
  sprintf(ChgMsgTo, "%04d-%02d-%02d %02d:%02d:%02d", NowDtTm.year(), NowDtTm.month(), NowDtTm.day(), NowDtTm.hour(), NowDtTm.minute(), NowDtTm.second());

//  Send2Srvr( "Ctrl", ChgMsgFr, false );
  delay(250);
//  Send2Srvr( "Ctrl", ChgMsgTo, false );

  }
1 Like

Thanks Bryce.
I liked your suggestion so gave it a try. The outcome is weird to say the least. Maybe I am doing something wrong but I can’t see it.

#include <Wire.h>
#include <RTClib.h>
#include <WiFiNINA.h>

// Declare global variables
// - Real-time clock object
RTC_DS1307 RT_Clock;
DateTime NowDtTm;
char TimeLine[25] = "";
char Sync_MMM[3] = "";
char Sync_dd[2] = "";
char Sync_yyyy[4] = "";
char SyncDate[12] = "";
char SyncTime[9] = "";

// WiFi config items
WiFiClient myTcpClient;
//byte SrvrIP[3];   // IP Address.
IPAddress SrvrIP(192,168,1,237);

char cSSID[24] = "";
char cPass[24] = "";
int Port = 0;

// Before and after messages for server-triggered changes
char ChgMsgFr[32];
char ChgMsgTo[32];

// Misc
unsigned long LoopExitTime;
char SrvrLine[60];
char InType[3];

void setup () 
  {

  // Launch send diagnostic messages
  Serial.begin(9600);
  while (!Serial);
  Serial.println( "" );
  Serial.println( F( "Arduino ***** DIAGS RUNNING *****" ));

  // Set Config stuff
  strcpy( cSSID, "Vodafone2.4G-520F8" );
  strcpy( cPass, "EtyxaMG6NeTr7nrs" );
  Port = 8888;

  // Establish WiFi session and TCP connection
  // Start WiFi
  Serial.println( cSSID );
  Serial.println( cPass );

  WiFi.begin(cSSID, cPass);
  // Reboot if we don't get WiFi within 60 seconds
  LoopExitTime = millis() + 60000;
  while ( ( WiFi.status() != WL_CONNECTED) && ( millis() < LoopExitTime ) )
    {
    delay(10);
    }

  Serial.println( F( "WiFi running" ) );

  // Start TCP Client
  // Reboot if we don't get TCP Connection within 60 seconds
  LoopExitTime = millis() + 60000;
  while ( ( !myTcpClient.connect(SrvrIP, Port) ) && ( millis() < LoopExitTime ) )
    {
    delay(10);
    }

  Serial.println( F( "TCP running" ) );

  // RTC launch
  Wire.begin();
  RT_Clock.begin();

  Serial.println( F( "RT_Clock running" ) );

  // Send time adjust trigger. and wait for response
  Send2Srvr( "AdjT", "Initial time adjust request", true  );
  AdjRTC();
  
  }

void loop()
  {
  }

void AdjRTC()
  {

  myTcpClient.readBytesUntil( '-', InType, 100 );
  myTcpClient.readBytesUntil( '\r', TimeLine, 100 );
  Serial.println( TimeLine );   // Sep 24 2021,22:31:35

  sscanf( TimeLine, "%s %s %s,%s", Sync_MMM, Sync_dd, Sync_yyyy, SyncTime );

  Serial.println( Sync_MMM );   // 2:31:35
  Serial.println( Sync_dd );    // ,22:31:35
  Serial.println( Sync_yyyy );  // 2021,22:31:35
  
  sprintf( SyncDate, "%s %s %s", Sync_MMM, Sync_dd, Sync_yyyy );

  Serial.println( SyncDate );   // 2:31:35 ,22:31:35 2021,22:31:35
  Serial.println( SyncTime );   // 31:35 2021,22:31:35

  // Note current date and time
  NowDtTm = RT_Clock.now();
  sprintf(ChgMsgFr, "%04d-%02d-%02d %02d:%02d:%02d", NowDtTm.year(), NowDtTm.month(), NowDtTm.day(), NowDtTm.hour(), NowDtTm.minute(), NowDtTm.second());
  
  // Adjust
  RT_Clock.adjust(DateTime(SyncDate, SyncTime));

  // Note New date and time
  NowDtTm = RT_Clock.now();
  sprintf(ChgMsgTo, "%04d-%02d-%02d %02d:%02d:%02d", NowDtTm.year(), NowDtTm.month(), NowDtTm.day(), NowDtTm.hour(), NowDtTm.minute(), NowDtTm.second());

  //Send2Srvr( "Ctrl", ChgMsgFr, false );
  delay(250);
  Send2Srvr( "Ctrl", ChgMsgTo, false );

  }

void Send2Srvr( char myType[], char myData[], bool InclDT_Stamp )
  {

  // Send line to Server via TCP
  if ( InclDT_Stamp )
    {
    sprintf( SrvrLine, "%s-%04d-%02d-%02d,%02d:%02d:%02d,%s", myType, NowDtTm.year(), NowDtTm.month(), NowDtTm.day(), NowDtTm.hour(), NowDtTm.minute(), NowDtTm.second(), myData );
    }
  else
    {
    sprintf( SrvrLine, "%s-%s", myType, myData );
    }

  myTcpClient.println( SrvrLine );
  delay(100);
  myTcpClient.flush();

  Serial.print( F( "Sent: " ) );
  Serial.println( SrvrLine );

  }

I have created a new cut-down sketch that just does this stuff in case there was some cross-influence somewhere that I hadn’t figured.

On the six Serial.println lines I have included the diagnostic output as a comment. The first is perfectly correct. This is what is sent from my VB.Net app that is acting as the server.

The other five don’t make the slightest bit of sense.

I have also tried with all separators comma or all spaces (and matching formatting at the VB.Net end). The outcomes are equally strange. (I can provide them if anyone is interested). I would need to double check but I think with all commas, the time field is parsed correctly but for the other three only MONTH is extracted. With all spaces I think it is that time is again correct but only YEAR is extracted for the other three.

Any thoughts from anyone.

1 Like

What is the reported memory usage at the end of the compile?

If you are getting results that are longer than the declared size of the char array then that indicates a problem with the terminating null, and that in turn indicates a problem with the size that is allowed for the strings. Push the declarations of the arrays out to a much larger size and see if the results change.

Thanks Bryce and Jeff.

I have given up with sscanf.

I first came across sscanf and serial.readBytesUntil in the same forum entry.
After days of trying combinations I have found I can get readBytesUntil to do what I want with very little “trial and error” whereas sscanf consistently does stuff that doesn’t make the slightest bit of sense.

I did play with Jeff’s last suggestion about char array sizes. This had some impact but still missed the comma between DAY and TIME when parsing the buffer - I can’t figure why anything changed since the incoming message and all of its pieces come from a VB.Net date/time formatted string so it is likely to exactly match the specified sizes at all times.

Anyway I settled on this and it seems to work perfectly:

  myTcpClient.readBytesUntil( ',', cSync_Yr, 100 );  iSync_Yr = atoi(cSync_Yr);
  myTcpClient.readBytesUntil( ',', cSync_Mo, 100 );  iSync_Mo = atoi(cSync_Mo);
  myTcpClient.readBytesUntil( ',', cSync_Dy, 100 );  iSync_Dy = atoi(cSync_Dy);
  myTcpClient.readBytesUntil( ',', cSync_Hr, 100 );  iSync_Hr = atoi(cSync_Hr);
  myTcpClient.readBytesUntil( ',', cSync_Mn, 100 );  iSync_Mn = atoi(cSync_Mn);
  myTcpClient.readBytesUntil( '\r', cSync_Sc, 100 );  iSync_Sc = atoi(cSync_Sc);

Thanks again.

Moving on.

2 Likes