Sensor overflow issue

Hi, I'm using this infrared sensor ITR20001 for lane tracking mounted on my robot.
Initially the robot starts following the track and the sensor reads meaningfull values at some point, usually after 5 seconds the sensors overflow, it stops following the track and and it starts speeding up. It basically exit my loop for lane tracking and then I guess it enters some kind of default state.

  • Why does it overflow if it works correctly ar the beginning and it's still on the track?
  • Why, after the overflow, it goes it enter the stop_trk() and then and it starts speeding up?
#include <IRremote.h>
#include <IRremoteInt.h>
#include <arduino.h>
#include <FastLED.h>
#include <avr/wdt.h>
#include <EEPROM.h>

#define min_speed 0
#define max_trk 950
#define min_trk 300
#define lim_trk 50 //limit for sensor's value read
#define lane_trk_speed 100 //for curving
#define pin_RBGled 4
#define NUM_LEDS 1
#define RECV_PIN 9

IRrecv irrecv(RECV_PIN);
static decode_results results;
static unsigned long prv = 0;

enum  class button : unsigned long {  
left = 0xFF22DD, 
fwd = 0xFF629D,
bkw = 0xFFA857,
right = 0xFFC23D, 
stop = 0xFF02FD, //ok
one = 0xFF6897,
two = 0xFF9867, 
three = 0xFFB04F,
four = 0xFF30CF,
five = 0xFF18E7,
unknown = 0xFFFFFFFF
};

const button buttons_vct[] = {
  button::left, 
  button::fwd, 
  button::bkw, 
  button::right, 
  button::stop, 
  button::one, 
  button::two, 
  button::three, 
  button::four, 
  button::five,
  button::unknown
};

//Classes definition

class LED_light {
  private:
    CRGB leds[NUM_LEDS];
    
  public:
  void led_init(void);
  void setcolor(uint8_t, uint8_t, uint8_t);
  void blinking(int);
};

class motor_setup {

  private:
    const int pinMotor_PWMB = 6;    // PWM pin for Motor B speed
    const int pinMotor_BIN1 = 7;    // Direction pin for Motor B
    const int pinMotor_PWMA = 5;    // PWM pin for Motor A speed
    const int pinMotor_AIN1 = 8;    // Direction pin for Motor A
    const int pinMotor_Enable = 3;  // Motors enable

  public:
    void driver_motor_init(void);
    void move_left_trk(void);
    void move_right_trk(void);
    void move_fwd_trk(void);
    void stop_trk(void);
};

class lane_trk:public motor_setup {

  private:
    const int lane_left = A2; //defining the PINs for the lane tracking sensor
    const int lane_middle = A1;
    const int lane_right = A0;

  public:
 
    void lane_trk_init(void);
    float *read_lane_trk(void);
    void lane_trk_control(const float *);
    void trk_sys(void);
};

//Function declarations

int check_sig_sts(unsigned long);
bool IR_outOfRange(void);

//Objects class instances

motor_setup App_motor;
lane_trk App_trk;
LED_light LED;

//SETUP
void setup() {
  Serial.begin(9600);
  App_motor.driver_motor_init();
  App_trk.lane_trk_init();
  LED.led_init();
  irrecv.enableIRIn();
}

//LOOP
void loop() {
  LED.setcolor(0, 0, 0);
  if(irrecv.decode(&results) && static_cast<button>(results.value) != button::unknown){
    delay(100);
    button button_prs = static_cast<button>(results.value);
    switch (button_prs) {
      case button::stop:
        App_motor.stop_trk();
      break;
      case button::one:
        App_trk.trk_sys();
      break;
      default:
      prv = 0;
      delay(3000);
      break;
    }
    if(prv == 0 || EEPROM.read(0) == 1){ //EEPROM.read(0) tells me if there is an HW reset
    irrecv.resume();
    }
  }
}

//Functions class definition LED_LIGHT

