Using a pointer in a struct

I am programming a M5Paper, so really an ESP2 with a capacitive, E-Ink touchscreen. I can print icons of the form:

// array size is 8192
static const int8_t Arrow_circle_down[]  = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,.........};

using the following command and function:

DrawIcon(20, 20, (uint16_t *)Arrow_circle_down, 64, 64);

void DrawIcon(int x, int y, const uint16_t *icon, int dx, int dy) {
  for (int yi = 0; yi < dy; yi++) {
    for (int xi = 0; xi < dx; xi++) {
      uint16_t pixel = icon[yi * dx + xi];
        canvasIcon.drawPixel(x + xi, y + yi, 15 - (pixel / 4096));
    }
  }
}

I am keeping the ,y coordinates of selection boxes together with the name of the icon in a structure;

struct coords {
  String icon;
  int x;
  int y;
};

// Create an array of Coordinates structs
coords box[numBoxes];

void makeBoxes() {
  for (int i = 1; i < numBoxes; i++) {
    box[i].x = 20;
    box[i].y = i * 100;
  }
  box[1].icon = "Arrow_circle_down";
  box[2].icon = "Arrow_circle_up";
  box[3].icon = "Arrow_circle_left";
  box[4].icon = "Grin_small";

  for (int i = 1; i < numBoxes; i++) {
    //Draw each box
    canvas.drawRect(box[i].x, box[i].y, 50, 50, 15);
    canvas.drawString(box[i].icon, 100, box[i].y);
  }
}

This is my first time working with pointers and I need some help. Although "DrawIcon(20, 20, (uint16_t *)Arrow_circle_right, 64, 64);" works fine if I replace the "Arrow_circle_right" with "box[1].icon" the IDE complains about converting a string to an integer. I have tried changing the "String icon" to "int16_t *icon" in the struct but that didn't help. I've tried changing the structure and the make boxes function ( box[1].icon = &Arrow_circle_down;, for instance) but nothing seems to work.

How can I save the pointer to the icon array into the coords structure?

Please post a small complete sketch, or several, to illustrate your problem. I quoted the line of code you are talking about taken from your prose - it makes no sense without context if that is the entire line.

I'm too lazy to guess what you are talking about.

a7

There is no complete sketch to post. I am asking a general programming question regarding pointers and structures.

The function call you posted does indeed make no sense without the function, which is why I included the function directly after it in my question. Please just look at the information I have given, if there is something more you need just tell me what it is. Or if there is not enough information for you there for you to help, that's ok too.

Happy New Year to you.

You used arrow_circle_right, or something like it, without quotes.

That symbol or string or String appears nowhere. I would have to extrapolate and assume, two things that are fraught when helping with code at a distance. Sry.

I quoted a line of code, expecting you to say you meant to quote arrow_circle_right.

It is hard to describe things, especially when quotes need to be quoted, which is why it would be best for you to place your question in the context of a small sketch that is complete and either compiles and doesn't do what you meant, or doesn't compile and you don't understand why.

Someone may have a better crystal ball than I.

a7

I appears in the first line of code I posted:

// array size is 8192
static const int8_t Arrow_circle_down[]  = {
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,.........};

(Ok, Arrow_circle_right somehow got replaced by Arrow_circle_down, but they are all required and included in the icon arrays.)

This cast is not allowed. See e.g. Don't use unions or pointer casts for type punning.

maybe something like this..



static const int8_t Arrow_circle_down[8192]  = {1};


struct coords {
  String icon;
  int x;
  int y;
  int8_t* imgData;
  unsigned long imgSize;
};

const byte numBoxes = 4;

// Create an array of Coordinates structs
coords box[numBoxes];

void makeBoxes() {
  //array indexes start at 0..
  for (int i = 0; i < numBoxes; i++) {
    box[i].x = 20;
    box[i].y = i * 100;
    //add pointer to the array data
    box[i].imgData = (int8_t*)&Arrow_circle_down;
    //save size of array..
    box[i].imgSize = sizeof(Arrow_circle_down);
    switch (i) {
      case 0: box[i].icon = "Arrow_circle_down";
        //add pointer to the array data
        box[i].imgData = (int8_t*)&Arrow_circle_down;
        //save size of array..
        box[i].imgSize = sizeof(Arrow_circle_down);
        break;
      case 1: box[i].icon = "Arrow_circle_up";
        box[i].imgData = (int8_t*)&Arrow_circle_down;
        box[i].imgSize = sizeof(Arrow_circle_down);
        break;
      case 2: box[i].icon = "Arrow_circle_left";
        box[i].imgData = (int8_t*)&Arrow_circle_down;
        box[i].imgSize = sizeof(Arrow_circle_down);
        break;
      case 3: box[i].icon = "Grin_small";
        box[i].imgData = (int8_t*)&Arrow_circle_down;
        box[i].imgSize = sizeof(Arrow_circle_down);
        break;
    }
  }

  for (int i = 0; i < numBoxes; i++) {
    //Draw each box
    Serial.println(box[i].icon);
    Serial.println(box[i].imgData[0]);
    Serial.println(box[i].imgSize);
    // canvas.drawRect(box[i].x, box[i].y, 50, 50, 15);
    // canvas.drawString(box[i].icon, 100, box[i].y);
  }
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Hello, ESP32!");
  makeBoxes();
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(10); // this speeds up the simulation
}

oh, array indexes start at 0..
and you lose me on DrawIcon, don't understand it, sorry..

Happy New Year!

good luck.. ~q

2 Likes

Thanks, it'll take me a while to check that out but it makes sense. I found the DrawIcon function on the M5Stack website and it works fine. I'll let you know how I get on.

