measure time in ms between light sensor and sound sensor

Hi all,

I'm relatively new to Arduino, and I'm looking to build a device that allows me to measure the time between events on two analog sensors.

Specifically, I'm looking to build a "sync checker" device that measures the timing between picture and sound for TV post production studios. I'm using an analog light sensor and sound sensor (microphone).

The plan is to play a flash frame of white on screen at the same time as a pop of sound, and see how close in time the two events are. The trouble is that either event could potentially be first, so the code needs to be able to measure the time difference either way.

So first I'd need to probably use an "if" statement to declare that nothing should be done until the signal measured by each sensor exceeds a given analog pin amount, right?

And then I'm guessing I need to work with timer interrupts to do the actual time measurement. Do I only need to work with one of the timers? If so, would one of the 8-bit timers be adequate or would I need to use the 16-bit timer?

Any guidance is greatly appreciated!

What difference is considered good? 10 ms apart? 1 ms apart?

In post we need to get things to within one quarter-frame. A single frame is approx. 33 milliseconds, so a quarter frame is approx. 8ms. So if I can get readouts in ms, I'm good.

(8/1000)/(1/16000000) = 128000 machine cycles per quarter frame. (8/1000)/(100/1000000) = 80 analog reads per quarter frame. It is very unlikely you will need to use interrupts.

micros has a granularity of 4 microseconds which is significantly finer than what you need.

The plan is to play a flash frame of white on screen...

How long will the flash frame be displayed? 33 milliseconds?

The flash frame and the audio pop are indeed both exactly 33 milliseconds in duration. And so the task is to determine the timing difference between the start of the two events. Given that either event could be first, how would one best code for this?

Here's one way, in pseudocode.

unsigned long flashtime;
unsigned long soundtime;
unsigned long timediff;

bool gotflash = false;
bool gotsound = false;

void setup() {
  // set up serial stuff here
}

void loop (){
  while waiting {
    if not gotflash {
      check for flash
        if flash  {
        set gotflash
          set flashtime
          if gotsound {
          calc timediff = flashtime - soundtime
            break
        }
      }
    }
    if not gotsound {
      check for sound
        if sound  {
        set gotsound
          set soundtime;
        if gotflash {
          if gotflash {
            calc timediff = soundtime - flashtime
              break
          }
        }
      }
    }
  }
}

This is a skeleton of how I would approach the problem...

const byte MicrophonePin = A0;
const int MicrophoneThreshold = 263;

const byte LightSensorPin = A1;
const int LightSensorThreshold = 941;

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

void loop() 
{
  boolean MicrophoneTrigger;
  boolean LightSensorTrigger;
  unsigned long Delta;
  unsigned long Start;
  
  // Wait for one of the two events to occur.
  do
  {
    analogRead( MicrophonePin );
    MicrophoneTrigger = (analogRead( MicrophonePin ) >= MicrophoneThreshold);
    analogRead( LightSensorPin );
    LightSensorTrigger = (analogRead( LightSensorPin ) >= LightSensorThreshold);
  }
  while ( ! MicrophoneTrigger && ! LightSensorTrigger );
  
  // One or both of the events occurred.  Decide what to do next.
  if ( MicrophoneTrigger && LightSensorTrigger )
  {
    // Both fired.  The two events are essentially simultaneous.
    Delta = 0;
  }
  else
  {
    // One fired.  Wait for the other.
    
    // Start the clock.
    Start = micros();

    if ( MicrophoneTrigger )
    {
      // The microphone triggered.  We need to wait for the light sensor.
      analogRead( LightSensorPin );
      do
      {
        LightSensorTrigger = (analogRead( LightSensorPin ) >= LightSensorThreshold);
      }
      while ( ! LightSensorTrigger );
    }
    else
    {
      // The light sensor triggered.  We need to wait for the microphone sensor.
      analogRead( MicrophonePin );
      do
      {
        MicrophoneTrigger = (analogRead( MicrophonePin ) >= MicrophoneThreshold);
      }
      while ( ! MicrophoneTrigger );
    }
    
    // Calculate the difference in microseconds between the two events.
    Delta = micros() - Start;
  }
  
  // Wait for both events to subside.
  do
  {
    analogRead( MicrophonePin );
    MicrophoneTrigger = (analogRead( MicrophonePin ) >= MicrophoneThreshold);
    analogRead( LightSensorPin );
    LightSensorTrigger = (analogRead( LightSensorPin ) >= LightSensorThreshold);
  }
  while ( MicrophoneTrigger || LightSensorTrigger );

  // At this point both events have occurred then subsided and Delta is the microseconds between the first event and the second.
  Serial.println( Delta );
}

