Measuring position using piezo sensor


I want to make a solution that uses Piezoelectric sensors. I have played with the basic disk type you can get cheaply and they are more than sensitive for what i need

In fact what i want it to be build an electronic scouring system for archery.

So quite straight forward, position 3 (could do it with 2 I know if for example i place them below the target and assume all hits are above)

So my current way to to do this is to keep running a loop checking each sensor for a change above a threshold and one it sees one, log the time stamp and then keep checking for a signal from the other 2. once it has this its a simple task to work out the location after calibration.

So this works fine but it means there is a loop running and if I am doing any thing else, such as updating a display there is a risk that i will miss the start of the signal and introduce some inaccuracy / Inconsistence.

So i was wondering is there a way to use an interrupt on the analogue pin. Because of course on an analogue pin is always going to have noise i don't want to have a full interrupt for all the back ground noise so I want to be able to say some thing like

If the change in signal is > 10 then interrupt.

I was thinking i could have it keep interrupting even with false positives and simply writing the value and time then returning and doing the checks once each loop, but if the intetupte get to frequent I could end up missing the real one due to a false one.

Any thoughts on a better way to do this than i am thinking of.

I suppose the other way would be to create a timed interrupt every few mills and using it to read the pin values before retuning so I can be sure other process don't make me miss them. and as soon as I record a signal, then lock in to reading the others until either

  1. I have recorded a peak from all three
  2. a set time has passed and reset so I don't get locked up due to miss read.

Really just looking for ways to improve my code, if any one can suggest some ideas.


Not at all. This topic comes up quite frequently in the forum, usually in the form of locating a sound source by timing signals from two or more microphones, and no one, to my knowledge, has reported success.

There are successful commercial devices that work on that principle, such as localizing gunshot reports in an urban environment. They are very expensive.

For something that travels as slowly as sound waves, the Arduino is certainly capable of timing impulses arriving at two or three detectors and working out the source location, but it is not at all a simple task to get it to work, let alone get it to work reliably.

Show us what you have actually done so far, and we can go from there.

Please post schematics and links to devices used.
Post the code You have made. Also post a flowchart showing what and how the code is intended to run.

I'll post some thing in a bit.

When i say straight forward I mean the theory, take 3 microphones, monitor the signal, recorded the times, and from this once you know the distance from each other the microphones are and have calibrated the time / distance values its simple trigonometry to convert to X/Y coordinates.

Simple :slight_smile:.... On paper

So my simple challenge if as you so no one has manged it so far might turn out a little tricky :wink:

One thing that might help here is I have a very controlled environment. both in terms of target, location of sensors and as I know its going to be one arrow per target every 1-2 minutes that give a very clear signal from the back ground.

Thanks for the input so far.

The first thing I would do is set up a single sensor on a target, and determine whether an arrow strike can be reliably detected, over a range of detector center to strike center distances.

For the timing to be accurate, the leading edge of the signal should be determined to within a few microseconds precision.

Through what medium will the strike sound wave be traveling, and what is the speed of sound in that medium?

I was looking at this.

In terms of detection it can pick it up easly. Even a hard finger flick on the target will give a clear spike across the other side of the target (standard target is 120cm)

In terms of speed the target is going to be hard rubber or straw. As speed of sound in wood is 4,000m/s and both straw and rubber are less dense that will be slower. But assuming worse case it is wood and then it would take 0.00025 seconds to travel one meter, and a micros is 0.000001 seconds. In theory the best case would be a resolution of 0.4cm's (about the width of an arrow)

But this is the best case, however for generaly shooting this is fine as when shooting at 80yards it more about "seeing" where the arrows are going rather than the exact spot, that is always a manual check. And this is the same you see in Olympic archery, where a shot close to a line is always "clarified" at the end of the round so I guess the system they use are also not that much more accurate. You would need extremely high time resolution and the material of the target would have to be extremely consistent any different in density and or tempture would change the the precision.

So I think as long as it can be <= to 1cm it would be fine.

First I will try to get there with a single Ardunio, but it may be that it best to use one mini / nano for each sensor. Use a master device to send a single interrupt to these to sync them. And then have them send the times back to the master to do the calculations.

Not at home now but will add the code when i can.

If my memory serves me Piezo sensors output very large signals when unloaded (or with a light load). Do you really need to use an ADC converter to sense the Piezo voltage?

I purchased a few Seeed studios XIAM modules running a SamD21G for about $6.

