Arduino processing stops after printing certain number of characters in Serial monitor

I'm using an arduino uno to create a light based communication for my college project. I'm using the arduino to take characters in a string, convert it to binary, and after encoding, sending it over a distance by a laser.
However, the infinite loop stops after processing 7-8 characters each time. The arduino stops, and Serial monitor stops printing (i'm printing the character, it's binary representation and its hamming encoded representation for debugging).

The individual blocks are:
char_to_binary(): convert char 'c' to binary and writes to global array 'c_binary'

repeater(): repeats each bit in a binary array 'reps' times
Can also perform majority detection for each set of 'reps' bits depending on 'arg' == 1 or 0

interleaver(): performs interleaving using matrix fill method
Can also perform deinterleaving for arg==0

hamming_code(): takes 4-bit array and converts it to a 7-bit hamming encoded array.
I use it twice to convert an 8-bit char to a 14-bit hamming_arr.
Can also decode 7-bit hamming codes with self-correction to 4-bits if arg==0

Here's the sketch:

#define LED_PIN A0
#define PERIOD 30
#define STARTUP_DELAY 0

//variables that the infinite loop uses
char c;
int c_binary[8]; //character's binary form
int left_nibble[4];
int right_nibble[4];
int* left_hamming;
int* right_hamming;
int hamming_arr[14]; //hamming array
int* rep_arr; //repeated array
int* tx_arr;
int tx_arr_len;

byte pin = LED_PIN;
int reps = 3; 
int il_cols = 4;
char* string = "qwertyuiopasdfghjklzxcvbnm";


//REPEATER VERIFIED
//does repetition for arg==1, majority detection for arg==0
int* repeater(int *input, int input_len, int reps, int arg) {
    int *output;
    int output_len;

    if (arg==1){ //repetition
        output_len = input_len*reps;
        output = (int*)malloc(sizeof(int)*output_len);
        for (int i=0; i<input_len; i++){
            for (int j=0; j<reps; j++){
                output[i*reps+j] = input[i];
            }
        }
    }
    else{ //majority detection
        output_len = input_len/reps;
        output = (int*)malloc(sizeof(int)*output_len);
        for (int i=0; i<output_len; i++){
            int sum = 0;
            for (int j=0; j<reps; j++){
                sum += input[i*reps+j];
            }
            output[i] = sum>reps/2 ? 1 : 0;
        }
    }

    return output;
}

//INTERLEAVER VERIFIED
//does interleaving for arg==1, deinterleaving for arg==0
int* interleaver(int *input, int input_len, int il_cols, int arg){
    int* output;
    int rows, cols;
    if (arg==1){
        cols = il_cols;
        rows = ceil((float)input_len/(float)cols);
    }
    else{
        rows = il_cols;
        cols = ceil((float)input_len/(float)rows);
    }

    output = (int*)malloc(rows * cols * sizeof (int));
    for (int i=0; i<rows; i++){
        for (int j=0; j<cols; j++){
            if (cols*i+j < input_len){
                output[rows*j+i] = input[cols*i+j];
            }
            else{
                output[rows*j+i] = 0;
            }
        }
    }
    return output;
}

