Removing Serial.println statement crashes the program

Hello Everybody,

I have a sketch where removing a Serial.println (to debug the output of a variable) statement crashes it. My expectations were for nothing to happen. If anything I have read about people having problems when adding a Serial.println statement in their sketch but not when removing them. I have tried the sketch on a different board and I get the same problem, so it does not seem to be H/W-related.

The funny thing is that it refers to 2 Serial.println statements. Removing any of them crashes the whole sketch. I managed to get past the 1st one by putting a Serial.flush() in front of it then I can comment it out. The second one is stubborn and crashes my sketch if I try to remove it, whatever I do.

Eventually I decided to have a look at the debug stack in more details and this is my progress so far:

Exception 29: StoreProhibited: A store referenced a page mapped with an attribute that does not permit stores
PC: 0x40208ae9
EXCVADDR: 0x00000029

So it looks like when removing those Serial.println statements the program checks something in memory and realizes it is checking in a location that is not supposed to be storing anything in the first place and crashes (my interpretation).

Looking at the top of the stack it gives me the following error:

0x40202496: String::getBytes(unsigned char*, unsigned int, unsigned int) const at C:\Users.....\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\cores\esp8266\WString.cpp line 629

So it looks like String::getBytes(unsigned char*, unsigned int, unsigned int) const does not like what I am doing on line 629 of the WString.cpp file.

I went to check the code in this file and it is what it looks like:

void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const {
    if(!bufsize || !buf)
        return;
    if(index >= len()) {
        buf[0] = 0;
        return;
    }
    unsigned int n = bufsize - 1;
    if(n > len() - index)
        n = len() - index;
    strncpy((char *) buf, buffer() + index, n);
    buf[n] = 0;
}

Line 629 is the one with buf[n] = 0;.

The next 2 lines on the stack are as follows:

0x40100974: malloc(size_t) at C:\Users....\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\cores\esp8266\umm_malloc\umm_malloc.cpp line 511
0x40203541: uart_flush(uart_t*) at C:\Users....\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.3\cores\esp8266\uart.cpp line 544

Then the next line points to my code:

0x4020105c: StringToCharray(String) at C:\Users...\Testing.ino line 72

When I go to this line, I find the following function I wrote:

char* StringToCharray(String str) {
  int str_int = str.length() + 1;
  char* str_charray[str_int];
  str.toCharArray(*str_charray, str_int);
  return *str_charray;
}

Line 72 in my code is the exiting curly bracket "}".
The function had been working previously so I am not certain why it is acting-up now.

So, at this stage it looks like I am doing something wrong in this function that generates some very odd behavior when the sketch is being compiled.

This is as far I could take it as my eyes are starting to blur out on me.

So, I would have 2 questions:

  1. Has anybody had any similar behavior and did they manage to fix it and how?
  2. Do any of the code gurus on here spot anything wrong in my function?

Thanks.

Can you post the whole sketch so we can see where that function is being called?

Your str_chararray is an array of char* I.e. An array of pointers. You just need an array of char.

A second problem is that you are trying to return a local variable from your function. That’s not going to go well.

@davie_gravy : Here is a curate version of the code that shows how the function is called. The full program is much larger and the crash happens only after what is stored in PROGMEM gets larger and the function gets called repeatedly. This sample will compile and not generate any problem but at least it shows you how the function is called

int dfltval01 = 5;          
int ip12 = 0;
const int array01 = 360;
char *f[array01];
const int htmlbufsiz = 4 * 160;
char htmlbuf[htmlbufsiz];
size_t html_len;

String html_addval_ibx(int defval) {
  String defvalcat = "value=\"" + String(defval) + "\">";
  return defvalcat;
}

char* StringToCharray(String str) {
  int str_int = str.length() + 1;
  char* str_charray[str_int];
  str.toCharArray(*str_charray, str_int);
  return *str_charray;
}

const char s0[]   PROGMEM = "<!DOCTYPE html> <head><style>body{background-color:lightblue;}</style></head> <html><body>\n"; //#0
const char s5[]  PROGMEM = "<h1 align=\"center\">Some text</h1>\n\n"; //#1
const char s10[] PROGMEM = "<input type=\"text\" id=\"SNaddress4\" name=\"SNaddress4\" size=\"3\" maxlength=\"3\" style=\"text-align:center;\" disabled=\"disabled\" "; //#2
const char s500[] PROGMEM = "</body></html>"; //#3

const char* const s_tbl[] PROGMEM = { s0, s5, s10, s500};

int html_num() {
  int x;
  x = sizeof(s_tbl) / sizeof(s_tbl[0]);
  return x;
}

