Array size of a sprintf created string

Hello there,

right now im working on dot matrix clock, able to display some scrolling text depending on the time.
Hardware used: arduino uno r2, 2x sure 8x32 matrix displays (ht1632c), ds1307 based RTC. Arduino IDE 1.0.5-r2.

Currenty im using this code and it is working just fine, but im pretty sure there is a much more elegant solution for this function. Besides its a pain in the ass to modify the whole "what text to show to which time" thing.

#include <HT1632.h>
#include <font_8x4.h>

char t1200 [] = "some text 1";
char t1230 [] = "some text 2";
char display [] = "hh:mm:ss";

//some RTC functions.....................

//initialising the matrix
void setup () {
 HT1632.begin(12, 13, 11, 10); // (CS1, CS2, WR, DATA)

//printing the text to the dot matrix
void loop () {
  
  int hh = hour;
  int mm = minute;
  int ss = second;
  
  //replacing time with predifined text
   if (hh == 12 && mm == 0)
   {
     sprintf(display, t1200);
     wd = HT1632.getTextWidth(t1200, FONT_8X4_END, FONT_8X4_HEIGHT);
   }
   else if (hh == 12 && mm == 30)
   {
     sprintf(display, t1230);
     wd = HT1632.getTextWidth(t1230, FONT_8X4_END, FONT_8X4_HEIGHT);
   }
   else
   {
      sprintf(display, "%02d:%02d:%02d", hh, mm, ss);
      wd = HT1632.getTextWidth(disp, FONT_8X4_END, FONT_8X4_HEIGHT);
   }
  
  HT1632.renderTarget(0);
  HT1632.clear();
  HT1632.drawText(display, OUT_SIZE * 2 - i, 0, FONT_8X4, FONT_8X4_END, FONT_8X4_HEIGHT);
  HT1632.render();
  
  HT1632.renderTarget(1);
  HT1632.clear();
  HT1632.drawText(display, OUT_SIZE - i, 0, FONT_8X4, FONT_8X4_END, FONT_8X4_HEIGHT);
  HT1632.render();
  
  i = (i+1)%(wd + OUT_SIZE * 2);
  
 delay(100);
}

That's not how to use sprintf. You must provide a character buffer long enough for all
situations - sprintf modifies this buffer (it sets a null at the end).

As your code is written it corrupts memory as t1200 is longer than display.

Well, this code compiles and runs without trouble.
Has anyone to say something concerning the main question?

That code is corrupting memory, its broken.

loock at this please

I suggest you save yourself a great deal of grief use snprintf instead...

http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#ga77070c245d4ca4f7ec7d7144260fb875

mike1988aaa:
loock at this please

Oh specious reasoning. I love the human brain.

You are overwriting memory because t1200 and t1230 are both longer than display.

Why are you trying to use sprintf with these strings at all? Just pass the right string
to drawText in the first place?

And you haven't declared disp.

Thank you for your answer. My C knowledge is less than basic, im not really getting your reference. Would you be so kind and give me a example using my variables?

proof.ino (3.86 KB)

Pass t1200 to drawText at 12:00, pass t1230 at 12:30, otherwise
use sprintf to update display and pass that.

MarkT:
You are overwriting memory because t1200 and t1230 are both longer than display.

Why are you trying to use sprintf with these strings at all? Just pass the right string
to drawText in the first place?

And you haven't declared disp.

Im trying to keep the code as minimal as possible. By inserting drawText into the if loop there will be 8 lines of code more then right now. Above i posted the full code.

Concerning disp, you are right it should be display.

MarkT:
Pass t1200 to drawText at 12:00, pass t1230 at 12:30, otherwise
use sprintf to update display and pass that.

You got it. That is what i am asking for. how to do that?
And i am already using sprintf to update display. :wink: Take a better look at the code.

We must be at cross-purposes, I understand your code, I'm saying don't always pass
display.

Just make display longer, as you have a 32 column LCD, eg.

char display [33];   // allow one byte for nul-delimiter

Thank you Nick! It does work perfectly. Now there is only one line per text replacement inside the if loop. The getTextWidth function is also mentioned once. That makes the code easyer to modify.

This is what the final version looks like:

char t1200 [] = "some text 1";
char t1230 [] = "some text 2";
char display [33] = "hh:mm:ss";

//some code here
//.
//.
//.
//.

void loop () {
  
  //Matrix brightness 16 steps 1-16 (max L =1024 min L = 0)
  L = analogRead(LIn);
  Serial.print("brightness ");
  Serial.print(L/64+1);
  Serial.print("  ");
  HT1632.setBrightness((L/64+1), 0b0011);
  
  int hh = hour;
  int mm = minute;
  int ss = second;
  int DD = dayOfMonth;
  int MM = month;
  int YY = year;
  int WD = dayOfWeek;
  
   //Magic starts here
   if (hh == 12 && mm == 0)
   {
     sprintf(display, t1200);
   }
   else if (hh == 12 && mm == 30)
   {
     sprintf(display, t1230);
   }
   else
   {
      sprintf(display, "%02d:%02d:%02d", hh, mm, ss); 
   }
  
  wd = HT1632.getTextWidth(display, FONT_8X4_END, FONT_8X4_HEIGHT);
  
  HT1632.renderTarget(0);
  HT1632.clear();
  HT1632.drawText(display, OUT_SIZE * 2 - i, 0, FONT_8X4, FONT_8X4_END, FONT_8X4_HEIGHT);
  HT1632.render();
  
  HT1632.renderTarget(1);
  HT1632.clear();
  HT1632.drawText(display, OUT_SIZE - i, 0, FONT_8X4, FONT_8X4_END, FONT_8X4_HEIGHT);
  HT1632.render();
  
  i = (i+1)%(wd + OUT_SIZE * 2);
  
 delay(100);
 getDateDs1307();//get the time data from tiny RTC;
}
char display [33] = "hh:mm:ss";

A 33 byte buffer for 9 bytes of data?

AWOL:

char display [33] = "hh:mm:ss";

A 33 byte buffer for 9 bytes of data?

Yes, because the content of display is sometimes replaced with some text.

But that's perverse - just pass the string you want to see to drawText, rather than copying it
into display. The sprintf to set display is done to convert the time to a string, using sprintf
to copy a fixed string is opaque and confusing.

We were explicitly asked for the elegant solution....