Code to time electrical pulses giving weird results

Hi, I’m trying to measure the length of wires by timing the time it takes for the electrical pulses or electromagnetic waves travel along the wires. To do this I’m just using the onboard 16mhz oscillator to time and a software denounced button on one transmitting arduino which has a wire from d7 connecting to d2 on the receiving arduino and then a wire from d7 on the receiving arduino to d3 on the transmitting one.(I’ll attach a sketch of this below.

I am aware that this will only measure to a resolution of 18.7ish meters and I do have wire longer than that. If I can get it working as it is then I’ll probably get a faster oscillator and a microcontroller that can work with it. This is mainly a test/practice before I upgrade.

The problem is that when I press the button the first result is 532, and x=2 (x was added to find any bugs). Then have a another completely different number (I think this is the bug since the x number goes negative and/or crazily high). These “random” numbers keep repeating every time I press the button. Does anyone know why or what I’m doing wrong.

Below is the results and then I’ve attached the code in each arduino


Frequency Counter
Took: 532
1
2
counts.
9.37 meters.
Took: 994
463
11318
counts.
4336.32 meters.
Took: 992
461
31217
counts.
4317.59 meters.
Took: 994
463
-22218
counts.
4336.32 meters.
Took: 994
463
-11588
counts.
4336.32 meters.

Transmitting arduino (arduino 1)


volatile boolean first;
volatile bool buttonPushed = false;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;
 
 
int x = 1;
unsigned long n;
float l;
 
// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect)
{
  overflowCount++;
}  // end of TIMER1_OVF_vect
 
ISR (TIMER1_CAPT_vect)
  {
  // grab counter value before it changes any more
  unsigned int timer1CounterValue;
  timer1CounterValue = ICR1;  // see datasheet, page 117 (accessing 16-bit registers)
  unsigned long overflowCopy = overflowCount;
 
  // if just missed an overflow
  if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 0x7FFF)
    overflowCopy++;
 
  // wait until we noticed last one
  if (triggered)
    return;
 
  if (first)
    {
    startTime = (overflowCopy << 16) + timer1CounterValue;
    first = false;
    return; 
    }
   
  finishTime = (overflowCopy << 16) + timer1CounterValue;
  triggered = true;
  TIMSK1 = 0;    // no more interrupts for now
  }  // end of TIMER1_CAPT_vect
 
void prepareForInterrupts ()
  {
  noInterrupts ();  // protected code
  first = true;
  triggered = false;  // re-arm for next time
  // reset Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
 
  TIFR1 = bit (ICF1) | bit (TOV1);  // clear flags so we don't get a bogus interrupt
  TCNT1 = 0;          // Counter to zero
  overflowCount = 0;  // Therefore no overflows yet
 
  // Timer 1 - counts clock pulses
  TIMSK1 = bit (TOIE1) | bit (ICIE1);   // interrupt on Timer 1 overflow and input capture
  // start Timer 1, no prescaler
  TCCR1B =  bit (CS10) | bit (ICES1);  // plus Input Capture Edge Select (rising on D8)
  interrupts ();
  }  // end of prepareForInterrupts
 
void PushButton() //interrupt with debounce
{
  volatile static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 50UL)  // ignores interupts for 50milliseconds
  {
    buttonPushed = true;
  }
  last_interrupt_time = interrupt_time;
}
 
void receive(){
    digitalWrite(8, HIGH);
    digitalWrite(8, LOW);
    x=x+1;
}
void setup ()
  {
  pinMode(8, OUTPUT);
  Serial.begin(250000);     
  Serial.println("Frequency Counter");
  prepareForInterrupts ();   // set up for interrupts
  attachInterrupt(digitalPinToInterrupt(2), PushButton,RISING);
  attachInterrupt(digitalPinToInterrupt(3), receive,RISING);
  } // end of setup
 
void loop ()
  {  if(buttonPushed)
  {buttonPushed = false;
  delay(100);
    digitalWrite(8, HIGH);
    digitalWrite(8, LOW);
    digitalWrite(7,HIGH);
    digitalWrite(7,LOW);
delay(1000);
  }
  else{
    return;
  }
 
  
  if (!triggered)
    return;
 
  // period is elapsed time
  unsigned long elapsedTime = finishTime - startTime;
  // frequency is inverse of period, adjusted for clock period
  n = elapsedTime-531;
  //l = n/5.3386282;
  l = n/0.10677256;
  Serial.print ("Took: ");
  Serial.println(elapsedTime);
  Serial.println (n);
  Serial.println (x);
  Serial.println (" counts.");
  Serial.print (l);
  Serial.println (" meters. ");
  // so we can read it 
  delay(1500);
  prepareForInterrupts ();
  }   // end of loop
 
 

Receiving arduino code (arduino 2)

void setup() {
  pinMode(7, OUTPUT);
attachInterrupt(digitalPinToInterrupt(2), transmit, RISING);
 
 
}
 
void loop() {
  // put your main code here, to run repeatedly:
 
}
 
void transmit(){
  digitalWrite(13, HIGH);
  digitalWrite(13, LOW);
  digitalWrite(7, HIGH);
 digitalWrite(7, LOW);
//The longer this is the bigger the random number gets
  }

