Serial.print causes reset of Arduino

Hello together,

I have problems with getting messages within my code on the serial monitor by using Serial.print. I've been working on this issue since last night but cannot find a reason. Hopefully somebody in this forum has an advice for me.

I am working with the latest Arduino IDE for windows (v1.8.2) and only with standard libraries.

The code works with the following hardware:

  • Arduino MEGA2560 R3 from Elegoo
  • key pad with 10 buttons (numbers 0 to 9) - every key is assigned to a separate Arduino input (no key matrix)
  • seven segment display with 4 digits driven by a MAX7219

The goal is to enter 2 numbers with 2 digits each. The numbers are entered via the key pad and shown on the seven segment display. The left two digits of the seven segment display are for the first number and the right two digits are for the second number. The numbers are separated on the seven segment display by a dot on the second digit. After entering they are stored in an array.
The numbers will be post processed after entering but I have removed the whole post processing code from the attached code exerpt.

My problem is:
My code seems to work - but I cannot verify that because I have problems with writing back variable values to the serial monitor.
In the code excerpt there are two "Serial.println" commands - one in the "setup" part and one in the "loop" part. The command is working in the "setup" part but in the "loop" part it is only working before the first "if" statement. If I place the command at any other position in the "loop" part the Arduino will be reset as soon as it arrives the "Serial.println" commands (=> same behaviour as when I press the RESET button on the board).

I have worked with the serial monitor many times before and never had problems with it until now - no matter how deep the code was nested.

In the web I found a lot of different possible reasons (bad voltage supply, hardware defect, too new IDE, full SRAM ...). I have checked all of these reasons but none of them has worked for me.

The only thing I did not try is to exchange the Arduino board. I don't think that it is a hardware fail because in all my other codes the Serial.println is still working.

So I guess the mistake is within my code and my question to you is:
Do you see anything within my code which could cause this strange behaviour?

Thanks in advance
Martin

test03.ino (9.67 KB)

    pinMode(PinBtn[0], INPUT_PULLUP);
    pinMode(PinBtn[1], INPUT_PULLUP);
    pinMode(PinBtn[2], INPUT_PULLUP);
    pinMode(PinBtn[3], INPUT_PULLUP);
    pinMode(PinBtn[4], INPUT_PULLUP);
    pinMode(PinBtn[5], INPUT_PULLUP);
    pinMode(PinBtn[6], INPUT_PULLUP);
    pinMode(PinBtn[7], INPUT_PULLUP);
    pinMode(PinBtn[8], INPUT_PULLUP);
    pinMode(PinBtn[9], INPUT_PULLUP);
        Digit[0] = 0;
        Digit[1] = 0;
        Digit[2] = 0;
        Digit[3] = 0;

Do you have an allergy to for loops?

Can't look at your code right now but this often indicates memory issues. Some things that can cause it:
1)
Use of String (capital S) that can cause memory fragmentation.
2)
Writing data outside the boundaries of arrays.

Note: the reset with a Serial.print is the symptom, not the cause.

no, no allergy to for loops :wink: ... I didn't think on a for loop for the pin config so far ... has already been implemented :wink: .

I was just thinking that if you reduced the size of your code, you could post it here, instead of forcing people to download it.

sterretje:
Can't look at your code right now but this often indicates memory issues. Some things that can cause it:
1)
Use of String (capital S) that can cause memory fragmentation.
2)
Writing data outside the boundaries of arrays.

Note: the reset with a Serial.print is the symptom, not the cause.

thanks for your quick reply

ad 1):
datatype "string" is not in use

ad 2):
I have checked that several times but I don't think that I handle the arrays in false way or fill them with false values.

and yes, I am fully aware of that symptom - for the cause I have been checking my voltage supply and (with my very small programming knowledge) my code - without success so far :confused: .

CurNumListEntriesWhat keeps this variable in-bounds?

In line 217 you have

CurNumListEntries++;

and in line 213 you are using that as an array index

EnteredNumber1[CurNumListEntries] =

but nowhere have you any code to prevent th value from exceeding the number of elements in the array.

...R

AWOL:
I was just thinking that if you reduced the size of your code, you could post it here, instead of forcing people to download it.

I have reduced the code (removed the config of the seven segment display driver which is only bitshifting):

