// Simulation of a 4 stroke engine (injection and ignition)
// https://forum.arduino.cc/t/micros-counting-time-for-engine-simulation/1255668/13
// 2024-04-05 by noiasca
constexpr uint8_t throttlePin {A0}; // Analog Input
constexpr uint8_t cylinders = 3;
constexpr uint8_t ignitionPin[cylinders] {3, 5, 6};
constexpr uint8_t injectionPin[cylinders] {9, 10, 11};
// parameters from 0 .. 720 for a 4 stroke
constexpr int16_t injectionStart = 720 - 90;
constexpr int16_t injectionEnd = 720 - 45;
constexpr int16_t ignitionEnd = 720 - 10;
int16_t ignitionStart = ignitionEnd - 105; // dwell
constexpr uint16_t firingOffset[cylinders] = {0, 480, 240}; // can also be used for firing order or a Big-bang engine
uint16_t cycle = 0; // counts from 0 to 720 for a 4 stroke
uint32_t lastToggleTimes[2][cylinders]; // on/off x cyls
void setup() {
Serial.begin(115200);
for (auto &i : ignitionPin) pinMode(i, OUTPUT);
for (auto &i : injectionPin) pinMode(i, OUTPUT);
}
void loop() {
static uint32_t previousMicros = 0;
static uint16_t lastInterval = -1;
uint32_t currentMicros = micros();
uint16_t interval = map(analogRead(throttlePin), 0, 1023, 2, 720);
if (interval != lastInterval) {
lastInterval = interval;
float rpm = 60.0 * 1000000 / (interval) / 720 * 2;
Serial.print("Interval:");
Serial.print(interval);
Serial.print(" RPM:");
Serial.print(rpm);
Serial.print(" dwell_deg:");
int dwellAngle = min(max(3000 / interval, 2), ignitionEnd - 2);
Serial.print(dwellAngle);
Serial.println();
ignitionStart = ignitionEnd - dwellAngle;
}
if (currentMicros - previousMicros > interval) {
previousMicros += interval;
//if(currentMicros - previousMicros > interval)Serial.print('!');
// previousMicros = currentMicros;
if (++cycle > 720) cycle = 0;
for (int i = 0; i < cylinders; i++) {
uint16_t thisCycle = (cycle + firingOffset[i]) % 720; // cylce angle for this cylinder
if (thisCycle >= ignitionStart && thisCycle <= ignitionEnd) {
if (digitalRead(ignitionPin[i]) == LOW) digitalWrite(ignitionPin[i], HIGH);
}
else if (digitalRead(ignitionPin[i]) == HIGH) digitalWrite(ignitionPin[i], LOW);
if (thisCycle >= injectionStart && thisCycle <= injectionEnd) {
if (digitalRead(injectionPin[i]) == LOW) digitalWrite(injectionPin[i], HIGH);
}
else if (digitalRead(injectionPin[i]) == HIGH) digitalWrite(injectionPin[i], LOW);
}
}
}