To successfuly start the car and for every aux item in the car to work,, RPM signal is needed. In the case of 1.6 16V ICE engine this signal is picked up at the transmission flywheel. On the flywheel is toothed belt with 60 teeth pattern. This pattern is broken by two teeth missing at the RPM count starting point. This helps determine the 0 position of the flywheel and the engine Top dead Center.
This particular configuration is called 60-2-2. 60 imaginary pulses with 56 actual pulses and 2 periods of gap and 2 periods of long pulse.
How this works you can see here:
https://www.tekniwiki.com/overview/how- ... t-sensors/
For our use we only need about 800RPM to incite ECU that engine is running. This will be done in code and transmitted on the same lines via PWM signal from VCU.
The code has been prepared so that:
Teensy 4.1 Arduino code to simulate a 60-2-2 crank wheel pattern using the Metro library.
57 evenly spaced pulses (1-tooth duration),
1 gap (1-tooth duration),
1 longer pulse (2-teeth duration),
1 gap (2-teeth duration),
Then repeat.
#include <Arduino.h>
#include <Metro.h> // Metro timer library
#include <FlexCAN_T4.h> // Teensy CAN library
const int tachoPin = 5; // Output pin for crank signal
const int teethCount = 60; // Total number of teeth (including missing ones)
const float rpm = 800.0; // Target engine RPM
const float revPerSec = rpm / 60.0;
const float revPeriodMs = 1000.0 / revPerSec; // 1 revolution duration in ms
const float toothPeriodMs = revPeriodMs / teethCount; // Time per tooth in ms
volatile int currentTooth = 0;
Metro tachoMetro(toothPeriodMs);
void setup() {
pinMode(tachoPin, OUTPUT);
digitalWrite(tachoPin, LOW);
Serial.begin(115200);
Can0.begin();
Can0.setBaudRate(500000);
Can0.setMaxMB(16);
Can0.enableMBInterrupts();
Can0.mailboxStatus();
}
void loop() {
Can0.events(); // Handle CAN events
tacho(); // Simulate crank pulse pattern
}
void tacho() {
if (tachoMetro.check()) {
// Standard 57 pulses
if (currentTooth < 57) {
digitalWrite(tachoPin, HIGH);
delayMicroseconds(100); // 100 µs pulse
digitalWrite(tachoPin, LOW);
}
// Tooth 57: 1-tooth gap — no pulse
else if (currentTooth == 57) {
// No pulse: simulate 1-tooth gap
}
// Tooth 58: single long pulse (2-tooth duration)
else if (currentTooth == 58) {
digitalWrite(tachoPin, HIGH);
delayMicroseconds(200); // Double pulse duration to simulate 2 teeth
digitalWrite(tachoPin, LOW);
}
// Tooth 59: 2-tooth gap — no pulse
else if (currentTooth == 59) {
// No pulse: simulate 2-tooth gap
}
currentTooth++;
if (currentTooth >= teethCount) {
currentTooth = 0; // R
I get a good 57 pulses per one "rotation" with a missing gap where the cycle repeats. Now i use a simple transistor module with 2.7kohm pullup so it will amplify signal to 12V.
Since the inductive sensor reads signal passing below 0V I needed to add one 1uf film cap in series which inverts the returning signal so what will be seen by the car will not be square wave, but rather pointed signal that goes down through the 0V...

- 4.png (79.02 KiB) Viewed 1763 times
This will give the ECU the appearance of a 60-2-2 wheel, synchronized to 800 RPM.
Connection to the former RPM connector... Since the sensor is induction type receiver needs to see a signal passing through zero to count it. My 50% PWM signal will not move the needle directly.
I solved the problem to cross the 0V line to trigger the count. I simply added one 1uF film cap in series with signal (!).

- image.png (24.52 KiB) Viewed 661 times
First i tried to connect RPM signal to one wire of the connector and the second one to GND. It moved the needle a little, but not enough. Then i simply removed one side of connector and car came to life.