Using printf with OLED display

I'm using the ss_oled library to run a 0.91" OLED display. I'd like to display RGB values on it which are held in a "colA[]" and "colB[]" array (each three elements). The following code performs this function nicely;

infoout = "RGB1";
    for(i=0; i <=2;i++){
    infoout = infoout + " ";
    infoout.concat (colA[i]);
    }

oledWriteString(0, 3, (char *)infoout.c_str(), 2, 0);

but the RGB values "Jump around" depending on their values...I'd like to pad them with zeroes and I thought printf could do this...but the following code results in RGB1 and no values (according to the display).

infoout = "RGB1";
    for(i=0; i <=2;i++){
    infoout = infoout + " ";
    infoout + printf("%03d",colA[i]);
    }
    oledWriteString(0, 3, (char *)infoout.c_str(), 2, 0);

Obviously I'm using printf wrong somehow...can anyone advise?

Thanks!

I learnt C about 3 years ago on PICs using XC8, having no high level language experience before. The person who taught me was pulling his hair out when I didn't understand what were, for him, simple concepts. When I see code like yours I cannot tell if it is doing something really clever I don't understand or if it's plain wrong. For this reason I've been waiting for someone else to reply.

The implementation of printf in XC8 is incomplete because it requires additional code to send the printed data to whatever output device is being used. As the design and configuration of that device is up to the designer it is beyond the ability of Microchip to write that code, so they provide a stub function in XC8 that has to be completed for the design in question. I've not seen any equivalent comment for the implementation of C++ that Arduino uses, but I would have thought something similar was required. How do you think printf gets its output to the device you want to print on? I don't know, but your code doesn't look right to me in that I would not expect printf to leave its output in a string, which I think is what you want, I would expect it to send its output to a printer or other device.

sprintf DOES leave its output as a string, which is what I think you want. Here is an example from my code, which does work:

void HMI_display_clock() {
  char timestring[9];
  sprintf(timestring, "%02d:%02d:%02d ", clock.hour, clock.minute, clock.second);
  Serial.println(timestring);
  Serial1.print(F("t1.txt=\""));
  Serial1.print(timestring);
  Serial1.print("\"");
  Serial1.write(0xff);
  Serial1.write(0xff);
  Serial1.write(0xff);
}

Note I use a character array for the output. I've never properly understood the difference between a character array and a string as they both seem to do the same job with slight differences, and I find a character array easier to understand.

This:

infoout + printf("%03d",colA[i]);

I don't understand. You have added infoout to printf... but not put the result anywhere. I think that in any case printf returns the number of characters it has printed, not text, which is what I think you want (but if someone knows I'm wrong...).

I hope that somewhere in the above is something that helps you, and I hope someone else comes along and adds something that teaches both of us something.

Thanks for using my library :slight_smile:
I believe what you're looking for is sprintf(). This will print a formatted string into another string. You can format the numeric output of printf/sprintf with modifiers to tell it how many digits to use. I usually use hex values for things like this since I find it easier to read, but you can use decimal too. Here's one way to do it:

char szOutput[32], szTemp[8];
int i;
szOutput[0] = 0; // start with empty string
for (i=0; i<3; i++)
{
  sprintf(szTemp, "%03d ", colA[i]); // notice the trailing space
  strcat(szOutput, szTemp);
}
oledWriteString(0,3, szOutput, FONT_LARGE, 0);

Perry: you are very astute...I'm making this up as I go along with no formal training, just a (reasonably) good grasp of logic and frequent searching of Google and the Arduino reference library.

PerryBebbington:
This:

infoout + printf("%03d",colA[i]);

I don't understand. You have added infoout to printf... but not put the result anywhere.

Rather than "added" I was aiming for "appended"...since

infoout.concat (colA[i]);

