Go Down

Topic: Measuring Speed of Light with Arduinos and no moving parts (Read 12733 times) previous topic - next topic


I have laser transmitterers and laser sensor modules for some time, and I am excited on the high performance of Arduino Due (that outperforms all other Arduino models including the new ones clearly).

My first thoughts on the current topic were these:
  • light takes 3.3ns to travel 1m
  • Arduino Due clock frequency is 84MHz
  • one clock cycle is therefore 1000/84=11.9ns
  • therefore light travels 3.6m in one Arduino clock cycle

That is a challenge. Our living room is 7m long, when opening kitchen door that can be increased to 9m, and with a mirror on the other side I can get 18m measurement distance at home -- which translates to 5 Arduino Due clock cycles.

I searched this forum for "speed of light" and found some threads and discussions, but no solution. Searching the web I found that speed of light typically gets measured with rotating mirrors or rotating gear wheels. I liked the idea to "just measure the time" for the light "to travel a given distance". I wanted to do it without moving parts, and I wanted to try it with Arduinos.

The laser transmitter has some delay, as well as the laser sensor modules. I wanted to cancel out these delays by delta measurements. So I came up with this experiment setup:

Of course (30+34.5) cm is by far too low, light travels less than 0.2 Arduino clock cycles for that distance, later the 2nd mirror would be placed 7m or 9m away from the laser.

Short description of the experiment:
  • laser transmitter sends a beam
  • half of the beam gets reflected at edge of mirror 1 (N) and goes to sensor 1
  • the other half does not hit mirror 1, gets reflected at mirror 2 and reaches sensor 2

I have a lot to write and photos to show, so my plan is to not write one monster posting but start this thread with several postings, the last one being on discussion of the results, discussions I had with colleagues at work, next steps, questions.

But before submitting this posting and starting this new thread, let me summarize the results obtained sofar:
  • speed of light c=299792.458 km/s
  • the order of magnitude of measurements is correct !
  • the 14m measurement calculation gives 56313 km/s (18.7% of c)
  • combining different measurements calculation gives 67355 km/s (22.5% of c)
  • bill of material for the whole experiment is 30$ only (2 Arduino Due, 1 Arduino Uno, ...)

The pictures/diagrams/photos that will be shown in the next postings are displayed not that big. You will get much better resolution by right clicking them and open in original size.



OK, first steps I did was to verify the experiment can be done and there is no road blocker.

As very first step I verified that splitting of laser bean can be easily done (even without Arduino, see the two laser points on wooden wardrobe):

Then I skrewed the laser transmitter and two laser sensors tightly on wooden Ivar shelf and verified that after 2x7m laser beam can hit sensor 2 (you can see that sensors shadow at kitchen door, both sensors are lit):

Next was the question on how precise timing can be done with Arduino Due. I started thread "Is there an Intel RDTSC equivalent clock counter on Due SAM3X8E microcontroller?" and was really happy on what I learned:

The tuple "( millies(), SysTick->VAL )" is a 84MHz precision counter, so measuring durations of less than 1ms can be done easily with Arduino clock cycle precision:
Code: [Select]
// only for durations <1ms
uint32_t ticks_diff(uint32_t t0, uint32_t t1) {
  return ((t0 < t1) ? 84000 + t0 : t0) - t1;


I did trigger two interrupt pins on a single Due (with another Arduino Due, just two digitalWrite()s) and they just executed "t0 = SysTick->VAL" and "t1 = SysTick->VAL" in Interrupt Service Routines (ISR) with volatile (needed) uint32_t variables t0 and t1. Unfortunately the durations reported were >200 Arduino clock cycles. That was a road blocker for measuring differences of less than 5 clock cycles with a single Arduino Due.

The solution I had in mind was just to use one Arduino Due per laser sensor.

Next I investigated the reliability of Arduino Due clock cycle duration measurements on different Arduino Dues. I used two Arduino Due with shared Reset line and did clock cycle duration measurement with same sketch on both. They show the same 206 clock cycle difference every time, with slightly different clock after reset values:

So with two Arduino Due for the two laser sensors I was good to go.


Next I had to "debug" laser beams which I had never done before. It turned out that a piece of paper

or a big rubber

can be used as screens for finding out current beam location and how to change it to finally hit the sensor. I used two 9V block batteries (depleted) for fixating the mirror. It worked, but is a really fagile solution!

I used an Arduino Uno to send a signal (over same length cables) to both Dues to take an initial timestamp, and then turned on laser after some delay (to make sure the ISRs on both Dues definitely had completed). So I had to control 3 Arduinos at the same time -- good that the laptop has 3 USB interfaces  :)  I started three terminals (I use Linux -- left, middle, right), started Arduino 1.6.4 IDE in each of them and then used 3 Serial Monitors with that setup:


OK, this was the initial setup already described in diagram of initial posting of this thread:

So here seems to be the place to list bill of material for the experiment:
  • two 12$ Arduino Due
  • one 2.70$ Arduino Uno
  • one 0.48$ red laser transmitter
  • two 0.85$ laser sensor modules
  • one 0.35$ 5x5 mini breadboard
  • one 0.50$ bidirectional logic level converter
  • some breadboard connectors
