Collect Data with time and break the loop

Hi,

I am doing a simple project which is using a voltmeter to collect voltage, graph it for one hour (time vs voltage) and save the data with a time column and the corresponding voltage column. I know that I must use a third party to graph the data Arduino collected. I was able to graph the voltage simultaneously on Processing, but I really would like to save the data and the graph.

Any suggestion on (1) how to express the time frame (e.g. at t=1sec, V=3.4V), (2) break the loop after 1 hour, and (3) save the data (2 columns) collected?

Here is the Arduino Code to collect the voltage, one entry per second.

int voltPin = 0; //voltmeter connected to analog pin 0
int analogValue = 0; //variable to hold the analog values

void setup(){
Serial.begin(9600); //open the serial port at 9600 bps
pinMode(voltPin, INPUT);
}

void loop(){
int analogValue = analogRead(voltPin); //read the analog input on pin 0
Serial.println(analogValue, DEC); //print the voltmeter reading
delay(1000); //delay 1 second before the next reading
}

And I used the Processing graphing code directly from here: http://arduino.cc/en/Tutorial/Graph

I tried to use MATLAB for graphing (and I used the codes from here: How To Send Data From The Arduino To MATLAB Communication). For some reason, MATLAB cannot reach Arduino and a error message is displayed. (See following. )

??? Error using ==> serial.fopen at 72
Port: COM3 is not available. Available port: COM1.
Use INSTRFIND to determine if other instrument objects are connected to the requested
device.

Many Thanks in Advance!

1 Like

Any suggestion on (1) how to express the time frame (e.g. at t=1sec, V=3.4V), (2) break the loop after 1 hour, and (3) save the data (2 columns) collected?

(1) Each time you read a voltage increment a counter so that you know which second it was read at.
(2) Only collect data when the time variable has a value below 3600.
(3) When each value is read send it and the time variable to Processing for storage and display.

Incidentally, you will not currently get a floating point value for your voltage because you are using an integer variable to hold it. Depending on what sensor you are reading you will get a value between 0 and 1023 which you will need to convert to an actual voltage. This may require you to calibrate the sensor against know voltages.

I cannot offer a solution that will save the graph but you could save the data on the PC as a comma separated file and use Excel to produce the graph. Other possible solutions include using an SD card shield on the Arduino to hold the data and reading it later on a PC or sending it as a file to the PC via the serial port once the hour has passed.

NOTE - the use of the delay() function in your program effectively stops the Arduino doing anything else. A better way to do the timing would be to use the millis() function as in the BlinkWithoutDelay example in the IDE. This would allow the Arduino to do other things such as responding to button presses if required.

UKHeliBob:

Any suggestion on (1) how to express the time frame (e.g. at t=1sec, V=3.4V), (2) break the loop after 1 hour, and (3) save the data (2 columns) collected?

(1) Each time you read a voltage increment a counter so that you know which second it was read at.
(2) Only collect data when the time variable has a value below 3600.
(3) When each value is read send it and the time variable to Processing for storage and display.

Incidentally, you will not currently get a floating point value for your voltage because you are using an integer variable to hold it. Depending on what sensor you are reading you will get a value between 0 and 1023 which you will need to convert to an actual voltage. This may require you to calibrate the sensor against know voltages.

It might be better to transmit/store the raw ADC values and do the voltage calculation and calibration during data analysis. That way he doesn't have to re-write firmware every time he needs to recalibrate, and one keeps as much floating point math off the AVR as one can. Easier to change on a computer than in firmware...

I cannot offer a solution that will save the graph but you could save the data on the PC as a comma separated file and use Excel to produce the graph. Other possible solutions include using an SD card shield on the Arduino to hold the data and reading it later on a PC or sending it as a file to the PC via the serial port once the hour has passed.

NOTE - the use of the delay() function in your program effectively stops the Arduino doing anything else. A better way to do the timing would be to use the millis() function as in the BlinkWithoutDelay example in the IDE. This would allow the Arduino to do other things such as responding to button presses if required.

Another benefit of using the millis() function as in the BlinkWithoutDelay example instead of the delay() function is using the delay() function will eventually drift. Each iteration of the loop with the delay will take the delay time plus all the other code execution time. analogRead() actually takes a few milliseconds (I forget how many off the top of my head, but I know it is costly in time), and what ever method you use to transmit the data (serial, SPI to a SD card, or any one of many other options) is also likely to take a significant amount of milliseconds. This means each reading will actually happen at a little over a second each, so 3600 readings would probably take several seconds longer than 1 hour.

