Problem with a char array

In the following code, ‘maxNumberCharacters’ is 136.
If the offending line (Line 7 of loop()) is commented out, the sketch runs fine. With that line operational, the sketch continuously restarts. This is running on a Uno so there should be no problem with SRAM (2K).

/*
   Colour_LCD_Display_Test_1
   Created: Oct 15, 2011
   Last modified: Oct 15, 2011
   PeteC
   
   *** Initial purpose:
   Display text on the display.
   
   *** Hardware:
   LCD Colour 132 x 132 Shield with Epson controller.
   
   *** Software:
   To adjust parameters on the fly, the program accepts commands from
   the serial input using a leading 1-character key to identify the
   parameter.
   'd' - Set the overall display colour as a 3 digit hex number.
   'b' - Set the character background colour as a 3 digit hex number.
   'f' - Set the character foreground colour as a 3 digit hex number.
   'c' - Set the contrast as a decimal number between 0 and 63.
   'x' - Set the horizontal starting position of the next line as a decimal number between 0 and 127.
   'y' - Set the vertical starting position of the first line as a decimal number between 0 and 127.
   's' - Set the line spacing as a decimal number between 0 and 63.
   '0' to '7' - Set a string to go into line n (n = 0 to 7).
*/

  //	Included files
  #include <string.h> 
  #include <stdlib.h> 
  #include <stdio.h> 
  #include <ctype.h> 
  #include <avr/io.h> 
  #include <avr/interrupt.h> 
  #include <avr/pgmspace.h> 
  #include "WProgram.h" 
  #include "HardwareSerial.h" 
  //	External Component Libs 
  #include "LCD_driver.h"
  
  #define maxLineLength 17
  #define maxNumberLines 8
  
  int contrast = 44;
  int x = 0;
  int y = 0;
  int s = 14;
  int fColour = 0xF00;
  int bColour = 0;
  int clearColour = 0;
  boolean starting = true;
  int maxNumberChars = maxNumberLines * maxLineLength;

  void setup() {
    ioinit();    // Initialize I/O.
    LCDInit();    // Initialize the LCD.
    Serial.begin(9600);
    Serial.println("Starting ...");
    Serial.println(maxNumberChars, DEC);
  }
  
  void loop() {
    char text[maxNumberChars];
    int maxPrintLength = maxLineLength - 1;
    int i;
    int j;
    if (starting) {
// **** This the offending line:
//      for (i=0; i<maxNumberChars; i++) text[i] = 'x';
      for (i=0; i<maxNumberLines; i++) text[maxLineLength * i + maxPrintLength] = 0;
      starting = false;
    }
    Serial.println(millis()/1000, DEC);
    LCDContrast(contrast);  // Set the contrast.
    LCDClear(clearColour);
    int yLine = y;
    char test[17] = "  Pete's test";
    for (i=0; i<maxNumberLines; i++) {
//      LCDPutStr(&text[i * maxLineLength], yLine, x, fColour, bColour);
      LCDPutStr(&test[0], yLine, x, fColour, bColour);
//      LCDPutStr("  Pete", yLine, x, fColour, bColour);
      yLine += s;
    }
    delay(1000);
    byte key = 0;
    boolean validInput = true;
    boolean processIncoming;
    int conversionType;
    int newNumber = 0;
    int textIndex = 0;
    int textLineNumber;
    char textLine[maxLineLength];
    for (i=0; i<maxLineLength; i++) textLine[i] = 0;
    while (Serial.available() > 0) {
      processIncoming = true;
      byte incoming = Serial.read();  // Read the next character.
      if (key == 0) {
        processIncoming = false;
        Serial.println("Setting key");
        key = incoming;
        if ((key == 'd') || (key == 'b') || (key == 'f')) {
          conversionType = 0;
        } else if ((key == 'c') || (key == 'x') || (key == 'y') || (key == 's')) {
          conversionType = 1;
        } else if ((key >= '0') && (key <= '7')) {
          conversionType = 2;
          textLineNumber = key - '0';
          for (j=0; j<maxLineLength; j++) textLine[j] = 0;
        } else validInput = false;
      } else if (incoming == 13) conversionType += 3;
      if (validInput && processIncoming) {
        if (conversionType == 0) {
//          case 0:  // Hex colour input.
            if (incoming >= 'a') incoming -= 'a' - 'A';
            if (incoming > '9') incoming -= 'A' - '9' - 1;
            Serial.println(incoming, HEX);
            if ((incoming < '0') || (incoming > '9' + 6)) validInput = false;
            newNumber = newNumber * 16 + incoming - '0';
//            break;
        } else if (conversionType == 1) {
//          case 1:
            if ((incoming < '0') || (incoming > '9')) validInput = false;
            newNumber = newNumber * 10 + incoming - '0';
//            break;
        } else if (conversionType == 2) {
//          case 2:
            if (textIndex < 16) textLine[textIndex++] = incoming;
//            break;
        } else if (conversionType == 3) {
//          case 3:
              if (key == 'd') clearColour = newNumber & 0xFFF;
              else if (key == 'b') bColour = newNumber & 0x0FFF;
              else fColour = newNumber & 0x0FFF;
              Serial.print("Got a 3 line with hex value: ");
              Serial.print(newNumber, HEX);
              Serial.print(" and key: ");
              Serial.println(key);
//            break;
        } else if (conversionType == 4) {
//          case 4:
              if (key == 'c') contrast = newNumber & 0x3F;
              else if (key == 'x') x = newNumber & 0xFFF;
              else if (key == 'y') y = newNumber & 0xFFF;
              else s = newNumber & 0x3F;
              Serial.print("Got a 4 line with decimal value: ");
              Serial.print(newNumber, DEC);
              Serial.print(" and key: ");
              Serial.println(key);
//            break;
        } else if (conversionType == 5) {
//          case 5:
//              for(i=0; i<maxLineLength; i++)
//                text[textLineNumber * maxLineLength + i] = textLine[i];
              Serial.print("Got a 5 line with text: ");
              Serial.print(&textLine[0]);
              Serial.print(" and key: ");
              Serial.println(key);
         }
//           break;
//          default:
//            break;
      }
    Serial.print("Test Input  ");
    Serial.println(conversionType, DEC);
    }
//      Serial.println(lineValid, DEC);
  }

