Coding my own aftermarket ECU
Re: Coding my own aftermarket ECU
ChatGPT is like the worst customer support.
"Oh, this looks promising... oh, nevermind."
"Oh, this looks promising... oh, nevermind."
Don't stress specific units.
Re: Coding my own aftermarket ECU
OK, there's something 100% ChatGPT that functions. Well, it specified pin 32, which I don't see, and caused boot looping when uploaded. Just changed it to pin 15 that I already had plugged in (random choice from earlier nonsense).
Hooked it up to my open source signal generator. Just directly, since they're both 3.3 volts.
Code: Select all
#define CRANK_SENSOR_PIN 15 // Pin connected to the crankshaft sensor
volatile unsigned long lastTime = 0; // Last pulse time
volatile unsigned long pulseHighTime = 0; // Time of high phase
volatile unsigned long pulseLowTime = 0; // Time of low phase
void IRAM_ATTR handleCrankSignal() {
unsigned long currentTime = micros(); // Use micros() for high precision timing
// If it's the rising edge (start of a new pulse)
if (digitalRead(CRANK_SENSOR_PIN) == HIGH) {
pulseHighTime = currentTime - lastTime; // Time the high phase
} else {
pulseLowTime = currentTime - lastTime; // Time the low phase
// Classify the tooth as '1' or '0' based on the duration comparison
if (pulseHighTime > pulseLowTime) {
Serial.println(0); // Tooth is a '1' (high phase is longer)
} else {
Serial.println(1); // Tooth is a '0' (low phase is longer)
}
}
// Update last time for the next pulse
lastTime = currentTime;
}
void setup() {
Serial.begin(115200);
pinMode(CRANK_SENSOR_PIN, INPUT); // Set crank sensor pin as input
attachInterrupt(digitalPinToInterrupt(CRANK_SENSOR_PIN), handleCrankSignal, CHANGE); // Interrupt on change (rising or falling)
Serial.println("Crankshaft Sensor Decoder Initialized.");
}
void loop() {
// The loop is empty as we're handling everything in the interrupt function
}
- Attachments
-
- Capture.PNG (35.27 KiB) Viewed 5627 times
Last edited by AngelMarc on Fri May 02, 2025 12:22 pm, edited 3 times in total.
Don't stress specific units.
Re: Coding my own aftermarket ECU
Reading the previous one just made sense. Familiar enough. Not this one
But it works.
Code: Select all
#define CRANK_SENSOR_PIN 15 // Pin connected to the crankshaft sensor
#define HISTORY_SIZE 8 // Number of recent results to store
volatile unsigned long lastTime = 0;
volatile unsigned long pulseHighTime = 0;
volatile unsigned long pulseLowTime = 0;
volatile int resultHistory[HISTORY_SIZE] = {0}; // Stores 1s and 0s
volatile int historyIndex = 0;
void IRAM_ATTR handleCrankSignal() {
unsigned long currentTime = micros();
if (digitalRead(CRANK_SENSOR_PIN) == HIGH) {
pulseHighTime = currentTime - lastTime;
} else {
pulseLowTime = currentTime - lastTime;
// Determine 1 or 0 based on high vs. low time
int result = (pulseHighTime > pulseLowTime) ? 0 : 1;
// Save result to history buffer
resultHistory[historyIndex] = result;
historyIndex = (historyIndex + 1) % HISTORY_SIZE;
// Print the current state of the buffer
Serial.print("Pattern: ");
for (int i = 0; i < HISTORY_SIZE; i++) {
Serial.print(resultHistory[(historyIndex + i) % HISTORY_SIZE]);
}
Serial.println();
}
lastTime = currentTime;
}
void setup() {
Serial.begin(115200);
pinMode(CRANK_SENSOR_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(CRANK_SENSOR_PIN), handleCrankSignal, CHANGE);
Serial.println("Crankshaft Decoder with History Initialized.");
}
void loop() {
// Nothing here – all action is in the interrupt
}
But it works.
- Attachments
-
- Capture.PNG (47.72 KiB) Viewed 5621 times
Don't stress specific units.
Re: Coding my own aftermarket ECU
https://chatgpt.com/.../68142e61-9fdc-8 ... 1ed13493b2
If anybody cares to pick up where I left off.
EDIT: Apparently I deleted that conversation.
If anybody cares to pick up where I left off.
EDIT: Apparently I deleted that conversation.
Last edited by AngelMarc on Mon May 05, 2025 10:07 am, edited 2 times in total.
Don't stress specific units.
Re: Coding my own aftermarket ECU
Well, it's not outpacing the P59 before even adding all that's needed. I think the interrupts tried to overlap, for lack of better explanation.
- Attachments
-
- Capture.PNG (68.62 KiB) Viewed 5601 times
Don't stress specific units.
Re: Coding my own aftermarket ECU
After getting rid of serial. It keeps up much better.
About 60,000 RPM here.
About 60,000 RPM here.
Code: Select all
#define CRANK_SENSOR_PIN 0 // Pin connected to the crankshaft sensor
volatile unsigned long lastTime = 0; // Last pulse time
volatile unsigned long pulseHighTime = 0; // Duration of the high phase
volatile unsigned long pulseLowTime = 0; // Duration of the low phase
volatile uint8_t signalHistory = 0; // Last 8 results (bit pattern of 1s and 0s)
const uint8_t matchPattern = 0b00001111; // Replace with your desired pattern
void handleCrankSignal() {
unsigned long currentTime = micros();
if (digitalRead(CRANK_SENSOR_PIN) == HIGH) {
pulseHighTime = currentTime - lastTime;
} else {
pulseLowTime = currentTime - lastTime;
// Determine if this tooth is a '1' or '0'
uint8_t bit = (pulseHighTime > pulseLowTime) ? 0 : 1;
// Shift in the new bit (keep only last 8 bits)
signalHistory = ((signalHistory << 1) | bit) & 0xFF;
// Check if the pattern matches
if (signalHistory == matchPattern) {
digitalWrite(11, HIGH);
} else {
digitalWrite(11, LOW);
}
}
lastTime = currentTime;
}
void setup() {
pinMode(CRANK_SENSOR_PIN, INPUT);
pinMode(11, OUTPUT);
attachInterrupt(digitalPinToInterrupt(CRANK_SENSOR_PIN), handleCrankSignal, CHANGE);
}
void loop() {
// Nothing here – logic handled in interrupt
}
Don't stress specific units.
Re: Coding my own aftermarket ECU
https://www.youtube.com/watch?v=LKwkRyWzyCE
Got 8 channels (4 fuel, 4 spark) with they're own timing working at 63,000. That's before implementing any dwell or injector pulse math. Just evenly spacing everything out right now. That's just a Pi Pico with just the crank sensor signal. No cam.
So far the code is generic. That will change at some point.
Serial commands slow it down too much. Had to test with oscilloscope instead.
EDIT: At about 60,000 it starts to drop output pulses.
Got 8 channels (4 fuel, 4 spark) with they're own timing working at 63,000. That's before implementing any dwell or injector pulse math. Just evenly spacing everything out right now. That's just a Pi Pico with just the crank sensor signal. No cam.
So far the code is generic. That will change at some point.
Serial commands slow it down too much. Had to test with oscilloscope instead.
EDIT: At about 60,000 it starts to drop output pulses.
- Attachments
-
- ECUTimingIntervalLogic.ino
- (2.01 KiB) Downloaded 35 times
Don't stress specific units.
Re: Coding my own aftermarket ECU
Also messing with a SAMD21. It has some interesting hardware features. The crank decode software can just compare the output of 2 registers. These registers after proper hardware setup code.
Then a custom timer upper limit to use timer overflow to trigger an "engine stopped" timer overflow interrupt.
Untested examples.
Will be developing both in parallel more or less.
EDIT: Not having luck with the SAMD21 hardware implementation.
Code: Select all
// Read the captured values from the register
uint16_t high_duration = TC3->COUNT8.CC[0].reg; // Capture 1 (high duration)
uint16_t low_duration = TC3->COUNT8.CC[1].reg; // Capture 2 (low duration)
Then a custom timer upper limit to use timer overflow to trigger an "engine stopped" timer overflow interrupt.
Code: Select all
TC3->COUNT32.CC[0].reg = YOUR_TOP_VALUE; // If using Match Compare
// OR
TC3->COUNT32.PER.reg = YOUR_TOP_VALUE; // If using waveform generation / periodic reset
Untested examples.
Will be developing both in parallel more or less.
EDIT: Not having luck with the SAMD21 hardware implementation.
Don't stress specific units.
Re: Coding my own aftermarket ECU
Added analog input with a rolling average. MAP sensor. Rolling average is initialized with max values, because that makes sense for fast startup.
Had serial enabled until it seemed to step through everything the way I wanted; but that limited it to dropping pulses by just 13,000 RPM.
After commenting serial out, still seems very stable at 60,000 RPM.
Maybe the rolling average isn't the best idea; I can change it later. I think next is the PIO pulse generator. Get them all triggering as they should, and varying by "MAP" input. Then more convoluted math and tables.
Had serial enabled until it seemed to step through everything the way I wanted; but that limited it to dropping pulses by just 13,000 RPM.
After commenting serial out, still seems very stable at 60,000 RPM.
Maybe the rolling average isn't the best idea; I can change it later. I think next is the PIO pulse generator. Get them all triggering as they should, and varying by "MAP" input. Then more convoluted math and tables.
Code: Select all
#define CRANK_SENSOR_PIN 2 // Pin connected to crankshaft sensor
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 = 4095 * 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;
const uint8_t pattern2 = 0b11101110;
const uint8_t pattern3 = 0b01110011;
const uint8_t pattern4 = 0b10011000;
const uint8_t pattern5 = 0b11000101;
const uint8_t pattern6 = 0b00101000;
const uint8_t pattern7 = 0b01000001;
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;
newSample = true; // Signal to loop() to read ADC and update output
// Output patterns
digitalWrite(4, (signalHistory == pattern0) ? HIGH : LOW);
digitalWrite(5, (signalHistory == pattern1) ? HIGH : LOW);
digitalWrite(6, (signalHistory == pattern2) ? HIGH : LOW);
digitalWrite(7, (signalHistory == pattern3) ? HIGH : LOW);
digitalWrite(8, (signalHistory == pattern4) ? HIGH : LOW);
digitalWrite(9, (signalHistory == pattern5) ? HIGH : LOW);
digitalWrite(10, (signalHistory == pattern6) ? HIGH : LOW);
digitalWrite(11, (signalHistory == pattern7) ? HIGH : LOW);
}
lastTime = currentTime;
}
void setup() {
analogReadResolution(12);
// Serial.begin(115200);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
pinMode(A0, INPUT);
pinMode(CRANK_SENSOR_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(CRANK_SENSOR_PIN), handleCrankSignal, CHANGE);
}
void loop() {
if (newSample) {
newSample = false;
// Rolling average: subtract old value, read new one, add it
total -= readings[bufIndex];
int newVal = analogRead(A0);
readings[bufIndex] = newVal;
total += newVal;
bufIndex = (bufIndex + 1) % 24;
int avg12bit = total / 24;
int avg8bit = avg12bit >> 4;
// Serial.println(avg8bit);
}
}
Don't stress specific units.
Re: Coding my own aftermarket ECU
An example of dealing with AI. I assume previous conversation memories are involved. Arduino does 10 bit by default, I've been doing 12 bit. It says 10 bit to 8 bit, but the code is 12 bit to 8 bit.
Have to understand enough to have any hope of getting good results.
Have to understand enough to have any hope of getting good results.
- Attachments
-
- Capture.PNG (11.19 KiB) Viewed 231 times
Don't stress specific units.