THe processor can run at 48 Mhz (have yet to verify the XIAM runs at 48 Mhz.
The processor has up to 5 16 bit timers and numerous interrupt capability.
I would think such a resource could get you the data you need.

That is interesting and had not thought of it like this, it may be that i could use light load to filter out the background noise. And than as you say just treat it like a digital signal. I will look in to that and then use the Arduino to test and can always move up to the resolution of something like the module you suggest.

an analogue signal / stream does allow for better analyse for filtering out the noise, but in sure a high sampling digital rate could also work.

I have a few ESP8266 and other models that run up to 80mhz that i will play with. it only has one ADC pin but if i can use standard digital it will be fine. Assuming it can sample the pins quick enough and the pizo can trigger a digital pin.

Thank you

What I would do, if using piezo sensors on digital pins, is put all of the sensors on one port. Run Timer1 at 16 MHz. Read the port and mask the used bits. If the value has changed, record the time and value.

// put all of the sensors on one port.
// PORTD (Pins 0-7)

void setup()
  // Run Timer1 at 16 MHz.
  TCCR1A = 0;  // WGM 0, No PWM
  TCCR1B = 1;  // EGM 0, prescale = 1

// Allow for bursts up to 256 input changes
uint16_t TimeBuffer[256];
uint8_t InputBuffer[256];
uint8_t Index = 0;
uint8_t PreviousInput = 0;

void loop()
  // Read the port and mask the used bits.
  uint8_t inputs = PIND & 0b00011100; // Pins 2, 3, and 4

  // If the value has changed, record the time and value.
  if (inputs != PreviousInput)
    PreviousInput = inputs;
    uint16_t time = TCNT1;
    TimeBuffer[Index] = time;
    InputBuffer[Index] = inputs;

  // Do we have more than 2 milliseconds of data
  if (Index > 0 && TCNT1 - TimeBuffer[0] > 32000u)
    // Process the data
    for (int i = 0; i < Index; i++)
      Serial.print(TimeBuffer[i] - TimeBuffer[0]);

    // Prepare for the next burst of inputs
    Index = 0;
    PreviousInput = 0;

I'm not sure what you mean by "light load".

Piezo basics:
Here are two ways a Piezo Ceramic disc can be used:

  1. As a Force sensor. Force on a piezo disk will generate a voltage albeit a very small voltage. For a 1N force, a voltage in the order of 200µV will be created at the output terminals.

  2. As an impact sensor (your application). Usually voltages generated are in the 50+ volt range and "ring" at the rate of the natural frequency of the assembly.

So I don't recommend using an A/D to measure the input because the signal is not stable at any point and the amount of variation from "hit" to "hit" will be very high.

Still learning about Piezo operation,

So i was thinking that I would be using A/D to check for a threshold, as I can see that outside there are always background noise coming from the sensor. and was thinking of a statement like

if (A0 pin >= to 100 )
{record time}

But from what i am learning the voltage is more than enough to trigger the digital pins.


thanks for that code, I think i get most of this (I can't claim to be in any way a programer )

So the to if statements make sense and i get that the idea is to check all pins at once but i just wanted to check the line

uint8_t inputs = PIND & 0b00011100; // Pins 2, 3, and 4

I understand the mask part but is "PIND" just telling it to read all PIN values.

So if just pin 2 is high the PIND result would be

00000100 (pin 2 high) & 000011100 (Mask) = 00000100 but if pin 6 was high it would not be match the mask the input would stay at 0 and so not trigger the IF

I have not used PIND and could not find how to use it on line. [EDIT Found this Arduino Reference - Arduino Reference , now that makes sense i was looking up PIND not PORTD :slight_smile: ]

Each port has three registers. Port D has:
DDRD (Data Direction Register) for setting INPUT (0) or OUTPUT (1)
PORTD for writing
PIND for reading

The PIN registers have a special extra feature: If you write a 1 into any bit of the PIN register, that bit in the PORT register will flip.

When the Data Direction is set to INPUT (0) the PORT register is used to turn the built-in pull-up resistor ON (1) or off (0).

Thanks for that, info I will be playing with this tonight.

So been playing with this and the issue i can see is that the signal is not clean.

So a signal that begins close to one sensor will cause a much higher signal, and so trigger the digital pin earlier than the same signal hitting a the sensor further away. So I'm not sure detecting the leading edge is the best way.

Instead using your example John I think i will use an interrupt on the first detect signal. Then capture a burst of 256 data points then calculate the mid point of the signal from each sensor and use this as the timing reference.

I figure capturing a burst means not running the loop between each reading so will be more consistence.

I figure that the speed of sound in wood is ~1500m/s and that the fastest material that a target is going to be made of, and a sampling speed of 1Mhz would give a resolution of 0.15cm which is far more accurate than required.

To get a 0.5cm resolution, A sample point of 200Khz for ~400 samples would allow for a hit any where on the target to reach all sensors. And this should be easy to reach on a 6mhz Ardunio.

The example I provided gives you a timestamp in 16ths of a microsecond for each change in the three inputs. From that, you can calculate the midpoint of each input.


Indeed i was more thinking that if i am going off doing something else in the main loop such as updating a display or any thing else that might delay the retrieving pin values it would be good to after the first input to just keep checking the pins for 2 milliseconds. But yes of course your code shows both then pins go high and low so midpoint is easy to find.

Something like the below to keep the program running in a tight loop while the "event" is taking place.

if (inputs != PreviousInput)

PreviousInput = inputs;
uint16_t time = TCNT1;
TimeBuffer[Index] = time;
InputBuffer[Index] = inputs;

While (TCNT1 - TimeBuffer[0] < 32000u)

    if (inputs != PreviousInput)
           PreviousInput = inputs;
           uint16_t time = TCNT1;
           TimeBuffer[Index] = time;
           InputBuffer[Index] = inputs;


Don't forget to add:

    inputs = PIND & 0b00011100; // Pins 2, 3, and 4

At the top of your 'while' loop.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.