By using the millis() the only long-term, cumulative drifting would be a function of the natural clock drift of the crystal and timing circuit of the AVR chip itself, and there isn't much you can do about that without attaching a more accurate 1PPS input signal to trigger off of. But I doubt the natural clock drift inherent in the Arduino board would matter that much even over a couple days of continuous data logging. My gut feeling is you would only be off by a few seconds over a few days, but I don't know the timing specs off the top of my head. Much better than several seconds each hour...

Hi UKHeliBob!
Thank you for your response!

Incidentally, you will not currently get a floating point value for your voltage because you are using an integer variable to hold it. Depending on what sensor you are reading you will get a value between 0 and 1023 which you will need to convert to an actual voltage. This may require you to calibrate the sensor against know voltages.

I just modified my code according to the Arduino's voltage reader's example so I think I am getting the floating point value for the voltage now. Thank you for this suggestion!

Here is my current code:

unsigned long time;

void setup(){
  Serial.begin(9600);  //open the serial port at 9600 bps
}

void loop(){
  time = millis();
  while(time < 3600*1023.0){
  int sensorValue = analogRead(A0); //read the analog input on pin 0
  float voltage = sensorValue * (5.00/1023.00);
  Serial.print(voltage,DEC);
  Serial.println(",");
  delay(1000);
}
}

Right now I am trying to write a while loop in the void loop using millis() so when the time is less one hour (time < 3600 * 1023.0), the void loop will continue. But I don't know how to write the proper code to stop the while loop after one hour (time > 3600*1023). Would you please give some insight on how to do that? (And I don't know how to calibrate the time that is why I am multiplying the 3600 seconds to 1023.0 for the while loop condition. Any insight on that as well? )

I cannot offer a solution that will save the graph but you could save the data on the PC as a comma separated file and use Excel to produce the graph.

I figured out how to put the comma between each data. So how may I save the data? I tried Processing but it said the 64-bit Win system cannot process serial data. And I do not have a local copy of MATLAB.

Thank you so much!!!

Hi Sembazuru!
Thank you for your reply!

I have modified my codes according to the Arduino's voltage reader example so now it should be displaying actual voltage values. And thank you for reinforcing the advantages of millis() function. I am trying to incorporate that into my codes in a while loop (which is in my response above).

Any ideas on (1) how to write the proper code to stop the while loop after one hour? and (2) save the data?

