using tft.pushColor to display PROGMEM rgb565 bitmaps

I’m powering the breadboard Atmega1284 using th FTDI Friend’s GND and VCC leads.

Maybe the verbose output will tell you more than it’s telling me:

avrdude: Version 6.0.1, compiled on Apr 15 2015 at 19:59:58
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2009 Joerg Wunsch

         System wide configuration file is "C:\Program Files\Arduino\hardware\tools\avr/etc/avrdude.conf"

         Using Port                    : COM20
         Using Programmer              : arduino
         Overriding Baud Rate          : 115200
         AVR Part                      : ATmega1284P
         Chip Erase delay              : 55000 us
         PAGEL                         : PD7
         BS2                           : PA0
         RESET disposition             : dedicated
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom        65    10   128    0 no       4096    8      0  9000  9000 0xff 0xff
           flash         65    10   256    0 yes    131072  256    512  4500  4500 0xff 0xff
           lock           0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           lfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           efuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00

         Programmer Type : Arduino
         Description     : Arduino
         Hardware Version: 3
         Firmware Version: 4.5
         Vtarget         : 0.3 V
         Varef           : 0.3 V
         Oscillator      : 28.800 kHz
         SCK period      : 3.3 us

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e9705
avrdude: reading input file "C:\Users\darkmind\AppData\Local\Temp\build97f72ef5debfac14b77c00792f51d20d.tmp/HeadUnit.ino.hex"
avrdude: writing flash (29892 bytes):

Writing | ####
avrdude: stk500_paged_write(): (a) protocol error, expect=0x14, resp=0x62
avrdude: stk500_recv(): programmer is not responding
The selected serial port avrdude: stk500_recv(): programmer is not responding
 does not exist or the board is not connected

I know that cars are a “noisy” environment for any circut. Among other things, I will use chokes and 22K/10K voltage dividers on input lines, as well as chokes, picofuses and TVS diodes on the power supplies. I plan to give my CarDuino network its own dedicated power supply off the ignition terminal, with its own fuse in the central electrical panel. Every individual slave module will have its own on-board 5V power regulator, I will use various denominations of Recom R78E5.0 regulators. They are good for over 24V input voltage. And the I2C bus will be pulled up to 5V throughout the network.

The programming report indicates that the processor has been reset and comms established but during the writing of the sketch image it failed to get an acknowledge to a block of data.

This suggests to me the ATmega1284 board has one or more of the following problems:

  • Baud rate error due to inaccurate 16MHz oscillator (115200 is on the limit already for baud rate tolerance)
  • Inadequate decoupling on the processor (at least 100nf close to chip + 470uF)
  • Noise on DTR/reset drive line (add 22 to 100pF on RESET line to GND)
  • An older boot loader is being used and there are 3 sequential ASCII exclamation mark codes in the compiled sketch block (known bug - causes Mega bootloader to abort)
  • Intermittent connection or supply rail collapsing due to FLASHing current demand or floating lines to TFT causing current demand peaks(does removing TFT display help)

hm well I've got a 220 uF cap across Vcc and GND now, it's the biggest I've got here tonight. Didn't make a difference.

I've also got a spare 1284P-PU and just wired it up on another breadboard, with the minimum bare essentials, i.e. 16MHz crystal with 18 pF caps, 100 uF decoupling cap right next to the Vcc/GND pins, 10K to +5V on reset, and AVcc and AGND wired to GND and Vcc. And a 0.1 uF cap on RST on the FTDI Friend's RST lead.

The problem was the same, which also answered the question whether the TFT was drawing too much power.

I've tried putting a 22 pF cap to GND on the reset line, but that only led to a "programmer not responding" error.

The power supply should be solid, I've got several interconnected breadboards here that get 5V from two Arduino Uno boards other than the FTDI Friend that's hooked up to the 1284. But that also seems to make no difference, I've got the same problem with or without the 1284 breadboard connected to the other breadboards.

The bootloader I burned onto the chip was burned using Nick Gammon's "Atmega_Board_Programmer" sketch:

// Atmega chip programmer
// Author: Nick Gammon
// Date: 22nd May 2012
// Version: 1.36

Not idea if that signifies an older version in which that bootloader bug was present. The bootloader burn itself using this sketch was apparently a success, going by the serial output that was visible while running the sketch.

You need high frequency decoupling too, ceramic 10nF or 100nF caps. The large polarized caps have a high ESR (Effective Series Resistance) in the multi-MHz range.