// pins
  // pins for the keypad
  const unsigned char PinLedTest = 53;
  const unsigned char PinBtn[10] = {34, 52, 50, 48, 46, 44, 42, 40, 38, 36};
  
  // pins for controlling the seven segment display via MAX7219
  const unsigned char SevenSegPinLatch = A0;
  const unsigned char SevenSegPinClock = A2;
  const unsigned char SevenSegPinData = A1;

// variables
  // general variables
  unsigned long int CurTime;  // auxiliary variable for timeouts, button debouncing etc.
  
  // variables for the keypad
  unsigned char CurStateBtn[4] = {HIGH, HIGH, HIGH, HIGH};  // variable for debouncing the buttons
  unsigned char OldStateBtn[4] = {HIGH, HIGH, HIGH, HIGH};  // variable for debouncing the buttons
  unsigned char CurDigitCount = 0;  // auxiliary variable for assigning the pressed button to the correct digit
  unsigned char Digit[4] = {0, 0, 0, 0};  // auxiliary variable for buffering the single digits of EnteredNumber1 and EnteredNumber2 while they are entered
  const unsigned char Tdeb = 10; // debouncing time of the buttons

  // variables for the panel controller
  unsigned char EnteredNumber1[10] = {};  // entered number 1 for further processing; maximum 10 values can be stored
  unsigned char EnteredNumber2[10] = {};  // entered number 2 for further processing; maximum 10 values can be stored
  unsigned char CurNumListEntries = 0;  // auxiliary variable for counting the current number of list entries
  
  // variables for the seven segment display
  unsigned char SevenSegDigits = 4; // number of vailable digits on the seven segment display
  unsigned char SevenSegDot = 0b10000000;  // address of the dot of the seven segment display
  unsigned char SevenSegDigit[4] = { 0x01,  // = Digit 0   // addresses of the digits of the seven segment display
                                     0x02,  // = Digit 1
                                     0x03,  // = Digit 2
                                     0x04,  // = Digit 3
                                   };
  unsigned char SevenSegChar[11] = { 0b01111110,  // = 0    // characters to be shown on the seven segment display
                                     0b00110000,  // = 1
                                     0b01101101,  // = 2
                                     0b01111001,  // = 3
                                     0b00110011,  // = 4
                                     0b01011011,  // = 5
                                     0b01011111,  // = 6
                                     0b01110000,  // = 7
                                     0b01111111,  // = 8
                                     0b01111011,  // = 9
                                     0b00000000,  // = blank
                                   };

// functions
  // functions for the keypad
    // function for monitoring the buttons (true = one of the buttons is pressed)
    bool BtnPressed()  {
      for(unsigned char x = 0; x <= 9; x++)  {      // number of buttons ("<= 9" for 10 buttons)
        if(digitalRead(PinBtn[x]))  {}
        else  { return true; }
      }
      return false;
    }

  // functions for the seven segment display
    // function for reseting all COM pins of the seven segment display, respectively of the MAX7219
    void SevenSegResetComPins()  {
      digitalWrite(SevenSegPinLatch, HIGH);
      digitalWrite(SevenSegPinClock, LOW);
      digitalWrite(SevenSegPinData, HIGH);
    }
    
    // function for sending the chosen digit/character to the seven segment display (via MAX7219)
    void SendSevenSegNumber(unsigned char SevenSegDigit[], unsigned char SevenSegChar[], unsigned char SevenSegDot = 0b00000000) {
      SevenSegResetComPins();
      digitalWrite(SevenSegPinLatch, LOW);
      shiftOut(SevenSegPinData, SevenSegPinClock, MSBFIRST, SevenSegDigit);
    
      // adding a dot if explicitly specified (SevenSegDot)
      if(SevenSegDot == 0b10000000)  {
        unsigned char a = SevenSegChar;
        SevenSegChar = a | SevenSegDot;
      }
    
      shiftOut(SevenSegPinData, SevenSegPinClock, MSBFIRST, SevenSegChar);
      digitalWrite(SevenSegPinLatch, HIGH);
    }
    
    // function for clearing the display of the seven segment display - except from the dot on the second digit
    void SevenSegClearDisplay() {
      SendSevenSegNumber(SevenSegDigit[0], SevenSegChar[10]);
      SendSevenSegNumber(SevenSegDigit[1], SevenSegChar[10], SevenSegDot);
      SendSevenSegNumber(SevenSegDigit[2], SevenSegChar[10]);
      SendSevenSegNumber(SevenSegDigit[3], SevenSegChar[10]);
    }


