-3

This is a program for using fonts in MCUs. I have 4 files. 'zar.h' and 'zar.cpp' contains data of one font. 'font.h' and 'font.cpp' contains pointers and functions for set fonts dynamically. There is a lot of problems with pointers. When access elements of "XB_Zar_name" array, by "uni_name[i]" pointer, MCU dos reset's. Another problem is counting the strings in "XB_Zar_name" string array. There are definitely other errors that will appear after fixing these errors. Pleas help me.

zar.h

/*v*/extern const unsigned char XB_Zar_118[];
/*w*/extern const unsigned char XB_Zar_119[];
/*x*/extern const unsigned char XB_Zar_120[];
/*y*/extern const unsigned char XB_Zar_121[];
/*z*/extern const unsigned char XB_Zar_122[];
extern  char XB_Zar_width[];
extern const unsigned char* XB_Zar_addr[];
extern String XB_Zar_name[];

zar.cpp

/*v*/ const unsigned char XB_Zar_118[] PROGMEM ={255,255,254,255,63,255};
/*w*/ const unsigned char XB_Zar_119[] PROGMEM ={255,127,255,254,255,5};
/*x*/ const unsigned char XB_Zar_120[] PROGMEM ={215,215,23,254,255,127};
/*y*/ const unsigned char XB_Zar_121[] PROGMEM ={255,127,25,254,255,9};
/*z*/ const unsigned char XB_Zar_122[] PROGMEM ={255,243,239,254,215,19};
char XB_Zar_width[]= {2,2,2,2,2};
const unsigned char* XB_Zar_addr[] = {XB_Zar_118,XB_Zar_119,XB_Zar_120,XB_Zar_121,XB_Zar_122};
String XB_Zar_name[] = {"۰","۱","۲","۳","۴","۵"};

font.h

#pragma once
#include "zar.h"
extern unsigned char *char_addr;
extern unsigned int *char_width;
extern String **uni_name;
extern void setFont(byte fontNumber);
extern int getGliphData(String *uniChar, int *index, int *width);

font.cpp

#include "font.h"


