//Modified from https://wokwi.com/projects/397101639069455361
// 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
// neopixel engine added
// https://wokwi.com/projects/397101639069455361
# include <Adafruit_NeoPixel.h>
# define LED_PIN A1
# define LED_COUNT 4
Adafruit_NeoPixel leds(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
constexpr uint8_t throttlePin {A0}; // Analog Input
constexpr uint8_t cylinders = 4;
constexpr uint8_t ignitionPin[cylinders] {3, 4, 5, 6};
constexpr uint8_t injectionPin[cylinders] {8, 9, 10, 11};
// parameters from 0 .. 720 for a 4 stroke
// fatter. just same all at the end of the cycle
constexpr int16_t injectionStart = 400;
constexpr int16_t injectionEnd = 600;
constexpr int16_t ignitionStart = 650;
constexpr int16_t ignitionEnd = 700;
int tTable[] = { 200, 350, injectionStart, injectionEnd, ignitionStart, ignitionEnd, 721,};
unsigned long colors[] = {0x0, 0x0000ff, 0x0, 0xffff00, 0x0, 0xff00, 0x0, 0x0,};
const byte NPHASES = sizeof tTable / sizeof *tTable;
unsigned long colorFor(int angle)
{
unsigned long theColor;
byte ii = 0;
do {
if (angle < tTable[ii]) return colors[ii];
ii++;
} while (ii < NPHASES);
return 0x0; // shouldn't?
}
//constexpr uint16_t firingOffset[cylinders] = {0, 360, 540, 180}; // can also be used for firing order or a Big-bang engine
constexpr uint16_t firingOffset[cylinders] = {0, 180, 540, 360}; // arbitrary serpentine 2x2
uint16_t cycle = 0; // counts from 0 to 720 for a 4 stroke
void setup() {
Serial.begin(115200);
Serial.println("\Wake up!\n");
leds.begin(); leds.show();
for (int tt = 0; tt < 800; tt += 25) {
Serial.print(tt); Serial.print(" 0x");
Serial.println(colorFor(tt), HEX);
}
for (auto &i : ignitionPin) pinMode(i, OUTPUT);
for (auto &i : injectionPin) pinMode(i, OUTPUT);
}
void loop() {
uint16_t interval = map(analogRead(throttlePin), 0, 1024, 0, 4096); // 16384
// interval = 0; // flat out w/ baggage
static uint32_t previousMicros = 0;
uint32_t currentMicros = micros();
if (currentMicros - previousMicros > interval) {
previousMicros = currentMicros;
if (++cycle > 720) cycle = 0;
leds.clear();
for (byte ii = 0; ii < cylinders; ii++) {
uint16_t thisCycle = (cycle + firingOffset[ii]) % 720;
leds.setPixelColor(ii, colorFor(thisCycle));
}
leds.show(); // I know, sue me.
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);
}
}
}
/*
if (1) {
static unsigned long timer;
static unsigned int count;
if (++count >= 720) {
count = 0;
Serial.print(millis() - timer);
Serial.println(" ");
Serial.println(interval);
timer = millis();
}
}
*/
/*
void xDigitalWrite(unsigned char thePin, unsigned char theValue)
{
digitalWrite(thePin, theValue ? HIGH : LOW);
}
// crude hack to catch input for the ring
# define digitalWrite xDigitalWrite
*/INJECT
IGNITE
3..........2..........1..........0