hi i am struggling to convert a crank signal for an engine conversion in my car one company makes a signal converter but has not replied to any of my messages for weeks and im trying to create one using an arduino nano.
The engine has a 36-1 reluctor crank trigger, the ecu that runs the dash and the transmission needs a 60-2 5v hall effect signal any help would be greatly appreciated. i have had about 12 failed attempts and need to get the car finished. i also have the option of using a tacho pulse signal that i can set to upto 8 pulses per revolution thru the haltech
Do you have a stable power source for that Arduino based on the car battery?
Hello jaydennewb
Welcome to the best Aruino forum ever ![]()
Post a connection and wiring diagram to see how we can help.
I would suggest buying the signal converter.
i have been trying to buy one but company is in south africa and dont respond to my emails
i have a stable 5v output from a haltech ecu
const byte inputPin = 2; // RPM signal input (interrupt capable)
const byte outputPin = 9; // Simulated crank signal output
volatile unsigned long lastPulseTime = 0;
volatile unsigned long pulseInterval = 100000; // default to 100ms
volatile bool newPulse = false;
void setup() {
pinMode(inputPin, INPUT);
pinMode(outputPin, OUTPUT);
attachInterrupt(digitalPinToInterrupt(inputPin), rpmPulseISR, RISING);
}
void loop() {
if (newPulse) {
noInterrupts();
unsigned long interval = pulseInterval;
newPulse = false;
interrupts();
// Total output: 60 teeth - 2 missing = 58
// Each input = 1/2 revolution -> we output 29 teeth per input pulse
unsigned long toothTime = interval / 29; // microseconds per pulse
for (int i = 0; i < 29; i++) {
if (i == 27 || i == 28) {
// Skip two pulses to simulate missing teeth
delayMicroseconds(toothTime);
continue;
}
digitalWrite(outputPin, HIGH);
delayMicroseconds(toothTime / 2);
digitalWrite(outputPin, LOW);
delayMicroseconds(toothTime / 2);
}
}
}
void rpmPulseISR() {
unsigned long currentTime = micros();
pulseInterval = currentTime - lastPulseTime;
lastPulseTime = currentTime;
newPulse = true;
}
something like this i feel it might be way off tho
I have a few questions.. Why do you think the input pulse is every half revolution?
Also, do you need to synch output TDC with input TDC? I guess not.
What is a RPM range you need to monitor?
What are typical values for lastPulseInterval?
the code looks not too bad, only missing a default state for outputPin.
Note that the toothTime will be truncated at (1) values 0..28 => zero etc
This might effect the timing in (2) and (3) where again a bit is lost
2nd look at the code.
[thinking out loud]
If there is one new pulse (from interrupt) you generate 29 pulses with blocking code.
If the RPM increases the blocking code can miss signals as newPulse is set to true before your blocking code has finished the handling of the previous pulse.
This is especially true for the first pulse as lastPulseTime == 0.
If the first pulse come after N seconds after start then the first pulseinterval will become N million microseconds. ==> so your code will block for N seconds.
You will miss several pulses causing again a too large pulseInterval.
Try the following change to prevent the first long block.
void rpmPulseISR() {
unsigned long currentTime = micros();
if (lastPulseTime ==0) lastPulseTime = currentTime;
pulseInterval = currentTime - lastPulseTime;
lastPulseTime = currentTime;
newPulse = true;
}
Will not solve all (too) long pulses but it should get that initial one removed
To keep the ratio in sync, one could add the 29 crank pulses per tach interrupt to a variable, and then subtract one per toothTime. Or double it and subtract 1 per toothTime/2 as one toggles the outputPin.
Made a quick variation that output only one pulse per loop() iteration.
It checks every iteration if we have to make 29 additional output pulses.
If a new input pulse comes the output pulses are adjusted in duration.
There is a guard statement preventing too long intervals == 100 ms now
(this is in line with what @daveX said in previous post)
Give it a try and let us know
const byte inputPin = 2; // RPM signal input (interrupt capable)
const byte outputPin = 9; // Simulated crank signal output
volatile unsigned long lastPulseTime = 0;
volatile unsigned long pulseInterval = 100000; // default to 100ms
volatile bool newPulse = false;
// NEW VARS
long pulseCount = 0;
unsigned long interval;
unsigned long toothTime;
void setup()
{
pinMode(inputPin, INPUT);
pinMode(outputPin, OUTPUT);
digitalWrite(outputPin, LOW);
attachInterrupt(digitalPinToInterrupt(inputPin), rpmPulseISR, RISING);
}
void loop()
{
// ISR has generated new pulse
// so we need to generate 29 output pulses.
if (newPulse)
{
noInterrupts();
newPulse = false;
interval = pulseInterval;
interrupts();
// guard that interval may not be too large
// must be tuned to reasonable value.
if (interval > 100000) interval = 100000;
toothTime = interval / 58; // microseconds per pulse
// update the output pulse counter
// need to generate 29 more pulses for every input pulse
pulseCount += 29;
}
// generate all output pulses, one per iteration.
if (pulseCount > 0)
{
pulseCount--;
int pulse = pulseCount % 29;
if (pulse == 27 || pulse == 28)
{
delayMicroseconds(toothTime);
}
else
{
digitalWrite(outputPin, HIGH);
delayMicroseconds(toothTime / 2);
digitalWrite(outputPin, LOW);
delayMicroseconds(toothTime / 2);
}
}
}
void rpmPulseISR()
{
unsigned long currentTime = micros();
pulseInterval = currentTime - lastPulseTime;
lastPulseTime = currentTime;
newPulse = true;
}
const byte inputPin = 2; // RPM signal input (interrupt capable)
const byte outputPin = 9; // Simulated crank signal output
volatile unsigned long lastPulseTime = 0;
volatile unsigned long pulseInterval = 100000; // default to 100ms
volatile bool newPulse = false;
// NEW VARS
long pulseCount = 0;
unsigned long interval;
unsigned long toothTime;
void setup()
{
pinMode(inputPin, INPUT);
pinMode(outputPin, OUTPUT);
digitalWrite(outputPin, LOW);
attachInterrupt(digitalPinToInterrupt(inputPin), rpmPulseISR, RISING);
}
void loop()
{
// ISR has generated new pulse
// so we need to generate 29 output pulses.
if (newPulse)
{
noInterrupts();
newPulse = false;
interval = pulseInterval;
interrupts();
// guard that interval may not be too large
// must be tuned to reasonable value.
if (interval > 100000) interval = 100000;
toothTime = interval / 58; // microseconds per pulse
// update the output pulse counter
// need to generate 29 more pulses for every input pulse
pulseCount += 29;
}
// generate all output pulses, one per iteration.
if (pulseCount > 0)
{
pulseCount--;
int pulse = pulseCount % 29;
if (pulse == 27 || pulse == 28)
{
delayMicroseconds(toothTime);
}
else
{
digitalWrite(outputPin, HIGH);
delayMicroseconds(toothTime / 2);
digitalWrite(outputPin, LOW);
delayMicroseconds(toothTime / 2);
}
}
}
void rpmPulseISR()
{
unsigned long currentTime = micros();
pulseInterval = currentTime - lastPulseTime;
lastPulseTime = currentTime;
newPulse = true;
}
ok so i input this and now the gauge has movement but it a little erratic and inaccurate i will post a video to show
ok cant post video but if i change the number of input pulses to give more reference points i was thinking about making it 10 pulses per revolution. On top of this if it is connected before starting it doesnt work but if connected after running it works... sort of.
or maybe simplify back to one pulse per revolution???
That points towards an initialization problem, possibly the first interval is too large.
// guard that interval may not be too large
// must be tuned to reasonable value.
if (interval > 100000) interval = 100000; <<<<< change these to 10 000
ok ive move up to a teensy 4.1 for better stability at high rpm but feel like i am goin backward somehow. I can generate a pretty good rpm sweep but get nothing when an actual input pin is applied.
// Outputs 60-2 crank signal and one cam signal (split three ways externally)
#include <FlexIO_t4.h>
// Pin configuration
const int crankOutPin = 10; // Crank output (FlexIO pin)
const int camOutPin = 12; // Cam output (single, split externally)
const int rpmInputPin = 2; // RPM input (one pulse per engine cycle)
const int ledPin = 13; // Built-in LED for activity
// Constants
const int crankTeeth = 60;
const int missingTeeth = 2;
const int pulsesPerCycle = 2; // Two crank revolutions per engine cycle
// Timing
volatile bool triggerCrank = false;
FlexIOHandler *flexIO = &FlexIO1;
FlexIOSignal timer;
FlexIOSignal shifter;
void rpmPulse() {
triggerCrank = true;
}
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(camOutPin, OUTPUT);
pinMode(rpmInputPin, INPUT);
attachInterrupt(digitalPinToInterrupt(rpmInputPin), rpmPulse, RISING);
// Initialize FlexIO for crank signal
flexIO->begin();
pinMode(crankOutPin, OUTPUT);
digitalWrite(crankOutPin, LOW);
// Cam signal default LOW
digitalWrite(camOutPin, LOW);
}
void loop() {
if (triggerCrank) {
triggerCrank = false;
// Flash LED for visual feedback
digitalWrite(ledPin, HIGH);
// Output 2 full 60-2 crank cycles (58 pulses per cycle)
for (int rev = 0; rev < pulsesPerCycle; rev++) {
for (int i = 0; i < crankTeeth - missingTeeth; i++) {
digitalWrite(crankOutPin, HIGH);
delayMicroseconds(200); // Adjust for RPM
digitalWrite(crankOutPin, LOW);
delayMicroseconds(200);
// Cam pulse once per crank revolution (twice per engine cycle)
if (i == 5) digitalWrite(camOutPin, HIGH);
if (i == 15) digitalWrite(camOutPin, LOW);
}
}
digitalWrite(ledPin, LOW);
}
}
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.