void hmpg() {
  for (int c = 0; c < html_num(); c++) {
    switch (c) {
      case 1: {
        strcat_P(htmlbuf, (char*)pgm_read_dword(&(s_tbl[c])));
        String str11 = html_addval_ibx(dfltval01);
        char *chr11 = StringToCharray(str11);
        strcat(htmlbuf, chr11);
      } break;

      case 2: {
        strcat_P(htmlbuf, (char*)pgm_read_dword(&(s_tbl[c])));
        //html_addval_ibx2(ip4);
        char *chr69a = "value=\"";
        String str69 = String(ip12);
        char *chr69b = StringToCharray(str69);
        char *chr69c = "\">";
        strcat(htmlbuf,chr69a);
        strcat(htmlbuf,chr69b);
        strcat(htmlbuf,chr69c);
        //Serial.flush();
        Serial.println(chr69a); // CRASHES THE OUTPUT IF COMMENTED OUT............!!!!!!!!!!!!!!!!!!!!!!
        Serial.println(chr69c); // CRASHES THE OUTPUT IF COMMENTED OUT............!!!!!!!!!!!!!!!!!!!!!!
      } break;

      default: //Serial.println(F("Normal concatenation"));
        strcat_P(htmlbuf, (char*)pgm_read_dword(&(s_tbl[c])));
        break;
    }
  }
  html_len = strlen_P(htmlbuf);
}

void setup() {
  Serial.begin(74880);
  hmpg();

  Serial.println("");
  Serial.print(F("The number lines in the HTML code is : "));
  Serial.println(html_num());

  Serial.print(F("The number of bytes used by the HTML string is : "));
  Serial.println(html_len);

  Serial.print(F("The number of bytes available to the HTML string is : "));
  Serial.println(htmlbufsiz);

  Serial.print(F("Total space used : "));
  float html_pct = (html_len*100.00/htmlbufsiz);
  Serial.print( html_pct);
  Serial.println(F("%"));
  Serial.println("");
}

void loop() {

}//End Loop

Does this actually work? If it does once, don't count on that to continue.

String html_addval_ibx(int defval) {
  String defvalcat = "value=\"" + String(defval) + "\">";
  return defvalcat;

Yes it does work. Why do you think it will not work for long?

defvalcat is a local variable, valid only within the defining block of code. By language definition, it ceases to exist when you return from the function.

You might get lucky and have it still on the stack somewhere, but you are certainly asking for serious trouble.

@wildbill & @jremington : Thank you, problem solved!

  • I declared the return variables outside the functions
  • I made StringToCharray(String str) return a char

Basic errors for a beginner like myself.

It now runs as smooth as a baby’s bum!

Cheers for the help.

A String is not a primitive data type so what is being returned to the calling program is not a value but a handle to an object. The storage space for the object belongs to the called function and is released when the function returns.

@6v6gt : I was under the wrong assumption that a function had an implicit constructor and destructor that wiped local variables clean after returning which is why I originally declared them locally thinking it would not mess with memory.

Now I understand, thanks to your explanation, why with a String this is not the case. I did not know about the concept of primitive data types and the implications either.

I am making slow progress everyday thanks to everybody on this forum. :slight_smile:

Yes, you've just got to learn the rules and these are not necessarily very obvious.

This is OK:

int html_addval_ibx ;  // global
. . .
void html_addval_ibx_function(int defval ) {
  int defvalcat = 99 + defval + 27 ;
  html_addval_ibx = defvalcat ;
  return ;
}

This is not OK (for all the reasons already given) although the structure looks similar enough to the previous example :

String html_addval_ibx ; // global
. . .
void html_addval_ibx_function(String defval ) {
  String defvalcat = "value=\"" + String(defval) + "\">";
  html_addval_ibx = defvalcat ;
  return ;
}

A String, wherever you declare it, is stored on the heap (not the stack as with other local variables). When the function in which it is declared goes out of scope, the memory is free for reuse (but not necessarily immediately wiped clean) giving rise to mysterious behaviour on subsequent attempts to use any existing handle to it. If you are using Strings, incidentally, it is always a good idea to use their reserve() method to dedicate as specific piece of heap storage to them to minimise general heap fragmentation.

I have read about how tricky using String can be if you do not know what you are doing, which is my case, and I guess this is a textbook example.

I have been trying to move away as much as possible from using it and into char arrays but it comes with teething problems as well.

I am not there yet but hopefully on my way, even if slowly...

This all seems like wasted effort. Why not simply use String.c_str()??

Regards,
Ray L.

I hesitated between toCharArray() and c_str(). Reading various comments on this topic, I did not find a clear answer as to one would be superior to the other.

One thing I did read was to be careful when using c_str() with regards to memory management especially when it comes to pointers. Given that I already managed to make a mess with String from a memory usage standpoint I thought I would steer towards the option giving me the least ability to make a mess of things.

Being a beginner can be a bit daunting as many people have different opinions on how to do things and it is easy to start getting lost amongst various opinions. For now I am attempting to get something that works and is clean even if it not the best way to go about things. If there are better way to do things down the road as my level improves then why not.

Ooops, just realized that changing my function output from char * to char only returns the 1st character of my character array. Just shows you how much of a beginner I am.