This is 30$ in total, prices are from http://www.aliexpress.com/ where I did buy all that stuff (all with free shipping, 15-34 days delivery time from China).

With that setup I did first measurements as exercise to make sure hardware is correctly setup, sketches are as they should, get some measuring handson.

I did always press the reset button on one Arduino Due (that triggered Reset on both Dues by a shared Reset link), and then immediately pressed the reset button on the Uno. I did that 36 times to get an idea, here are the results:

average(medium distance duration) - average(short distance duration) is 5.5 but should be either 0 or 1 (because light travels 64.5cm in less than an Arduino Due clock cycle).


To complete the hardware side (there are not much connectors needed) an ASCII wiring diagram:
Code: [Select]
      Laser      Uno        Level Shifter
      =====      ===        =============       D23(Due1)
      "-" ------ GND ------ GND       GND        |
      "M"        D4  ------ H1         L1 -------+
      "S" ------ D5         H234     L234        |
                 5V  ------ HV                  D23(Due2)
                 3.3V ---------------- LV
Sensor1    Due1   |   Due2    Sensor2
=======    ====   |   ====    =======
GND ------ GND ---+--- GND ------ GND
OUT ------ D25         D25 ------ OUT
VCC ------ 3.3V       3.3V ------ VCC
           Reset --- Reset


And here is the (simple) software side.

This is the Uno sketch used:
Code: [Select]
volatile unsigned cnt=0;

#define D(stmts) stmts; stmts;

