OOP mit einem Kringel zu viel?

Angeregt durch ein anderes Thema und @noiasca "Also das Aufleuchten eines kleinen Punktes als Funke von der Zündkerze und dann das verzögerte Ausbreiten der Flamme im Brennraum." wollte ich das mal ausprobieren, wie es aussieht. Die weißen LED von RGBW machen sich als Zündfunke ganz gut, finde ich. Aber auch wenn das meine Motivation war, so geht es mir in diesem Thema um "richtiges" Programmieren.

Mangels Hardware habe ich jedem Zylinder nur einen Lichtpunkt (nach Adafruit "Pixel" genannt) zugeordnet. Die einzelnen Schritte:

  • Blau von 0 nach 255 faden
  • weißer Blitz
  • Rot von 255 nach 0 faden
  • kurze Pause alle Farben aus

Das Programm tut, was es soll.

Frage 1: Hat das Programm Fehler?

Frage 2: Ich habe das Gefühl, irgendwas zu kompliziert gemacht zu haben. Ist das so?

#include <Adafruit_NeoPixel.h>

#define NEOPIN 6
#define ANZAHLDERPIXEL 7  // hier müßte 42 stehen
#define PIXELPROZYL 1     // hier müßte 7 stehen
#define BRIGHTNESS 255

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
Adafruit_NeoPixel strip = Adafruit_NeoPixel(ANZAHLDERPIXEL, NEOPIN, NEO_GRBW + NEO_KHZ800);

const unsigned long intervalle[] = {10, 50, 10, 500};
unsigned long aktMillis;