unsigned char *char_addr = NULL;
unsigned int *char_width = NULL;
String **uni_name = NULL;
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
void setFont(byte fontNumber)
{
    switch (fontNumber)
    {
    case 0:
        char_addr = (unsigned char *)(XB_Zar_addr);
        *uni_name = XB_Zar_name;
        char_width = (unsigned int *)XB_Zar_width;
        break;

    default:
        char_addr = (unsigned char *)XB_Zar_addr;
        break;
    }
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
int getGliphData(String *uniChar, int* index, int* charWidth)
{
Serial.println("uniChar data= "+*uniChar);
Serial.println("uni_name[i]= "+*uni_name[0]);// <---this resets the mcu
int countOfUniChars = sizeof(uni_name) / sizeof(String*);
    for (int i = 0; i < countOfUniChars ; i++)
    {    
        if (*uniChar == *uni_name[i])
        {
            *index = i;
            *charWidth = char_width[i];
            return i;
        }
    }
    return -1;
}
  • What is a `String` it's not `std::string` ? – Richard Critten Jul 23 '21 at 19:47
  • This is an Arduino class. https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/ I don't think by default you get much access to the standard library. Also: [https://stackoverflow.com/questions/37575303/is-the-c-standard-library-fully-supported-on-arduino](https://stackoverflow.com/questions/37575303/is-the-c-standard-library-fully-supported-on-arduino) – drescherjm Jul 23 '21 at 19:51
  • I use all properties of std::string for arduino string. I don't see Difference between them. – mohammad bagher Malekhosseini Jul 23 '21 at 20:11
  • `uni_name` needs to point somewhere. if you merely `*uni_name = XB_Zar_name;`, kaBOOOM! because you cannot dereferecnce NULL. – user4581301 Jul 23 '21 at 20:12
  • 1
    Side note: It's really rare day when you need a pointer to a container like `String` and far rarer that you need a pointer to a pointer to a container. – user4581301 Jul 23 '21 at 20:14
  • Where there is a will there is a way ( ͡° ͜ʖ ͡°) – mohammad bagher Malekhosseini Jul 23 '21 at 20:25
  • Yup, but doing stuff for no reason usually leads to doing debugging for no reason. – user4581301 Jul 23 '21 at 20:31
  • `String **uni_name;` -- Also, I would have slapped together a `vector` class, just so that stuff like this need not exist. It's amazing how so many people who say that they don't have access to STL on their platform, *but* do have access to a decent C++ compiler won't spend the extra hour or so to put together a home-made vector class, just to save the headache of doing all of this raw pointer contortions over and over again. – PaulMcKenzie Jul 23 '21 at 20:34
  • I'm an amateur. Please give clear guidance if possible. – mohammad bagher Malekhosseini Jul 23 '21 at 20:53

2 Answers2

1

Inside of getGliphData(), uni_name is just a pointer, so the sizeof() trick you are trying to use to calculate countOfUniChars simply won't work (there are numerous questions on StackOverflow on that very issue).

So, either store the array length into another global variable at the same time you store the array pointer, or else terminate each array with a sentry value (in this case, a blank string) and then loop through the array until you reach the sentry.

Also, there is no need to use a String** pointer when a String* pointer will suffice, as you are iterating through only 1 array of Strings. Were you iterating through an array of pointers to arrays of Strings then String** would make more sense.

Also, another problem I see is that XB_Zar_width[] and XB_Zar_addr[] only have 5 elements in them, but XB_Zar_name[] has 6 elements, and getGliphData() is trying to loop through XB_Zar_name[] using its indexes to access the elements of XB_Zar_width[] and XB_Zar_addr[], which will obviously not work for the 6th element.

That being said, try something more like this instead:

// zar.h

/*v*/extern const unsigned char XB_Zar_118[];
/*w*/extern const unsigned char XB_Zar_119[];
/*x*/extern const unsigned char XB_Zar_120[];
/*y*/extern const unsigned char XB_Zar_121[];
/*z*/extern const unsigned char XB_Zar_122[];
extern const unsigned int XB_Zar_width[];
extern const unsigned char* XB_Zar_addr[];
extern const String XB_Zar_name[];
extern const int XB_Zar_name_len;
//zar.cpp

/*v*/ const unsigned char XB_Zar_118[] PROGMEM ={255,255,254,255,63,255};
/*w*/ const unsigned char XB_Zar_119[] PROGMEM ={255,127,255,254,255,5};
/*x*/ const unsigned char XB_Zar_120[] PROGMEM ={215,215,23,254,255,127};
/*y*/ const unsigned char XB_Zar_121[] PROGMEM ={255,127,25,254,255,9};
/*z*/ const unsigned char XB_Zar_122[] PROGMEM ={255,243,239,254,215,19};
const unsigned int XB_Zar_width[] = {2, 2, 2, 2, 2, 0}; // TODO: fix the 6th element!
const unsigned char* XB_Zar_addr[] = {XB_Zar_118, XB_Zar_119, XB_Zar_120, XB_Zar_121, XB_Zar_122, NULL}; // TODO: fix the 6th element!
const String XB_Zar_name[] = {"۰", "۱", "۲", "۳", "۴", "۵"}; // TODO: remove the 6th element?
const int XB_Zar_name_len = sizeof(XB_Zar_name)/sizeof(XB_Zar_name[0]);
//font.h

#pragma once
#include "zar.h"
extern const unsigned char **char_addr;
extern const unsigned int *char_width;
extern const String *uni_name;
extern int uni_name_len;
extern void setFont(byte fontNumber);
extern int getGliphData(const String &uniChar, int *index, int *width);
//font.cpp

#include "font.h"


const unsigned char **char_addr = NULL;
const unsigned int *char_width = NULL;
const String *uni_name = NULL;
int uni_name_len = 0;
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
void setFont(byte fontNumber)
{
    switch (fontNumber)
    {
    case 0:
        char_addr = XB_Zar_addr;
        char_width = XB_Zar_width;
        uni_name = XB_Zar_name;
        uni_name_len = XB_Zar_name_len;
        break;

    default:
        char_addr = XB_Zar_addr;
        char_width = XB_Zar_width;
        break;
    }
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
int getGliphData(const String &uniChar, int* index, int* charWidth)
{
    Serial.println("uniChar data= " + uniChar);
    if (uni_name)
    {
        for (int i = 0; i < uni_name_len; ++i)
        {  
            Serial.println("uni_name[" + String(i) + "]= " + uni_name[i]);
            if (uniChar == uni_name[i])
            {
                *index = i;
                *charWidth = char_width[i];
                return i;
            }
        }
    }
    return -1;
}

Online Demo

Alternatively:

// zar.h

/*v*/extern const unsigned char XB_Zar_118[];
/*w*/extern const unsigned char XB_Zar_119[];
/*x*/extern const unsigned char XB_Zar_120[];
/*y*/extern const unsigned char XB_Zar_121[];
/*z*/extern const unsigned char XB_Zar_122[];
extern const unsigned int XB_Zar_width[];
extern const unsigned char* XB_Zar_addr[];
extern const String XB_Zar_name[];
//zar.cpp

/*v*/ const unsigned char XB_Zar_118[] PROGMEM ={255,255,254,255,63,255};
/*w*/ const unsigned char XB_Zar_119[] PROGMEM ={255,127,255,254,255,5};
/*x*/ const unsigned char XB_Zar_120[] PROGMEM ={215,215,23,254,255,127};
/*y*/ const unsigned char XB_Zar_121[] PROGMEM ={255,127,25,254,255,9};
/*z*/ const unsigned char XB_Zar_122[] PROGMEM ={255,243,239,254,215,19};
const unsigned int XB_Zar_width[] = {2, 2, 2, 2, 2, 0}; // TODO: fix the 6th element!
const unsigned char* XB_Zar_addr[] = {XB_Zar_118, XB_Zar_119, XB_Zar_120, XB_Zar_121, XB_Zar_122, NULL}; // TODO: fix the 6th element!
const String XB_Zar_name[] = {"۰", "۱", "۲", "۳", "۴", "۵", ""}; // TODO: remove the 6th element?
//font.h

#pragma once
#include "zar.h"
extern const unsigned char **char_addr;
extern const unsigned int *char_width;
extern const String *uni_name;
extern void setFont(byte fontNumber);
extern int getGliphData(const String &uniChar, int *index, int *width);
//font.cpp

#include "font.h"


const unsigned char **char_addr = NULL;
const unsigned int *char_width = NULL;
const String *uni_name = NULL;
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
void setFont(byte fontNumber)
{
    switch (fontNumber)
    {
    case 0:
        char_addr = XB_Zar_addr;
        char_width = XB_Zar_width;
        uni_name = XB_Zar_name;
        break;

    default:
        char_addr = XB_Zar_addr;
        char_width = XB_Zar_width;
        break;
    }
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
int getGliphData(const String &uniChar, int* index, int* charWidth)
{
    Serial.println("uniChar data= " + uniChar);
    if (uni_name)
    {
        for (int i = 0; uni_name[i].Length() > 0; ++i)
        {  
            Serial.println("uni_name[" + String(i) + "]= " + uni_name[i]);
            if (uniChar == uni_name[i])
            {
                *index = i;
                *charWidth = char_width[i];
                return i;
            }
        }
    }
    return -1;
}

Online Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I wanted to delete the question. But your answer was so great and comprehensive that it added worthiness to my question. Thank you for taking the time to respond to me. The only thing I need to point out is that because of the use of ‍`PROGMEM`, the data is stored in Flash and `pgm_read_word_near()` must be used to fetch it. Based on [this link](https://www.arduino.cc/reference/en/language/variables/utilities/progmem/). – mohammad bagher Malekhosseini Jul 24 '21 at 09:24
  • All of your cods is working ritgh. The only thing that gives an error when compiling is this sentence: `char_addr = XB_Zar_addr;` Error is: `a value of type "const unsigned char **" cannot be assigned to an entity of type "unsigned char *"` – mohammad bagher Malekhosseini Jul 24 '21 at 10:55
  • @mohammadbagherMalekhosseini Look more closely at my examples. I had changed (amongst other things) `char_addr` to be declared as `const unsigned char **`, not as `unsigned char *` as it is in your original code. You need to update your code. – Remy Lebeau Jul 24 '21 at 14:47
  • I know . I changed it like your code. This error does not make sense. – mohammad bagher Malekhosseini Jul 24 '21 at 16:21
  • @mohammadbagherMalekhosseini as you can see from the demo links in my answer, my examples do compile. So you probably have an old version of your code lingering around somewhere, being picked up by the compiler instead of the new code. Unless the error is coming from another piece of code you haven't shown yet. – Remy Lebeau Jul 24 '21 at 16:37
  • Thank you very much @RemyLebeau.There is no problem with your code. But your compiler is for PC and my compiler is for microcontroller. The problem seems to be the compiler's behavior in comparing `char_addr` pointer with `XB_Zar_addr` pointer. Pointer one is a regular c++ `const` pointer that points to memory space and pointer two is a special pointer that point to flash space(read only memory). The compiler does not see the same type when comparing them. I have completed your excellent answer for running on ESP32 microcontroller. I will put it in full below. – mohammad bagher Malekhosseini Jul 24 '21 at 18:09
0

I have completed @RemyLebeau excellent answer for running on ESP32 microcontroller.

    // zar.h

/*v*/extern const unsigned char XB_Zar_118[];
/*w*/extern const unsigned char XB_Zar_119[];
/*x*/extern const unsigned char XB_Zar_120[];
/*y*/extern const unsigned char XB_Zar_121[];
/*z*/extern const unsigned char XB_Zar_122[];
extern const unsigned int XB_Zar_width[];
extern const unsigned char* XB_Zar_addr[];
extern const String XB_Zar_name[];
extern const int XB_Zar_name_len;

zar.cpp file:

//zar.cpp    
/*v*/ const unsigned char XB_Zar_118[] PROGMEM ={255,255,254,255,63,255};
/*w*/ const unsigned char XB_Zar_119[] PROGMEM ={255,127,255,254,255,5};
/*x*/ const unsigned char XB_Zar_120[] PROGMEM ={215,215,23,254,255,127};
/*y*/ const unsigned char XB_Zar_121[] PROGMEM ={255,127,25,254,255,9};
/*z*/ const unsigned char XB_Zar_122[] PROGMEM ={255,243,239,254,215,19};
const unsigned int XB_Zar_width[] = {2, 2, 2, 2, 2, 0}; // TODO: fix the 6th element!
const unsigned char* XB_Zar_addr[] = {XB_Zar_118, XB_Zar_119, XB_Zar_120, XB_Zar_121, XB_Zar_122, NULL}; // TODO: fix the 6th element!
const String XB_Zar_name[] = {"۰", "۱", "۲", "۳", "۴", "۵"}; // TODO: remove the 6th element?
const int XB_Zar_name_len = sizeof(XB_Zar_name)/sizeof(XB_Zar_name[0]);

font.h file:

//font.h

#pragma once
#include "zar.h"
extern const unsigned char **char_addr;
extern const unsigned int *char_width;
extern const String *uni_name;
extern void setFont(byte fontNumber);
extern int getGliphData(const String &uniChar, int *index, int *width);

font.cpp file:

//font.cpp

#include "font.h"


const unsigned char **char_addr = NULL;
const unsigned int *char_width = NULL;
const String *uni_name = NULL;
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
void setFont(byte fontNumber)
{
    switch (fontNumber)
    {
    case 0:
        char_addr = (unsigned char *)pgm_read_ptr(XB_Zar_addr[0]); // use pgm_read_ptr() becuse there is PROGMEM space in flash memory
        char_addr = XB_Zar_addr;
        char_width = XB_Zar_width;
        uni_name = XB_Zar_name;
        break;

    default:
        char_addr = (unsigned char *)pgm_read_ptr(XB_Zar_addr[0]); // use pgm_read_ptr() becuse there is PROGMEM space in flash memory
        char_addr = XB_Zar_addr;
        char_width = XB_Zar_width;
        uni_name = XB_Zar_name;
        break;
    }
    //Now. char_addr points to flash space and no need to use pgm_read_word_near()  
    // or using pgm_read_byte_near(). Simply use "*(char_addr)" or "*(char_addr+number)"
    //Serial.println("char_addr[0]= "+String(*(char_addr))));
    //Serial.println("char_addr[1]= "+String(*(char_addr+1)));
    //Serial.println("char_addr[3]= "+String(*(char_addr+3)));   
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
int getGliphData(const String &uniChar, int* index, int* charWidth)
{
    Serial.println("uniChar data= " + uniChar);
    if (uni_name)
    {
        for (int i = 0; uni_name[i].Length() > 0; ++i)
        {  
            Serial.println("uni_name[" + String(i) + "]= " + uni_name[i]);
            if (uniChar == uni_name[i])
            {
                *index = i;
                /* *charWidth = char_width[i]; it works if 'i' is 
                 * a const number at compile time. 
                 * constant 'i' only works because the compiler 
                 * sets the value of char_width[i] during 
                 * compilation, char_width[i] is a constant value
                 * pointing to one specific location in progmem 
                 * that the compiler knows the value of while 
                 * compiling see:https://forum.arduino.cc/t/passing-a-pointer-to-progmem-in-function/637756/5
                */
                *charWidth = pgm_read_word_near(char_width+*index);
                return i;
            }
        }
    }
    return -1;
}