22pF should have no significant effect on normal operation if you have ~100nanoFarads between DTR and RESET. This points to a problem somewhere or the capacitor was not 22picoFarads! This is what is typically fitted to Mega boards for noise suppression.

Xtal lines must be short, (proto boards may cause problems here due to capacitance and pickup).

I doubt this is the !!! bootloader code problem as a sketch with that sequeence would never complete upload and it was fixed circa 2010.

I would buy a Mega clone and get that working until you have the code finalised, that will save a lot of grief.

The upload report indicates the board support package is using a very large delay for the chip erase delay of 55000us, usually this is more like 9000us. Check you are using a well proven board package for the 1284 chip.

Thanks for all the help... but I'm still struggling to understand why it's so difficult to program a breadboard 1284... I've programmed numerous 328P-PU and Attiny85 on breadboards, and for the most part, uploads always went without a hitch.

Not sure if it sheds any more light on the problem, but I am using the "maniacbug Mighty1284P 16 MHz using Optiboot" board definitions off Github.

I had a look at the maniacbug stuff it looks OK. Optiboot is a non-standard bootloader but I have used it on 328 processors.

If you are using the 40pin packaged part then try adding 100nF decoupling close to the chip on both sides of the device. Wire power up on both sides too.

I have used the 1284 without problems before, so it can work fine. But that is not much consolation for you... I have now run out of ideas to help you!

And I feel bad because you've racked your brain for me here unsuccessfully...

I guess I am just going to have to keep trying... I'll try going through your list of suggestions again one by one tomorrow... maybe I missed something that will do the trick.

Thanks anyway for now...

I checked what board package I used with the 1284 and it was from the Microduino-Core+ and I used the board package downloaded from here. That was a few years ago though and it is an older IDE version.

If it is circuit level problem though it is unlikely that board package will change things though it would co-exist with your current setup.

well I’ve got a spare 1284P-PU here and still one or two empy breadboards… maybe a good approach will be to carefully build a test circuit from the ground up, with just the bare essentials that are needed to program it at all… perhaps that way it’s easier to see where the problem might lie.

Do you think completely reinstalling the Arduino IDE would help? Maybe there’s a bug in the definition files somewhere as they exist on my computer?

Ah, see this link.

It describes the same problem you have.

Thanks, I will... first thing tomorrow morning... it's 01:57am here, and really way past my bedtime... :(

Well at least I just finished one thing successfully today that I managed to upload... an adaptive dimmer for my TFT display. I will connect one Atmega pin to the instrument cluster lighting... if the lights are off, the display will be at full brightness in "daytime" mode... but then when you turn on the lights, (presumably at night), brightness is reduced by half, and also dependent on the instrument cluster's own brightness wheel pot.

int lightSense = analogRead(lightSensePin); // Is the light switch on?

if (lightSense > 5 ) {  // If it's on... taking into account a bit of "noise" on the pin...

     int doDim = (int) (255 * 0.5 * lightSense / 1023); // ...dim it like this.
     analogWrite(displayDimmer, doDim); // nighttime operation
}

else analogWrite(displayDimmer, 255); // if the lights are off, daylight operation.

ok... bedtime... :)

You might like to test visibility of the display outdoors, which is typically quite poor. You may need a sunshade or keep the soft-top up :)

ok so I have tried putting a lowpass filter into my circuit, as described in that thread. 0.1 uF cap to GND on pin 14 of the 1284P-PU, 10K resistor in series on the RXD0 pin (pin 14).

It has only worsened the problem. Now sketches don't even begin to upload, and all I get is:

avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0x53
avrdude: stk500_getsync() attempt 2 of 10: not in sync: resp=0x65
avrdude: stk500_getsync() attempt 3 of 10: not in sync: resp=0x72
avrdude: stk500_getsync() attempt 4 of 10: not in sync: resp=0x69
avrdude: stk500_getsync() attempt 5 of 10: not in sync: resp=0x61
avrdude: stk500_getsync() attempt 6 of 10: not in sync: resp=0x6c
avrdude: stk500_getsync() attempt 7 of 10: not in sync: resp=0x20
avrdude: stk500_getsync() attempt 8 of 10: not in sync: resp=0x4d
avrdude: stk500_getsync() attempt 9 of 10: not in sync: resp=0x6f
avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0x6e

This is really starting to drive me crazy. Right now, progress on my CarDuino hinges on the 1284, and has all but ground to a halt because it takes up to half an hour to successfully upload any changes and additions to my code. I've written complete and finished sketches for the other chips in my I2C network in as little as a day and a half, but with the sketch for the Atmega1284P, it's been well over two weeks and I'm still just baby steps from where I started.