Are you compiling and linking this using the IDE? If so, why are you including all those include files that the IDE adds? Some of them, like avr/interrupts.h. I don't see that you need.

  int maxNumberChars = maxNumberLines * maxLineLength;

I don't think that works at global scope. Just create the variable here and then set it equal in setup()

Delta_G:

  int maxNumberChars = maxNumberLines * maxLineLength;

I don't think that works at global scope. Just create the variable here and then set it equal in setup()

That is legal. It is the same as using "int foo = 2 * 4". If it wasn't legal, the complier wouldn't succeed.

Pete, don't forget that when you declare an array you declare how many elements it contains. If you create an array like: bar[8], the elements only run from 0 to 7.

I haven't studied your code, but I have a feeling you didn't take this isn't account.

I don't see anything if 7th line should make any problem other than that which Delta_G pointed:

Delta_G:

  int maxNumberChars = maxNumberLines * maxLineLength;

I don't think that works at global scope. Just create the variable here and then set it equal in setup()

@James.. you might be right but he should try this.. Cuz i don't see anything else wrong..

Oh, I thought you couldn't do math operators at global scope. I thought it was like making function calls in a declaration at global scope.

Paul, Yes I am using IDE. The reason for all the include files is that Peter Davenport told me to in his manual in the LCD_Display library. I got my first Arduino module last Friday. When I become more familiar with the IDE, hopefully I'll know more about what I need.

Delta, I also wondered about this at one point. That is why I write maxNumberChars out the serial port during setup. That does give me the right number.

