HenkJan van der Pol
HenkJan van der Pol

Reputation: 23

c++ map to unsigned char arrays

I'm trying to pull weather icons from openweathermap.org api and display them on an ESP32 driven OLED display using the u8g2 library (Arduino IDE, C++11).

In the openweathermap.org weather api, 16 types of weather are defined as const *char[] such as "01d", "01n", "02d", etc.

The u8g2 library uses unsigned char arrays to define icons in the XBM bitmap format. I have defined all icons like this:

static unsigned char icon01d[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   ...
}

An icon can be displayed on the OLED by:

u8g2.drawXBMP( 96, 0, 32, 32, icon01d );

I wanted to use a std::map to link the strings from the weather api to pointers to the icons like:

map< string, unsigned char[]* > WEATHER_ICONS = { 
  { "01d", &icon01d },
  { "01n", &icon01n },
  ...
};

and would then (in my fantasy) be able to display the appropriate icon like:

u8g2.drawXBMP( 96, 0, 32, 32, *WEATHER_ICONS[ weatherIcon ] );

with weatherIcon being the const *char[] casted to a string.

I have tried various alternatives to setup the map:

std::map< std::string, <<variable reference>> > WEATHER_ICONS = { 
  { "01d", &icon01d },
  { "01n", &icon01n }
};

With <<variable reference>> according to the table, I get various error messages:

variable referenced used error message returned
unsigned char[128]* template argument 2 is invalid
unsigned char[]* template argument 2 is invalid
unsigned char*[128] could not convert '{{"01d", (& icon01d)}, {"01n", (& icon01n)}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, unsigned char* [128]>'
unsigned char*[] could not convert '{{"01d", (& icon01d)}, {"01n", (& icon01n)}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, unsigned char* []>'
unsigned char** could not convert '{{"01d", (& icon01d)}, {"01n", (& icon01n)}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, unsigned char**>'

None of the error messages are very helpful to me. It looks like I have arrived in 'c pointer hell'.

I see advice on stackoverflow to define a pointer to a pointer array such as unsigned char** but I'm missing enough detail to get it working.

What would be the right solution?

As a workaround, I have implemented it like:

      if     ( strcmp( weatherIcon, "01d" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon01d );
      else if( strcmp( weatherIcon, "01n" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon01n );
      else if( strcmp( weatherIcon, "02d" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon02d );
      else if( strcmp( weatherIcon, "02n" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon02n );
      else if( strcmp( weatherIcon, "03d" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon03d );
      else if( strcmp( weatherIcon, "03n" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon03n );
      else if( strcmp( weatherIcon, "04d" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon04d );
      else if( strcmp( weatherIcon, "04n" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon04n );
      else if( strcmp( weatherIcon, "09d" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon09d );
      else if( strcmp( weatherIcon, "09n" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon09n );
      else if( strcmp( weatherIcon, "11d" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon11d );
      else if( strcmp( weatherIcon, "11n" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon11n );
      else if( strcmp( weatherIcon, "13d" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon13d );
      else if( strcmp( weatherIcon, "13n" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon13n );
      else if( strcmp( weatherIcon, "50d" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon50d );
      else if( strcmp( weatherIcon, "50n" )==0 ) u8g2.drawXBMP( 96, 0, 32, 32, icon50n );

which works, but is not very elegant.

Upvotes: 1

Views: 160

Answers (3)

HenkJan van der Pol
HenkJan van der Pol

Reputation: 23

Looks like the solution is:

std::map< std::string, unsigned char (*)[128] > WEATHER_ICONS = { 
  { "01n", & icon01n },
  { "02d", & icon02d },

Don't ask me how I found out..

Thanks both for trying to answer the question, I'm sorry if the question was not clear.

Upvotes: 0

Jim Rhodes
Jim Rhodes

Reputation: 5095

In your first example call to drawXBMP you have:

u8g2.drawXBMP(96, 0, 32, 32, icon01d);

So, what is icon01d in this case? Since you are passing the name of an array without an index, your are actually passing a pointer to the first element in the array. In this case it is an unsigned char*.

When you declare your map, that is what you should be using. Like this:

map<string, unsigned char*> WEATHER_ICONS = {

Then you should call drawXBMP like this:

u8g2.drawXBMP(96, 0, 32, 32, WEATHER_ICONS[weatherIcon]);

Upvotes: 0

Sam Varshavchik
Sam Varshavchik

Reputation: 118445

In C and C++ evaluating a name of an array produces a pointer to the first value in the array.

Your static unsigned char icon01d[] is really an unsigned char *, in disguise. So:

map< string, unsigned char* > WEATHER_ICONS = { 
  { "01d", icon01d },
  { "01n", icon01n },
  ...
};

It is unclear from your question whether or not it is necessary to know the size of each such array. If all of these unsigned char arrays are the same size, well, there you go. Otherwise, perhaps, you can store both, spitballing something like this:

map< string, tuple<unsigned char*, size_t> > WEATHER_ICONS = { 
  { "01d", {icon01d, sizeof(icon01d)} },
  { "01n", {icon01n, sizeof(icon01n)} },
  ...
};

Upvotes: 0

Related Questions