I'm not adding any of that to this code. Not from speeduino or anything.
Coding my own aftermarket ECU
Re: Coding my own aftermarket ECU
Don't stress specific units.
Re: Coding my own aftermarket ECU
Not doing it. Maybe in a separate project, not this one.hjtrbo wrote: ↑Thu Jun 12, 2025 9:35 pm Untested, no lookups and no floats. For a common 36-1 trigger wheel
Code: Select all
#include <stdio.h> #include <stdint.h> #define NUM_TEETH 36 #define MISSING_TOOTH_GAP_MULTIPLIER 3 #define TOOTH_DEGREES_FIXED 40 // 10° * 4 #define ANGLE_SCALE 4 // Resolution is 0.25 crank degrees #define MAX_ANGLE (360 * ANGLE_SCALE) uint32_t last_tooth_time = 0; uint32_t delta_time = 0; uint16_t last_known_angle_qdeg = 0; // in 0.25° units uint16_t angle_per_us_qdeg = 0; // angle per µs in 0.25° units uint8_t synced = 0; uint8_t tooth_number = 0; // High speed timer module ISR void on_tooth_detected(uint32_t timestamp_us) { static uint32_t last_delta = 0; uint32_t now = timestamp_us; delta_time = now - last_tooth_time; last_tooth_time = now; if (!synced) { if (last_delta > 0 && delta_time > (last_delta * MISSING_TOOTH_GAP_MULTIPLIER)) { synced = 1; tooth_number = 0; last_known_angle_qdeg = 0; printf(">> Sync achieved\n"); return; } last_delta = delta_time; return; } // Calculate angle per microsecond in fixed-point (0.25° units) angle_per_us_qdeg = TOOTH_DEGREES_FIXED / delta_time; last_known_angle_qdeg = (tooth_number * TOOTH_DEGREES_FIXED); if (last_known_angle_qdeg >= MAX_ANGLE) last_known_angle_qdeg -= MAX_ANGLE; printf("Tooth %d: base angle = %u (x0.25°)\n", tooth_number, last_known_angle_qdeg); tooth_number = (tooth_number + 1) % NUM_TEETH; } uint16_t get_current_crank_angle_qdeg(uint32_t current_time_us) { uint32_t time_since_last = current_time_us - last_tooth_time; uint32_t interpolated_angle = last_known_angle_qdeg + (time_since_last * angle_per_us_qdeg); if (interpolated_angle >= MAX_ANGLE) interpolated_angle -= MAX_ANGLE; return (uint16_t)interpolated_angle; }
Also, my code doesn't speak angles. It coincidentally gets an angle reference every 15 degrees, but that's it.
Don't stress specific units.
Re: Coding my own aftermarket ECU
The good news is, the interpolation code isn't keeping it from holding together at even 75,000 RPM. Shaky, but it's doing it. Need to add 3 more of the same for each table.
I think I'll add a rolling average for RPMtime like the MAP sensor has. Would like to try the signal from a running engine, but I doubt I will. Maybe the wheel mounted to an electric motor.
I think I'll add a rolling average for RPMtime like the MAP sensor has. Would like to try the signal from a running engine, but I doubt I will. Maybe the wheel mounted to an electric motor.
Last edited by AngelMarc on Thu Jun 12, 2025 10:07 pm, edited 1 time in total.
Don't stress specific units.
Re: Coding my own aftermarket ECU
Mount it to an angle grinder 