//HAMMING CODE VERIFIED
//converts between 4-length word and 7-length codeword acc to arg==1 or 0
int* hamming_code(int *input, int arg){
    int hamming_gmat[4][7] = {{1, 1, 1, 0, 0, 0, 0},
                              {1, 0, 0, 1, 1, 0, 0},
                              {0, 1, 0, 1, 0, 1, 0},
                              {1, 1, 0, 1, 0, 0, 1}};

    int hamming_hmat[3][7] = {{1, 0, 1, 0, 1, 0, 1},
                              {0, 1, 1, 0, 0, 1, 1},
                              {0, 0, 0, 1, 1, 1, 1}};

    //each row is row[0] = binary form of syndrome, row[1] = corrupted bit index
    int syndrome_table[8][2] = {{1,3},
                                {2,1},
                                {3,5},
                                {4,0},
                                {5,4},
                                {6,2},
                                {7,6}};

    //first 4 indices are the word, next index is decimal-converted codeword for easy comparision
    int codeword_table[16][5] = {{0,0,0,0, 0},
                                 {0,0,0,1, 105},
                                 {0,0,1,0, 42},
                                 {0,0,1,1, 67},
                                 {0,1,0,0, 76},
                                 {0,1,0,1, 37},
                                 {0,1,1,0, 102},
                                 {0,1,1,1, 15},
                                 {1,0,0,0,112},
                                 {1,0,0,1, 25},
                                 {1,0,1,0, 90},
                                 {1,0,1,1, 51},
                                 {1,1,0,0, 60},
                                 {1,1,0,1, 85},
                                 {1,1,1,0, 22},
                                 {1,1,1,1,127}};
    int* output;
    if (arg==1){ //encoding
        output = (int*)malloc(7*sizeof(int));
        for (int i=0; i<7; i++){
            int sum=0;
            for (int j=0; j<4; j++){sum+=input[j]*hamming_gmat[j][i];}
            output[i] = sum%2;
        }
    }
    else{ //decoding
        int *codeword = (int*) malloc(7*sizeof (int));
        for (int j=0; j<7; j++){codeword[j] = input[j];} //let codeword be same as input initially

        int synd=0; //now we find the syndrome int
        for (int i=0; i<3; i++){
            int sum=0;
            for (int j=0; j<7; j++){
                sum += input[j]*hamming_hmat[i][j];
            }
            synd += (sum%2)*(int)pow(2,2-i);
        }

        //check if syndrome is in the syndrome table, if yes, then make necessary corrections
        for (int i=0; i<7; i++){
            if (syndrome_table[i][0]==synd) {
                codeword[syndrome_table[i][1]] = (codeword[syndrome_table[i][1]] + 1) % 2;
                break;
            }
        }

        output = (int*)malloc(4*sizeof (int));
        for (int i=0; i<4; i++){output[i]=0;}
        //now that we have the corrected codeword, we can find what the initial word was
        int codeword_int=0;
        for (int i=0; i<7; i++){codeword_int += codeword[i]*pow(2,6-i);}

        for (int i=0; i<16; i++){
            if (codeword_table[i][4]==codeword_int){
                for (int j=0; j<4; j++){output[j] = codeword_table[i][j];}
                break;
            }
        }
    }
    return output;
}

//no
//function to convert a string to a array of bits
void char_to_binary(char c) {
    int i, j;
    for (i = 7; i >= 0; i--) {
        j = 1 << i; // Bitwise left shift to get the current bit value
        c_binary[7 - i] = (c & j) ? 1 : 0; // Check if the current bit is set and set the binary string accordingly
    }
}

//no memory leak problems
void setup(){
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  delay(STARTUP_DELAY); //delay to help the RX set up first, so both TX and RX need not be activated at the exact same time
}

//all variables shifted to global scope
void loop(){
  for (int i=0; i<strlen(string); i++){
    //Serial.print(i); Serial.println();
    c = string[i];
    Serial.println(c);
    Serial.println("binary complete");
    char_to_binary(c); 

    //verified!!!
    for (int j=0; j<8; j++){Serial.print(c_binary[j]);}
    Serial.println();

    for (int j=0; j<4; j++){
      left_nibble[j] = c_binary[j];
      right_nibble[j] = c_binary[j+4];
    }

    left_hamming = hamming_code(left_nibble, 1);
    right_hamming = hamming_code(right_nibble, 1);
    for (int j=0; j<7; j++){
      hamming_arr[j] = left_hamming[j];
      hamming_arr[j+7] = right_hamming[j];
    }
    Serial.println("hamming complete");

    rep_arr = repeater(hamming_arr, 14, reps, 1); //repeated array
    
    Serial.println("repetition complete");

    tx_arr = interleaver(rep_arr, 14*reps, il_cols, 1); //transmitted array aka the interleaved array
    Serial.println("interleaving complete");

    if (14*reps % il_cols == 0){tx_arr_len = 14*reps;}
    else{tx_arr_len = 14*reps + il_cols - (14*reps)%il_cols;}

    send_bits();
  }
}