Edit: Missing code added. Primer analogReads added.

Wouldn't you just want the times when the signals start / stop so you can calculate deltas afterwards.
in other words: is it enough to have only the deltas?

some remarks on Coding Badly's code

  • missing a Serial.begin(115200); in setup()
  • one might do an additional analogRead() per sensor to prevent signals affecting each other (There is only one ADC)

robtillaart:
Wouldn't you just want the times when the signals start / stop so you can calculate deltas afterwards.
in other words: is it enough to have only the deltas?

The OP said he's only interested in the start times of each type of event.

  • one might do an additional analogRead() per sensor to prevent signals affecting each other (There is only one ADC)

Do you mean "do two analogReads of one sensor in succession"? Will an analogRead affect the next analogRead if they are in close time proximity?

lar3ry:

robtillaart:
Wouldn't you just want the times when the signals start / stop so you can calculate deltas afterwards.
in other words: is it enough to have only the deltas?

The OP said he's only interested in the start times of each type of event.

He said he is interested in the difference in time.
so the code needs to be able to measure the time difference either way.

I have done quite some requirement elicitation and often people say what they think they need but as the process goes further they find out that they need more, something extra etc. That is why I ask it,
Knowing the start and stop time of both, gives more information than only the difference. Not knowing the details of his project at least he can determine which of the two was first. With only the difference this information is lost from the measurements.

  • one might do an additional analogRead() per sensor to prevent signals affecting each other (There is only one ADC)

Do you mean "do two analogReads of one sensor in succession"? Will an analogRead affect the next analogRead if they are in close time proximity?
[/quote]
I'm nor Electrical Engineer so all disclaimers apply. The Arduino multiplexes one ADC.
IIRC:
If one line has a high impedance and the other a low impedance than the ADC cannot adjust perfectly when switched. By doing a dummy analogRead() before the real one, the ADC can adjust to the electrical properties of the line. If both lines have equal properties I assume it is not needed, but as said I'm no EE.

code becomes something like

  do
  {
    analogRead( MicrophonePin );
    MicrophoneTrigger = (analogRead( MicrophonePin ) >= MicrophoneThreshold);
    analogRead( LightSensorPin );
    LightSensorTrigger = (analogRead( LightSensorPin ) >= LightSensorThreshold);
  }

As the analogRead() is more than fast enough for this application, the extra read will not affect the endresult in a substantial way.

Thank you all so much for helping with code suggestions!!

To add more clarity, the relevant relationship is the difference in time between the start of the two events only. The duration and end are not significant. Here's a quick bit of info that hopefully explains this:

I work in post production sound, and we are held responsible for achieving proper "sync" between our sound and the video that we've been provided. Sync is determined by the alignment of the "frame edge", which is the starting point in time of the frame as it plays.

When we work on the audio we use various video playback devices (LCD TV screens, LCD computer displays, projectors, etc) for reference, and we need to guarantee that our audio is playing in proper sync with the video devices. We are able to "offset" the video from the audio by quarter-frame increments in order to achieve this sync. We can always "eyeball" it, but even the sharpest eyes typically can only determine sync within 1.5 frames of duration. Basically we need to measure things accurately to 1/6th of that duration.

To add a slight bit of complication, we work with two different frame rates: 29.97 frames per second (basically 30) and 23.976 frames per second (basically 24). So a 29.97 frame is approx. 33 milliseconds, and a 23.976 frame is approximately 42 milliseconds.

In either circumstance, we gauge sync according to the relationship of the start of a frame to the start of the corresponding sound. So to test sync, one plays a single frame of all-white (nice and bright) along with a single frame of 1kHz audio (nice and loud!). So I figured that an arduino would be perfect for measuring the timing difference between the events, allowing us to make very precise adjustments.