Re: Coding my own aftermarket ECU
Anything LS will be a dog of a thing without them. I've tuned enough to know you'll need at least something to compensate.
Re: Coding my own aftermarket ECU
I look forward to showing how unneeded a lot of that is.
Don't stress specific units.
Re: Coding my own aftermarket ECU
Ha. Maybe if it was a weight balanced design. They are not by default.
Don't stress specific units.
Re: Coding my own aftermarket ECU
There, stable. Rolling average identical to MAP sensor code.
Still allows TerpD = 0 for some reason.
Spoke too soon. Forgot to change back some test constants.
Still allows TerpD = 0 for some reason.
Code: Select all
#define CRANK_SENSOR_PIN 2 // Pin connected to crankshaft sensor
#include "TuneArrays.h"
//PICO_FLOAT_IN_RAM=1;
//cmake -DPICO_COPY_TO_RAM=1;
//__attribute__((section(".ramfunc"))) void loop()
enum PinState { WAITING_FOR_ON, WAITING_FOR_OFF };
PinState pinState = WAITING_FOR_ON;
unsigned long previousToggleTime = 0;
uint8_t adcIndex = 0;
bool pulseActive = false;
bool triggerPulse = false;
// Pin 11 (pattern1)
bool pulseActive11 = false;
bool triggerPulse11 = false;
unsigned long pulseStartTime11 = 0;
unsigned long currentOnDelay11 = 1000;
unsigned long currentOffDelay11 = 1000;
// Pin 4 (pattern0)
bool pulseActive4 = false;
bool triggerPulse4 = false;
unsigned long pulseStartTime4 = 0;
unsigned long currentOnDelay4 = 1000;
unsigned long currentOffDelay4 = 1000;
volatile int readings[24] {4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095, 4095}; // Circular buffer
int bufIndex = 0; // Buffer index
long total = 4095L * 24; // Set this to the initial sum
volatile int readings2[24]; // Circular buffer
int bufIndex2 = 0; // Buffer index
long total2 = 0L * 24; // Set this to the initial sum
volatile unsigned long lastTime = 0;
volatile unsigned long pulseHighTime = 0;
volatile unsigned long pulseLowTime = 0;
volatile uint8_t signalHistory = 0;
volatile bool newSample = false;
const uint8_t pattern0 = 0b00001111;
const uint8_t pattern1 = 0b01111101;
uint8_t avg8bit = 0;
void handleCrankSignal() {
unsigned long currentTime = micros();
if (digitalRead(CRANK_SENSOR_PIN) == HIGH) {
pulseHighTime = currentTime - lastTime;
} else {
pulseLowTime = currentTime - lastTime;
uint8_t bit = (pulseHighTime > pulseLowTime) ? 0 : 1;
signalHistory = ((signalHistory << 1) | bit) & 0xFF;
if (signalHistory == pattern1) triggerPulse11 = true;
if (signalHistory == pattern0) triggerPulse4 = true;
//digitalWrite(4, (signalHistory == pattern0) ? HIGH : LOW);
//digitalWrite(11, (signalHistory == pattern1) ? HIGH : LOW);
newSample = true; // Signal to loop() to read ADC
}
lastTime = currentTime;
}
void setup() {
analogReadResolution(12);
pinMode(4, OUTPUT);
pinMode(11, OUTPUT);
digitalWrite(11, LOW);
previousToggleTime = micros();
pinMode(A0, INPUT);
pinMode(CRANK_SENSOR_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(CRANK_SENSOR_PIN), handleCrankSignal, CHANGE);
//Serial.begin(115200);
}
void loop() {
if (newSample) {
newSample = false;
unsigned long RPMTime = pulseHighTime + pulseLowTime;
total2 -= readings2[bufIndex2];
int newVal2 = RPMTime;
readings2[bufIndex2] = newVal2;
total2 += newVal2;
bufIndex2 = (bufIndex2 + 1) % 24;
int RPMTimeavg = total2 / 24;
// --- ADC rolling average (12-bit to 7-bit scale) ---
total -= readings[bufIndex];
int newVal = analogRead(A0);
readings[bufIndex] = newVal;
total += newVal;
bufIndex = (bufIndex + 1) % 24;
int avg12bit = total / 24;
adcIndex = (avg12bit >> 5) - 1; // () for simple order of opperation, minus 1 for avoiding index wrapping
RPMTimeavg = constrain(RPMTimeavg, 134, 125000);
// Calculate IndexFull in fixed-point 30.2 format (<<2 = 2 fractional bits)
uint32_t IndexFull_fixed = (uint32_t)(((uint32_t)2500000 << 4) / (RPMTimeavg * 600UL));
uint8_t Frac_fixed = IndexFull_fixed & 0x0F;
uint32_t IndexWhole = IndexFull_fixed >> 4;
IndexWhole = constrain(IndexWhole, 0, 31);
uint8_t IndexPlusOne = IndexWhole + 1;
uint32_t TerpA = Iend[20][adcIndex];
uint32_t TerpB = Iend[21][adcIndex];
int32_t Diff = (int32_t)TerpB - (int32_t)TerpA;
uint32_t TerpD = TerpA + ((Diff * Frac_fixed) >> 4);
// --- Update delay values from tables ---
currentOnDelay11 = Dstart[IndexWhole][adcIndex];
currentOffDelay11 = Dend[IndexWhole][adcIndex];
currentOnDelay4 = Istart[IndexWhole][adcIndex];
//currentOnDelay4 = Iend[IndexWhole][adcIndex];
currentOffDelay4 = TerpD;
}
// --- Check for new pulse triggers from crank handler ---
if (triggerPulse11) {
triggerPulse11 = false;
pulseStartTime11 = micros();
pulseActive11 = true;
}
if (triggerPulse4) {
triggerPulse4 = false;
pulseStartTime4 = micros();
pulseActive4 = true;
}
// --- Pulse timing for pin 11 (pattern1) ---
if (pulseActive11) {
unsigned long now = micros();
unsigned long elapsed = now - pulseStartTime11;
if (elapsed < currentOnDelay11) {
digitalWrite(11, LOW);
} else if (elapsed < currentOnDelay11 + currentOffDelay11) {
digitalWrite(11, HIGH);
} else {
digitalWrite(11, LOW);
pulseActive11 = false;
}
}
// --- Pulse timing for pin 4 (pattern0) ---
if (pulseActive4) {
unsigned long now = micros();
unsigned long elapsed = now - pulseStartTime4;
if (elapsed < currentOnDelay4) {
digitalWrite(4, LOW);
} else if (elapsed < currentOnDelay4 + currentOffDelay4) {
digitalWrite(4, HIGH);
} else {
digitalWrite(4, LOW);
pulseActive4 = false;
}
}
}
Don't stress specific units.
Re: Coding my own aftermarket ECU
The 0 value output is a separate thing. I probably noticed it before and didn't pay no mind or something. Does need sorted out.
Well, think I've had enough minimum setting simulation failures for today.
Well, think I've had enough minimum setting simulation failures for today.
Don't stress specific units.
Re: Coding my own aftermarket ECU
Was stabbing in the dark with includes.
Gives a drop down of options.
Seen
Looking for those files now.
File explorer has been searching the Arduino15 folder for about 20 minutes now...
Gives a drop down of options.
Seen
Code: Select all
#include <mbed/platform/bare_metal/
Looking for those files now.
File explorer has been searching the Arduino15 folder for about 20 minutes now...
Don't stress specific units.