I've also just tried pulling RX up to +5V using a 10K resistor, to eliminate noise that might be present on the pin, but it also had no effect.

I see there is an unfortunate error in the advice given in the topic linked to in post #29. It should say 10K resistor in series and a 100 pF (picofarad not nanofarad) capacitor to ground, both put very close to the RX pin 14. This will give a cutoff frequency of around 1MHz, whereas 100nF would give a cutoff of about 1kHz and stop 115200 baud getting though!

Decrease the resistor to 4K7 if that does not work.

ok so I’ve got all the upload issues out of the way, having installed a different bootloader and what-have-you…

Now, back to the issue at hand of printing out sensor readings on my screen as a succession of bitmaps…

I’ve been busy concocting a function to do this.

void floatToProgmem(float number, byte x_0, byte y_0, byte x_dim, byte y_dim, int bgColor) {

//Converting float into char array
  int numberLength = sizeof(number);
  char charBufFloat[numberLength];
  dtostrf(number, numberLength, 2, charBufFloat);

// Generating PROGMEM array name for every digit
  for (int i = 0; i <= (sizeof(charBufFloat) - 1); i++) {
    String fileNameString = (String) charBufFloat[i];
    fileNameString.replace(".", "d");                      // replacing dots for "d"s to be able to call dot bitmap, named "cd"
    String bitmapName = "c" + fileNameString;        // Assembling PROGMEM bitmap name; if I don't put a character in front of it, it tells me to use an "unqualified id".
 
   //Calculating coordinates for digit printouts
    byte start_x = x_0 + (i * x_dim) - i;
    byte end_x = x_0 + ((i + 1) * x_dim) - i;
    byte end_y = y_0 + y_dim - 1;

    tft.drawRect(start_x, y_0, end_x, end_y, bgColor);       // Drawing (white) background

    tft.setAddrWindow(start_x, y_0, end_x, end_y);           // Setting address window for each bitmap

    for (int img2Byte = 0;  img2Byte < (sizeof(bitmapName) / 2); img2Byte++) {        // Pushing pixels onto screen for each bitmap
      
      uint16_t pixel = pgm_read_dword(&bitmapName[img2Byte]);
      tft.pushColor(pixel);
       }

    start_x = end_x + 1; // Calculating new x coordinate for next bitmap digit
    i++;
  }
}

I then call the function as, for example:

floatToProgmem(fuelConsumptionValue, 45, 87, 18, 25, ST7735_WHITE);

…but this isn’t really working so far. I’ve checked it as far as making sure “bitmapName” is correct, but somehow I can’t get this function to display the digits correctly. In truth, they’re not displayed at all. I’ve had it print out via serial monitor the calculated coordinates, and somehow they’re way off. But also, I’ve tried to have the bitmap arrays just printed out via serial, but all I get is the first four values of each array.

The PROGMEM bitmaps are each 18 by 25 pixels, but I would like to keep the function flexible for use with different sized bitmaps.

What’s going wrong here? :frowning:

This function will be central to the way I will display sensor readings on the screen. The idea is that I will be able to show every sensor reading in “neat” full color, anti-aliased bitmaps, simply by calling this function.

Eventually, I would also like to do this for sensor readings that are present as integers, without decimals, but I’m not really sure yet how to define the function parameter “float number” otherwise, so that it will accept both floats and integers as needed.

EDIT:

I’ve now made a function which should theoretically be able to process both integers and floats. But it too has not led to the desired results on the screen. So far, it still displays nothing.

Here are the relevant code parts again… I’ve had to leave out the PROGMEM bitmaps because it was telling me that it made this post too long… but they’re basically 18x24 color bitmaps that are - at the moment - 18 pixels wide and 25 pixels high.

They all look like this:

