karplus4arduino

2011 July 13

Simple timer

Filed under: Data acquisition,Software — gasstationwithoutpumps @ 01:51
Tags: , , ,

One of the simplest and most useful tasks that a data acquisition system can do is to measure the time between two events.

On the Arduino, this is easily done with the digitalRead() to detect the transition and micros() to time the events in microseconds. First you have to figure out what pins the inputs will use, and in the setup() routine, specify that they will be used for input, not output. In my example, I have used pin 4 for the first event and pin 5 for the second event.

In the loop() routine, have the Arduino do a busy-wait for the first event. Busy-waiting is a crude technique for determining when an event happens—you just keep checking the input until it is the value you want for starting the event. Modern computers rarely do busy waiting, because it ties up the processor doing nothing useful. The more common approach is to use an interrupt, which is possible with the Arduino, but a bit trickier to code. Since we weren’t doing anything else with the Arduino while waiting for the initial event, it made sense to me to do a busy wait.

Immediately after the starting event, record the current time. I also turned on the on-board LED (pin 13) to indicate that the board was now waiting for the ending event.

Then busy-wait for the ending event, and record the time right after it.

My example reports the difference in times on the USB line, but you could store the results in an array or do further processing of the times.

// Timer test
// Kevin Karplus
// 12 July 2011

// One of the simplest data acquisition tasks is
// to time the interval between two events.

// This program waits for pin 4 to go high,
//   starts a timer, lights the on-board LED,
//   then waits for pin 5 to go low,
//   when it turns of the LED and resets the timer.
// It reports the time between pin 4 going high and pin 5 going low
//   in microseconds.

// It is easy to change the code to use
// either transitions to high or transitions to low.
// Using a pull-up resistor can convert any make or break contact to
// a low-going or high-going edge.
// Opposite polarities were used in this example, so that the
// same square wave could be fed to both pins and the width of the
// positive pulse timed.


// The minimum time it can report (if pin 5 is already low when
// pin 4 goes high is about 12 microseconds +- 4 microseconds).
// The reported time seems to be always a multiple of 4 microseconds,
// which is most likely the resolution of the micros() call.

// Timing seems to be fairly reliable down to about 60 microsecond.

void setup()
{
    pinMode(4, INPUT);
    pinMode(5, INPUT);

    pinMode(13, OUTPUT);    // LED output
    Serial.begin(115200);
}

void loop()
{
    unsigned long start_time;
    unsigned long stop_time;

    digitalWrite(13,0);
    while(digitalRead(4)==0) {}
    start_time=micros();
    digitalWrite(13,1);
    while (digitalRead(5)) {}
    stop_time=micros();
    digitalWrite(13,0);
    Serial.print("time hi->lo=");
    Serial.print(stop_time-start_time);
    Serial.println(" microsec");
    delay(500);
}

[Update: 8 October 2011. I redid the code using the sourcecode tag, so that WordPress no longer discards the spacing.]

I put this little test program on gist as git://gist.github.com/1079561.git

2011 June 29

Data Acquisition

Filed under: Accelerometer,Data acquisition,Robotics,Software — gasstationwithoutpumps @ 19:28
Tags: , , ,

One of the science-teacher blogs that I read has recently discussed using an Arduino for data acquisition: The DAQ-ness Monster « Science Learnification.

I’ve not played with the Arduino much that way, but I do have a 3-axis accelerometer, the ADXL335 from Analog Devices, on a breakout board from Adafruit Industries.  This accelerometer has analog readout, so I connected the three X, Y, and Z pins to analog pins 0,1, and 2 of the Arduino, and powered the breakout board from the Arduino 3.3 volt supply.  I also sent the 3.3 volt input to the AREF pin of the Arduino so that the I could use “ratiometric” measurements.  The ADXL335 is designed so that 0g acceleration is mid-scale, so using the same voltage for powering the chip and for the Arduino AREF input means that 512 is 0g and the scaling is approximately 1g for each difference of 100.  It turns out that the calibration is not perfect (no surprise there), so you can improve the measurements by tweaking the offset and scaling for each axis separately.  I did this manually for my code, and the results are only good to about 1%.

I should be able to do better if I write a calibration program on my laptop that gathers results from the Arduino and tries to optimize the magnitudes of the reported acceleration vectors to be one.  If the accelerometer is held steady in several different orientations, this should auto-calibrate.

I wrote a little program that measured the X, Y, and Z values 1000 times, averaged them, and sent the average over the USB line to my laptop.  I’ve put the code on-line at https://gist.github.com/1054649  Each of the measurements takes about 3.3 msec (about 1msec per DAC reading)

Limitations of the Arduino as a data-acquisition device:

  • Only 6 analog inputs
  • Only 10-bit resolution
  • All 6 channels on same scale, and input must be between 0 and a single reference voltage (3.3v for the setup I used).
  • No faster than about 1msec/sample (so don’t try to collect audio or video data)

UPDATE 8 October 2011.  I’ve just found out how to put code into the blog itself, so here it is:

// Accelerometer (ADXL335) test
// Kevin Karplus
// 29 June 2011

void setup()
{
  analogReference(EXTERNAL);   // essential if you are going to connect to AREF
  Serial.begin(115200);
  Serial.println("setup");
}

// I used wires to connect the breakout board to 
// three analong inputs, 3.3v, and ground.
// I also connected the AREF input of the Arduino to 3.3v

const int Xpin=2;
const int Ypin=1;
const int Zpin=0;

const int num_to_sum=1000;
const float xscale = 0.0098/num_to_sum;
const float yscale = 0.0097/num_to_sum;
const float zscale = 0.0099/num_to_sum;

const int zero_x = 500;
const int zero_y = 506;
const int zero_z = 511;

long xsum,ysum,zsum;

void loop()
{
  unsigned long startTime=millis();
  xsum = ysum = zsum = 0;
  for (int i=num_to_sum; i>0; i--)
  {    
     xsum += analogRead(Xpin) -zero_x;
     ysum += analogRead(Ypin) -zero_y;
     zsum += analogRead(Zpin) -zero_z;
  }
  Serial.print(millis()-startTime);
  float x=xscale*xsum;
  float y=yscale*ysum;
  float z=zscale*zsum;
  Serial.print(" X=");
  Serial.print(x);
  Serial.print(" Y=");
  Serial.print(y);
  Serial.print(" Z=");
  Serial.print(z);
  
  Serial.print(" total=");
  Serial.println(sqrt(x*x+y*y+z*z));
}

Blog at WordPress.com.