void LED_light::led_init(void){

  FastLED.addLeds<NEOPIXEL, pin_RBGled>(leds, NUM_LEDS);
  leds[0] = CRGB::White; // Set the first (and only) LED to white
  FastLED.show();
  delay(1000);
  leds[0] = CRGB::Black;
  FastLED.show();
}
void LED_light::setcolor(uint8_t r, uint8_t g, uint8_t b){

  leds[0] = CRGB(r, g, b); // Set the LED color
  FastLED.show(); 
}
void LED_light::blinking(int mode_color){

  unsigned long currentMillis = millis(); //returns the number of milliseconds that have passed since the Arduino board began running the current program
  static unsigned long previousMillis = 0; // Save the last toggle time/
  //The reason why prvmillis is static is because in this way the var will retain its value between fcn calls, otherwise it's initialized every time
    switch (mode_color){  
      case 1:  
        if (currentMillis - previousMillis >= 500) {
        previousMillis = currentMillis;
        if(leds[0] == CRGB::Green) leds[0] = CRGB::Black;
        else leds[0] = CRGB::Green;
        FastLED.show(); // Update the LED
        break;
      }
      case 2:  
        if (currentMillis - previousMillis >= 500) {
        previousMillis = currentMillis;
        if(leds[0] == CRGB::Blue) leds[0] = CRGB::Black;
        else leds[0] = CRGB::Blue;
        FastLED.show(); // Update the LED
        break;
      }
      case 3:  
        if (currentMillis - previousMillis >= 500) {
        previousMillis = currentMillis;
        if(leds[0] == CRGB::Red) leds[0] = CRGB::Black;
        else leds[0] = CRGB::Red;
        FastLED.show(); // Update the LED
        break;
      }
    }
}

//Functions class definition MOTOR_SETUP

void motor_setup::driver_motor_init(void){
  pinMode(pinMotor_PWMA, OUTPUT);
  pinMode(pinMotor_PWMB, OUTPUT);
  pinMode(pinMotor_AIN1, OUTPUT);
  pinMode(pinMotor_BIN1, OUTPUT);
  pinMode(pinMotor_Enable, OUTPUT);

  digitalWrite(pinMotor_Enable, LOW);
  digitalWrite(pinMotor_BIN1, LOW);
  digitalWrite(pinMotor_AIN1, LOW);
  analogWrite(pinMotor_PWMB, min_speed);
  analogWrite(pinMotor_PWMA, min_speed);    
}
void motor_setup::move_left_trk(void){
  digitalWrite(pinMotor_Enable, HIGH);
  digitalWrite(pinMotor_BIN1, HIGH);
  digitalWrite(pinMotor_AIN1, LOW);
  analogWrite(pinMotor_PWMB, 60);
  analogWrite(pinMotor_PWMA, 60);
}
void motor_setup::move_right_trk(void){ //One motor fwd, the other bkw
  digitalWrite(pinMotor_Enable, HIGH);
  digitalWrite(pinMotor_BIN1, LOW);
  digitalWrite(pinMotor_AIN1, HIGH);
  analogWrite(pinMotor_PWMB, 60);
  analogWrite(pinMotor_PWMA, 60);
}
void motor_setup::motor_setup::move_fwd_trk(void){
  digitalWrite(pinMotor_Enable, HIGH);
  digitalWrite(pinMotor_BIN1, HIGH);
  digitalWrite(pinMotor_AIN1, HIGH);
  analogWrite(pinMotor_PWMB, lane_trk_speed);
  analogWrite(pinMotor_PWMA, lane_trk_speed);
}
void motor_setup::stop_trk(void){
  digitalWrite(pinMotor_Enable, LOW);
  digitalWrite(pinMotor_BIN1, LOW);
  digitalWrite(pinMotor_AIN1, LOW);
  analogWrite(pinMotor_PWMB, min_speed);
  analogWrite(pinMotor_PWMA, min_speed); 
  Serial.println("Exit_stop_trk_mode"); 
}

//Functions class definition LANE_TRK

