# reading the frequency of a sine wave

I'm trying to build a speedometer for my car, using the output from the cars speed sensor. The speed sensor outputs a sine wave. here it is on an oscilloscope: as I understand it, in order for the arduino to be happy, this signal needs to be between 0 and 5v.

It seems that the peak to peak of this wave is about 8v-ish, which means if I somehow "chop" the negative voltage off of this signal, I should be left with something like 0-4v which is what I need.

How do I go about reading this signal by the arduino? I'd like to keep the circuitry simple due to the harsh(ish) environment that it'll be sitting in.

I've seen some people suggest simply a diode and a resistor, and others suggest op amps, schmitt triggers, and other concepts I'm not strong on.

Any help is very welcome!

• +1 for providing the picture, which is very informative of the shape of your signal.
– Edgar Bonet
Jan 12, 2018 at 8:44
• What Arduino model are you using? Could you please tag your question with the relevant Arduino type?
– Edgar Bonet
Jan 12, 2018 at 9:52
• @EdgarBonet The Arduino really is incidental in this question. It's about conditioning a sinewave for reading by a digital input of an MCU. I'm going to migrate it to EE where it'd be more at home. Jan 12, 2018 at 10:38
• Have you measured/scoped the signal at all engine speeds? Some sensors vary amplitude as well as frequency with changing speed - usually the higher the speed the higher the amplitude of the sine wave. This sensor characteristic can have a significant effect on many common "squaring" circuits. I also see from the photo that this signal has a significant amount of "noise" as witnessed by the numerous overlapping sinusoidal traces in the photo. You will need to quantize this to formualte a squaring circuit which will faithfully translate the frequency of the sine wave to a square wave. Jan 12, 2018 at 16:49

There are several ways. Using an op-amp and a few discrete components, you can get a zero-crossing detector that provides a pulse no larger in amplitude than the positive supply rail to the op-amp, which can be detected with an external interrupt or pin-change interrupt on the Arduino.

Below is one example of a zero-crossing detector. simulate this circuit – Schematic created using CircuitLab

• forgive my ignorance, but how do you determine the value of the resistors and capacitor in this circuit? and what is the purpose of the resistor before the diode?
– Sajid Ansari
Jan 12, 2018 at 3:23
• Values depend on what the expected frequencies you expect to see. The diode blocks the negative-going falling edge, as the detector will detect both rising and falling zero crossings. To measure frequency, you need only to measure the time between like-direction crossings. For resistor and capacitor values, you’re basically adjusting to get a pulse of the desired shape. Experiment. Jan 12, 2018 at 3:33
• I was taught that Op Amps would blow if the input voltage was outside of the supply voltage. I've never tried it, maybe it's not true. Jan 12, 2018 at 7:45
• @MarkSmith There's different op-amps with different limitations. some op-amps will behave what you just described. Read the datasheet for every specific op-amp if you want to be sure. Jan 12, 2018 at 10:48
• Diodes don't really block falling edges, they just limit the range to positive voltage only. Plus, you should've really used a comparator instead of an opamp which just sits in saturation all the time. Jan 12, 2018 at 14:24

From what I see in your scope picture, your signal spans a range from roughly −4 V to about +2.5 V. The main issue with this signal is not its negative part, it's the fact that the positive voltage does not go high enough. Most Arduinos are powered at 5 V and have Schmitt triggers on their digital inputs. The voltage threshold for the pin switching to HIGH is typically about 2.6 V, but this is only a typical value. The only thing the datasheet guarantees is that the pin will read HIGH if its potential is at least 0.6 VCC, i.e. 3 V on a 5 V Arduino.

One option would be to use an Arduino powered at 3.3 V, like the 3.3 V version of the Pro Mini. With this, the digital pins are guaranteed to read HIGH at 2 V. This is, however, borderline for your signal. Your picture shows that some of the oscillations barely reach 2 V at their maximum. Thus I would avoid this option.

The other option is to add a DC offset to the signal in order to have it oscillate around VCC/2 = 2.5 V. This way you have a shifted sine wave that goes roughly from −0.75 to +5.75 V. Then you are guaranteed to hit both the threshold for reading LOW, which is no lower than 0.3 VCC = 1.5 V, and the threshold for reading HIGH. I would use a circuit like this: simulate this circuit – Schematic created using CircuitLab