So hopefully that helps clarify that all we need to do is measure the difference in the start-times of the two events.

Thanks again everyone!

robtillaart:

  • missing a Serial.begin(115200); in setup()

Corrected.

  • one might do an additional analogRead() per sensor to prevent signals affecting each other (There is only one ADC)

Have to know more about the hardware. If the sensors are low impedance (I assume <= 10K) the extra reads are not necessary.

As the analogRead() is more than fast enough for this application, the extra read will not affect the endresult in a substantial way.

I agree. The price of the extra read is essentially zero but, if it's needed and not included, the application will not work correctly. I modified the skeleton.

Hey all,

thanks again for your help on this. I've added to the suggested code and done some other modifications. I've got it set up in such a way that it waits for a button press before beginning the sync check routine.

So the idea is that you press a button, then the arduino starts polling the light sensor and microphone for input, then you play the sync pop and flash frame and the arduino measures the timing difference and writes it out to the console.

The problem I'm having now is that the readings are very inconsistent. The gear I'm trying to measure should be playing the sync pop and flash frame with very consistent and reliable timing. But the numbers I'm getting from the sync check program on the arduino fluctuate by 30 milliseconds or more sometimes. I need to measure the timing difference to an accuracy of 10ms or better.

I'm wondering if the problem is my code, or if my Arduino Uno just doesn't have a very accurate internal clock? If the hardware is the problem, is there a better Arduino model to use that has a more stable internal clock? Here's my code currently:

const byte MicrophonePin = A0;
const int MicrophoneThreshold = 650;

const byte LightSensorPin = A1;
const int LightSensorThreshold = 250;

int buttonPin = 2;
int val1;     
int val2;
int buttonState; 

void setup() 
{
  Serial.begin( 9600 );
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH); //activate built-in pull up resistors
  buttonState = digitalRead(buttonPin); 
}

