I am working on creating an LED display panel for showing speed, rpm, fuel, neutral, etc. on my motorcycle. I have some background in coding (specifically processing), however I lack experience in the electrical engineering department....I have already worked out a display (two 8x8 neopixel RGB LED panels) and have the panels displaying numbers, turn signals, etc.
The first problem that I want to tackle is to "read" the RPM from the crank position sensor (CPS). From what I understand (and have researched for my bike) the signal could either be 1) a 5v square wave with frequency representing RPM, or 2) variable voltage ac, with frequency representing RPM. (a sine wave of some type)
I believe I understand conceptually what needs to happen in order to get the signals to be proper and safe for the arduino:
5v square wave - remove negative voltages, positive voltage regulated to 5v in order to protect the board from spikes.
AC sine wave - the wave needs to be squared, the negative voltages removed, and the positive voltage regulated.
These signals could then be read via a digital pin on the board. Does this sound correct? I figure I would make sure that I grasp the concept of what needs to happen before I get into the specifics of the circuitry and components. I am planning on borrowing an oscilloscope from work this week to get more concrete info on the signals being generated by my bike. Here's to hoping I don't blow anything up. =P
I hooked up the oscilloscope to the crank position sensor (CPS) and got two readings, the first at roughly 1400rpm (idle) and the second at roughly 5000rpm. (my bike redlines at 14,000)
I also hooked it up to the turn signals and the speed sensor. I can't remember which one I snapped the image of the oscilloscope from, but they both looked similar, 12 volt square wave if I am reading the scope properly.
The images I attached show:
rpm reading at 1400rpm
rpm reading at 5000rpm
speed or turn signals...can't remember which
I'm not sure exactly where to go from here. I've tried to research as much as I can, and I'll try and find time to put up my thoughts on a basic schematic. If a circuit to get the data from these sensors to the arduino is relatively simple, it would be great to see someone's suggestion that I could study to understand how it would work. Or at least terms or circuit types that I could research.
The speed / turn signal is no problem at all, you just need to use a resistor divider network to reduce the signal from 0 to 12 V to 0 to 5V and then you can feed it into any of the digital pins.
I presume that the frequency of the speed / turn signal increases with speed.
You can use the pulsein() function to tell you the width of the pulse and then the reciprocal of that is proportional to the speed (you'll need to work out the scale factor to convert 1/pulse width to speed in mph / kph
You may be able to just use a resistor divider on the crank signal, but I'm not sure if it goes below zero V or if its a modified sine wave that never goes below zero.
Assuming it never goes below zero, the resistor divider network would scale it to a range 0 - 5V, but it would probably be too noisy to use as a trigger or for pulsein. You probably need to feed it through some "signal conditioning".
I single transistor may be enough, but you are probably better off using an opamp
Perhaps if you use a suitable voltage divider and a diode to take out any negative voltages and feed the result into a Schmidt trigger chip you will get a suitable signal to feed the Arduino. And at least you will only ruin the Schmidt trigger chip. If my reference book isn't too old a 74HC14 or 40106B chip would do the job.
The top two traces look like the output of an MVR sensor, and these have the characteristic that the generated voltage and current varies substantially with revs. Usually, this means variable or switchable gain to get enough sensitivity at very low rpm, while staying within TTL levels at higher rpm.
The period of your waveform for the 1400 RPM plot is 5 ms. If frequency equals RPM then your engine is at 200 RPM. If frequency equals RPS then your engine is at 12,000 RPM. On the other hand...
1400 RPM = 23.3 RPS
200 / 23.3 = 8.58 teeth
Can you count the number of teeth on the wheel? Looks like it is either 8 or 9.
I've put together two little schematics, one for the sine wave and one for the square. The idea:
sine wave - resistors to bring the voltage down, zener diode to prevent negative voltages, and schmitt trigger to square the signal.
square wave - resistors to bring down the voltage, zener diode to prevent negative voltages.
These seemed simple enough to grasp and should protect the board. Anything glaringly wrong with what I drew up? I was a little unsure of how to scale the voltage AC sine wave. First off, am I reading it correctly (according to the images I posted a few posts back) that at 1400rpm the wave peaks around 12 volts, and at 5000rpm it peaks at 30? 30 seems high...but I'm no electrical engineer. Also the fact that the voltage varies, means that the "on" and "off" or "high" and "low" (forgive my use of unscientific terms) parts of the wave will vary...would the schmitt trigger account for this? I know schmitt triggers are a comparator with hysteresis (there are my special terms for the day), so it somehow uses the past value to determine the current one...
So I was able to get a signal into the arduino that worked! The RPM number that I was calculating was very close the bike's actual RPM, but I'm wondering if it is my code that could be written better to get a more accurate RPM.
The circuit was: Sine wave > Zener clipper circuit > Schmitt Trigger > Arduino interrupt pin.
I basically wrote a subroutine that increased a counter every time a pulse was received.
In the main loop I just noted the time and counter number, waited a second, and noted the time and counter number, subtracted the counter, did some math to figure out RPM...it wasn't terribly accurate, definitely close to the bikes tach, but I wonder if it could be closer.
I am wondering a few things:
What's the best way to keep track of time? Making a note of when the pulse happens INSIDE the subroutine? Using delay() vs millis()? Also resetting the counter...I probably can figure it out I haven't spent much time thinking about it.
I read somewhere that I might want to disable the interrupt while doing the math in the main loop...
Regarding the baud rate for serial communication, 9600 vs 576000 etc...I'm assuming that affects the speed of the communication? Does this slow down the rest of the code?
volatile long counter = 0;
int count1;
int count2;
void setup()
{
attachInterrupt(0, countUp, RISING);
Serial.begin(9600);
Serial.println("heyo!");
}
void loop()
{
count1 = counter;
/// wondering if using delay is slower/worse than marking the time using "variable = millis()"
delay(1000);
count2 = counter - count1;
count2 = (count2 * 60) / 8;
Serial.println(count2);
}
void countUp()
{
counter = counter++;
/// this is a probably dumb way to reset the counter, I just did it quick...
/// I'm assuming I can't let the counter count up forever...
if (counter > 10000)
{
// maybe keep track of the "what time it is" for each pulse in this subroutine?
counter = 0;
}
}
The count1 and count2 global variables are not needed.
You should suspend interrupts around access to counter in the main context.
The best way IMO to detect changes in a volatile variable is to keep a (non-volatile) copy of the value that you read previously, and use subtraction to calculate how much it has changed since then. This keeps the time that interrupts are disabled for to the minimum. Make sure you use unsigned variables for the counts.
For accurate timing, you should use millis() to determine when it is time to make the next reading rather than using a fixed delay.
volatile unsigned long counter;
unsigned long speed;
...
void updateSpeed()
{
static unsigned long lastCount;
static unsigned long lastTime;
const unsigned long INTERVAL = 1000; // ms
if(millis() - lastTime >= INTERVAL)
{
lastTime += INTERVAL;
noInterrupts();
unsigned long currentCount = counter;
interrupts();
speed = currentCount - lastCount;
lastCount = currentCount
}
}
Thanks for the advice, the code worked really well...after I fixed the circuit I had messed up...
I adjusted the math to display the number of pulses every second, just so I could see how close I was by comparing it to the number reading on the bike itself.
The bike at idle runs at ~1500rpm: (1500rpm / 60 sec) * 8 teeth = 200 pulses
At first I was getting upward of 375 pulses per second at idle, so I hooked up my oscilloscope to compare the original CPS signal vs the Schmitt trigger output. Alas, my circuit was clearly causing the Schmitt trigger to see more than 8 pulses per revolution, so after removing one of my zener diodes, I had both signals matching! Once I did this, however I was only down to ~280 pulses per second...still almost 80 pulses more than it should be.
I am wondering if there is a better way to check to see exactly test to see if it's something in the programming that is causing the variance vs something wrong with the signal. I just realized I could probably set the interrupt to trigger a pin OUT, and hook that up to the o-scope to compare to the signal going in...
I think the schmitt trigger signal might be inverted -I took a comparator from Radio Shack and followed directions to create a schmitt trigger...I think could invert it but I'll probably wait until the actual schmitt trigger IC's that I ordered come in the mail...
I can't remember if I posted it here, but I'll attach a picture of the gear that triggers the sensor. One of the teeth is offset, so you can see a clear pattern in the waveform.
So I've been re-checking my sine wave to square wave circuit, and I had a few questions about grounding. I've attached a schematic of my circuit (let me know if anything looks terribly wrong) and a screenshot about the CPS from my bike's service manual. There are two leads coming from the CPS sensor, a positive and negative. I assumed that I needed to tie all three of the grounds together...
By connecting back to the negative lead from the CPS sensor, am I successfully grounding the circuit?
Do I need to ground it to the arduino? (that seems dangerous...)
Are there two separate sets of grounds that I need to have? (one pre-zener diode and one post-zener diode...)