void lane_trk::lane_trk_init(void){
  pinMode(lane_left, INPUT);
  pinMode(lane_middle, INPUT);
  pinMode(lane_right, INPUT);
}
float* lane_trk::read_lane_trk(void){
    
  float *LMR = (float*)malloc(3 * sizeof(float));

    LMR[0] = (analogRead(A2)); //3rd element -> Left
    LMR[1] = (analogRead(A1)); //2nd element -> Middle
    LMR[2] = (analogRead(A0)); //1st element -> Right
    Serial.print("left: ");
    Serial.println(LMR[0]);
    Serial.print("midlle: ");
    Serial.println(LMR[1]);
    Serial.print("right: ");
    Serial.println(LMR[2]);
  return LMR;
}
void lane_trk::lane_trk_control(const float *sns_data){

  float L = *sns_data, M = *(sns_data + 1), R = *(sns_data + 2);
  
  if((L > max_trk && M > max_trk && R > max_trk)) {  //Not on the ground or overflow
    Serial.println("NOT on GROUND"); 
    stop_trk(); 
  }
  else if (M > min_trk && M < max_trk) {  //On track
    Serial.println("MOVE FORWARD");
    move_fwd_trk();
  }
  else if (R > min_trk && R < max_trk) {  //Only right is aligned-> turn right
    Serial.println("TURN RIGHT");
    move_right_trk();
  }
  else if (L > min_trk && L < max_trk) {  //Only left is aligned-> turn left
    Serial.println("TURN LEFT");
    move_left_trk();
  }
  else { 
    Serial.println("NOT on TRACK");
    stop_trk(); 
  }
}

void lane_trk::trk_sys(void){
  unsigned long t_c = 0;
  unsigned long t_new = 0;
  float *LMR_sensor = new float[3];
  do {
    LED.blinking(2);
    t_c = millis();
    if(t_c - t_new > 100){ 
        t_new = t_c;
        LMR_sensor = App_trk.read_lane_trk();
    }   
    App_trk.lane_trk_control(LMR_sensor); 
    if(irrecv.decode(&results)) if(check_sig_sts(results.value) == true) break;
  } while(true);
    delete[] LMR_sensor;
    Serial.print("Exit_lane_trk");
    LED.setcolor(0, 0, 0);
}

//Functions definition

int check_sig_sts(unsigned long val){
  
  if(prv == static_cast<unsigned long>(results.value) || static_cast<button>(results.value) == button::unknown){
    irrecv.resume();
    return 0;
  }
  else if(prv != val && prv != 0 && IR_outOfRange() == true){
    prv = val;
    return 1;
  }
  else{
    prv = val;
    irrecv.resume();
    return 0;
  }
}
bool IR_outOfRange(){
  for(int n = 0; n < 11; ++n){
    if(static_cast<unsigned long>(results.value) == static_cast<unsigned long>(buttons_vct[n]))
      return true;
  }
  return false;
}

Thanks for posting your code properly on your first post!

However, "sensor overflow" is not a plausible or useful description of the problem.

Variables can overflow, so if you think that is a problem, put some Serial.print statements at key points in the sensor code to print out the values of important variables, and compare those with your expectations.

Post some typical values you see during "expected robot behavior" and "unexpected robot behavior".

As you can see from the screenshot, while I'm reading reasonable values at some point the sensor overflow and then it stays in the do-while loop but instead of stopping the motors speeds up.

  • So I don't understand why does it overflow all of a sudden (while still on track)?

  • Why, when in overflow, it stops reading the values but it keeps printing "NOT on GROUND" and "exit_stop_trk_mode"?

  • Why does it speed up instead of stopping ( since it prints exit_stop_trk_mode I would expect it to stop the motors since that is the last motion control applied) ?

  • Why I read NOT on GROUND and then NOT on TRACK, if it overflows I should be in the case NOT on GROUND? (Sometimes it prints NOT on GROUND for a few second and then again NOT on TRACK, in this screenshot it prints NOT on GROUND only once).

Thank you.

The problem has nothing to do with "sensor overflow". You are running out of dynamic memory in this line. Avoid using malloc() on an MCU.

float *LMR = (float*)malloc(3 * sizeof(float));

A possible solution is to make LMR a global variable, and remove the above line and the "LMR" from this line in the function lane_trk::read_lane_trk()

  return LMR;

However, I see other similar problems. Did you write this code?

Yes, I wrote the code.

Can I ask you why I should avoid using malloc() on MCU?

What other similar problems do you see in the code?

Could you explain me why do I run out of dynamic memory? I thought that once allocated I would have overwritten the pointer during the loop. Can I replace the pointer with a vector?

Thank you.

There isn't much memory on an MCU, and because of exactly such misuse of dynamic memory allocation, your program is running out of it.

That line is totally unnecessary, as is this one:

  float *LMR_sensor = new float[3];

Allocate a global LMR array of three elements at the beginning of the program, and be done with it.

1 Like

Thank you @jremington for the help. I defined a static array and it's working.

That being said, Can I ask you your opinion about the code I developed. Do you think it's robust or there is something that need to be addressed?