Adafruit LED Matrix - Bitmaps within Classes

Hi everyone.

I am trying to build a dash for my motorcycle using 8x 8x16 mini LED matrices from Adafruit. [So imagine a display 16 LEDs high, 64 wide]

As part of the project as a whole I would like to display an icon on screen when an input is triggered. [ie press turn signal and an ‘arrow’ icon flashes on screen].

Have got this working with many inputs, but the code is very repetitive so I would like to consolidate this down into a class. I would then instantiate a version with a specific bitmap icon for a given input. However I can’t seem to get it work with a bitmap as one of the arguments.[is that the right term?]

I have tried various versions of the below code with out any luck, but i think it indicates what i am trying to achieve.

Any pointers?

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>

Adafruit_8x16minimatrix matrix[8] =
{ 
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix(),
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix(),
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix(),
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix()
};

class sensor
{
  public:
  sensor( int pin, int matrix, int bmp, int cursorX, int cursorY, int width, int height, long checkInt);

  void begin();
  void update();
  
  private:
  unsigned long _previousMillis;
  unsigned long _currentMillis;

  int _pin;
  int _matrix;
  long _checkInt;                   
  int _cursorX;                          
  int _cursorY;
  int _width;
  int _height;
  int _bmp;
  
  int _state;
  uint8_t _BMP;
};

sensor::sensor(int pin, int matrix, int bmp, int cursorX, int cursorY, int width, int height, long checkInt)
{
  _pin = pin;
  _checkInt = checkInt;
  _bmp = bmp;
  _cursorX = cursorX;
  _cursorY = cursorY;
  _width = width;
  _height = height;
  _matrix = matrix;
}  

void sensor::begin()
{
  pinMode(_pin, INPUT_PULLUP);
}

void sensor::update()
{
  _state = digitalRead(_pin);

  if(_bmp == 1)
  {
    static const uint8_t PROGMEM _BMP[]=    // leftBMP
    { 
      B00001000,
      B00011000,
      B00110000,
      B01100000,
      B11000000,
      B01100000,
      B00110000,
      B00011000,
      B00001000    
    };
  }
    
  if(_bmp == 2)
  {      
    static const uint8_t PROGMEM _BMP[] =   // rightBMP
    { 
      B10000000,
      B11000000,
      B01100000,
      B00110000,
      B00011000,
      B00110000,
      B01100000,
      B11000000,
      B10000000
    };
  }
       
  _previousMillis = 0;
  _currentMillis = millis();
    
  if(_currentMillis - _previousMillis >= _checkInt)
  {
    if(_state == HIGH)
    { 
      matrix[_matrix].drawBitmap(_cursorX, _cursorY, _BMP, _width, _height, LED_OFF);//display 'negative' icon on matrix display - NOTE LED_'OFF'
      matrix[_matrix].writeDisplay();
    }                                           
    else 
    {
      matrix[_matrix].drawBitmap(_cursorX, _cursorY, _BMP, _width, _height, LED_ON); //display icon on matrix display
      matrix[_matrix].writeDisplay();
    }
    _previousMillis = millis();
  }
}
       
// (pin no, matrix no, bitmap no, cursorX, cursorY, bitmap width, bitmap height, check frequency)      
sensor left     (2, 0, 1, 0, 4, 5, 9, 250);  
sensor right    (3, 7, 2, 3, 4, 5, 9, 250);


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

  for (int i = 0; i < 8; i++)
  {
    matrix[i].begin(0x70 + i);
    matrix[i].setRotation(0);
    matrix[i].clear();
    matrix[i].writeDisplay();
  }

  left.begin();
  right.begin();
}


void loop() 
{ 
  left.update();
  right.update();
}

You want these declarations at top, outside of your code. They define fixed areas of memory containing those patterns. They should have different identifiers like “leftBMP”, “rightBMP”

static const uint8_t PROGMEM leftBMP[]=  { 
      B00001000,
      B00011000,
      B00110000,
      B01100000,
      B11000000,
      B01100000,
      B00110000,
      B00011000,
      B00001000    
};

    
static const uint8_t PROGMEM rightBMP[]= { 
      B10000000,
      B11000000,
      B01100000,
      B00110000,
      B00011000,
      B00110000,
      B01100000,
      B11000000,
      B10000000
  };

Have a look at the examples on the PROGMEM reference page and then set up a table to refer to your strings bitmaps.

const char* const bmp_table[] PROGMEM = {leftBMP, rightBMP, and so on...};

You can then select which bitmap with a number (starting at 0) by:

void sensor::update()
{
  _state = digitalRead(_pin);

  // remove all this lot:
  // if(_bmp == 1)  etc.   Note that _bmp will now have to be 0 for leftBMP, 1 for rightBMP etc.

  ...

  _previousMillis = 0;                                  // <--- what's this being set to zero
  _currentMillis = millis();
    
  if(_currentMillis - _previousMillis >= _checkInt)       // <--- and then being used here ???
  {
    if(_state == HIGH)
    { 
       // output the bitmap selected by '_bmp'
       matrix[_matrix].drawBitmap(_cursorX, _cursorY, bmp_table[_bmp], _width, _height, LED_OFF);

  ...

Yours,
TonyWilk

Cheers Tony!

Thanks for your input.

On implementating this I keep getting an error “call of overloaded ‘drawbitmap...’ is ambiguous”.

But I will read through the reference info to try n get my head around where I might be going wrong.

S

I am still not having any luck getting a class parameter into the drawBitmap. function.

I have revised the code…

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>

Adafruit_8x16minimatrix matrix[8] =
{ 
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix(),
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix(),
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix(),
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix()
};

static const uint8_t PROGMEM leftBMP[] =
{ 
  B00001000,
  B00011000,
  B00110000,
  B01100000,
  B11000000,
  B01100000,
  B00110000,
  B00011000,
  B00001000    
};

static const uint8_t PROGMEM rightBMP[] =
{ 
  B10000000,
  B11000000,
  B01100000,
  B00110000,
  B00011000,
  B00110000,
  B01100000,
  B11000000,
  B10000000
};

const char* const bmp_table[] PROGMEM = {leftBMP, rightBMP};


class sensor
{
  public:
  sensor( int pin, int matrix, int bmp, int cursorX, int cursorY, int width, int height, long checkInt);

  void begin();
  void update();
  
  private:
  unsigned long _previousMillis;
  unsigned long _currentMillis;

  int _pin;
  int _matrix;
  long _checkInt;                   
  int _cursorX;                          
  int _cursorY;
  int _width;
  int _height;
  int _bmp;
  
  int _state;
};

sensor::sensor(int pin, int matrix, int bmp, int cursorX, int cursorY, int width, int height, long checkInt)
{
  _pin = pin;
  _checkInt = checkInt;
  _bmp = bmp;
  _cursorX = cursorX;
  _cursorY = cursorY;
  _width = width;
  _height = height;
  _matrix = matrix;
}  

void sensor::begin()
{
  pinMode(_pin, INPUT_PULLUP);
}

void sensor::update()
{
  _state = digitalRead(_pin);

  _currentMillis = millis();
    
  if(_currentMillis - _previousMillis >= _checkInt)
  {
    if(_state == HIGH)
    { 
      matrix[_matrix].drawBitmap(_cursorX, _cursorY, bmp_table[_bmp], _width, _height, LED_OFF);//display 'negative' icon on matrix display - NOTE LED_'OFF'
      matrix[_matrix].writeDisplay();
    }                                           
    else 
    {
      matrix[_matrix].drawBitmap(_cursorX, _cursorY, bmp_table[_bmp], _width, _height, LED_ON); //display icon on matrix display
      matrix[_matrix].writeDisplay();
    }
    _previousMillis = millis();
  }
}
       
// (pin no, matrix no, bitmap no, cursorX, cursorY, bitmap width, bitmap height, check frequency)      
sensor left     (2, 0, 0, 0, 4, 5, 9, 250);  
sensor right    (3, 7, 1, 3, 4, 5, 9, 250);


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

  for (int i = 0; i < 8; i++)
  {
    matrix[i].begin(0x70 + i);
    matrix[i].setRotation(0);
    matrix[i].setTextWrap(false);
    matrix[i].setBrightness(1);  //set display brightness
    matrix[i].clear();
    matrix[i].writeDisplay();
  }

  left.begin();
  right.begin();
}


void loop() 
{ 
  left.update();
  right.update();
}

I get the following errors…

Arduino: 1.8.5 (Mac OS X), Board: "Arduino/Genuino Uno"

/Users/Sean/Desktop/temp/temp.ino: In member function 'void sensor::update()':
temp:93: error: call of overloaded 'drawBitmap(int&, int&, const char* const&, int&, int&, int)' is ambiguous
       matrix[_matrix].drawBitmap(_cursorX, _cursorY, bmp_table[_bmp], _width, _height, LED_OFF);//display 'negative' icon on matrix display - NOTE LED_'OFF'
                                                                                               ^
/Users/Sean/Desktop/temp/temp.ino:93:95: note: candidates are:
In file included from /Users/Sean/Desktop/temp/temp.ino:2:0:
/Users/Sean/Documents/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h:67:5: note: void Adafruit_GFX::drawBitmap(int16_t, int16_t, const uint8_t*, int16_t, int16_t, uint16_t) <near match>
     drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
     ^
/Users/Sean/Documents/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h:67:5: note:   no known conversion for argument 3 from 'const char* const' to 'const uint8_t* {aka const unsigned char*}'
/Users/Sean/Documents/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h:71:5: note: void Adafruit_GFX::drawBitmap(int16_t, int16_t, uint8_t*, int16_t, int16_t, uint16_t) <near match>
     drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
     ^
/Users/Sean/Documents/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h:71:5: note:   no known conversion for argument 3 from 'const char* const' to 'uint8_t* {aka unsigned char*}'
temp:98: error: call of overloaded 'drawBitmap(int&, int&, const char* const&, int&, int&, int)' is ambiguous
       matrix[_matrix].drawBitmap(_cursorX, _cursorY, bmp_table[_bmp], _width, _height, LED_ON); //display icon on matrix display
                                                                                              ^
/Users/Sean/Desktop/temp/temp.ino:98:94: note: candidates are:
In file included from /Users/Sean/Desktop/temp/temp.ino:2:0:
/Users/Sean/Documents/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h:67:5: note: void Adafruit_GFX::drawBitmap(int16_t, int16_t, const uint8_t*, int16_t, int16_t, uint16_t) <near match>
     drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
     ^
/Users/Sean/Documents/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h:67:5: note:   no known conversion for argument 3 from 'const char* const' to 'const uint8_t* {aka const unsigned char*}'
/Users/Sean/Documents/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h:71:5: note: void Adafruit_GFX::drawBitmap(int16_t, int16_t, uint8_t*, int16_t, int16_t, uint16_t) <near match>
     drawBitmap(int16_t x, int16_t y, uint8_t *bitmap,
     ^
/Users/Sean/Documents/Arduino/libraries/Adafruit_GFX_Library/Adafruit_GFX.h:71:5: note:   no known conversion for argument 3 from 'const char* const' to 'uint8_t* {aka unsigned char*}'
exit status 1
call of overloaded 'drawBitmap(int&, int&, const char* const&, int&, int&, int)' is ambiguous

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

Is this something that needs to be dealt with in the Adafruit_GFX_Library?

Sorry, my mistake...

I hadn't noticed that drawBitmap is defined like this in Adafruit_GFX.h

drawBitmap(int16_t x, int16_t y, const uint8_t bitmap[],
      int16_t w, int16_t h, uint16_t color),

so this line

const char* const bmp_table[] PROGMEM = {leftBMP, rightBMP};

should be:

const uint8_t* const bmp_table[] PROGMEM = {leftBMP, rightBMP};

now it compiles for me at least :slight_smile:

Yours,
TonyWilk

Brilliant. You're a legend!

Cheers, Tony.

My only problem now is that the bitmaps do not display as they should. They appear to be quite random and tend to change on each compile. the 'LED_ON' version of the bitmap is also different from the 'LED_OFF' bit maps which usually would cancel each other out.

I don't know what that means...

OK. I don't why this works, but it does...

Instead of this portion of the code outside of the class:

static const uint8_t PROGMEM leftBMP[] =
{ 
  B00001000,
  B00011000,
  B00110000,
  B01100000,
  B11000000,
  B01100000,
  B00110000,
  B00011000,
  B00001000    
};

static const uint8_t PROGMEM rightBMP[] =
{ 
  B10000000,
  B11000000,
  B01100000,
  B00110000,
  B00011000,
  B00110000,
  B01100000,
  B11000000,
  B10000000
};

const char* const bmp_table[] PROGMEM = {leftBMP, rightBMP};

I moved it inside void sensor::update() & deleted the 'static const ' from the bitmap arrays and 'const' from the table array:

void sensor::update()
{
  _state = digitalRead(_pin);
  
  uint8_t PROGMEM leftBMP[] =  // _bmp = 0
  { 
    B00001000,
    B00011000,
    B00110000,
    B01100000,
    B11000000,
    B01100000,
    B00110000,
    B00011000,
    B00001000    
  };
  
  uint8_t PROGMEM rightBMP[] = // _bmp = 1
  { 
    B10000000,
    B11000000,
    B01100000,
    B00110000,
    B00011000,
    B00110000,
    B01100000,
    B11000000,
    B10000000
  };
  
  uint8_t* const PROGMEM bmp_table[]  = 
  {
    leftBMP,
    rightBMP
  };
  
  _currentMillis = millis();
    
  if(_currentMillis - _previousMillis >= _checkInt)
  {
    if(_state == HIGH)
    { 
      matrix[_matrix].drawBitmap(_cursorX, _cursorY, bmp_table[_bmp], _width, _height, LED_OFF);//display 'negative' icon on matrix display - NOTE LED_'OFF'
      matrix[_matrix].writeDisplay();
    }                                           
    else 
    {
      matrix[_matrix].drawBitmap(_cursorX, _cursorY, bmp_table[_bmp], _width, _height, LED_ON); //display icon on matrix display
      matrix[_matrix].writeDisplay();
    }
    _previousMillis = millis();
  }
}

works as expected...

Here is the complete working code for anyone following this…

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>

Adafruit_8x16minimatrix matrix[8] =
{ 
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix(),
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix(),
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix(),
 Adafruit_8x16minimatrix(), Adafruit_8x16minimatrix()
};

class sensor
{
  public:
  sensor( int pin, int matrix, int bmp, int cursorX, int cursorY, int width, int height, long checkInt);

  void begin();
  void update();
  
  private:
  unsigned long _previousMillis = 0;
  unsigned long _currentMillis;

  int _pin;
  int _matrix;
  long _checkInt;                   
  int _cursorX;                          
  int _cursorY;
  int _width;
  int _height;
  int _bmp;
  
  int _state;
};

sensor::sensor(int pin, int matrix, int bmp, int cursorX, int cursorY, int width, int height, long checkInt)
{
  _pin = pin;
  _checkInt = checkInt;
  _bmp = bmp;
  _cursorX = cursorX;
  _cursorY = cursorY;
  _width = width;
  _height = height;
  _matrix = matrix;
}  

void sensor::begin()
{
  pinMode(_pin, INPUT_PULLUP);
}

void sensor::update()
{
  _state = digitalRead(_pin);
  
  uint8_t PROGMEM leftBMP[] =  // _bmp = 0
  { 
    B00001000,
    B00011000,
    B00110000,
    B01100000,
    B11000000,
    B01100000,
    B00110000,
    B00011000,
    B00001000    
  };
  
  uint8_t PROGMEM rightBMP[] = // _bmp = 1
  { 
    B10000000,
    B11000000,
    B01100000,
    B00110000,
    B00011000,
    B00110000,
    B01100000,
    B11000000,
    B10000000
  };
  
  uint8_t* const PROGMEM bmp_table[]  = 
  {
    leftBMP,
    rightBMP
  };
  
  _currentMillis = millis();
    
  if(_currentMillis - _previousMillis >= _checkInt)
  {
    if(_state == HIGH)
    { 
      matrix[_matrix].drawBitmap(_cursorX, _cursorY, bmp_table[_bmp], _width, _height, LED_OFF);//display 'negative' icon on matrix display - NOTE LED_'OFF'
      matrix[_matrix].writeDisplay();
    }                                           
    else 
    {
      matrix[_matrix].drawBitmap(_cursorX, _cursorY, bmp_table[_bmp], _width, _height, LED_ON); //display icon on matrix display
      matrix[_matrix].writeDisplay();
    }
    _previousMillis = millis();
  }
}
       
// (pin no, matrix no, bitmap no, cursorX, cursorY, bitmap width, bitmap height, check frequency)      
sensor left     (2, 0, 0, 0, 4, 5, 9, 250);  
sensor right    (3, 7, 1, 3, 4, 5, 9, 250);


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

  for (int i = 0; i < 8; i++)
  {
    matrix[i].begin(0x70 + i);
    matrix[i].setRotation(0);
    matrix[i].setTextWrap(false);
    matrix[i].setBrightness(1);  //set display brightness
    matrix[i].clear();
    matrix[i].writeDisplay();
  }

  left.begin();
  right.begin();
}


void loop() 
{ 
  left.update();
  right.update();
}

Well done !

It’s taken me a while to get to the bottom of this one, I think it’s a bit of a compiler bug.

I’ve fixed your earlier version by simply changing:

const uint8_t* const bmp_table[] PROGMEM = {leftBMP, rightBMP};

to:

const uint8_t* const bmp_table[] = {leftBMP, rightBMP};

It seems to only go wrong when you have a member variable of a class used as the index into ‘bmp_table’. And that shouldn’t happen.

Ah… on the PROGMEM reference page it does say:

Notes and Warnings
Please note that variables must be either globally defined, OR defined with the static keyword, in order to work with PROGMEM.

Anyhow… you got it going :slight_smile:

Yours,
TonyWilk