const uint16_t c9[450] PROGMEM = {
  0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
  0xffff, 0xffff, 0xdedb, 0x4a69, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4a69, 0x8c71, 0xad55, 0xffff, 0xffff,
  0xffff, 0xffdf, 0x738e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x632c, 0x5aeb, 0x10a2, 0xb596, 0xffff,
  0xffff, 0xffff, 0xffff, 0x9cf3, 0x10a2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2965, 0x738e, 0x2965, 0x0841, 0xa514, 0xffff,
  0xffff, 0xe73c, 0x4228, 0x0000, 0x94b2, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x9492, 0x0861, 0x0861, 0xad75, 0xffff,
  0xffff, 0xe71c, 0x39e7, 0x0000, 0x6b6d, 0xef5d, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x8c51, 0x0861, 0x1082, 0xbdd7, 0xffff,
  0xffff, 0xdefb, 0x3186, 0x0000, 0x73ae, 0xef7d, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x7bef, 0x0841, 0x10a2, 0xc638, 0xffff,
  0xffff, 0xdedb, 0x2945, 0x0000, 0x7bef, 0xf79e, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x73ae, 0x0020, 0x18c3, 0xce79, 0xffff,
  0xffff, 0xd6ba, 0x18e3, 0x0000, 0x8c51, 0xf7be, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x6b4d, 0x0020, 0x18e3, 0xd6ba, 0xffff,
  0xffff, 0xd69a, 0x10a2, 0x0000, 0x9492, 0xf7be, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x630c, 0x0020, 0x2104, 0xe71c, 0xffff,
  0xffff, 0xce79, 0x0861, 0x0000, 0x9cd3, 0xffdf, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x5acb, 0x0000, 0x2124, 0xef5d, 0xffff,
  0xffff, 0xce59, 0x0020, 0x2965, 0x0861, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2965, 0xef7d, 0xffff,
  0xffff, 0xdefb, 0x39e7, 0x0861, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x528a, 0xffff, 0xffff,
  0xffff, 0xffff, 0xffff, 0xf7be, 0x1082, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8430, 0x9cd3, 0x52aa, 0xffff, 0xffff,
  0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffdf, 0x9492, 0x0000, 0x39e7, 0xffff, 0xffff,
  0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xef7d, 0x39c7, 0x0000, 0x4228, 0xffff, 0xffff,
  0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xe73c, 0x31a6, 0x0000, 0x4a69, 0xffff, 0xffff,
  0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xdefb, 0x2965, 0x0000, 0x5acb, 0xffff, 0xffff,
  0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xd6ba, 0x2945, 0x0000, 0x630c, 0xffff, 0xffff,
  0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xce79, 0x2124, 0x0000, 0x6b6d, 0xffff, 0xffff,
  0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xc638, 0x2104, 0x0020, 0x73ae, 0xffff, 0xffff,
  0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xbdd7, 0x18e3, 0x0020, 0x8410, 0xffff, 0xffff,
  0xffff, 0xffff, 0xb596, 0x18e3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2124, 0x6b6d, 0x39e7, 0x0020, 0x8c71, 0xffff, 0xffff,
  0xffdf, 0x738e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x632c, 0x5aeb, 0x10a2, 0xb596, 0xffff, 0xffff,
  0xffff, 0xd69a, 0x39e7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x630c, 0x8430, 0xbdd7, 0xffff, 0xffff, 0xffff
};

Yes, these are black, but the plan is also to store a second set of bitmaps in PROGMEM, in red, which will be displayed when sensor readings reach critical levels (i.e. for example when there’s less than five liters left in the fuel tank, or when coolant or oil temperatures rise towards overheating).

And how here’s the rest of the relevant parts of my code:

char valueBuf[6]; // Our sensor reading gets stored in this array
byte numberLength;

void valueToBitmaps(float numberFloat, int numberInt, byte x_0, byte y_0, byte x_dim, byte y_dim) {

  if (numberInt == 0) {                                         // Are we floating...

    String numberFloatString = (String) numberFloat; 
    numberLength = numberFloatString.length();
    char floatBuf[numberLength];
    dtostrf(numberFloat, numberLength, 2, floatBuf);
    for (int i = 0; i < numberLength; i++) valueBuf[i] = floatBuf[i]; 
  }

  else if (numberFloat == 0) {                                // ...or are we integering? ;-)

    String numberIntString = (String) numberInt;
    numberLength = numberIntString.length();
    char intBuf[numberLength];
    itoa(numberInt, intBuf, 10);
    for (int i = 0; i < numberLength; i++) valueBuf[i] = intBuf[i];
  }

  for (int j = 0; j < numberLength; j++) {               

    // Generating the name of the bitmap that we want to load from PROGMEM
    String fileNameString = (String) valueBuf[j];
    fileNameString.replace(".", "d");
    String bitmapName = "c" + fileNameString;    
 
    // Calculating address window coordinates
    byte start_x = x_0 + (j * x_dim) - j;
    byte start_y = y_0;
    byte end_x = x_0 + ((j + 1) * x_dim) - j;
    byte end_y = y_0 + y_dim - 1;

    // Preparing address window
    tft.setAddrWindow(start_x, start_y, end_x, end_y);

    for (int img2Byte = 0;  img2Byte < (sizeof(bitmapName) / 2); img2Byte++) { //counting elements
      uint16_t pixel = pgm_read_dword(&bitmapName[img2Byte]);
      tft.pushColor(pixel);
    }

    //Setting starting coordinates for next bitmap
    x_0 = end_x + 1;
    j++;
  }
}