void setup() {
  // Arduino setup
    // starting serial monitor communication at baud rate 9600
    Serial.begin(9600);

  // keypad setup
    // selecting pin mode for seven segment pins
    for(byte i = 0; i <= 9; i++)  { pinMode(PinBtn[i], INPUT_PULLUP); }
    pinMode(PinLedTest, OUTPUT);

  // sevent segment display setup
    // selecting pin mode for seven segment pins
    pinMode(SevenSegPinLatch, OUTPUT);
    pinMode(SevenSegPinClock, OUTPUT);
    pinMode(SevenSegPinData, OUTPUT);
  
/* ... config of the seven segment driver ... */

    Serial.println("setup finished");
}

void loop() {
  // checking if a button of the keypad is pressed and processing the entered numbers 1 and 2
  if(BtnPressed())  {
    if(CurDigitCount <= 3)  {      // number of digits to be entered ("<= 3" for 4 digits)
      byte x;
      for(x = 0; x <= 9; x++)  {      // number of buttons ("<= 9" for 10 buttons)
        CurStateBtn[x] = digitalRead(PinBtn[x]);

        // debouncing the button when pressing it
        if(CurStateBtn[x] != OldStateBtn[x])  {
          OldStateBtn[x] = CurStateBtn[x];
          CurTime = millis();
          while(millis() - CurTime < Tdeb)  {}
        }

        if(CurStateBtn[x] == LOW) {
          // activating the test LED when the button is pressed
          digitalWrite(PinLedTest, HIGH);

          // no further action as long as the button is pressed
          while(CurStateBtn[x] == LOW)  {
            CurStateBtn[x] = digitalRead(PinBtn[x]);

            // debouncing the button when releasing it
            if(CurStateBtn[x] != OldStateBtn[x])  {
              OldStateBtn[x] = CurStateBtn[x];
              CurTime = millis();
              while(millis() - CurTime < Tdeb)  {}
            }
          }

          // deactiving the test LED when the button has been released
          digitalWrite(PinLedTest, LOW);

          // assigning the value of the pressed button to the current digit
          Digit[CurDigitCount] = x;
        }
      }

      // displaying the entered value on the respective digit of the seven segment display
      if(CurDigitCount == 1)  { SendSevenSegNumber(SevenSegDigit[CurDigitCount], SevenSegChar[Digit[CurDigitCount]], SevenSegDot); }
      else  { SendSevenSegNumber(SevenSegDigit[CurDigitCount], SevenSegChar[Digit[CurDigitCount]]); }
      CurDigitCount++;

      // process after a value for every digit has been entered
      if(CurDigitCount > 3)  {

        // displaying the complete entered value for a short time on the seven segment display
        CurTime = millis();
        while(millis() - CurTime < 2000)  {}

        Serial.println("serial monitor test");
        
        // creating the entered numbers 1 and 2 out of the values of the entered digits
        EnteredNumber1[CurNumListEntries] = (10 * Digit[0]) + (1 * Digit[1]);
        EnteredNumber1[CurNumListEntries] = (10 * Digit[2]) + (1 * Digit[3]);
       

        CurNumListEntries++;
        SevenSegClearDisplay();

        // reseting variables
        CurDigitCount = 0;
        for(byte i = 0; i <= 3; i++)  { Digit[i] = 0; }
      }
    } 
  }
}
for(byte i = 0; i <= 9; i++)  { pinMode(PinBtn[i], INPUT_PULLUP); }

You've got a ten element array; doesn'tfor(byte i = 0; i < 10; i++)  { pinMode(PinBtn[i], INPUT_PULLUP); } seem like a more natural way to write your loop?

in the complete code I have another if-else statement directly after