//no memory leak problems
void send_bits(){
  digitalWrite(LED_PIN, HIGH);
  delay(PERIOD);
  digitalWrite(pin, LOW);
  delay(PERIOD);

  for (int i=0; i<tx_arr_len; i++){

    digitalWrite(pin, HIGH);
    delay(PERIOD/3);
    if (tx_arr[i]==0){digitalWrite(pin, LOW);}
    Serial.print(tx_arr[i]);
    delay(PERIOD/3);
    digitalWrite(pin, LOW);
    delay(PERIOD/3);
  }
  Serial.println();
}

Post your sketch, well formated, with comments and in so called code tags "</>" to see how we can help.

Sorry about that, new to arduino and new to this forum. accidently posted the incomplete thing.

Check that all used indicies for the arrays stay in range during processing.

I checked, and array indices never go out of bounds. I was advised that this sudden halting of the arduino could be due to 'memory issues' but i don't know why that could be happening.

The memory allocated to variables created in functions like 'char_to_binary', 'hamming_code' etc. gets deallocated when the function returns.

I'm not creating any new variables in the infinite loop, but only rewriting values to the variables declared in global.

I see lots of use of malloc() and no use of free(). Uno has only 2K RAM memory and that could quickly be used up. Even if you did use free(), the heap can become fragmented and the space not re-used.

I also see several quite large (for Uno) arrays which are declared as int when all the values in then would fit into byte, which would halve the size of the arrays. These could even be moved into PROGMEM I suspect, so they consume no RAM at all.

These things make me think that the code was written by someone used to coding in C on PC where memory is not a serious limitation and is not carefully planned as it needs to be on a small mictocobtroller.

1 Like

@udkhndlwl
You actively use memory allocation, but then you do not clear this memory anywhere.
For example, you run this procedure in your loop():

You allocate memory for output array, but don't clean it up when the array is no longer needed. Are you not understand that memory is still busy? Every time you enter this function, you take up new pieces of memory. At some point, free memory runs out and the board freezes or reboots

Why did you choose to use malloc in this function? Why not create a normal local array?

1 Like

So, my assumption that "allocated memory gets freed automatically when a function returns" is dead wrong.
Thank you for pointing that out. I will try the changes you suggested, as well as my dumb coding methods rightly pointed out by PaulRB.

you are wrong. The memory, allocated by malloc, doesn't deallocated automatically at the end of function. It still busy.

Again wrong. For example, you are created a new output array at each call of repeater function, but never free the memory.

1 Like

it is true for non-static local variables, but wrong for dynamic allocation.
As an example, this will be safely deallocated at the end of function:

int* repeater(int *input, int input_len, int reps, int arg) {
  
    int output_len = input_len*reps;
    int output[output_len] = {0};

 }   // output array will deallocate in this point

but this is not

int* repeater(int *input, int input_len, int reps, int arg) {
  
   int output_len = input_len*reps;
   int*  output = (int*)malloc(sizeof(int)*output_len);

 }   // output array still busy
1 Like

To test if it is simply a limitation of the current MCU, you could run the code on a simulator that supports a better specified MCU. Maybe try an ESP32 on https://wokwi.com/.

There is no garbage collector running in C++ which could clean the heap.

I'm not calling you dumb. You are clearly a programmer with a good level of expertise and intelligence. Most of that experience is as valid for use on small microcontrollers as it is on PCs. But coding on a small microcontroller brings some "new" challenges. I hope you find these interesting and I'm sure that solving them will make you a better coder in general and the techniques you learn will change and improve the way you code on any platform.

These "new" challenges for you are, in reality, old challenges, and the techniques needed to overcome them have not been taught in schools and colleges for many decades because they are no longer needed on PCs and larger machines.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.