void setup{}

void loop{ 

valueToBitmaps(0, rpmValue, 45, 35, 18, 25);
valueToBitmaps(fuelConsumptionValue, 0, 45, 87, 18, 25);
}

It’d be great to get this function functioning… because as I said, it’s going to be one of the most vital functions in the entire sketch.

An array name is actually a pointer so you can't simply construct a pointer string name as you just end up creating a pointer to the string, not the array. You could use this approach with file names of bitmaps stored on an SD card though.

There are a number of ways of referencing a set of arrays:

  • Have an array of array pointers (this is the method I use to map an ASCII value on to a set of font bitmaps)
  • Put all the bitmaps in one array and have a table of offsets (used by Adafruit)
  • Use a switch+case construct (simple to implement)

bodmer: An array name is actually a pointer so you can't simply construct a pointer string name as you just end up creating a pointer to the string, not the array. You could use this approach with file names of bitmaps stored on an SD card though.

I see... well, I'll make a note of that. Good to know. But the idea is still to have all the number icons in PROGMEM...

bodmer: There are a number of ways of referencing a set of arrays:

  • Have an array of array pointers (this is the method I use to map an ASCII value on to a set of font bitmaps)

I'm intrigued... could you give a few lines of example code for this?

bodmer: - Put all the bitmaps in one array and have a table of offsets (used by Adafruit)

I've just looked at a few .h files in the Adafruit font library. I think I kind of get the basic idea, but it still hasn't quite "clicked" yet...

carguy:
I’m intrigued… could you give a few lines of example code for this?

As you only have a few bitmaps the switch+case method would be simpler to understand! Really you need to read up about C code arrays and using pointers otherwise this is really going to scramble your brain :slight_smile: .

Look at this file.

At the start there is an array of character widths (e.g. widtbl_f16, necessary because this is a proportional spaced font, you don’t need that if all bitmap arrays have the same pixel width), then all the character bitmaps (e.g. chr_f16_20 which is ASCII code 0x20 bitmap viz “space”), then right at the end of the header file is an array of pointers to the character bitmap arrays (e.g. chrtbl_f16). Note that the first value in the array corresponds to ASCII code 0x20 in this case.

You can then use lines like this:

unsigned int flash_address = 0; // 16 bit address OK for Arduino if font files <60K total

flash_address = pgm_read_word(&chrtbl_f16[asciiCode - 0x20]);

Then retrieve a byte “i” (or word if it is a 16bit array that holds the bitmaps) from the bitmap:

byte bitmap_data = pgm_read_byte(flash_address + i); // Get 8 bit data from a 8 bit array
unsigned int  bitmap_data = pgm_read_word(flash_address + i); // Get 16 bit data from a 16 bit array

I think I am beginning understand the concept, actually…

So within my function as posted above, right now I’ve got

 for (int j = 0; j < numberLength; j++) {               

    // Generating the name of the bitmap that we want to load from PROGMEM
    String fileNameString = (String) valueBuf[j];
    fileNameString.replace(".", "d");
    String bitmapName = "c" + fileNameString;   
 
 
  }

In my function as it it now, valueBuf[j] carries the value of whatever digit I want the function to display as a bitmap during that iteration of the “for j” loop. So for example, valueBuf[2] might equal a 7. So then I would somehow have to point to element number seven within a character allocation table…

I would have to call something like myCharacterTable[7], and then within myCharacterTable[7] would be contained the name of the actual bitmap array that will then be used to display the digit on the screen… right?

The penny has only dropped about 80 percent here so far for me, but at the end of the day, it kind of seems like a concept that’s not that difficult to implement, especially not if you really just want to display numbers from 0 to 9 (and a dot). As far as that dot, of course it would look much more neat if the “dot” bitmap was more narrow.

I think I will go the “array of array pointers” route. Even if it means that I’ll have to do some additional thinking to fully get my head around it. As far as I can see now, it seems like a quite neat approach.

And maybe then I will put it into a .h file as well, to increase the code’s neatness yet again.