snprintf() - stuck

Folks,

I AM trying, but find it frustrating when I search and get a "File not found" when I click on a link;

snprintf() search on google

I am really not getting how things are explained in this case and many others.

Seems any errors I enter into google and follow links, all the results/explanations are for people who ALREADY know the answer.

Not really helpful.

This is a small part of my code:

    lastMsg = now;
    ++value;
    snprintf (msg, 75, "hello world #%ld", value);
    Serial.print("Publish message: ");
    Serial.println(msg);
    client.publish("outTopic", msg);

That works.

This is the part I need to get working:

  message = String(dat[0]) + '.' + String(dat[1]) + "    " + String(dat[2]) + '.' + String(dat[3]);

  //  These next TWO lines are not really needed.
  Serial.println("========");
  Serial.println(message);

  client.publish("outTopic", message);

But I can't wrap my head around what the snprintf( ) command is/does.

I have a C++ book and there is no such animal.

Searching on google (see top) it fails.

Following other links, it is written in a language for someone who already knows the answer.

Could someone please help me understand what I need to do to get this "conflict" resolved.

first result on google for "snprintf c" is this page, which looks to me like it explains the whole thing in easy-to-understand terms.
http://joequery.me/code/snprintf-c/

There are many other excellent results from the same google search - I don't know what you're searching for that doesn't get results.

It's also almost identical to sprintf, about which oodles has been written, since it's a complicated function with a lot of caveats

Yeah, maybe it is clear for you, but not for me.

The link you supplied confused me even more as it has undeclared variable names throughout and calling routines/functions with qualifiers I don't understand.