Thank you!!!

  while(time < 3600*1023.0){

Why are you comparing a long to a float? There is no reason to multiply 3600 by a float. 3600 should be followed by UL, so that the multiplication is done using unsigned longs, not ints (and NOT floats).

3600 * 1023 is NOT one hour.

Hi PaulS
Thank you for your reply!

Why are you comparing a long to a float? There is no reason to multiply 3600 by a float. 3600 should be followed by UL, so that the multiplication is done using unsigned longs, not ints (and NOT floats).

3600 * 1023 is NOT one hour.

I am not sure what is the difference between long and float. (This is my second week of learning Arduino.) I read this site (Data Types in Arduino - SparkFun Learn) but I am still unclear what is the difference in properties and usage. My guess is that long has a smaller size than float and so it is more efficient? Would you please help me to better understand the difference?

And may I do this?

unsigned long time;

void setup(){
  Serial.begin(9600);  //open the serial port at 9600 bps
}

void loop(){
  time = millis()*(1/1023.0);
  while(time < 3600){
  int sensorValue = analogRead(A0); //read the analog input on pin 0
  float voltage = sensorValue * (5.00/1023.00);
  Serial.print(voltage,DEC);
  Serial.println(",");
  delay(1000);
}
}

Thank you!

My guess is that long has a smaller size than float

No. longs and floats are the same size. Math involving longs is far faster than math involving floats.

And may I do this?

You may. You'll loose precision. And why? There are 3,600,000 milliseconds in an hour, not 3,682,800.

time = millis();
if(millis() - time < 3600UL * 1000UL) // NOT 1023
{
    // It's been less than an hour
}

Hi PaulS,

So I modified the code to see if it would stop in 30 seconds and it won't stop. Would you please give me some insight on why it is not stopping?

And the reason why I put 1023 is that I copied the voltage reading example (http://arduino.cc/en/Tutorial/ReadAnalogVoltage) where it used 5.00/1023.0. I thought the purpose of division by 1023 is to calibrate.

unsigned long time;

void setup(){Serial.begin(9600); }

void loop(){
  time = millis();
 if(millis()-time<30UL*1000UL){
  int sensorValue = analogRead(A0); //read the analog input on pin 0
  float voltage = sensorValue * (5.00/1023.00);
  Serial.print(voltage,DEC);
  Serial.println(",");
  delay(1000);
  }
}

Thank you!

  time = millis();
 if(millis()-time<30UL*1000UL){

Set the time variable to millis() in setup() not in every iteration of loop() otherwise the difference will never be 30 seconds. If you need to repeat the delay then set the variable again after the delay has elapsed.

DDGao:
Hi PaulS,

So I modified the code to see if it would stop in 30 seconds and it won't stop. Would you please give me some insight on why it is not stopping?

And the reason why I put 1023 is that I copied the voltage reading example (http://arduino.cc/en/Tutorial/ReadAnalogVoltage) where it used 5.00/1023.0. I thought the purpose of division by 1023 is to calibrate.

unsigned long time;

void setup(){Serial.begin(9600); }

void loop(){
  time = millis();
if(millis()-time<30UL*1000UL){
  int sensorValue = analogRead(A0); //read the analog input on pin 0
  float voltage = sensorValue * (5.00/1023.00);
  Serial.print(voltage,DEC);
  Serial.println(",");
  delay(1000);
  }
}




**Thank you!**

In addition to what UKHeliBob said while I was writing this long explanation:

Well, the problem seems to be a misunderstanding of the loop() function. The way the Arduino IDE compiles it will not to halt execution at the end of the loop() function, but continue execution back at the beginning of loop() once the end of loop() is reached. (Thus the name "loop"... :wink: ) If you moved the above contents of loop() into setup() and leave loop() empty your code should end. Another way to force operation to appear to end at a certain point is to insert an infinite loop doing nothing (like while (1) {}) where you want to sketch to stop (or appears to stop, in reality it is very busy doing nothing over and over again). Using the infinite loop doing nothing can be a handy technique for troubleshooting a program to make sure it doesn't execute past a certain point.

The purpose of the 1023 when dealing with values returned from analogRead() is because over an input of 0VDC to 5VDC, analogRead() will return positive integer values 0 through 1023. The full equation of float voltage = sensorValue * (5.00/1023.00); will proportionally convert the range of 0 through 1023 ADC (Analog to Digital Converter) counts to 0 through 5 Volts DC. I was taught in high school a method of converting between different units where one uses cross multiplication. Knowing that 5 VDC = 1023 ADC counts Thus:

sensorValue ADC counts      5 VDC
                        *  -----------------
                            1023 ADC counts

Cross multiplication on "ADC counts" will cancel those units out, leaving only the numbers for the equation and the unit "VDC". Adding the ".00" (well you can actually get away with just ".0" and save yourself keystrokes) forces the compiler to handle the 5 and the 1023 as floating point values. (Float is the only datatype we use that can handle decimals, all the other datatypes that were explained in the SparkFun tutorial you read are just different sizes of integer ranges. This is one of the major differences between float and long.) The massive increases in processing time that float costs you (as demonstrated by the SparkFun tutorial) is why I was suggesting having your Arduino sketch simply output the ADC values, and then leave the conversion to VDC to the computer where you are interpreting the data (and as I remember from the beginning of this thread, making graphs).

It's late right now, so if no one else gets to it, I'll write up some pseudocode for totally getting rid of the need for that delay(1000) that you keep using tomorrow evening. Unless, of course, either you come up with something or someone else shows you the way down that path.

I apologize for not responding sooner.

Thank you to everyone for all the detailed explanations! They are very helpful.

Just 10 minutes ago, I followed the suggestion of putting the code in the void setup and changed the IF(millis() ... //stop in 30 sec) to WHILE(millis() ...) and IT WORKED!

void setup(){
  Serial.begin(9600);  //open the serial port at 9600 bps
  unsigned long time;
  time = millis();
  while(millis() - time < 30UL * 1000UL){
  int sensorValue = analogRead(A0); //read the analog input on pin 0
  float voltage = sensorValue * (5.00/1023.00);
  Serial.print(voltage,DEC);
  Serial.println(",");
  delay(1000);
}
}

void loop(){
  //everything already happened//
}

Now I just have to figure out how to save the data and graph with Excel. (Any suggestion?)

THANK YOU!

I am trying this with Processing. I thought it would just simple as saving the numbers with commas by writing them to a file. But the reality is not. :~

I used the PrintWrite() on Processing and tried it but I can't even find a txt file after running the Arduino code for 30 seconds simultaneously with Processing. I thought the file will be created in a new folder (like Arduino). But anyway, I looked in all the Processing folders as well, and I still could not find the file.

Also, since the Arduino code already printed all the data with commas for 30 seconds and completely stopped, can I just put one y-variable and x will just be the time (automatically one second for each y)? If so, how may I do that?

import processing.serial.*;
Serial myPort;

PrintWriter output;

void setup() {
  // Create a new file in the sketch directory
  output = createWriter("data.txt"); 
}

void draw() {
  point(mouseY);
  output.println(mouseY); // Write the coordinate to the file
}

void keyPressed() {
  output.flush(); // Writes the remaining data to the file
  output.close(); // Finishes the file
  exit(); // Stops the program
}

Thank you!