Any solutions or ideas would be helpful

An Arduino can not be used to measure the speed of light.
A signal through a cable is slower than the speed of light: https://en.wikipedia.org/wiki/Velocity_factor#Typical_velocity_factors.

Let's say that you have 1km of good cable and signals travels at the speed of light. Then it will take 3µs.
For 18 meters, it is 60 ns (nano seconds).

A interrupt routine itself needs a few microseconds: https://gammon.com.au/interrupts.

To measure the speed of the signal in a cable with an Arduino, you can start with 100km of cable. With some luck you might be able to measure it, but not in a precise way.

You need extra hardware for the high speed timing.

P.S.: I hope my numbers are right, I checked them twice.

Agree with @Koepel ’s fundamentals…

The cheapest way of doing this is probably a very high speed EXTERNAL hardware counter…

Start the count when you send the ping, and stop it when the reflection is detected.
Using a fairly simple algorithm, you can determine the Time Of Flight , which can then be converted to distance for your chosen media.

Yep, agreed. There is no chance of using a microcontroller clocked at 16MHz to time something that takes less that one clock cycle. Realistically, you are out by two orders of magnitude.

Hi, thanks for the velocity factor link. I knew the electrical signal was slightly slower than the speed of light but didn’t realise it varied depending on your wire. I am using a single core copper wire like below.
image
If I’m right this What is the speed of electromagnetic waves propagating in copper wires? - Quora suggests that the waves in these wire travel at only 70% the speed of light--slower than I thought.

The code I’m using here is from nick gammons website and gives one count every 62.5 nanoseconds or 18.7 meters (at the actual speed of light). I’m aware that currently 20 meters is unrealistic but I just want to have something that works fairly consistently (without as many random results) now before getting a faster clock.

@koepel The overall code takes 33 microseconds to complete so the long interrupts I think are being counted. I think that the completion time of the interrupts is very consistent though so I was just going to count this as a constant and take it away from the overall result.

Yes, that's about right, and as you say, it varies a bit between types of cable.

So that is one count per clock cycle at 16MHz.

So your measurement overhead is about 500 times higher than the thing you are measuring. However, if you can guarantee that the overhead never varies, so that you can subtract it reliably, then I agree - it should be doable, at least in theory.

I'm sorry for being so sceptical initially!

Out of interest, @electronicdtcreator1, may I ask what the actual purpose of this will be when the project is fully developed?

i actually worked on a project where i needed to account for the travel time of light through an 18 km spool of equalizing fiber in an optical communication node. i needed to measure the power at both ends using 2 separate processors and needed the measurements ~60 nsec apart.

Have I missed your point? Those results don't look random, they are consistently 4317.59 or 4336.32 metres. Exactly 18.73 metres difference, which is what you'd expect as that is the limit of your resolution. So don't you just need to subtract 4000-odd from the received measurement, to allow for the measurement overhead?

Or am I being dumb?

No, I was also sceptical that the first result might be the wrong one and the rest right. But if you look at the code on arduino 2 where I wrote “the longer this is the bigger the random number gets” I don’t mean that if I add stuff before making pin 7 rising. I mean that if I copy in a load of making pin 13 high/low a couple of times after making pin 7 rising, This increases the random numbers however changing/adding stuff within the timing frame doesn’t. Also as I mentioned I added an int x that increases by 1 every time the interrupt is called to stop the timer and after the first result x comes up as extremely random numbers. These all make me suspicious that these other timings are random-ish. Not random but caused by some sort of random event.

However, if you can guarantee that the overhead never varies, so that you can subtract it reliably, then I agree

Also your previous answer about it working if the overhead doesn’t vary, this is why I’m trying to get it working relatively well now. To understand if it does vary or not cause it doesn’t seem like it is but with all the error I want to be sure.

Out of interest, @electronicdtcreator1, may I ask what the actual purpose of this will be when the project is fully developed?

This is actually like a step between trying to use radio instead. I was originally using radio to start with but encountered a load of problems with the timing going up and down. So I decided to switch to wires which I guess doesn’t make any sense but it seems to have found the problem with my original radio code using the radios which was that I was relying a lot on serial print mid timing and serial print varies a lot in how long it takes to execute. I was trying to smooth out these errors to make sure this didn’t do the same before trying again on radio

1 Like

Just realised I might have caused some confusion there by saying I solved the original problem by taking out serial print. That was a different problem, I still haven’t solved this one yet. Sorry for any confusion I might have caused!

No worries.
When someone corrects me, I sometimes edit my post and fix it. For example: a pullup with a rhinoceros [EDIT] Oops, I meant a resistor.

I still don't see how an Arduino can do measurements with a cable. I have a phone cables and Cat5 cables, but they are maximum 20 meters.

1 Like

Yeah it’s slightly weird way to try solve it but originally I wasn’t sure whether or not the first problem I had when I started was the fault of the radio so I thought wire would be more reliable, plus I happen to have like 60 meters of copper wire. Also as I said above wire probably isn’t the final solution