Happy New Year!

1 Like

sounds like you want to do something like

struct Coord {
  int            x;
  int            y;
  const uint8_t *icon;
};

Coord cords [] = {
    { 123, 456, & Arrow_circle_down [0] },
};
1 Like

@qubits-us @gcjr Thanks to you both I have a solution! My code now looks like this;

struct coords {
  String icon;
  int x;
  int y;
  uint16_t* imgData;
};

void makeBoxes() {
  for (int i = 1; i < numBoxes; i++) {
    box[i].x = 20;
    box[i].y = i * 100;
  }
  box[1].icon = "Arrow_circle_down";
  box[1].imgData = (uint16_t*)&Arrow_circle_down;
  box[2].icon = "Arrow_circle_up";
  box[2].imgData = (uint16_t*)&Arrow_circle_up;
  box[3].icon = "Arrow_circle_left";
  box[3].imgData = (uint16_t*)&Arrow_circle_left;
  box[4].icon = "Grin_small";
  box[4].imgData = (uint16_t*)&Grin_small;

  // Print the coordinates for each box
  for (int i = 1; i < numBoxes; i++) {
    //Draw each box
    canvas.drawRect(box[i].x, box[i].y, 50, 50, 15);
    canvas.drawString(box[i].icon, 100, box[i].y);
  }
}

I can now use the box[i].imgData command to draw the required icon.

I can now press on and finish my sketch and then some reading up on pointers is required in the new year.

Thank you both for your patience and a Happy New Year to you both.

PS @qubits-us, the reason for starting my array with 1 is to do with the layout on the E-Ink screen.

It is difficult to solve a particular problem by answering a general question.

Looking at parts of the code, it doesn't make sense to me that it somehow works.

Regarding the question about pointers. In C arrays are just pointers to data of a particular type. This means that they can be used interchangeably.

For example Arrow_circle_down is an array of signed bytes (int8_t). So I suppose you identify each pixel with one byte. If you are using 256 different shades (0 to FF) it would make more sense to choose unsigned bytes (uint8_t) for this purpose.

Back to pointers. You can address i-th pixel as an array member: Arrow_circle_down [i] or if you use array name as a pointer *(Arrow_circle_down + i), where Arrow_circle_down is the start of the array and i is offset from the start measuring in bytes (when int8_t data type is used). (Arrow_circle_down + i) is thus a pointer to i+th element (pixel) and *(Arrow_circle_down + i) is i-th element (pixel) itself. For the C compiler, both notations are exactly the same.

However, if you define a pointer to unsigned 16 bit integer (uint16_t), for example *icon, the addressing changes to 2-byte manner. You can assign pointers of different types one to another by casting the type:

icon = (uint16_t *) Arrow_circle_down

but this changes the way C treats both, the pointer and the element (pixels) they point to.

Arrow_circle_down points to the first byte (int8_t) of the array
icon points to the first 2-bytes (uint16_t) of the array

which may be the same, but

*(Arrow_circle_down + 1)
and
*(icon + 1)

are not the same any more. (icon + 1) would be the same as (Arrow_circle_down + 2), if we don't even consider that they point to variables (memory locations) of different types (1 byte signed integer vs 2 bytes unsigned integer).

Taking a look at the DrawIcon function - it assumes that pixels are stored in 2-bytes format (with uint16_t data type you are unable to index each byte separately). Basically you only address each second byte and take 2 consecutive bytes as uint16_t which you shift right for 12 bits (pixel / 4096 - by the way, pixel >> 12 would work much faster) and then you subtrach this value from FF (15) which is actually only 8 bit value: 11111111.

So your code doesn't make sense to me.

1 Like

Who created these layouts? If were you, it would be better to change the layout format to be indexed from zero too.
Indexation from zero is a "rule of tumb" for entire C/C++ ecosystem, it is important to keep it clean in any project.

One of the reason's I am sometimes reluctant to post a whole unfinished sketch when asking for help on a particular topic, is that someone, with the best intentions, will pick on an aspect of the code which has nothing to do with the question I am asking. Often the whole thread will then concentrate on an issue which is not the one I posted requesting help on.

Please rest assured that the tempory, quick fix layout format will be corrected now that @qubits-us @gcjr have answered my actual question.

It is nothing wrong in it, because newbies usually don’t have enough experience to understand what’s important in their project and what’s not, and relatively speaking, they come with a request to wipe the dust off a car that doesn’t have a motor or wheels

2 Likes

why not

Coord cords [] = {
    { },
    { "Arrow_circle_down", 20, 100, & Arrow_circle_down [0], sizeof(Arrow_circle_down) },
    { "Arrow_circle_up",   20, 200, & Arrow_circle_up   [0], sizeof(Arrow_circle_up)   },
    { "Arrow_circle_left", 20, 300, & Arrow_circle_left [0], sizeof(Arrow_circle_left) },
    { "Grin_small",        20, 400, & Grin_small        [0], sizeof(Grin_small)        },

};

Yes, that is indeed nicer!

why just not Arrow_circle_down ?

because i've struggled over the decades with different versions of compilers that didn't like that

yes, i see that works as well

Any compiler that didn't should have been immindiantly been thrown in the trash.

Of course there was a time long ago when one couldn't throw the only compiler that was available right in the trash.

Or the only compiler a person could afford.

This is a great time to be alive.

a7

1 Like

I had to include (uint16_t*) to get it to work. Like this:

box[1] = {"Arrow_circle_down", 20, 100, (uint16_t*)&Arrow_circle_down};

But it is still more elegant!