The voltage divider creates a DC offset of VCC/2. The capacitor on the left allows the AC waveform in. Together, the capacitor and the voltage divider make a high-pass filter with a cutoff frequency of 1/(πRC). If you choose, say, R = 100 kΩ and C = 1 µF, then you have a cutoff at 3.18 Hz. Your signal is about 300 Hz, thus you should be able to measure a signal up to 100 times slower than the one pictured.

As for the negative part of the signal, the Arduino inputs have protection diodes that take care of it. You just have to make sure that the current through those diodes does not exceed 1 mA. This is why the above filter has a current limiting resistor after the voltage divider. A 10 kΩ resistor ensures you can safely overshoot the normal input voltage range by as much as 10 V, i.e. you are safe as long as the shifted sine wave stays between −10 V and +15 V.

Edit: As pointed out by Dmitry Grigoryev's comment, when the car speed is zero this circuit will let the noise through. Having some noise on a digital input is often not an issue, as it is cancelled by the input Schmitt triggers. Your noise amplitude, however seems quite large, and is probably larger that the ~ 0.5 V of hysteresis you have on the Arduino inputs. Then you may detect spurious transitions that will be seen as a finite speed.

If this happens, a simple fix is to change the DC offset in order to move it away from the transition thresholds. For example, a 47 kΩ pull-down combined with a 100 kΩ pull-up will set the DC bias to 1.6 V. You can use more asymmetric resistors if you need to make the DC bias still lower. Note that changing the resistors will also change the cutoff frequency. The 47 kΩ/100 kΩ resistors combined with a 1 µF capacitor will have a cutoff of about 5 Hz.

• When the car stands still, your circuit output will settle at V/2, and the high-pass filter will let through whatever noise will come from the sensor. Jan 12, 2018 at 14:35
• @DmitryGrigoryev: This would be an issue if the noise amplitude is higher than the hysteresis of the MCU's Schmitt triggers. But it would seem that the OP's noise is indeed quite large... Jan 12, 2018 at 15:18
• I don't think what is seen on the scope is noise, more like unstable signal amplitude. But in a real car environment noise can indeed be quite large. There's a reason why CAN bus is using 120Ω termination and not 10kΩ like I2C - that's how it makes sure the signal doesn't pick a few extra volts along the way. Jan 12, 2018 at 15:31

Assuming the speed is proportional to frequency and your plan is to measure it by counting the edges, something as simple as a common-emitter BJT should work fine: simulate this circuit – Schematic created using CircuitLab

Most BJTs can withstand a few volts of reverse voltage with no problem, and 2V of positive voltage are plenty to drive them in saturation. The fact that the signal will be inverted and the duty cycle won't be 50% shouldn't prevent you from counting the edges properly.

• If you added a 1N914 or Schottky diode between the input resistor and base of the transistor, would that prevent higher negative voltages from damaging the transistor and would the circuit still work as intended? Jan 12, 2018 at 21:15
• @VE7JRO A better idea would be to add clamping diodes (to both VCC and GND) if overvoltage protection is needed. Schottky diodes have high leakage currents, so they won't protect anything: for the purpose of a closed transistor such diodes do conduct in reverse. Jan 13, 2018 at 21:01

Once you get the schematic figured out from the excellent answers so far, set up a rising edge interrupt: https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

This will let you execute a bit of code each time the waveform goes from negative to positive. I actually wrote some code to do this a while back in order to measure some PWM stuff ( convenient 0-5v input, none of the filtering issues you are dealing with). I chose to keep a FIFO list of crossing time deltas, and then to get the freq, added them up and bit shifted to divide them and get the avg of those samples. The arduino has no hardware division so that dictated the number of samples in order to be able to do the bitshift division. Without averaging, I found the signal to be very jittery.

You could use a resistor, diode and zener diode like the circuit pictured here: I used "mains" voltage with a step down transformer to test the output of the circuit which looks like this: • Two issues with your circuit: 1. You should add a pull-down resistor at the right, otherwise there is nothing to bring the signal down. On your experiment, the scope's input resistance fills this role, but the Arduino inputs are essentially purely capacitive. 2. The OP's signal peaks at about 2.5 V. Subtract the forward voltage of the 1N914 and you are guaranteed to never hit the LOW → HIGH input threshold.
– Edgar Bonet
Jan 12, 2018 at 9:46
• I completely missed the fact that the voltage peaks at about 2.5V and I agree with you about adding a pull-down resistor. Right now, there are 5 answers to the question and I really like Dmitry's answer the best because the OP wanted to keep the circuitry simple. Jan 12, 2018 at 20:15