void setup() {

  // signal both Arduino DUEs to take 1st timestamp (via level switcher)

  // generate some small delay (96 increments of volatile unsigned var)

  // turn on laser for 100ms

  // turn off laser

  // cleanup

void loop() {}

This is the sketch running on both Due:
Code: [Select]
volatile uint32_t t0=0, t1=0, cnt=0;

// Interrup Service Routines (ISR)

// ISR for taking first timestamp, triggered by Arduino Uno
void int23(void) {
   t0 = SysTick->VAL;

// ISR for taking second timestamp, triggered by laser sensor
void int25(void) {
   t1 = SysTick->VAL;

void setup() {
  // register ISRs
  attachInterrupt(digitalPinToInterrupt(23), int23, RISING);
  attachInterrupt(digitalPinToInterrupt(25), int25, RISING);

  // wait 3 seconds for interrupts to happen

  // report timestamps taken
  Serial.print(" ");
  Serial.print(" (");

  // report timestamp delta
  Serial.println(") ");

void loop() {}


Ok, here is the 13.7m experiment done.

This is the "lonely" mirror at the terrace door on other end of the room:

This is the wooden Ivar shelf based experiment at kitchen door:

And these are the measurement results:

The difference of short and long averages is 20.1 and should be either 3 or 4 because light travels 10.8m / 14.4m in 3 / 4 Arduino clock cycles.

Anyway the 56314 km/s calculated (18.7% of c) are cool as a first shot.


In thread "Arduino Due high precision tick/timestamp taking and diff functions" I posted some functions based on what I learned while "Measuring Speed of Light" (MSoL) work:

I knew that Arduino Due can be overclocked and did that in the past (84/96/114 MHz):

But I was surprised to learn in that thread from ard_newbie that he was able to overclock his Due up to 234MHz. He said that this is at the edge of what a Due can do, and some Due might work that fast, while another do not.

I did some short experiments yesterday with the only Due at hand (will do more when back home on weekend, there are 3 more Dues), and my Due was not capable to report 234 MHz, but it successfully reported 216 MHz!

Next I tried simple Blink sample and found it did not work at 216 MHz -- I do not want to rely on an overclocked system that cannot run Blink sample. The reason I identified was that "delay()" does not work anymore.

I tried to push working Blink to the limit of my Due, and was surprised that replacing "delay(1000)" with "delayMicroseconds(1000000)" was all that was needed to get Blink sample working at 192 MHz (but not higher).

This Blink program now works, and it does so fine for some minutes. But at some point in time blinking just stops! I verified that original Arduino Blink sample can blink forever at 84 MHz. But some minutes is fine, I do need increased clock cycle timestamp measurement for only some seconds for MSoL experiments!

At 192 MHz light travels only 1.58m instead of 3.6m per Arduino clock cycle.
This increases clock cycles from 5 to more than 11 for my 18m living room+kitchen+mirror distance.

This is sketch from ard_newbie in the other thread with my Blink modifications:
Code: [Select]
volatile uint32_t cnt=0;

Pio *p = digitalPinToPort(13); // B 
uint32_t b13 = digitalPinToBitMask(13);

void setup() {
uint32_t SCC, mode=31, t0=0, t1=0;
// 31: 192MHz  digitalwrite,delayMicroseconds not(delay)

  pinMode(13, OUTPUT);
  // 38 for 234 M Hz
        SCC = SystemCoreClock;

  t0 = SysTick->VAL;
  t1 = SysTick->VAL;
  // 13 for 84 M Hz restore initial clock speed   
  Serial.print(" SystemCoreClock = ");
  Serial.print(SCC/pow(10,6)); Serial.println(" M Hz");
  Serial.println( ((t0<t1)?84000+t0:t0) - t1 );

void loop() {
  digitalWrite(13,  HIGH);
//  p->PIO_SODR = b13;   
//  delay(1000);

  digitalWrite(13,  LOW);
//  p->PIO_CODR = b13;
//  delay(1000);

void Clock_System(uint8_t clock_mul) {

                            | CKGR_PLLAR_MULA(clock_mul) \
                            | CKGR_PLLAR_PLLACOUNT(0x3fUL) \
                            | CKGR_PLLAR_DIVA(0x1UL))

/* Initialize PLLA to X MHz */
while (!(PMC->PMC_SR & PMC_SR_LOCKA));
 /* Setting up prescaler */
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));

 /* Update SystemCoreClock */

/* For your experiment you don't need to re-synchronize UART
 so don't serial print when your uc is running at 236 M Hz */


Determining a better approximation to speed of light from the measurements done is possible.

Colleagues told me that I cannot rely on that both laser sensors used have identical delay behavior. That said I realized that I already had two measurements for same sensor 2, the medium and the long from both experiments done sofar.

Doing the math for the corresponding distances shows better result 67355 km/s (22.5% of c) :
Code: [Select]
$ bc -ql


I discussed what I did with a hardware engineer for the product I am working for  since 9 years.

He told me that ultra short duration measurements can be done with using a Ramp interpolator. Using a 100μF capacitor and a 12bit A/D resolution the Arduino Due has a two digit picosecond measurement resolution can be achieved. That would allow to "see" differences for light moving every 3.3cm!

Another option is to use a delay genarator like the AD9501 and determine the real time taken by interval search of the programmed delay and the measured delay. That chip can generate delay at 10ps resolution and should give a difference of 3 for each centimeter the light travels.


Back to what can be done more easily with the experiment as described above.

Some colleagues told me the the brightness of the laser beam hitting the laser sensor also has an effect. As you can see here (copied out/together from two photos above) the laser beam is definitely not point-like after 14 meters:

So only a small part of the laser beam photons hit the laser sensor after a longer distance, and therefore less photons are there to hit laser sensor electrons for a given time frame, and therefore it takes longer for the sensor to trigger for longer distances.


OK, this seems to be all I have for now, next steps on the weekend:
  • try with 192MHz for better time precision (after more stability/reliability experiments!)
  • compare measurements with same sensor for longer distance,
    but closer together for having less brightness difference

If you have any recommendations or ideas on how to increase measurement precision for above described MSoL experiment, please respond here.



Although this is not rocket science, here are some  ideas I would experiment to realize this measurement :

1/ Hook a first light sensor to ADC channel 7(A0)

1/ Code DAC0 to trigger the laser beam for 200 ns and at the same time start an
ADC PDC DMA to record  the output of  ADC channel 7. Code your ADC for a low resolution on 10 bits instead of 12 by default because the conversion is faster.

To wait for a precise number X of ticks, you can use X times the NOP instruction, because the NOP does nothing and waits exactly for 1 tick.

Calibrate your ADC PDC DMA to know how many words are affected by the high level of the light sensor for 200 ns and detect the level for a rising edge above the noise. Now you have a ratio  : number of Words for 200 ns

2/ Hook a second laser sensor to ADC channel 6 (A1), same model as the first one.

Code DAC0 to trigger the laser beam for 200 ns and at the same time start an
ADC PDC DMA to record  the outputs of  ADC channels 7 and 6.

See how many words are affected by the rising edge of sensor 1 and how many words are affected by the rising edge of sensor 2 in the ADC PDC DMA recording, subtract the 2 numbers.

Permut laser sensors 1 and 2 and do II/2 again. You will have an average of the 2 measurements.

With the previous calibration of the ADC PDC DMA, you know exactly the corresponding time between the rising edge of sensor 1 and the rising edge of sensor 2 (which should be around 50 ns).

With this method, you don't have to bother with the laser starting time and/or the sensor reactivity .

With this method, you get the maximum precision thanks to the super fast PDC DMA.
There is an even  faster recording method with the Peripheral to Memory Advanced High performance bus DMA (AHB DMA) but seems to be very tricky to use.

To enhance the UC speed, start Clock_System with the max you can  just before you trigger DAC0 and restore 84 M Hz with Clock_System (13) just after the 200 ns recording. Of course Clock_System should be used with the same number for calibration and final experiment.


Thanks, I will try.

I never did "ADC PDC DMA", found this thread and hope it is related:

Code DAC0 to trigger the laser beam for 200 ns
The laser transmitter I have is a 650nm 5V laser. And I tested that it does not work with 3.3V (beam is nearly not existent). I did run "03 Analog Fading" demo on DAC0 and the scope says that not even 3V are reached:

Is this 3V laser transmitter what is needed for working with DAC0?

If I understand correctly your method to read input avoids use of ISR, at least for the initial steps. How is the >200 clock cycles overhead avoided when doing the measurements for two events later (on same Arduino Due?)?


Go Up