I'm trying to run a tachometer from the W terminal of an alternator. The Alternator provides a =/- sine wave. I found this thread: Alternator 'W' Phase Terminal AC to Arduino and used the circuit depicted. It works, up to around 1300rpm, when the input pulses drop to zero. I'm wondering if it's a problem with noise in the circuit, and if there's another way to convert the output?
Is there an AC output (and what is its frequency) when the RPM crosses 1300? Could be that the frequency at that speed is too high for the arduino?
The output from the W terminal is about 7v RMS.
Here's what code I'm using (All the canbus stuff is working):
#include <SPI.h>
#include "mcp2518fd_can.h"
// pins for CANBed FD
const int SPI_CS_PIN = 17;
const int CAN_INT_PIN = 7;
mcp2518fd CAN(SPI_CS_PIN); // Set CS pin
const int pulsePin = 9;
long pulseHigh;
long pulseLow;
long pulseTotal;
float freq;
float rpm;
int rpmscale;
int poles = 14;
void setup()
{
pinMode(pulsePin, INPUT);
Serial.begin(115200);
while (0 != CAN.begin(CAN_250K_1M)) {
Serial.println("CAN init fail, retry...");
delay(100);}
}
void loop()
{
pulseHigh = pulseIn(pulsePin,HIGH);
pulseLow = pulseIn(pulsePin,LOW);
pulseTotal = pulseHigh + pulseLow;
freq = 1000000/pulseTotal;
rpm = (60*freq)/poles;
rpmscale = 8*rpm;
unsigned char msgrpmscale[8] = {0xFF, 0xFF, 0xFF, lowByte(rpmscale), highByte(rpmscale), 0xFF, 0xFF, 0xFF};
CAN.sendMsgBuf(0x0CF00417, 1, 8, msgrpmscale);
delay (100);
Serial.print("pulseLow ");
Serial.println(pulseLow);
Serial.print("pulseHigh ");
Serial.println(pulseHigh);
Serial.print("pulseTotal ");
Serial.println(pulseTotal);
Serial.print("rpm ");
Serial.println(rpm);
Serial.print("rpm scale ");
Serial.println(rpmscale);
}
This is the serial output. It starts with high RPM and then drops to idle, and starts reading pulses again.
pulseLow 0
pulseHigh 0
pulseTotal 0
rpm -4.29
rpm scale -34
pulseLow 0
pulseHigh 0
pulseTotal 0
rpm -4.29
rpm scale -34
pulseLow 2225
pulseHigh 377
pulseTotal 2602
rpm 1645.71
rpm scale 13165
pulseLow 0
pulseHigh 0
pulseTotal 0
rpm -4.29
rpm scale -34
pulseLow 24
pulseHigh 8935
pulseTotal 8959
rpm 475.71
rpm scale 3805
pulseLow 244
pulseHigh 3106
pulseTotal 3350
rpm 1277.14
rpm scale 10217
pulseLow 552
pulseHigh 3299
pulseTotal 3851
rpm 1110.00
rpm scale 8880
pulseLow 796
pulseHigh 3518
pulseTotal 4314
rpm 990.00
The data sheet for the 4N35 shows a load resistor of 100 ohms at 10 volts. WAY less than what you have. Try 100 Ohms.
Is pin 9 normally pulled high or pulled low?
The circuit is just like as depicted. I don't know enough to know if that might be the problem?
IF you don't know, then likely pin 9 is FLOATING and eventually gets charged just like a capacitor does and then that circuit will not work any longer. Why this circuit? Why not use the one that supposedly works?
That is the one which works in the other thread. It sounds like I need to add a pull down resistor and see if that makes a difference.. Thanks for the suggestion, I appreciate it!
That (dangerous) circuit only protects the pin when the Arduino is on before voltage to the W-terminal is applied. When the Arduino is off, max pin voltage is 0.3volt. The zener does not protect in that case.
Use Schottky clamping diodes, not useless zeners.
Leo..
What you need is a proper comparator instead of that zener scheme. For any pull down resistor, unless you make it small enough the capacitor will charge to 5V.
You have a delay and a lot of prints , all of which take time - maybe this stops your circuit responding at higher frequencies ( rpm).
I would have used an opto isolator as a basic for the input circuit on the input to give protection to the Arduino and prevent any parasitic power routes ,
why not use an ignition signal
- if your car has an ECU that may be readily available
The alternator may not run at crank speeds
The prints I added to get more information on why it wasn't working. It's an old diesel with no ECU or ignition system.
I think there's a good consensus here that the Zener circuit needs to go. I'll try an opto isolator. It's definitely a learning experience, and thank you all for the help, I really appreciate it!
I know it can be done. I once had a 1982 Chevrolet V8 Diesel pickup. I added an after-market tachometer that was connected exactly as you are trying. It worked very well, but I have no clue as to the internals of the meter. It must have been designed specifically to display the engine RPM based on the alternator signal.
It was a common method to get a tach signal even for some factory gauges. Most had dip switches on the back to adjust the ratio based on the number of poles of the alternator. My project uses a gauge that can only display based on a CANBUS message, which is what I'm using the Arduino for. So far, that part appears to be working, as the gauge is showing the rpm that the Arduino is calculating, for lower RPM's when it works. I'll find out how badly I messed that up after I get the input working.
An update. I'm using a 4N35. Initially I tried using a 100Ohm resistor as on the datasheet, but this only pulled the output voltage to about 2.8v rather than 0. I replaced this with a 1k resistor, which worked.
Hooked up to the RPM gauge, and the system works, with one small flaw. Occasionally, maybe 1 in 50, the rpm is miss calculated, as if a pulse is being missed. This causes the gauge to receive either a 0rpm or much higher rpm message, and it will cause the needle to twitch or turn on the low rpm warning light before it receives the next message that is correct. I could fix this within the code very easily, and I generally think this is a good idea to make it reject errors, but I'd rather prevent them from happening. Any idea if there's something going wrong?
Maybe collecting some RPM values into a buffer and averaging them might help.
Basically something like this:
#define COUNT 10
class Buffer
{
private:
double _values[COUNT];
short _count = 0;
public:
bool IsFull()
{
return (_count >= COUNT);
}
void Reset()
{
_count = 0;
}
void Push(double val)
{
if (_count < COUNT)
{
_values[_count] = val;
_count++;
}
}
double GetAverage()
{
double sum = 0;
for(int i = 0; i < COUNT; i++)
{
sum += _values[i];
}
return (sum / COUNT);
}
}
Buffer buf;
//In loop
void loop()
{
if (buf.IsFull())
{
double avg = buf.GetAverage();
buf.Reset();
//TODO: Display avg
}
else
{
double val = 0; //TODO: actually read from sensor.
buf.Push(val);
}
}
I've adjusted so that it's reading two pulse cycles instead of one. This means that if one is dropped, it won't report zero, or a huge divergence to the gauge. Then I've increased the rate, so the gauge receives the next correct message much more quickly, and the needle has no time to move. It might be a workaround, but it does work.
Great to know that its working.
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.