(eg: If you have a routine which is set something like:

void routine_name(blah, foo, boo)
{
  code here
}

why would you call it with:

void routine_name();

Anyway, I'm still stuck/confused.

You wouldn't.

Try this
CPLUSPLUS snprintf()

try:

BUF_SIZE = 100;
char message[BUF_SIZE];

snprintf(message, BUF_SIZE, "%d.%d   %d.%d" , dat[0], dat[1], dat[2], dat[3]);
//replace %d to your dat[] type.

Sorry arduino_new,
But not only that doesn’t compile, but it’s more confusing than the reference pages,
Best example is a really simple snippet that will compile, so the OP can tweak it and learn.

int dat[] = {000, 111, 222, 333};
char message[100];  // max length you’ll need +1
snprintf(message, strlen(message), "%d.%d   %d.%d" , dat[0], dat[1], dat[2], dat[3]);
Serial.print(message);

-or-
If the predetermined ‘n’ isn’t needed,

int dat[] = {000, 111, 222, 333};
char message[32];  // max length you’ll need +1
sprintf(message, "%d.%d   %d.%d" , dat[0], dat[1], dat[2], dat[3]);
Serial.print(message);

Thanks to all.

I am suspicious I am missing something.

(I shall look at the link soon, but this is just "as it happens")

Using the code supplied, I made a simple sketch to try it and get a feel for the command.

void setup() 
{
  // put your setup code here, to run once:
  int dat[] = {2,3,4,5};
  char message[50];

}

void loop() 
{
  // put your main code here, to run repeatedly:
  snprintf(message, strlen(message), "%d.%d  %d.%d", dat[0], dat[1], dat[2], dat[3]);
  Serial.print(message);
  delay(3000);
  
}

Granted it is messy, but as suggested: I was trying to get a feel for the command.

Alas I got this error:

exit status 1
'message' was not declared in this scope

Errrr, message was declared/defined in the setup() part of the code.
What am I missing?

I am not trying to be difficult. I am showing what I see and what happens and therefore my confusion.

I am really wanting it to be a simple mis-understanding, but I can't say.

Thanks again.

Update:
If I make it like this:

void setup() 
{

}

void loop() 
{
  // put your main code here, to run repeatedly:
  int dat[] = {2,3,4,5};
  char message[50];
  snprintf(message, strlen(message), "%d.%d  %d.%d", dat[0], dat[1], dat[2], dat[3]);
  Serial.print(message);
  delay(3000);
  
}

It works.

Maybe not a good time to disclose this but I am using a 8266 wifi card (standalone) for the sketch.

But I don't get/understand why that point (wifi card) is critical for how the code is structured.

I thought that setup was done in setup() and the main code was in loop().
That is the whole point of structured languages.

Still confused.

Read about variable scope....

For you question I’m not sure where the difficulty is. The printf() function and its variants takes a template string which has placeholders for where your data will go. Those placeholders start with a percent sign and then you need to specify what the data type is and how the data needs to be formatted (for example you tell my data is an integer and I want it right aligned in a field of 10 chracaters always, or my data is a floating point number and I want to see it with 4 digits after the decimal point). You pass to the function a pointer to a place in memory (a buffer) that has to be big enough to hold the resulting string plus one extra character at the end (a null character). The version of the sprintf command that has the n (snprintf()) is to pass to the function the size of the buffer so that if the resulting string was too long it would know and not overwrite memory beyond the end of your buffer (which may trash other variables)

Careful on arduino not all placeholder formats are supported and also if you happen to fill up your buffer with the n version of the function, you will miss the trailing null char which will create challenges when using that cstring later on since it’s ill formatted (need the trailing null char)

most folks willing to build such a result string have just in mind printing it and forget that you don’t need to build it up first, you can just print it in chunks...

lost_and_confused:
Thanks to all.

I am suspicious I am missing something.

(I shall look at the link soon, but this is just "as it happens")

Using the code supplied, I made a simple sketch to try it and get a feel for the command.

void setup() 

{
 // put your setup code here, to run once:
 int dat = {2,3,4,5};
 char message[50];

}

void loop()
{
 // put your main code here, to run repeatedly:
 snprintf(message, strlen(message), "%d.%d  %d.%d", dat[0], dat[1], dat[2], dat[3]);
 Serial.print(message);
 delay(3000);
 
}




Granted it is messy, but as suggested: I was trying to get a feel for the command.

Alas I got this error:



exit status 1
'message' was not declared in this scope





Errrr, message was declared/defined in the setup() part of the code.
What am I missing?


I am not trying to be difficult. I am showing what I see and what happens and therefore my confusion.

I am really wanting it to be a simple mis-understanding, but I can't say.

Thanks again.



Update:
If I make it like this:


void setup()
{

}

void loop()
{
 // put your main code here, to run repeatedly:
 int dat = {2,3,4,5};
 char message[50];
 snprintf(message, strlen(message), "%d.%d  %d.%d", dat[0], dat[1], dat[2], dat[3]);
 Serial.print(message);
 delay(3000);
 
}




It works.

Maybe not a good time to disclose this but I am using a 8266 wifi card (standalone) for the sketch.

But I don't get/understand why that point (wifi card) is critical for how the code is structured.

I thought that setup was done in setup() and the main code was in loop().
That is the whole point of structured languages.

Still confused.

i thought you knew the basic since you have 900+ posts.
but here is the working sketch:

const int BUF_SIZE = 100; //size of buffer
char message[BUF_SIZE];

void setup()
{
}

void loop()
{

   snprintf(message, BUF_SIZE, "%d.%d  %d.%d", dat[0], dat[1], dat[2], dat[3]); //assume data[] is defined somewhere

}

OP already has the code working...

As J-M-L said...
OP’s current issue is scope of the variables - hence my example had the declarations blocked together with the code - literally so the variables and their values stayed ‘in scope’.

  // put your setup code here, to run once:
  int dat[] = {2,3,4,5};
  char message[50];

These lines are not ‘code’ as such.
They are declarations- and ‘scope’ comes into play when they are used.

Again thanks to all who take the time to reply.

Going through them as best I can:
arduino_new
Yes, I know some things.
Alas when I am compiling codes which "worked" now and am getting warnings about DEPRECATED COMMANDS, I am confused.

The code still works but it is ...... worrying to me..... that commands have "changed".

J-M-L
I get that functions need the right type of variables sent to them to work.
If I send text to a maths routine, it won't work.
Likewise if I send numbers to a routine expecting text: it will error.

The program "works" in that I can Serial.print the stuff and it looks ok.

When I add the MQTT part and it is sent to the client.publish( ) part, it expects the correct kind/type of data.
Be it "string" or "char", or what ever......

The line of code is:

client.publish("outTopic",message);

The error I am now getting is convoluted because of recent efforts to modify the code so I won't post the error, as it is not the "real" one.

But it says it wants a different type of variable.

Yes, it is probably me and not understanding what is written on the screen, but that is because I haven't been shown/had it explained to me. So it is basically gibberish.

I also accept that that shouldn't stop me learning, but my efforts at that are not yielding anything helpful. That isn't against anyone here. It is just somehow not "working" for me.

lastchancename
Thank you very much for the efforts.

Reading what you say, then those things need to be set up..... in the main part of the code?
loop() or main()?

But then: As I am calling them global names, when I call functions it all falls apart as the function can't see the name.

I am seriously missing something in how/where to declare things.

On a very side note:
The latest IDE - 1.8.5 - which I now have because of the wifi card:
On the old one, I could type away and CHECK the code and not have it saved.

The new IDE (even with the tick box un-ticked in the preferences about "save when verifying or uploading" it still wants to save.
So if I goof, I can't go back to the original one I loaded - unless I save the working one as a very temporary name and then remember to save it with the REAL NAME before quitting.

Can someone help me with that bit too?

https://www.google.com.au/search?q=c+what+is+scope&rlz=1C9BKJA_enAU768AU768&oq=c+what+is+scope+&aqs=chrome..69i57j0l3.13207j0j4&hl=en-GB&sourceid=chrome-mobile&ie=UTF-8

Read this !

But then: As I am calling them global names, when I call functions it all falls apart as the function can't see the name.

I am seriously missing something in how/where to declare things.

As mentionned in my post above - have you Read about variable scope....

The program "works" in that I can Serial.print the stuff and it looks ok.

When I add the MQTT part and it is sent to the client.publish( ) part, it expects the correct kind/type of data.
Be it "string" or "char", or what ever......

The line of code is:

client.publish("outTopic",message);

The error I am now getting is convoluted because of recent efforts to modify the code so I won't post the error, as it is not the "real" one.

But it says it wants a different type of variable.

If you keep the code and the error to yourself, we can’t help....

But I can't wrap my head around what the snprintf( ) command is/does.

I don't understand what it is that you don't understand...

The xxprintf(xxstuff, "format string", arg1, arg2, ...) functions all print the contents of the format string, with a variable number of arguments that are translated and substituted according to special sub-strings in the format string (something starting with the '%' character.)

Plain printf(...) is the base form. It sends the output to "standard output", which doesn't normally exist on an Arduino.
sprintf(outputstring, ...) sends the output to a C character array, which you must pre-allocate.
snprintf(outputstring, maxSize, ..) also outputs to the string, but allows you to provide the length of the array, and prevents the call from writing over the end of it (because some number was bigger than you expected, or something.)

So using lastchance's example

sprintf(message, "%d.%d   %d.%d" , dat[0], dat[1], dat[2], dat[3]);

the first thing sprintf() sees is "%d" - a substring that means "insert a decimal number." It grabs dat[0] (the first argument after the format string, converts it to a decimal ascii number, and starts filling in characters in message.
Then it sees a "." and copies that to message, after the number that's already there. Then another %d, so it adds dat[1] (converted) to the string, then there are a couple of spaces, and so on...

J-M-L and lastchancename:

I have read the stuff on SCOPE.

Honestly I can't see where it applies.
Indulge me.

In my code I have local and global variables.
I get that.
Global ones are set up in setup() or loop() (or is that main()? I'm not sure. Is there a difference? Does it have to be main() to work or does loop() work just as well?)

Variables named in functions are local to that function and unless stated as static, are wiped every time the function is called.

Variables named inside "for next" loops are local to that loop.

As best I can tell, I am obeying those rules.

I have the basic code written (on a remote machine) and it works.
I can "see" it on MQTT and talk to it and get message from it.
All that works.

I then add a routine to get the temperature and send it back via MQTT.
But when I try to make the message it gives an error. Something to do with TYPE. Not - as far as I can tell - SCOPE.

As I just also mentioned I am being driven crazy by the new IDE insisting on saving even when verifying the code.
So I fell into the trap and lost the base line working version.
Now I have the messy one with all the hacks trying to get rid of errors which seem to keep compounding.

So I don't want to insult anyone with that code. it is just too messy.
(Which I agree may not be helping)

But I have been back to the (luckily saved) working one and am trying all sorts of things.

Shall keep you posted when I get a "clear" error message which I can post.

It was for your post #6… which you later edited. You had the variables defined in your setup()

Don’t confuse defining (declaring what is the type of the variable AND allocating the necessary memory) and initializing (writing a value in the memory)

Global variables have memory allocated for the full duration of your program and can be initialized / updated / read anywhere in your code (to simplify*)

Local variables (if not static) will have memory allocated only for the duration of the scope and can be initialized / updated / read just in that scope

* ==> If you work with multiple files or have global and local variables that have the same name then it’s a bit more complicated

====

Just press escape or hit cancel to not save when you want to compile

This line in the code works:
(given the variables are set, etc...)

snprintf(msg, 75, "hello world #%ld", value)
client.publish("outTopic", msg);

So: it sets up msg as the thing to where the message is going.
It is 75 characters long.
And "hello world" with the value printed after it.
The #%ld is the "format"/"type"? to which the variable "value" is converted.

That works, and so is from where I have to extrapolate the next part:

That is where the snprintf(message, strlen(message), .........) part comes in.
(Sorry this is difficult as the code is on a remote machine and I can't simply cut/paste to this screen.
Attempts to set up a PUBLIC share have failed and I am not going to waste time on that as this problem is annoying enough at this point in time.)

So I do the snprintf(.....) stuff and "message" is where it is stored.

I then go down and client.publish("outTopic", message); and it spits the dummy at me.
Complaining something about char* and a whole lot more stuff which I don't find clear on what it means.

Shall try to copy errors, but remember: A lot of them I am getting are not "real" because of botched attempts being saved in the past. Not helping. :frowning:

When you dosnprintf(message, strlen(message), .........)if your message has not been set yet it’s length is 0 or whatever you have written there before, but it’s not the full buffer size... if message is just a char array and you want to use its size, you need to use the sizeof() command

Yes post here how you define message, how you fill it in. And the error you get

Ah!

Well, that's something I didn't get.

I thought strlen(message) got the length of the message.

But now you say sizeof(message) is what I need.

That'll help.

Just on a tangent:
To get around any problems with the extra stuff added to the "message" - like the dat stuff, could I just substitute a large enough number rather than the sizeof(message)?

So say it would be:

 snprintf(message, 20, ..........);

If you have

char message[100]; // reserve 100 bytes for the buffer
....
strcpy(message, “HELLO”); // copy HELLO with a trailing null char in the buffer

Then sizeof(message) is 100 and strlen(message) is 5 (because HELLO has 5 ascii letters)

To get around any problems with the extra stuff added to the "message" - like the dat stuff, could I just substitute a large enough number rather than the sizeof(message)?

You need to pass the size of the memory you allocated so that the function knows how many bytes it can write there without overflowing in the next variables; so don’t make up a number. If your buffer has 100 bytes, then say 100. The function will only take the space needed for your resulting string - without overflowing - may be only 23 bytes will be used and then add a trailing null char to denote the end of the cstring. You still have a 100 byte long buffer but only the first 23 bytes hold the message and because of the trailing null the other functions working on cstrings know where to stop when using the message.