Using millis() to serial.print at a lower rate than calculations are happening within a loop

I have an Uno, and a Hall effect current sensor, and thermistor. I am trying to record some battery discharge characteristic curves at various discharge rates. The Arduino will not be controlling the discharge process, but should automatically start writing serial data to my laptop once discharge current flow is detected.

For the sake of accuracy, I was trying to set this up so that the measurement of current and the calculation of battery capacity (current*time) would integrate just as quick as the Arduino could do the calculations. Because I don't want to make big-ass datafiles, I figured would let it run the math at full speed and just snag the data once every second or thereabouts, and write it to my laptop on that interval.

So, the problem is this; I have not been able to get my serial.prints to happen on nice 1000ms intervals like I planned, and I don't know why.
Here's the code:

#include "MultiMap.h"
 unsigned long previousTime =0 ;
  unsigned long currentMillis;
  
  const unsigned long printout_interval = 1000;
  unsigned long time_this_cycle;
  float lookupR[] = {59.4,76.4,113.9,151.3,238.1,329,551,796,1177,2205,2752,3457,4373,5572,7153,9256,12073,15873,28146,51791,99326};
  float lookupT[] = { 140, 130,  115,  105,   90, 80, 65, 55,  45,  30,  25,  20,  15,  10,   5,   0,   -5,  -10,  -20,  -30,  -40};

void setup() {
  // initialize serial communication at 9600 bits per second:
  float capacity;
// time_this_cycle = millis();
  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
  //set up some time stuff
  unsigned long currentTime= millis();
  unsigned long last_print_time;
  unsigned long print_time;
   //set up some capacity stuff
   float capacity;
   float capacity_this_cycle;
   float last_capacity;
   float discharge_time =0;
 // read the inputs on analog pins 
  float amp_counts = analogRead(A0);
  float volt_counts = analogRead(A1);
  float temp_counts = analogRead(A2);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  //float current_sensor_volts = (amp_counts *5) / 1024;
  //float current = (((amp_counts-512) *125) / 1024);  //transfer function for the 50A hall sensor from DFrobots
  float current = (2.0); //dumb fixed value for testing code
  float voltage = (volt_counts *5) / 1024;
  
  //ambient temp stuff
  float ambient_temp;
temp_counts = analogRead(A2);
float sensor_resistance;
sensor_resistance = ((10000*temp_counts)/(1023-temp_counts)); //based on a 10k voltage divider
ambient_temp = multiMap<float>(sensor_resistance, lookupR, lookupT, 21);
  
  // OK, let's measure some battery stuff:
  if (current > 1.0){ 
    //looks for a minimum current load before it starts writing serial data to my laptop   
  discharge_time = currentTime+(currentTime-previousTime);
  capacity_this_cycle = ((current*(currentTime-previousTime))); //figure out how much battery discharge capacity exerted since the last loop, in A*milliseconds lolololol
  capacity = capacity_this_cycle + last_capacity; //add what's happened this loop to the total so far
  last_capacity = capacity; //make the most recent summed value into the accumulated value for the next loop
  previousTime = currentTime; // set the time value for the next go-round
  print_time = currentTime;
  if (print_time - last_print_time >= printout_interval) { //calcs above go full speed, but only dump data to the laptop at a reasonable interval
  Serial.print(voltage);
  Serial.print (" V, ");
  Serial.print (current);
  Serial.print (" A, ");
  Serial.print (capacity);
  Serial.print (" A*msec, ");
 // Serial.print (capacity_this_cycle,4);
 // Serial.print (" cap this cycle A*ms, ");
 // Serial.print (last_capacity);
 // Serial.print (" last capacity, A*ms, ");
  Serial.print (discharge_time/1000);
  Serial.print (" discharge time,secs, ");
  Serial.print (ambient_temp);
  Serial.println (" deg C");
  last_print_time = print_time; //record the time of this printing so I can make sure the print interval (probably 1000ms/1s has elapsed before I do it again
  
  }
 // Serial.print (current_sensor_volts);
 // Serial.println ("current sensor volts");
  //Serial.print 
  
  }
  else {
    
    Serial.println ("still waiting, bro...");
    delay (500);
  }
  }