if(BtnPressed())  {

:

if(CurNumListEntries < sizeof(EnteredNumber1))  { ... CurNumListEntries++; ... } 
else  { Serial.println("List is full!"); }

But the reset is already done before this part of the program is reached :confused: ...

in the complete code I

Wuh?

AWOL:

for(byte i = 0; i <= 9; i++)  { pinMode(PinBtn[i], INPUT_PULLUP); }

You've got a ten element array; doesn't

for(byte i = 0; i < 10; i++)  { pinMode(PinBtn[i], INPUT_PULLUP); }

seem like a more natural way to write your loop?

yes, that's true - thanks for the hint ...

AWOL:
Wuh?

? the complete code is quite huge. I have reduced and reduced the code to find the reason for the behaviour. In the end I have been ending with the part posted above.

The code for the SendSevenSegNumber function looks very suspicious. You pass a char array, seem to treat it as a char, modify it as a char. I suspect that something like that will indeed crash your code.

I have just found the error - the problem was indeed writing data outside the boundaries of arrays.

In a first prototype I only had 4 key pad buttons. Meanwhile I have 10 key pad buttons. I have updated the amount of pins but not the amount of debouncing variables:

CurStateBtn[4] => CurStateBtn[10]
OldStateBtn[4] => OldStateBtn[10]

What a stupid fault from me >:(

I have updated these variables and everything is fine now. Thanks to all of you for the hints and for "forcing" me to have a look on my arrays a fifth time :wink:

sterretje:
The code for the SendSevenSegNumber function looks very suspicious. You pass a char array, seem to treat it as a char, modify it as a char. I suspect that something like that will indeed crash your code.

I am just wondering why I have used "unsigned char" as data type for this function. I think "byte" would be the fitting one.

Or are your qualms about another thing (not the data type)?

unsigned char and byte are the same thing; my problem is that your function has an (pointer to) array argument (SevenSegChar[]) but you treat it like a char.

  // adding a dot if explicitly specified (SevenSegDot)
  if (SevenSegDot == 0b10000000)
  {
    unsigned char a = SevenSegChar;
    SevenSegChar = a | SevenSegDot;
  }

The compiler on my system (IDE1.6.6) is absolutely not amused with it.

sketch_apr30a:29: error: invalid conversion from 'unsigned char*' to 'unsigned char' [-fpermissive]
     unsigned char a = SevenSegChar;
                       ^

sketch_apr30a:30: error: invalid conversion from 'int' to 'unsigned char*' [-fpermissive]
     SevenSegChar = a | SevenSegDot;
                      ^

The first error tells you that you can't convert from a pointer to a char.
The second error tells you that you can't convert from an int (the right hand side of the statement) to a pointer (SevenSegChar).

You actually can do those conversions if you tell the compiler that you know what you're doing but it's not what you want in this case.

If the code would have compiled, it would not have been the cause of the crash (I realise that now) because the pointer itself is passed by value so will not be modified.

When you get it running the way you want it to, this is a great candidate for you to work though various blocks of code to learn - and rewrite/optimise with better practices.

hmm ... I don't get it but I have just tried it with an older version of the IDE - now I also get error messages and the code won't compile. Is the new IDE more fault-tolerant?

But anyways, I want to code it clean ...

The array values are passed to the function as references, right? So the values of the arrays are not "copied" to the function.
Within the function I want to assign the value of the refered variable to a new variable "a" so that I can edit it. So I don't want to convert a pointer to a char, I just want to assign the value of the pointed variable to a new variable. If that is not done in the way I did it, what would be the correct way:

unsigned char a = SevenSegChar;

So what I want to do:

  • define global arrays for number of sevenseg display digit and representable seven seg characters
  • pass a defined character and a defined digit to a function within the main loop which sends this information bitwise to the sevenseg display driver
  • optionally: edit the predefined character before sending to the driver in an auxiliary variable within the function to add a dot to the character; the value in the global array shall not be edited!

Can you give me a hint with which method I can solve these specifications?

Thanks
Martin

lastchancename:
When you get it running the way you want it to, this is a great candidate for you to work though various blocks of code to learn - and rewrite/optimise with better practices.

hehe, yes... I'm fully aware of the fact that the code must be quite crappy :wink: but I'm working on it everytime I read something helpful (starting from replacing all delay(x); statements :wink: ...).
My main challenge is simplifying the structure ...

But I am also really happy for getting almost everything run as I want to - I haven't been that bullish when starting with Arduino a few month ago :wink: ...