void setup() {
  strip.setBrightness(BRIGHTNESS);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

struct Fade  // Für jede Farbe wird ein Startwert (0-255, 0=dunkel, 255=hell) angegeben, der mit dem Fadewert jedes Intervall (in ms) verändert wird.
             // start() setzt die Startwerte, run() macht das Faden. 
             // Der Rückgabewert ist true, wenn alle Farben so nahe an 0 oder 255 sind, daß sie nicht mehr verändert werden können.
{
  Fade(byte rot, int8_t rotfade, byte gruen, int8_t gruenfade, byte blau, int8_t blaufade, byte weiss, int8_t weissfade, uint32_t intervall):
    rot(rot), rotfade(rotfade), gruen(gruen), gruenfade(gruenfade), blau(blau), blaufade(blaufade), weiss(weiss), weissfade(weissfade), intervall(intervall), ablaufMillis(0) {}

  void start(byte ersterpixel)
  {
    ep = ersterpixel;
    r = rot;
    rf = rotfade;
    g = gruen;
    gf = gruenfade;
    b = blau;
    bf = blaufade;
    w = weiss;
    wf = weissfade;
    i = intervall;
    ablaufMillis = aktMillis;
    for (byte pixel = ep; pixel < ep + PIXELPROZYL; pixel++)
    {
      strip.setPixelColor(pixel, strip.Color(r, g, b, w));
    }
    strip.show();
  }

  bool run()
  {
    bool rueckgabe = false;
    if (aktMillis - ablaufMillis >= i)
    {
      ablaufMillis = aktMillis;
      if (r >= -rf) r += rf;
      if (g >= -gf) g += gf;
      if (b >= -bf) b += bf;
      if (w >= -wf) w += wf;
      for (byte pixel = ep; pixel < ep + PIXELPROZYL; pixel++)
      {
        strip.setPixelColor(pixel, strip.Color(r, g, b, w));
      }
      strip.show();
      if ((r <= -rf || r > 255 - rf) && (g <= -gf || g > 255 - gf) && (b <= -bf || b > 255 - bf) && (w <= -wf || w > 255 - wf))
      {
        rueckgabe = true;
      }
    }
    return rueckgabe;
  }
  byte rot; int8_t rotfade; byte gruen; int8_t gruenfade; byte blau; int8_t blaufade; byte weiss; int8_t weissfade; uint32_t intervall;

  byte ep;
  byte r;
  int8_t rf;
  byte g;
  int8_t gf;
  byte b;
  int8_t bf;
  byte w;
  int8_t wf;
  byte i;
  unsigned long ablaufMillis;
};
Fade fade[]
{ // rot, rotfade, gruen, gruenfade, blau, blaufade, weiss, weissfade, intervall
  {0, 0, 0, 0, 0, 1, 0, 0, intervalle[0]},
  {255, -1, 0, 0, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, 0, 0, intervalle[0]},
  {255, -1, 0, 0, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, 0, 0, intervalle[0]},
  {255, -1, 0, 0, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, 0, 0, intervalle[0]},
  {255, -1, 0, 0, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, 0, 0, intervalle[0]},
  {255, -1, 0, 0, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, 0, 0, intervalle[0]},
  {255, -1, 0, 0, 0, 0, 0, 0, intervalle[2]},
};

struct Zyl  // Durchläuft die Schritte einer Schrittkette, wobei jeder Schritt dem Takt eines 4-Takt-Otto-Motors entsprechen könnte, 
            // was hier aber eher künstlerisch zu betrachten ist.
            // startphase: Bei welchem Schritt (Takt) die Animation beginnt.
            // startpixel: Erstes Pixel für den Zylinder. Bei 42 Pixeln 0, 7, 14 ...
            // fadetyp: Notwendig, damit sich die Fadephasen nicht in die Quere kommen.
{
  Zyl(const byte startphase, const byte startpixel, const byte fadetyp): schritt(startphase), startpixel(startpixel), fadetyp(fadetyp), einmal(true), ablaufMillis(0) {}
  void run()
  {
    switch (schritt) {  // schritt würde dem Takt eines 4-Takt-Otto-Motors entsprechen.
      case 0:  // Blau von 0 nach 255 faden
        if (einmal) {
          einmal = false;
          fade[fadetyp].start(startpixel);
        } else {
          if (fade[fadetyp].run()) {
            schritt++;
            einmal = true;
          }
          break;
        case 1:  // zusätzlich zu Blau ein weißer Blitz
          if (einmal) {
            einmal = false;
            for (byte pixel = startpixel; pixel < startpixel + PIXELPROZYL; pixel++) {
              strip.setPixelColor(pixel, strip.Color(0,   0,   255, 255));
            }
            strip.show();
            ablaufMillis = aktMillis;
          }
          if (aktMillis - ablaufMillis >= intervalle[schritt]) {
            schritt++;
            einmal = true;
          }
          break;
        case 2:  // Rot von 255 nach 0 faden
          if (einmal) {
            einmal = false;
            fade[fadetyp+1].start(startpixel);
          } else {
            if (fade[fadetyp+1].run()) {
              schritt++;
              einmal = true;
            }
          }
          break;
        case 3:  // kurz alle Farben aus
          if (einmal) {
            einmal = false;
            for (byte pixel = startpixel; pixel < startpixel + PIXELPROZYL; pixel++) {
              strip.setPixelColor(pixel, strip.Color(0,   0,   0,   0));
            }
            strip.show();
            ablaufMillis = aktMillis;
          }
          if (aktMillis - ablaufMillis >= intervalle[schritt]) {
            schritt++;
            einmal = true;
          }
          break;
        default:
          schritt = 0;
          break;
        }
    }
  }
  byte schritt;
  const byte startpixel;
  const byte fadetyp;
  bool einmal;
  unsigned long ablaufMillis;
};
Zyl zylinder[]
{ // startphase, startpixel, fadetyp
  {0, 1, 0},
  {2, 2, 2},
  {3, 3, 4},
  {0, 4, 6},
  {2, 5, 8},
  {3, 6, 10}
};

void loop() {
  aktMillis = millis();
  for (Zyl &z : zylinder) z.run();
}

PS: Was ich hier programmiert habe, beruht unverkennbar auf dem süßen Honig fleißiger Bienchen und anderer freundlicher Menschen.

EDIT 20180727 10:45: Kommentare ergänzt.

Hallo agmue,

da ich von den bunten Pixeln noch sehr wenig bis keine Ahnung habe, schreib doch mal, welche Pixel (Leds) du hier verwendest.

Danke

Farbe: RGBW
Chip: SK6812

HotSystems:
da ich von den bunten Pixeln noch sehr wenig bis keine Ahnung habe, schreib doch mal, welche Pixel (Leds) du hier verwendest.

Ich dachte, das ergibt sich aus dem anderen Thema, aber gerne doch. Ich verwende NeoPixel Jewel - 7 x 5050 RGBW LED w/ Integrated Drivers - Cool White - ~6000K. Da ich nur ein Juwel habe, verteile ich die sechs Zylinder auf die sieben Pixel. Ansteuerung wie WS2812 mit Daten und Takt auf einer Leitung (siehe #5).

noiasca:
Was ich noch vermisse ist die Angabe, wie du die Pixel angeordnet hast - einfache schematische Zeichnung sollte reichen.

Geht auch ein Foto? Pixel 0 ist unbenutzt. Gegenüberliegende Pixel entsprechen unterschiedlichen Zylindern, zeigen aber das selbe Muster. Rechts die Zündung:

Zuendung.png

noiasca:
Und kannst du verbal bechreiben was deine Codeabschnitte tun (tun sollen???)

Was steckt hinter den structs, was sind die fadetyps, was sind "schritt" in den Cases die du hardcoded als 0, 1,2,... hingeschrieben hast?

Ich habe Kommentare in #0 ergänzt. Wenn es nicht reicht, bitte melden :slight_smile:

Die Schritte sollten den Takten, also Ansaugen, Verdichten, Zündung ... entsprechen, tun sie aber (noch) nicht. Beim Faden könnte man alle Farben in alle Richtungen verändern, was ich derzeit nicht nutze, aber für eine Gelb-Rot-Übergang wie in den Videos zu sehen, nützlich sein könnte. Ich will so ein Modell ja nicht bauen, mich interessiert das Programmieren und die Lichteffekte.

noiasca:
Ich habe erst wieder ab Sonntag Zugriff auf Neopixels ...

Das ist natürlich schade, aber ich habe keinerlei Eile, da keine wie auch immer genannte Arbeit bis zu einem bestimmten kurzfristigen Termin abgegeben werden muß, ich programmiere ja nur zum Spaß :grin:

Zuendung.png

Ansteuerung wie WS2812.

Naja....
Vom Timing, her ja, da sind sich die Chips sehr ähnlich.

WS2812 RGB 24Bit/Chip
SK6812 RGBW 32Bit/Chip

Das Datenblatt, was die da zu "TECHNICAL DETAILS" aufgeführt haben, passt nicht zu den Chips.
Dieses ist das richtige Datenblatt

combie:
Das Datenblatt, was die da zu "TECHNICAL DETAILS" aufgeführt haben, passt nicht zu den Chips.
Dieses ist das richtige Datenblatt

Ist ja schon verwirrend. Ich habe Lady Ada eine Korrekturmitteilung zukommen lassen.

hm, meine Pixel sind offenbar ohne Weiß, deine Effekte kann ich somit kaum nachvollziehen. Das wird eine schwere Geburt.

ad

Zyl zylinder[]
{ // startphase, startpixel, fadetyp
  {0, 1, 0},
  {2, 2, 2},
  {3, 3, 4},
  {0, 4, 6},
  {2, 5, 8},
  {3, 6, 10}
};

warum wiederholen sich die Startphasen von 1 & 4, 2 & 5, 3 & 6? Steige da nicht wirklich durch...

noiasca:
hm, meine Pixel sind offenbar ohne Weiß, deine Effekte kann ich somit kaum nachvollziehen. Das wird eine schwere Geburt.

Soll ich das Programm für RGB umschreiben?

noiasca:
warum wiederholen sich die Startphasen von 1 & 4, 2 & 5, 3 & 6? Steige da nicht wirklich durch...

Wenn ich mich richtig erinnere, macht ein Viertakter Ansaugen, Verdichten, Verbrennen und Ausstoßen. Bei einem Vierzylinder macht Zylinder 1 Takt 1, Zylinder 2 Takt 2, Zylinder 3 Takt 3, Zylinder 4 Takt 4. Nee, tatsächlich läuft das in einer anderen Reihenfolge ab, siehe Viertakt-Ottomotor.

Und wie nun beim Sechszylinder? Ich kann ja keinen fünften und sechsten Takt hinzufügen. Also müssen zwei Zylinder wohl dasselbe machen oder etwas zeitlich versetzt.

An dieser Stelle steigt der Programmierer aus der Motortechnik aus. Ich habe drei für den Einstieg nutzbare Takte (Schritte), weshalb ich bei sechs Zylindern je zwei Zylinder mit der selben Animation habe. Je zwei Pixel zeigen die selbe Animation an. Welcher Zylinder mit welcher Animation startet, lege ich in Zyl als startphase fest.

Die Schritte habe ich nicht Takte genannt, weil es Animationsschritte sind und sie nicht den Takten entsprechen. So ist es notwendig, das Verbrennen im Programm in den Schritt "Zündblitz" (case 1) und "Verbrennen" (case 2) aufzuteilen.

Hi

Denke, der Grund FÜR einen 6-Zylinder ist die größere Laufruhe.
Beim 4-Zylinder (1-4-2-3) sind die Kolben um 180° versetzt - zwei haben immer die gleiche 'Höhe', aber unterschiedlichen Takt.
Auch wird (wenn möglich) verhindert, daß benachbarte Zylinder in der Zündreihenfolge direkt nacheinander dran sind.
Beim 4-Zylinder geht Das nicht, bei 2-3 haben wir zwei aufeinander folgende Zündungen an benachbarten Zylindern - scheint aber sonst trotzdem zu klappen - millionenfach auf den Straßen dieser Welt.

Beim 6-Zylinder sind die Kolben um 120° versetzt.
So habe ich bei 2 Umdrehungen (=720°) die 6 Zündungen gleichmäßig verteilt.
Anders als beim 4-Zylinder habe ich hier die Zündungen 'auseinander'.
1-5-3-6-2-4
Die aufeinander folgenden 'Schläge' wirken hier immer auf andere Lager, beim 4-Zylinder ist das Lager zwischen Zylinder 2 und 3 direkt 2x hintereinander dran.

Auch beim 6-Zyinder haben wir wieder zwei Kolben, Die 'auf gleicher Höhe' laufen, aber im anderen Takt (also um 2 Takte versetzt).

Somit kann die Simulation nicht bei zwei Zylindern gleich sein - Die muß ebenfalls versetzt 'kommen'.

MfG

postmaster-ino:
Somit kann die Simulation nicht bei zwei Zylindern gleich sein - Die muß ebenfalls versetzt 'kommen'.

Wenn ich eine Simulation bauen wollte, stimme ich Dir zu. Ich habe mich aber nur von der Simalation inspirieren lassen. Für mich ist das eine mir selbst gestellt Programmierübung zum Spaß. Da kann es erstmal Blinken und Faden, wie es will. Aber selbst wenn ich die Animation der Simulation angleichen wollte, so bräuchte ich zunächst ein funktionierendes Programm, um die Wirkung beurteilen zu können.

Hi

Ansaugen kannst Du ggf. den Ansaugstutzen bläulich leuchten lassen.
Ausstoßen Auslaßstutzen hellrötlich.
Verdichten Brennraum bläulich zu rötlich faden, wird vom Zündfunken und der Verbrennung abgelöst.

MfG

noiasca:
wenn du die Zeit aufbringen kannst, würde ich das glatt in meinen mal reinspielen zum Vergleichen. hab hier einen 8er Streifen mit WS2812 (NEO_GRB)

Aber gerne! Die Pixel aus meinem Avatar möchten NEO_RGB. Bespaßt werden die Pixel 0 bis 5. Der weiße Blitz wird mit allen Farben "an" erzeugt.

// Variante für RGB anstelle RGBW
#include <Adafruit_NeoPixel.h>

#define NEOPIN 6
#define ANZAHLDERPIXEL 9  // hier müßte 42 stehen
#define PIXELPROZYL 1     // hier müßte 7 stehen
#define BRIGHTNESS 255

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
Adafruit_NeoPixel strip = Adafruit_NeoPixel(ANZAHLDERPIXEL, NEOPIN, NEO_RGB + NEO_KHZ800);

const unsigned long intervalle[] = {10, 50, 10, 500};
unsigned long aktMillis;

void setup() {
  strip.setBrightness(BRIGHTNESS);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

struct Fade  // Für jede Farbe wird ein Startwert (0-255, 0=dunkel, 255=hell) angegeben, der mit dem Fadewert jedes Intervall (in ms) verändert wird.
             // start() setzt die Startwerte, run() macht das Faden. 
             // Der Rückgabewert ist true, wenn alle Farben so nahe an 0 oder 255 sind, daß sie nicht mehr verändert werden können.
{
  Fade(byte rot, int8_t rotfade, byte gruen, int8_t gruenfade, byte blau, int8_t blaufade, uint32_t intervall):
    rot(rot), rotfade(rotfade), gruen(gruen), gruenfade(gruenfade), blau(blau), blaufade(blaufade), intervall(intervall), ablaufMillis(0) {}

  void start(byte ersterpixel)
  {
    ep = ersterpixel;
    r = rot;
    rf = rotfade;
    g = gruen;
    gf = gruenfade;
    b = blau;
    bf = blaufade;
    i = intervall;
    ablaufMillis = aktMillis;
    for (byte pixel = ep; pixel < ep + PIXELPROZYL; pixel++)
    {
      strip.setPixelColor(pixel, strip.Color(r, g, b));
    }
    strip.show();
  }

  bool run()
  {
    bool rueckgabe = false;
    if (aktMillis - ablaufMillis >= i)
    {
      ablaufMillis = aktMillis;
      if (r >= -rf) r += rf;
      if (g >= -gf) g += gf;
      if (b >= -bf) b += bf;
      for (byte pixel = ep; pixel < ep + PIXELPROZYL; pixel++)
      {
        strip.setPixelColor(pixel, strip.Color(r, g, b));
      }
      strip.show();
      if ((r <= -rf || r > 255 - rf) && (g <= -gf || g > 255 - gf) && (b <= -bf || b > 255 - bf))
      {
        rueckgabe = true;
      }
    }
    return rueckgabe;
  }
  byte rot; int8_t rotfade; byte gruen; int8_t gruenfade; byte blau; int8_t blaufade; uint32_t intervall;

  byte ep;
  byte r;
  int8_t rf;
  byte g;
  int8_t gf;
  byte b;
  int8_t bf;
  byte i;
  unsigned long ablaufMillis;
};
Fade fade[]
{ // rot, rotfade, gruen, gruenfade, blau, blaufade, intervall
  {0, 0, 0, 0, 0, 1, intervalle[0]},
  {255, -1, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, intervalle[0]},
  {255, -1, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, intervalle[0]},
  {255, -1, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, intervalle[0]},
  {255, -1, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, intervalle[0]},
  {255, -1, 0, 0, 0, 0, intervalle[2]},
  {0, 0, 0, 0, 0, 1, intervalle[0]},
  {255, -1, 0, 0, 0, 0, intervalle[2]},
};

struct Zyl  // Durchläuft die Schritte einer Schrittkette, wobei jeder Schritt dem Takt eines 4-Takt-Otto-Motors entsprechen könnte, 
            // was hier aber eher künstlerisch zu betrachten ist.
            // startphase: Bei welchem Schritt (Takt) die Animation beginnt.
            // startpixel: Erstes Pixel für den Zylinder. Bei 42 Pixeln 0, 7, 14 ...
            // fadetyp: Notwendig, damit sich die Fadephasen nicht in die Quere kommen.
{
  Zyl(const byte startphase, const byte startpixel, const byte fadetyp): schritt(startphase), startpixel(startpixel), fadetyp(fadetyp), einmal(true), ablaufMillis(0) {}
  void run()
  {
    switch (schritt) {  // schritt würde dem Takt eines 4-Takt-Otto-Motors entsprechen.
      case 0:  // Blau von 0 nach 255 faden
        if (einmal) {
          einmal = false;
          fade[fadetyp].start(startpixel);
        } else {
          if (fade[fadetyp].run()) {
            schritt++;
            einmal = true;
          }
          break;
        case 1:  // ein weißer Blitz
          if (einmal) {
            einmal = false;
            for (byte pixel = startpixel; pixel < startpixel + PIXELPROZYL; pixel++) {
              strip.setPixelColor(pixel, strip.Color(255, 255, 255));
            }
            strip.show();
            ablaufMillis = aktMillis;
          }
          if (aktMillis - ablaufMillis >= intervalle[schritt]) {
            schritt++;
            einmal = true;
          }
          break;
        case 2:  // Rot von 255 nach 0 faden
          if (einmal) {
            einmal = false;
            fade[fadetyp+1].start(startpixel);
          } else {
            if (fade[fadetyp+1].run()) {
              schritt++;
              einmal = true;
            }
          }
          break;
        case 3:  // kurz alle Farben aus
          if (einmal) {
            einmal = false;
            for (byte pixel = startpixel; pixel < startpixel + PIXELPROZYL; pixel++) {
              strip.setPixelColor(pixel, strip.Color(0,   0,   0));
            }
            strip.show();
            ablaufMillis = aktMillis;
          }
          if (aktMillis - ablaufMillis >= intervalle[schritt]) {
            schritt++;
            einmal = true;
          }
          break;
        default:
          schritt = 0;
          break;
        }
    }
  }
  byte schritt;
  const byte startpixel;
  const byte fadetyp;
  bool einmal;
  unsigned long ablaufMillis;
};
Zyl zylinder[]
{ // startphase, startpixel, fadetyp
  {0, 0, 0},
  {2, 1, 2},
  {3, 2, 4},
  {0, 3, 6},
  {2, 4, 8},
  {3, 5, 10}
};

void loop() {
  aktMillis = millis();
  for (Zyl &z : zylinder) z.run();
}

Ich hoffe, ich habe auf die Schnelle an alle Stellen gedacht. Funktionieren tut es.

noiasca:
ok danke funzt soweit. Ich mach jetzt mal mechanisch eine Grundplatte mit 7 LED's pro Zylinder...

Möchtest Du Dir tatsächlich eine Simulation bauen? Mir geht es ja um das Programmieren, aber dann würde ich natürlich von "künstlerisch" zu "technisch" schwenken und noch weitere Animationsschritte hinzufügen.

Ach ja: Was Takte sind, ist technisch vorgegeben. Aber ein Takt kann natürlich in mehrere Animationsschritte zerfallen. Darum hatte ich die Schritte als Schritte und nicht als Takte benannt.

Ich habe mit einem Zylinder angefangen, weil einen das Geblinke sonst irgendwann kirre macht.

Angeregt durch ein anderes Thema und @noiasca "Also das Aufleuchten eines kleinen Punktes als Funke von der Zündkerze und dann das verzögerte Ausbreiten der Flamme im Brennraum."
....

Habe mich auch mal anregen lassen....

So richtig schön ist es noch nicht geworden.... Nacharbeit könnte sich lohnen.
Optimierungen/Verschönerungen sind noch an vielen Ecken möglich


Verwendet wird für jeden Zylinder eine RGB LED

Als Drehpunkt habe ich die Kurbelwelle genommen.
720° für einen vollen Zyklus, beginnend mit "Ansaugen"

Die Kurbelwelle mit ihrer Zündfolge ist in dem Array "motor" verankert.
So ist das leicht austauschbar und es können eigene Motorkreationen "getestet" werden.

Die Takte:
1 "Ansaugen"
Dimmt von Schwarz zu Blau. Die Stärke des Blau soll die angesaugte kalte Frischgasmenge darstellen.

2 "Verdichten"
Dimmt von Blau nach Violett/Mangenta, soll die zunehmende Verdichtung und Erwärmung darstellen

  1. "Arbeiten"
    Dimmt von Gelb zu Rot, Entspannung des Gases und die Abkühlung dabei

4."Ausstoßen"
Dimmt von Rot nach Schwarz, das noch recht heiße Gas wird ausgestoßen

Zwischen 2 und 3 gibt es einen Weißen Blitz, welcher die Durchzündung des Gemisches symbolisieren soll

#include <Adafruit_NeoPixel.h>

const byte stripePin = 6;

const byte zylinder = 3;

// 3 Zylinder 1-2-3
int motor[zylinder] {0, 240, 480};

// 4 Zylinder 1-3-4-2
//int motor[zylinder] {0, 360, 540 , 180};

// 6 Zylinder 1-5-3-6-2-4
//int motor[zylinder] {0, 480, 240, 600, 120, 360};

int winkel = 0;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(zylinder,stripePin, NEO_GRB + NEO_KHZ800);

int normalisiereWinkel(int winkel)
{
  while(winkel >= 720) winkel -= 720;
  while(winkel <    0) winkel += 720;
  return winkel;
}

void berecheFarbe(int winkel,int zylinder)
{
  winkel = normalisiereWinkel(winkel-motor[zylinder]);
  int fortschritt = winkel % 180; // innerhalb eines Taktes
  int takt = winkel / 180;
  int r,g,b;

  if(winkel > 355 && winkel < 365) // Blitz
  {
    pixels.setPixelColor(zylinder, pixels.Color(255,255,255));
  }else
  {
    switch(takt) // Farben fuer den jeweiligen Takt
    {
      case 0 : r = 0          ; b = fortschritt; g = 0; break; 
      case 1 : r = fortschritt; b = 179        ; g = 0; break; 
      case 2 : r = 179        ; b = 0          ; g = 179-fortschritt; break; 
      case 3 : r = 179-fortschritt; b = 0; g = 0; break; 
    }
    pixels.setPixelColor(zylinder, pixels.Color(r,g,b));
  }
}



void setup() 
{
  pixels.begin(); 
}


void loop() 
{
  for(int i=0;i<zylinder;i++) berecheFarbe(winkel,i);
  pixels.show(); 
  winkel = normalisiereWinkel(++winkel); // wrapp around
  delay(10); 
}

Moin!

Der Titel heißt ja "OOP mit einem Kringel zu viel?"

Also habe ich das/mein Programm etwas überarbeitet.
Etwas mehr OOP, und ein paar Kringel dran gebaut.
Damit es dem Titel etwas gerechter wird.

Funktionalität und Farben unverändert.

Der Motor ist jetzt ein Array von Zylindern geworden. Was mir von der Betrachtungsweise her, mehr als angemessen erscheint.
Auch ist der aktuelle Kurbelwellenwinkel jetzt ein Objekt, welches nur die 4T üblichen 720° aufnehmen kann. Über und Unterläufe werden selbstständig abgehandelt.

Der Speicherverbrauch hat sich dadurch kaum geändert. Es sind 2 Byte mehr Flash Bedarf.
Ich glaube, dass man eine solche Ressourcen Verschwendung gerade noch tolerieren kann.
:sunglasses: :sunglasses: :sunglasses: :sunglasses:

Das Hauptprogramm schaut jetzt so aus, der Rest im Anhang.

#include <Adafruit_NeoPixel.h>

#include "Zylinder.h"

//Zylinder motor[] {0, 360};// 2 Zylinder parallel Twin
//Zylinder motor[] {0, 180};// 2 Zylinder gegenlaeufer
Zylinder motor[] {0, 240, 480};// 3 Zylinder 1-2-3
//Zylinder motor[] {0, 540 , 180, 360};          // 4 Zylinder 1-3-4-2
//Zylinder motor[] {0, 480, 240, 600, 120, 360}; // 6 Zylinder 1-5-3-6-2-4

const byte anzahlZylinder = sizeof(motor)/sizeof(motor[0]);
const byte stripePin      = 6;
Adafruit_NeoPixel stripe {anzahlZylinder,stripePin, NEO_GRB + NEO_KHZ800};
KurbelWinkel kurbelwinkel;

void showMotor(Adafruit_NeoPixel & stripe,KurbelWinkel & winkel)
{
  for(int i=0;i<anzahlZylinder;i++)
  {
    Color farbe = motor[i].getColor(winkel);
    stripe.setPixelColor(i, stripe.Color(farbe.r,farbe.g,farbe.b));
  }
  stripe.show(); 
}

void setup() 
{
  stripe.begin(); 
}

void loop() 
{
  showMotor(stripe,kurbelwinkel);
  kurbelwinkel++;  
  delay(10); 
}

Motor001.zip (1.99 KB)

Ich komm nicht so recht weiter mit der Animation innerhalb des Taktes. Das werden zu viele IFs in Schlangencode. Da combie ohnehin auch schon ein fertiges OOP geliefert hat, bin ich dann mal raus.

Noch ein Bildchen von meinem Test-Motor mit 3 Zylinder (bzw. eine Bank eines V6 ) - das - wie man sehen kann - in der Bohrmaschine fast um die Ohren geflogen ist :wink:

Da combie ohnehin auch schon ein fertiges OOP geliefert hat, bin ich dann mal raus.

Ja, nee...
So gehts ja nicht, und ich soll auch noch die Schuld tragen...
(NIE wieder, werde ich hier OOP Code posten)
:o :o :o :o :o :o

Ich komm nicht so recht weiter mit der Animation innerhalb des Taktes. Das werden zu viele IFs in Schlangencode.

If Kaskaden mag ich auch nicht.
Habe dann immer das Gefühl was falsch gemacht zu haben.

Was machen die if denn?
evtl. kann man das Problem ja mit einer "Lookup Table" erschlagen.

combie, mir fehlen da positive Smiles in deinem Post. Falls ich falsch rübergekommen bin, - ich habe deine Antwort sehr geschätzt nur sehe ich mich nicht mehr im Stande einen added-value beizutragen. Das OOP Problem ist ja gelöst (von dir).

ich denke nicht, dass mein Animations-Versuch was taugt.

Die Grundidee war, ein Pixel als Zündkerze zu nehmen (bei mir die dritte @9Uhr), und die drei Nachbar-LEDs heller (Pixel 2 @11Uhr, 0 = Mitte, Pixel 4@7 Uhr) ("weil schneller durch den Funken erreicht") - darzustellen. Wirkt aber nicht besonders.

Außerdem haben sich meine Bedenken bestätigt: das wird immer mehr geflickere und taugt als Abience-Light nichts - und als Simulation auch nicht.

#include <Adafruit_NeoPixel.h>

const byte stripePin = 6;

const byte zylinder = 3;
const byte ledPerZylinder = 7;

// 1 Zylinder 1
//int motor[zylinder] {0};

// 3 Zylinder 1-2-3
int motor[zylinder] {0, 240, 480};

// 4 Zylinder 1-3-4-2
//int motor[zylinder] {0, 360, 540 , 180};

// 6 Zylinder 1-5-3-6-2-4
// int motor[zylinder] {0, 480, 240, 600, 120, 360};

int winkel = 0;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(50, stripePin, NEO_RGB + NEO_KHZ800);       // meine Kette hat momentan 50 LED's und ich will sie nicht abschneiden

int normalisiereWinkel(int winkel)
{
  while (winkel >= 720) winkel -= 720;
  while (winkel <    0) winkel += 720;
  return winkel;
}

void berechneFarbe(int winkel, int zylinder)
{
  winkel = normalisiereWinkel(winkel - motor[zylinder]);
  int fortschritt = winkel % 180; // innerhalb eines Taktes
  int takt = winkel / 180;
  int r, g, b;

  if (winkel > 355 && winkel < 365) // Blitz
  {
    pixels.setPixelColor(zylinder * ledPerZylinder + 3, pixels.Color(255, 255, 255));   // +3 = Lage der Zündkerze
  }
  else
  {
    switch (takt) // Farben fuer den jeweiligen Takt
    {
      case 0 : r = 0          ; b = fortschritt;       g = 0; break;                  // Ansaugen:   Dimmt von Schwarz zu Blau. Die Stärke des Blau soll die angesaugte kalte Frischgasmenge darstellen.
      case 1 : r = 0;           b = 179 - fortschritt  ; g = 0; break;                // Verdichten: NEU zu Schwarz 
      case 2 : r = 179        ; b = 0                ; g = 179 - fortschritt; break;  // Arbeiten:   Dimmt von Gelb zu Rot, Entspannung des Gases und die Abkühlung dabei
      case 3 : r = 179 - fortschritt; b = 0; g = 0; break;                            // Ausstoßen:  Dimmt von Rot nach Schwarz, das noch recht heiße Gas wird ausgestoßen
    }

    for (byte actual = 0; actual < ledPerZylinder; actual++)
    {
      byte actualPixel = zylinder * ledPerZylinder + actual;
      pixels.setPixelColor(actualPixel, pixels.Color(r, g, b));
    }

    if (winkel > 365 && winkel < 400)
    {
      pixels.setPixelColor(zylinder * ledPerZylinder + 2, pixels.Color(r, g + 40, b));  // Pixel 0, 2 und 4 je Zylinder sind am nächsten zur Zündkerze 
      pixels.setPixelColor(zylinder * ledPerZylinder,     pixels.Color(r, g + 40, b));
      pixels.setPixelColor(zylinder * ledPerZylinder + 4, pixels.Color(r, g + 40, b));
    }
  }
}



void setup()
{
  Serial.begin(115200);
  pixels.begin();
  for (byte i = 0; i < 50; i++) pixels.setPixelColor(i, pixels.Color(0, 0, 0));  //
  pixels.show();
}


void loop()
{
  for (byte i = 0; i < zylinder; i++) berechneFarbe(winkel, i);
  pixels.show();
  winkel = normalisiereWinkel(++winkel); // wrapp around
  delay(10);
}

OT: Pixel 0 der Kette wegen OSI Layer 8 geschrottet, 12V Netzteil vermutlich auch ... ergo lass ich das jetzt ;-/ ...

ich denke nicht, dass mein Animations-Versuch was taugt.
.... ergo lass ich das jetzt ;-/ ..

Ja, ok, dann höre ich auch mal auf zu drängeln...

combie, mir fehlen da positive Smiles in deinem Post.

Ach, da mache dir mal nichts draus.
Die vergesse ich öfter mal.
Auch weiß ich nicht unbedingt, was "positiv" ist. (youtube)

Ärgern wollte ich dich nicht.
(ok, vielleicht ein ganz kleines bisschen...)
Es wäre mir schon ganz recht, wenn ich heute nicht eine Eskalationsspirale vorwärts treiben müsste.