Turning string output of function into a String

This seems like an easy ask, but I can't for the life of me figure it out. Code is here

If you look at line 155 I am able to create a log entry in this function using a literal string. Great. Now I want to append a time stamp in front of the string. I tried (see line 157) to simply put my time function in and concatenate but the append() function won't have it where as Serial.print() is perfectly happy to concatenate them. The error is

bool append(String record, bool timestamp = true)

In an example for the logger_spiffs.h (line 27-30) you can see where they create a "counter" variable and use that somehow.

All this to say, I want to be able to append the output of getEpochStringByParams() to the front of the sysLog.append() literal string. Any ideas? Usual disclaimer of being a newb.

Line 155 of what ?

Please post your code here following the advice in How to get the best out of this forum about formatting your code and using code tags

Why do you want to turn the output of a function into a String ?

do yourself a favour and please read How to get the best out of this forum

why do you need this into a variable? can't you just "print" both separately?

The code I linked to in the first line of the post. Or should everything be in-line? I can do that....

//V2 add time stamps
#include <elegantWebpage.h>
#include <AsyncElegantOTA.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <logger_spiffs.h>
#include "SPIFFS.h"
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <Timezone.h>
#include <WebSerial.h>

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

int counter = 72; //Delete me

// Specify the path where the log is placed
LoggerSPIFFS sysLog("/log/syslog.txt");

AsyncWebServer  server(80);

//Structure time
/**
 * Input time in epoch format and return tm time format
 * by Renzo Mischianti <www.mischianti.org> 
 */
static tm getDateTimeByParams(long time){
    struct tm *newtime;
    const time_t tim = time;
    newtime = localtime(&tim);
    return *newtime;
}

/**
 * Input tm time format and return String with format pattern
 * by Renzo Mischianti <www.mischianti.org>
 */
static String getDateTimeStringByParams(tm *newtime, char* pattern = (char *)"%m/%d/%Y  %r"){
    char buffer[30];
    strftime(buffer, 30, pattern, newtime);
    return buffer;
}

 
/**
 * Input time in epoch format format and return String with format pattern
 * by Renzo Mischianti <www.mischianti.org> 
 */
static String getEpochStringByParams(long time, char* pattern = (char *)"%m/%d/%Y  %r"){
//    struct tm *newtime;
    tm newtime;
    newtime = getDateTimeByParams(time);
    return getDateTimeStringByParams(&newtime, pattern);
}
 

// Configure NTPClient
// You can specify the time server pool and the offset, (in seconds)
// additionally you can specify the update interval (in milliseconds).
WiFiUDP ntpUDP;
int gtmOffset = 0; //Using GMT as Timezone.h will adjust accordingly
int ntpPollInterval = 5; 
NTPClient timeClient(ntpUDP, "pool.ntp.org", gtmOffset*60*60, ntpPollInterval*60*1000); //Set to EST and update every 5 minutes

//Configure DST

// US Eastern Time Zone (New York)
TimeChangeRule usEDT = {"EDT", Second, Sun, Mar, 2, -240};  // Eastern Daylight Time = UTC - 4 hours
TimeChangeRule usEST = {"EST", First, Sun, Nov, 2, -300};   // Eastern Standard Time = UTC - 5 hours
Timezone usET(usEDT, usEST);


void setup()
{
  Serial.begin(115200);

  
  // Connect to WiFi network
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if(!SPIFFS.begin(true)){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

//Start logging
  while(!Serial);
  Serial.println();
  Serial.println("ESP Logger - Log on internal flash memory");

  sysLog.begin();
  sysLog.setFlusherCallback(senderHelp);

  Serial.println("Starting to log...");
  
//OTA support at /update
  AsyncElegantOTA.begin(&server); 

// WebSerial is accessible at "<IP Address>/webserial" in browser
  WebSerial.begin(&server);

//Configure web server
  server.on("/log", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/log/syslog.txt", "text/html");
  });


  
  server.begin();

//Start NTP client
  timeClient.begin();
  delay (5000);
  if (timeClient.update()){
     Serial.print ( "Adjust local clock" );
     WebSerial.print ( "Adjust local clock" );
     unsigned long epoch = timeClient.getEpochTime();
     // Update local clock
     setTime(epoch);
  }else{
     Serial.print ( "NTP update failed!!!" );
     WebSerial.print ( "NTP update failed!!!" );
  }


 sysLog.flush(); //clear the log on startup, remove this line to keep logs forever. 

}

void loop() {

  timeClient.update();


  delay(5000);
  Serial.println(getEpochStringByParams(usET.toLocal(now())) + "This is a log entry!<br>");
  WebSerial.println(getEpochStringByParams(usET.toLocal(now())) + " This has formatted time.");

  
   
   sysLog.append("This is a log entry!!!<br>");
  
  //sysLog.append(getEpochStringByParams(usET.toLocal(now())) + "This is a log entry!<br>"); //Write a log entry every 5 seconds

    
}

/**
 * Flush a chuck of logged records. To exemplify, the records are
 * flushed on Serial, but you are free to modify it to send data
 * over the network.
 */
bool senderHelp(char* buffer, int n){
  int index=0;
  // Check if there is another string to print
  while(index<n && strlen(&buffer[index])>0){
    Serial.print("---");
    int bytePrinted=Serial.print(&buffer[index]);
    Serial.println("---");
    // +1, the '\0' is processed
    index += bytePrinted+1;
  }
  return true;
}

/* Library sources 
https://github.com/fabianoriccardi/esp-logger
https://github.com/me-no-dev/ESPAsyncWebServer
https://github.com/ayushsharma82/AsyncElegantOTA
https://github.com/arduino-libraries/NTPClient
https://github.com/JChristensen/Timezone
 */