appends the value of colA[i] (sorry - strange edit to avoid forum italicising my post!) to the existing infoout value of "RGB1", it gives "RGB1 98" (or whatever the value of colA[0] happens to be, then "RGB1 98 255", "RGB1 98 255 3" for colA[1] and colA[2].

I realised I couldn't use "concat" with "printf", as the required variables are string.concat(integer) so naively hoped the original "+" would work for two strings.

Looks like i had the entirely wrong command, wil give it a go with sprintf instead.

bitbank:
Thanks for using my library :slight_smile:

Thanks for writing it - moreover, thanks for actually listing what the commands are to use it, which seems to be a rarity in everyone's libraries from the individual all the way through to big companies like Adafruit.

I'm using it on a Trinket Pro, so memory is the big issue, hence using yours rather than other, larger versions. YOur library is quite intuitive (for which many thanks!) , just a pity that I don't have the memory to get the "pixel by pixel" control level working.

GreyArea:
I'm using it on a Trinket Pro, so memory is the big issue, hence using yours rather than other, larger versions. YOur library is quite intuitive (for which many thanks!) , just a pity that I don't have the memory to get the "pixel by pixel" control level working.

I can add limited graphics primitives such as straight lines, boxes and even pixels without needing a backing buffer. It depends what you need. Can you describe what you need to accomplish? The linker is pretty smart about not linking unused code, so I can potentially add a bunch of things to the library that won't necessarily make it take up more FLASH space if you don't make use of those functions.

bitbank:
Thanks for using my library :slight_smile:

Thanks again - and foithermore... :slight_smile:

char szOutput[32], szTemp[8];
int i;
szOutput[0] = 0; // start with empty string
for (i=0; i<3; i++)
{
  sprintf(szTemp, "%03d ", colA[i]); // notice the trailing space
  strcat(szOutput, szTemp);
}
oledWriteString(0,3, szOutput, FONT_LARGE, 0);

I haz questions, my apologies if they turn out to be of the "dumb" variety...
szOutput[32] and szTemp[8]...what are they doing? 32 I assume because it's a 32-bit display? 8 is the size of the character perhaps?

One thing I do want is not to unecessarily be clearing the display all the time - things like "RGB1" are semipermanent headings. The only "official" clearing statement seems to be oledFill(0). I'm using a string of spaces to "blank" just the characters I want (using the x and y positions).

Is this valid, or am I going to run into potential problems?

AS to graphics....dunno yet, just thought it would be nice. I'm only using a tiny little 0.91" display as some way of communicating basic info to a user for an LED sculpture about what colours are being used etc...with the intent that pressing a button (when I've added some buttons) will run some code (when I've written the code) that will enable them to potentially store "favourite" settings for the lighting effects.

GreyArea:
Thanks again - and foithermore... :slight_smile:

char szOutput[32], szTemp[8];

int i;
szOutput[0] = 0; // start with empty string
for (i=0; i<3; i++)
{
 sprintf(szTemp, "%03d ", colA[i]); // notice the trailing space
 strcat(szOutput, szTemp);
}
oledWriteString(0,3, szOutput, FONT_LARGE, 0);





I haz questions, my apologies if they turn out to be of the "dumb" variety...
szOutput[32] and szTemp[8]...what are they doing? 32 I assume because it's a 32-bit display? 8 is the size of the character perhaps?

One thing I do want is not to unecessarily be clearing the display all the time - things like "RGB1" are semipermanent headings. The only "official" clearing statement seems to be oledFill(0). I'm using a string of spaces to "blank" just the characters I want (using the x and y positions).

Is this valid, or am I going to run into potential problems?

AS to graphics....dunno yet, just thought it would be nice. I'm only using a tiny little 0.91" display as some way of communicating basic info to a user for an LED sculpture about what colours are being used etc...with the intent that pressing a button (when I've added some buttons) will run some code (when I've written the code) that will enable them to potentially store "favourite" settings for the lighting effects.

You don't need to clear the screen constantly if you're overwriting existing text. The characters are written such that the black and white pixels overwrite what was under them, so it's not like drawing ink on paper. Your new text will completely overwrite the old if you write it in the same position.
I defined character arrays of length 32 and 8 above to have enough space to fit the characters you were writing. C/C++ memory management can be "dangerous" if you don't know what you're doing and write past the ends of buffers. It's not reasonable for me to explain C programming in this thread. Please have a look at a reference on C strings/character arrays and you'll see why I did what I did.

That was already explanation enough...you limited the memory to the right value to stop it writing where it shouldn't.

I didn't the first time (out of oversight whilst changing variable names, which explains why my animation now freezes. I put your values in, and hey presto, fixed.

Sllightly modified to following;

infotemp[8] = 0;
    sprintf(infoout,"RGB1 ");

    for (i = 0; i <= 2; i++) {
      sprintf(infotemp, "%03d ", colA[i]);
      strcat(infoout, infotemp);
    }
    oledWriteString(0, 0, (char *)infoout, 2, 0);

    infotemp[8] = 0;
    sprintf(infoout,"RGB2 ");
    
    for (i = 0; i <= 2; i++) {
      sprintf(infotemp, "%03d ", colB[i]);
      strcat(infoout, infotemp);
    }
    oledWriteString(0, 3, (char *) infoout, 2, 0);

    delay(150);

Was playing with writing the RGB1 and RGB2 at start as titles, and then oledWriting RGB data to x,y = 31,0, but its giving me a little trouble. Will writing RGB1 ONCE and then rewriting RRR GGG BBB values be any noticeably more efficient than writing the whole of "RGB1 RRR GGG BBB" EVERY time? My mind says it should be, but that the gain is probably negligible...in which case I can move on to streamlining the rest of my code (or in my case, cod - its always a bit fishy.)

bitbank:
Thanks for using my library :slight_smile:

Having a slightly different problem with it...changed processor from 5v PRO Trinket to Trinket M0 and it's now throwing these errors...is this something that can be fixed by us (you! haha,) or do we need to wait for Adafruit to fix it?

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:408:6: error: 'SERCOM4' was not declared in this scope

{ &SERCOM4->SPI.DATA.reg, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:408:29: error: 'SERCOM4_DMAC_ID_TX' was not declared in this scope

{ &SERCOM4->SPI.DATA.reg, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:408:49: error: 'SERCOM4_DMAC_ID_RX' was not declared in this scope

{ &SERCOM4->SPI.DATA.reg, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:409:6: error: 'SERCOM5' was not declared in this scope

{ &SERCOM5->SPI.DATA.reg, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:409:29: error: 'SERCOM5_DMAC_ID_TX' was not declared in this scope

{ &SERCOM5->SPI.DATA.reg, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:409:49: error: 'SERCOM5_DMAC_ID_RX' was not declared in this scope

{ &SERCOM5->SPI.DATA.reg, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX },

GreyArea:
Having a slightly different problem with it...changed processor from 5v PRO Trinket to Trinket M0 and it's now throwing these errors...is this something that can be fixed by us (you! haha,) or do we need to wait for Adafruit to fix it?

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:408:6: error: 'SERCOM4' was not declared in this scope

{ &SERCOM4->SPI.DATA.reg, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:408:29: error: 'SERCOM4_DMAC_ID_TX' was not declared in this scope

{ &SERCOM4->SPI.DATA.reg, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:408:49: error: 'SERCOM4_DMAC_ID_RX' was not declared in this scope

{ &SERCOM4->SPI.DATA.reg, SERCOM4_DMAC_ID_TX, SERCOM4_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:409:6: error: 'SERCOM5' was not declared in this scope

{ &SERCOM5->SPI.DATA.reg, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:409:29: error: 'SERCOM5_DMAC_ID_TX' was not declared in this scope

{ &SERCOM5->SPI.DATA.reg, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX },

^

Directory\Local\Arduino15\packages\adafruit\hardware\samd\1.5.0\libraries\SPI\SPI.cpp:409:49: error: 'SERCOM5_DMAC_ID_RX' was not declared in this scope

{ &SERCOM5->SPI.DATA.reg, SERCOM5_DMAC_ID_TX, SERCOM5_DMAC_ID_RX },

I got the same error with the Trinket M0. Here's the fix (line 408):

OH you absolute star...erm...

...where do I put that? in the library? in my code?

OK...I figured it I think...since the error occurs at line 409 in the cpp file...and you suggest inserting it at line 408...logic says it goes in SPI.cpp...?

Yes, in SPI.cpp

Works like a charm, Karma added.

For other noobs like me who may be reading this...it's only necessary to add the "if...endif" lines around the existing "&SERCOM4" and "&SERCOM5" lines...