James, although I've only started with Arduino, I have been coding since 1959 (started on an IBM 650 - a decimal machine constructed completely using vacuum tubes and with a drum memory). So, thanks for the tip but I am familiar with arrays.

The solution appears to be that the compiler doesn't correctly handle the statement:
char text[maxNumberChars];

I changed it to:
char text[maxNumberLines * maxLineLength];

and that problem disappeared.

In other words, it appears that the software cannot correctly set up the array during execution (because only then does it know the value of maxNumberChars).
With the change, the compiler knows the size of the array and can set it up before execution. Does this make sense?

So thanks to all of you.

I now have a new problem that is related. The purpose of this array is to have an array of strings that I can modify and output to the LCD display. The LCDPutStr library routines requires a pointer to the beginning of the string. So far I haven't been successful in providing it with the right value. I'll play with it some more but if I don't solve it I will probably start a new topic.

With the change, the compiler knows the size of the array and can set it up before execution. Does this make sense?

Yes, it does. The calculation for maxNumberChars is not done at compile time. It is done at run time. The space for the array is allocated at compile time. Since maxNumberChars at compile time contains a 0, the array size is pretty small.

If maxNumberChars were a #define, as maxNumberLines and maxLineLength are, I think the compiler would kick out an error message, or at least a warning, although warnings are suppressed, so you’d never see it.

The LCDPutStr library routines requires a pointer to the beginning of the string.

So, create one that points to the start of the array. Then, increment it to walk through the array. When it points to the right place in the array, call LCDPutStr. Or just offset it to the correct location.

Actually, pointers and arrays are very closely linked. I'd be surprised if you couldn't just pass it something like &text[n], where n is the start of the data you want to show.

That was my thought also - &text[i * maxLineLength] with i = 0 to maxNumberLines-1. However, there is no output and the sketch continuously restarts. By the way, I assume that restarting is the default response when execution goes of the rails.

I then tried making my own pointer: char* textPtr = &text[0] and manipulating that - the same problem. In fact I couldn't even print the value.

A side question: Is there any way of finding out how much SRAM is in use? Is there any warning if you exceed the available space?

char *textPtr = text;

Point to the start of the array.

A style note:
The * can go next to the type (char* ptr) or next to the variable being typed (char *ptr). I prefer the latter, because you see the *ptr, and know that it is a pointer.

You can mix types on a declaration. In that case, the * must go with the variable, otherwise...

char *ptr1, *ptr2;

declares two variables of type pointer to char.

char* ptr1, ptr2;

declares two variables. The first is a pointer to char. The second is a char.

One would look at char* and think that you are declaring a series of variables of type pointer to char, but the compiler sees that differently.

Here is a peculiar twist on the pointer problem.

    char *textPtr = &text[0];
    long tp = (long)textPtr;
    Serial.println(tp, DEC);

This does not work. Execution goes through setup() and then hangs. If I change tp to -123456, the sketch works; if I comment out the println, it works. But it refuses to print the value of the pointer. How does it know what I'm printing?

You are assigning the pointer to point to the address that is in text[0], not the address of text[0].

What is in text[0]? Unless you have changed the contents of text, it contains 0 (or NULL, depending on your point of view).

So, tp is NULL. You try to dereference a NULL pointer in the Serial.println() call. Not surprising that it doesn't work.

So, tp is NULL. You try to dereference a NULL pointer in the Serial.println() call.

tp is not a pointer, so how can it be dereferenced?
The contents of “text[0]” are irrelevant if the address-of (&) operator is used on it.

Sorry, away from an Arduino at the mo, so can’t try anything.

Paul,

I don't buy your explanation. &text[0] is the address of byte 0 of the text array. And tp is a value, not an address.

However, I have been fiddling with the sketch and find that there is something later in the program that is causing the problem although I haven't found it yet. Most of the rest of the sketch only operates if there is serial input. By commenting out everything after the delay(1000), the sketch works. Now I'll start putting it back together.

Thanks for your help. I am sure I'll be back with more questions.

Pete