I should apologize in advance before you tell me how bad my code is. I'm a mechanical engineer who somehow slipped through the cracks of the educational system without ever having any sort of education on programming languages like C/Basic etc. I have experienced many self-inflicted problems due to the self-taught nature of my 'training' and I feel certain that the problem I'm having here is yet another.

Is the output a constant amount of ms or does it vary? I think the millis() function works on the number of clock cycles so it might vary some depending on how many cycles other instructions take to run. Something like an interrupt may work better for your application.

As far as your coding skills go, I'm in a similar boat and live by "It's not bad code if it works."

Your print routine is wrapped inside your if() statement so if that if() is false, it never gets called but then does a delay(500) in the else part. Your last print time variable needs to be global so the value is retained for each iteration of loop()

I would suggest

  1. Bring your serial baud rate into this century

Serial.begin(115200);

  1. No need to read A2 twice each time through the loop

  2. move your printing outside that if()

  3. remove the else clause with delay()

#include "MultiMap.h"
unsigned long previousTime = 0 ;
unsigned long last_print_time;

const unsigned long printout_interval = 1000;
unsigned long time_this_cycle;
float lookupR[] = {59.4, 76.4, 113.9, 151.3, 238.1, 329, 551, 796, 1177, 2205, 2752, 3457, 4373, 5572, 7153, 9256, 12073, 15873, 28146, 51791, 99326};
float lookupT[] = { 140, 130,  115,  105,   90, 80, 65, 55,  45,  30,  25,  20,  15,  10,   5,   0,   -5,  -10,  -20,  -30,  -40};

void setup() {
  // initialize serial communication at 9600 bits per second:
  float capacity;
  // time_this_cycle = millis();
  Serial.begin(115200);
}

// the loop routine runs over and over again forever:
void loop() {
  //set up some time stuff
  unsigned long currentTime = millis();
  unsigned long print_time;

  //set up some capacity stuff
  float capacity;
  float capacity_this_cycle;
  float last_capacity;
  float discharge_time = 0;

  // read the inputs on analog pins
  float amp_counts = analogRead(A0);
  float volt_counts = analogRead(A1);
  float temp_counts = analogRead(A2);

  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  //float current_sensor_volts = (amp_counts *5) / 1024;
  //float current = (((amp_counts-512) *125) / 1024);  //transfer function for the 50A hall sensor from DFrobots
  float current = (2.0); //dumb fixed value for testing code
  float voltage = (volt_counts * 5) / 1024;

  //ambient temp stuff
  float sensor_resistance;
  sensor_resistance = ((10000 * temp_counts) / (1023 - temp_counts)); //based on a 10k voltage divider
  float ambient_temp = multiMap<float>(sensor_resistance, lookupR, lookupT, 21);

  // OK, let's measure some battery stuff:
  if (current > 1.0) {
    //looks for a minimum current load before it starts writing serial data to my laptop
    discharge_time = currentTime + (currentTime - previousTime);
    capacity_this_cycle = ((current * (currentTime - previousTime))); //figure out how much battery discharge capacity exerted since the last loop, in A*milliseconds lolololol
    capacity = capacity_this_cycle + last_capacity; //add what's happened this loop to the total so far
    last_capacity = capacity; //make the most recent summed value into the accumulated value for the next loop
    previousTime = currentTime; // set the time value for the next go-round

  }
  if (currentTime - last_print_time >= printout_interval) { //calcs above go full speed, but only dump data to the laptop at a reasonable interval
    Serial.print(voltage);
    Serial.print (" V, ");
    Serial.print (current);
    Serial.print (" A, ");
    Serial.print (capacity);
    Serial.print (" A*msec, ");
    // Serial.print (capacity_this_cycle,4);
    // Serial.print (" cap this cycle A*ms, ");
    // Serial.print (last_capacity);
    // Serial.print (" last capacity, A*ms, ");
    Serial.print (discharge_time / 1000);
    Serial.print (" discharge time,secs, ");
    Serial.print (ambient_temp);
    Serial.println (" deg C");
    last_print_time = currentTime; //record the time of this printing so I can make sure the print interval (probably 1000ms/1s has elapsed before I do it again
  }
}

A better choice...

    last_print_time += printout_interval;

...given this...

last_capacity has not been initialized at that point. I suggest turning all warnings on.

That was the OP's original code not mine. I do not have the "MultiMap.h" library installed so did not compile/verify. I should have stated as such.