Thank you. That is much more convenient to see and copy for examination

As asked in reply #3, why do you need the data in a variable, particularly a String, when you could just output it directly as 2 separate calls to append() ?

https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/concat/

(although there will likely be lots of noise about using String functions...) Full list here:
https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/

Assuming you mean

sysLog.append(getEpochStringByParams(usET.toLocal(now())) );
 sysLog.append(" This is a log entry!<br>");

That dies a horrible death too:

var/folders/6p/6yqyy3td171fh1q60jc0zkt80000gn/T/arduino_modified_sketch_111762/LoggingTestV2.ino:157:61: warning: 'bool Logger::append(String, bool)' is deprecated: record is duplicated, augmenting heap fragmentation: consider append(const char*, bool) that is zero-copy [-Wdeprecated-declarations]
   sysLog.append(getEpochStringByParams(usET.toLocal(now())) );
                                                             ^
In file included from /Users/xxx/Documents/Arduino/libraries/esp-logger-master/src/logger_spiffs.h:32,
                 from /var/folders/6p/6yqyy3td171fh1q60jc0zkt80000gn/T/arduino_modified_sketch_111762/LoggingTestV2.ino:6:
/Users/xxx/Documents/Arduino/libraries/esp-logger-master/src/logger.h:93:8: note: declared here
   bool append(String record, bool timestamp = true)

The reason I went down the String path is it seemed in the library's example that was what they were doing. Specifically:

int counter = 0;

void somethingHappening(){
  counter++;
  
  // counter is a multiple of 3, log it!
  if(counter%3==0){
    Serial.println(String("Hey, event ->") + counter + "<- is just happened");
    String record = String("val:") + counter;
    myLog.append(record.c_str());
  }
}

They have a counter variable in a String and I was trying to use that as a basis of reverse engineering this.

You're going to ask a bunch of "why didn't you" and the answer will be I'm a PHP/Bash guy and C is barely sort of kind of making sense barely.

Especially when you don't need to use a variable in the first place

One does get a different view of the problem when the source code is posted ... but, as I was in edit at the time and did not have that available ...
Well you get my drift.

Lots and lots of ESP32/ESP8266 posted code uses Strings. They are easy and the ESP uC's seem to have enough SRAM that the usage does not create the problem that was seen back when Arduino was AVR Atmega328.

I have ESP code running for many years that use Strings and it just works. Would I use the same code on a tiny85? No.

Context is everything, and when replying we are often not in possession of all of the facts. There is certainly heavy use of Strings in the ES32 universe and with the extra memory available compared to AVRs it certainly seems to cause no problem

Meanwhile, back at the original question, as has been pointed out the data does not need to be in a variable of any kind.

Having said that, it can be useful to have intermediate values in variables for debugging purposes and there are other possible "gotchas" such as checking whether data is available and reading it as part of a print statement. All OK until you decide to output the data to both Serial and LCD and do the read() in both print statements which I saw recently

+1

Ray

It doesn't need to be in a variable, yet I can't nest it in the append() function as I can in a Serial.print(). Is there an answer here I am missing?

As someone who started programming with Perl and Bash, I can totally relate to that feeling: "what does it matter if a variable contains a string or a number?" C is statically typed and lower-level, though, so it does matter in C.

Others will disagree with me, but I believe that forgoing the String class and going the hard way of learning about c-strings upfront pays off in the long run. In C, a c-string is just a numerical, null-terminated, array of values that you then interpret as ASCII symbols for display. It took me awhile for it to feel natural or obvious, but now it does, and I feel like I have no need for the "easy" String class at all.

int counter = 0;

void somethingHappening(){
  counter++;
  
  // counter is a multiple of 3, log it!
  if(counter%3==0){
    Serial.println(String("Hey, event ->") + counter + "<- is just happened");
    String record = String("val:") + counter;
    myLog.append(record.c_str());
  }
}

Following this example, try this syntax for the .append()

String record = getEpochStringByParams(usET.toLocal(now()));
myLog.append(record.c_str());

This, this is great info. That was not at all apparent and the couple of books I have been using don't state that in any real sense. The Arduino C book I have basically says "lucky you, String() is a C++ class we get to use which has extra features. Isn't it neat we can set everything to capitals!" and the C++ book I have just goes into usage, NOT the under the covers what's actually happening. Having spent 10000 years in *nix environments I am thinking... they are built on C. Bash and many of the GNU utilities show their C-ness in their syntax and usage - I have to be missing something. Now the fact printf() understands 64 as a character makes a ton of sense.

That compiles fine but I get no output. :-/ I may be better off forgetting this library and simply making my own strings and appending to SPIFFs manually.

The ReadMe for https://github.com/fabianoriccardi/esp-logger which contains logger_spiffs.h has this for .append()

Append
bool append(const char* record)
it creates and stores a Record. You cannot log data containing non-printable characters, nor new line or carriage return. Return true if the data is saved, false otherwise.

Check the format of what you are trying to save, and the the return value from .append(). You may have an issue with the retrieval if the store looks correct.

This may be true on a computer, but an arduino has orders of magnitude less memory and processing power and doesn't offer the protection of an OS and a kernel acting as a middle layer between the hardware and the user program. On a µC board, the single program you run takes full control of all the silicon. The String class, with its dynamic memory management, can lead to memory leaks that are particularly hard to debug, because thay can show up after hours or days of perfectly smooth running. It is possible, if one takes extra precautions, to safely use the String class, but, ironically, this takes a higher level of expertise as compared to plain old c-strings.

To paraphrase the old saying that goes "I cannot afford to pay so little money", I don't feel confident enough to use so easy a class as String.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.