void loop() 
{
  boolean MicrophoneTriggered;
  boolean LightSensorTriggered;
  unsigned long Delta;
  unsigned long Start;


  val1 = digitalRead(buttonPin);      // read button input value and store it in val1
  delay(10);
  val2 = digitalRead(buttonPin);    //check button again for de-bouncing
  if (val1 == val2){
    if (val1 != buttonState) {          // the button state has changed!
      if (val1 == LOW) {                // check if the button is pressed
        
        // Wait for one of the two events to occur.

        do
        {
          analogRead( MicrophonePin );
          MicrophoneTriggered = (analogRead( MicrophonePin ) >= MicrophoneThreshold);
          analogRead( LightSensorPin );
          LightSensorTriggered = (analogRead( LightSensorPin ) >= LightSensorThreshold);
        }
        while ( ! MicrophoneTriggered && ! LightSensorTriggered );

        // One or both of the events occurred.  Decide what to do next.
        if ( MicrophoneTriggered || LightSensorTriggered )
        {
          // One fired.  Wait for the other.

          // Start the clock.
          Start = millis();

          if ( MicrophoneTriggered )
          {
            // The microphone triggered.  We need to wait for the light sensor.
            
            do
            {
              LightSensorTriggered = (analogRead( LightSensorPin ) >= LightSensorThreshold);
            }
            while ( ! LightSensorTriggered );
          }
          else
          {
            // The light sensor triggered.  We need to wait for the microphone sensor.
            do
            {
              MicrophoneTriggered = (analogRead( MicrophonePin ) >= MicrophoneThreshold);
            }
            while ( ! MicrophoneTriggered );
          }

          // Calculate the difference in milliseconds between the two events.
         
            Delta = millis() - Start;
     
        Serial.println( Delta/41.7 ); //shows the difference in frames, which are approx. 41.7ms long
      }
    }
    }
    buttonState = val1;
  }
}
  val1 = digitalRead(buttonPin);      // read button input value and store it in val1
  delay(10);
  val2 = digitalRead(buttonPin);    //check button again for de-bouncing
  if (val1 == val2){
    if (val1 != buttonState) {          // the button state has changed!
      if (val1 == LOW) {                // check if the button is pressed

Why are you debouncing at the start? It's only going to cause problems. You should be checking for a clean transition to HIGH after the sync check (ensure the button has been released before continuing). As an added bonus I believe buttonState becomes irrelevant and can be removed.

Hi there,

I'm trying to develop exactly the same device - I work in the cinema industry.

I need some help from the hardware point of view though. My electronic skills are scarce and I am unable to design this on my own.

I have acquired a photodiode and I have an output but the sensitivity of the diode seems to be quite high and I have to feed it very little light to see something which is not 1023 on the analogue input! Maybe that would be ok for a low level light source as the one you can find in a cinema room - 48 candelas on screen - but I was wondering if there was a way to change the sensitivity by turning a trimmer or so.

Sound-wise is the same issue. I have purchased a ready-made sensor on the internet but it doesn't seem to do what I want. The mike output only reaches roughly +/- 20mV when some pink noise is played.

Once I have the inputs sorted and I can see what I want with my oscilloscope, then I'll work on the code.

Anybody can help me?

Thanks
Tony

tony359:
Hi there,

I'm trying to develop exactly the same device - I work in the cinema industry.

I need some help from the hardware point of view though. My electronic skills are scarce and I am unable to design this on my own.

I have acquired a photodiode and I have an output but the sensitivity of the diode seems to be quite high and I have to feed it very little light to see something which is not 1023 on the analogue input! Maybe that would be ok for a low level light source as the one you can find in a cinema room - 48 candelas on screen - but I was wondering if there was a way to change the sensitivity by turning a trimmer or so.

Sound-wise is the same issue. I have purchased a ready-made sensor on the internet but it doesn't seem to do what I want. The mike output only reaches roughly +/- 20mV when some pink noise is played.

Once I have the inputs sorted and I can see what I want with my oscilloscope, then I'll work on the code.

Anybody can help me?

Thanks
Tony

Need more details. Your circuit, code and the make/model/code of the photodiode you have.

Hi Johnny,

Thanks for your help. For now I have purchased a couple of pre-made modules from adafruit. They seem to work - the light sensor does not 'clip' anymore, we'll see if that's enough for a 48 candelas flash.

Now I can see some kind of signal I'll play with the code and I'll come back if (when!) I need more help.

Thanks again!
Tony

Keep in mind that the further the mic is from the speaker, the greater the error caused by speed of sound delay.

Hi Polymorph,

The delay caused by the speed of sound - and the processing delay caused by the equipment - is exactly what I need to compensate. I want that the audience gets perfectly synchronized sound and picture, regardless of the distance of the speaker. I can change the delay of picture and sound in the equipment and here is where I need a sync checker :slight_smile:

Right,

I have made some progress. I've purchased an Adafruit microphone preamplifier and a light sensor. I have also purchased an LCD display.

The code works and I have been playing with it with cool results - please bear in mind this is my first Arduino project!

I believe my hardware problem lies in the microphone. It does not seem to be sensitive enough to give a good separation between noise floor and the "beep".

I have written a quick code to display the max value read by the sensors. The microphone feeds 555 with no sound (the trimmer on the back of the board is set to maximum). Checking with my oscilloscope the board seems to introduce about 2V bias when the trimmer is set to maximum.

That said the BEEP is clearly read at 2V p/p on the oscilloscope. But the analog pin reads a value between 650 and 700. I feel this may be too close to the noise floor (mostly caused by the bias) to give a precise reading.

When testing, I have 74ms most of the time (sound is indeed out of sync) but I also have other values (89,100, 120) too many times. I feel the signal coming from the sensors is not large enough to provide a reliable reading.

I am not too concerned by the light sensor for now. How can I improve the microphone?

Maybe this is not a big deal in a cinema, after all maybe all I have to do is to raise the volume a little. But I'd like to be able to have a better reading in the first place.

Any help appreciated.

Thanks
Tony

The delay caused by the speed of sound - and the processing delay caused by the equipment - is exactly what I need to compensate. I want that the audience gets perfectly synchronized sound and picture, regardless of the distance of the speaker. I can change the delay of picture and sound in the equipment and here is where I need a sync checker :slight_smile:

I'm lost, what you are measuring. To get video and sound in sync you have to delay video proportionaly to distance to the audience. It's would n'r be possible if people distributed over big area, as each of them 'd need different delay time. Delay is well known, it's speed of sound, don't need to be measured.