investing 5 hours of time how to use functions.
Save 50 hours with each microcontroller.

Does this sound like a good investmenet?

Then your loop could look like this

void loop() {
   measureData();
   CalculateEnergy()
   PrintAccumulatedData();
}

if this sounds attractive to you
start learning how to use functions

best regards Stefan

I think you pushed me in the right direction here...
Now I just need to read up on why using global variables is less desirable than other approaches...

#include "MultiMap.h"
  unsigned long previousTime =0 ;
  unsigned long currentTime;
  unsigned long print_time;
  unsigned long last_print_time;
  const unsigned long printout_interval = 1000;
  float discharge_time = 0;

  float ambient_temp;
  float current;
  float voltage;
  float capacity;
  float capacity_this_cycle;
  float last_capacity;
  
  float lookupR[] = {59.4,76.4,113.9,151.3,238.1,329,551,796,1177,2205,2752,3457,4373,5572,7153,9256,12073,15873,28146,51791,99326};
  float lookupT[] = { 140, 130,  115,  105,   90, 80, 65, 55,  45,  30,  25,  20,  15,  10,   5,   0,   -5,  -10,  -20,  -30,  -40};

void setup() {
  // initialize serial communication:
  Serial.begin(115200);
}

///////////////// the loop routine runs over and over again forever/////////////////////////
void loop() { //call the functions for the tasks I want done


MeasureBattery();
MeasureTemperature();
Integrate_current();
Track_discharge_time();
Print_data();
}

void MeasureBattery(){ 
  float volt_counts = analogRead(A1);
  float amp_counts = analogRead(A0);
   // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  //float current_sensor_volts = (amp_counts *5) / 1024;
  //float current = (((amp_counts-512) *125) / 1024);  //transfer function for the 50A hall sensor from DFrobots
  current = (2.0); //dumb fixed value for testing code
  voltage = (volt_counts *5) / 1024;
}  
  
void MeasureTemperature(){//ambient temp stuff

  int temp_counts = analogRead(A2);
  float sensor_resistance;
  sensor_resistance = ((10000*temp_counts)/(1023-temp_counts)); //based on a 10k voltage divider
  ambient_temp = multiMap<float>(sensor_resistance, lookupR, lookupT, 21);
}

void Integrate_current(){     //set up time and some capacity stuff
  currentTime= millis();
  
  capacity_this_cycle = ((current*(currentTime-previousTime))); //figure out how much battery discharge capacity exerted since the last loop, in A*milliseconds lolololol
  capacity = capacity_this_cycle + last_capacity; //add what's happened this loop to the total so far
  last_capacity = capacity; //make the most recent summed value into the accumulated value for the next loop
  previousTime = currentTime; // set the time value for the next go-round
}

void Track_discharge_time(){
if (current > .4){ 
    //looks for a minimum current load before before it starts counting the discharge time   
  discharge_time = currentTime+(currentTime-previousTime);
}

} 

void Print_data(){
  //now that we know current is flowing, now to print detailed data...
  if (currentTime - last_print_time >= printout_interval && (current >.4)) //measurement&integration of battery discharge above go full speed, but only dump data to the laptop at 
    //a reasonable interval of around 1-2 hz set by printout_interval milliseconds specified
  {
  Serial.print(voltage);
  Serial.print (" V, ");
  Serial.print (current);
  Serial.print (" A, ");
  Serial.print (capacity);
  Serial.print (" A*msec, ");
 // Serial.print (capacity_this_cycle,4);
 // Serial.print (" cap this cycle A*ms, ");
 // Serial.print (last_capacity);
 // Serial.print (" last capacity, A*ms, ");
  Serial.print (discharge_time/1000);
  Serial.print (" discharge time,secs, ");
  Serial.print (ambient_temp);
  Serial.println (" deg C");
  last_print_time = currentTime; //record the time of this printing so I can make sure the print interval has elapsed before it goes again
  }
  else if (currentTime - last_print_time >= printout_interval && (current <.4))
  {
    Serial.println ("still waiting, bro...");
    last_print_time = currentTime; //record the time of this printing so I can make sure the print interval has elapsed before it goes again
  }
  
  }

because you change change them (sometimes unintentionally) from anywhere in your code. If variables are local to your function, other code can't influence them.

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