Incomplete output: Serial.print(), the increasing size of a variable

hello. I'm trying to read the location of an encoder implanted in a treadmill so that i can get the speed of mice running on it.
but i found the output was incomplete. code like this:

void EncoderCal()
{
  lDist_Abs++;
  if (digitalRead(ENCODER_B_PIN) == HIGH)
  {
    lDist_Rel++;
  }
  else
  {
    lDist_Rel--;
  }
  dist_Rel_Time += "|";
  dist_Rel_Time += String(lDist_Rel*Calibration);
  dist_Rel_Time += "=";
  dist_Rel_Time += String(trial_millis());//ms          
}

void setup() {


    Serial.begin(115200);
    pinMode(ENCODER_A_PIN, INPUT_PULLUP);
    pinMode(ENCODER_B_PIN, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(ENCODER_A_PIN), EncoderCal, RISING); //

}

void loop() {
 
 i = 0;
  Serial.println("Session start");
  while (i < maxTrialNum)
  {

    Serial.print("current_trial");
    Serial.println(i+1);

    dist_Rel_Time = "|";
    lDist_Rel = 0;
    lDist_Abs = 0;

    delay(10000);

    Serial.print("o");
    Serial.print("/Trial_Num=");
    Serial.print(i + 1);
    Serial.print("/Absolute_distance=");
    Serial.print(lDist_Abs*Calibration);
    Serial.print("/Relative_distance_time=");
    Serial.print(dist_Rel_Time);

  i++;


    delay(interTrialInterval);
      
    }
    }

i found the output of running distances was fewer than the actual movements. Is this related to the limit of memory size or string?
Besides, after i added another variable which also increased with time just like 'dist_Rel_Time', i found both of the two variables became shorter (even more incomplete).

where is 'i' declared ?

I declared all the variables in the beginning of the code, but did not put the declaration on this discussion above.

#define ENCODER_A_PIN 3
#define ENCODER_B_PIN 2
int i = 0;

unsigned long trialStartMillis; // For offsetting trial events times.
volatile unsigned long lDist_Abs = 0;
volatile long lDist_Rel = 0;
String dist_Rel_Time;

uint32_t trial_millis()
{
  return millis() - trialStartMillis;
}

The above variables are multibyte variables. You will need to disable interrupts while reading or manipulating these variables from loop() or any functions it calls.

Where does trialStartMillis get set?

Sorry, I don't quite understand why I need to disable the interrupt. Is this possible to lose some data when reading these variables if not disabling the interrupt?
So in this code, I need to use the noInterrupts() before

and the trialStartMillis was set in the beginning of each trial.
that is before

Thanks

by the way, I still worry about the memory size of arduino. Do you think it's possible that the variable dist_Rel_Time was too large for arduino to memorize? How could I test this? If it's the memory size, is there any method to print the variables before the storage runs out? So I can get the complete data.

That is certainly a risk. This is called by an interrupt:

void EncoderCal()
{
  lDist_Abs++;
  if (digitalRead(ENCODER_B_PIN) == HIGH)
  {
    lDist_Rel++;
  }
  else
  {
    lDist_Rel--;
  }
  dist_Rel_Time += "|";
  dist_Rel_Time += String(lDist_Rel*Calibration);
  dist_Rel_Time += "=";
  dist_Rel_Time += String(trial_millis());//ms          
}

so dist_Rel_Time could get quite long if the conditional code in the loop() does not reset it.

yeah, I know. I reset the variable in the beginning of each trial. But my goal is to get the speed during a trial. My strategy is to get the time when the encoder moved and the corresponding location. This seems to be necessary.
Do you have any suggestions to avoid too long variable for data loss during a trial?

The interrupts will continue to work even if the code is stuck in that delay(10000) statement, so that string dist_Rel_Time would still continue to grow as long as a mouse is active on that treadmill.

You are doing quite a lot of processing in the ISR EncoderCal().
This is presumably driven by pulses from the encoder connected to the treadmill.
Surely, you just need the start time of the session, the number of pulses (relative/absolute) and the session end time. Building up a long string should definitely not be done in that interrupt service routine.

How are you intending to start a session ? Push a button or wait for the first pulse from the encoder or a just a simple time slot or what ?
Is the length of a session fixed, say 10 seconds from the start, or what ?

As the mouse could run at different speed during a trial, the duration of trial and the number of pulses are not enough to calculate the speed at specific timepoint. But the sample rate was not demanding (30 Hz was enough).

I suddenly get an idea. Maybe millis() could be helpful. I mean I can get the location after a fixed duration. But another problem is that I found millis() could not work together with delay(). As I need the trial at a relative fixed duration (not too short), I should learn more about this. (Sorry, maybe this was too puzzling)

In my design, the mice would get some stimulation for a fixed duration (like 15 seconds). But actually, the stimulation system is affected by the mice's behaviors. It's a feedback system. So the durations of each trial vary from 17 s to 30 s. When all the code runs out, the session ends and another starts. Maybe I should change the design to make the session shorter.

I get it that you want performance statistics from these mice.
You can certainly do all sorts of things:

  1. Maximum speed in a session
  2. Average speed in a session
  3. Length of complete pause within a session.
  4. Duration of a session.

You can break each session into a number of sub-sessions (time windows) and display the
results on the serial monitor as each sub session end. At the end of a complete session
you can display the aggregate results for a whole session.

Use millis() for the timing or, if the mice are really fast and the encoder has a high resolution, then maybe micros().

1 Like

thanks a lot! Your suggestion is very helpful.

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