Arduiono Projekt für Modellbau

Danke :slight_smile:

Das Grundlegende Gerüst wäre vermutlich fertig...
Es fehlen jetzt noch ein paar Sachen wie z.B. verschiedene LED Dimmer (FadeIn -> dann stabil halten) bzw. per Tastendruck...

Aber über einige andere Details bin ich mir aktuell auch noch nicht so sicher.
Eine Option könnte es ggf. sein, blinkAnzahl über einen Potentiometer / Drehregler einstellbar zu machen... aber darüber bin ich mir selber auch noch nicht ganz im klaren... im zweifel wird das dreimal blitzen ja sowieso bei einem längeren Tastendruck mehrfach hintereinander ausgeführt, sodass es eigentlich nicht notwendig wäre.

Da ich jetzt erst einmal die wichtigsten Komponenten habe - kann ich mich jetzt noch einmal darauf konzentrieren, die Grundlagen etwas zu vertiefen :smiley:

  • ist zwar nicht der Ideelle Ansatz so, aber es ist schwierig, sich auf Grundlagen zu fokussieren, solange Ideen und offene Projekte im Hinterkopf herum schwirren ^^

Hallo Zusammen,

ich habe das Programm jetzt soweit, dass es die wichtigsten Aufgaben durchläuft.
Jetzt käme der nächste Schritt... Aktuell habe ich eine Funktion, welche ich zum FadeIn einer LED nutze.

das Fade-In wird direkt bei Programmstart aufgerufen - hat aber ansonsten noch keinerlei weitere Optionen / Möglichkeiten.

Ich würde das auch gerne in eine Klasse auslagern, welche es mir ermöglicht, das FadeIn für mehrere LEDs unterschiedlich zu nutzen - bzw. sollten ggf. folgende Optionen möglich sein:

  1. Definieren, welcher Pin (welche LED) faden soll
  2. Die Dauer des Fade-Effektes
  3. Die steps / increment für das Fading
  4. FadeIn oder FadeOut
  5. Soll die LED pulsieren, also FadeIn -> FadeOut -> FadeIn ? oder nur einmal ein- bzw. aus gehen?

Dabei sollen bestimmte LEDs bei Programmstart an gehen (ggf. ein Zeitverzögerter Start, was ich beim Aufrufen steuern könnte)
Und andere sollten auf einen Tastendruck an gehen - und so lange an bleiben, bis der Taster erneut gedrückt wird.

Ich habe bereits versucht, die Klasse "Blinker" zu übernehmen und abzuwandeln, bisher aber leider noch keinen Erfolg damit gehabt.

Hier meine aktuelle funktion:

  // bisheriger Code zum Blinken der Übersichtlichkeit halber entfernt

byte increment = 1; 
byte brightness = 0; 
unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 250;  //the value is a number of milliseconds between changes of brightness
const byte ledPin = 10;

void setup()
{
  pinMode(ledPin, OUTPUT);
  startMillis = millis();  //initial start time
}

void loop()
{
  FadeInWarpEngine();
}

void FadeInWarpEngine()
{
  currentMillis = millis();  //get the current time
  if (currentMillis - startMillis >= period)  //test whether the period has elapsed
  {
    analogWrite(ledPin, brightness);    //set the brightness of the LED

    if(brightness != 255) {
      brightness += increment;    //will wrap round from 255 to 0 because brightness is an unsigned data type
    }
    startMillis = currentMillis;  //IMPORTANT to save the start time of the current LED brightness
  }
}

Über Ansätze, mit denen ich ggf. weiter machen kann, wäre ich dankbar :slight_smile:

Es wird immer schlimmer, denn je mehr Du kannst, desto mehr und intensiver schwirrt es. Daher ist es für den Hobbybastler wichtig, eine Balance zu finden zwischen Grundlagen und Spaß.

das ist klar :slight_smile:
Allerdings steht die Arbeit an dem aktuellen Projekt im Vordergrund... danach habe ich zumindest Zeit und den Fokus, mich mit anderen Spielereien zu befassen und kann experimentieren - ohne, dass ein konkretes Ziel dahinter steht :slight_smile:

zum Beispiel habe ich hier noch ein weiteres Modellbau-Projekt, in welchem ich Teile von diesem hier weiterverwenden kann - diese aber u.U. leicht modifizieren muss.

Da das zweite Projekt allerdings noch lange nicht an dem Stand ist, dass ich mich mit der Elektronik befassen muss, kann ich dann daran arbeiten und üben / experimentieren :slight_smile:

und btw: Danke für deinen Code-Rework... Tatsächlich verstehe ich die Classe Blinker so, wie du sie aufbereitet hast sogar besser wie zuvor :slight_smile:

Das freut mich :slightly_smiling_face:

Über Nacht sind mir kosmetische Änderungen eingefallen:

class Blinker
{
  protected:
    const byte ledPin;
    const uint32_t hellPhase;
    const uint32_t dunkelPhase;
    uint32_t zeitMerker = 0;
    enum Schritt : byte {Start, HellPhase, DunkelPhase};
    Blinker::Schritt schritt = Schritt::Start; // Zustandsmerker

  public:
    Blinker(const byte ledPin, const unsigned long hellPhase, const unsigned long dunkelPhase): ledPin(ledPin), hellPhase(hellPhase), dunkelPhase(dunkelPhase) {}

    void run(byte & blinkZahl)
    {
      uint32_t jetzt = millis();

      switch (schritt)
      {
        case Schritt::Start:
          if (blinkZahl)
          {
            digitalWrite(ledPin, HIGH);
            schritt = Schritt::HellPhase;
            zeitMerker = jetzt;
          }
          break;
        case Schritt::HellPhase:
          if (jetzt - zeitMerker >= hellPhase)
          {
            digitalWrite(ledPin, LOW);
            schritt = Schritt::DunkelPhase;
            zeitMerker = jetzt;
          }
          break;
        case Schritt::DunkelPhase:
          if (jetzt - zeitMerker >= dunkelPhase)
          {
            if (blinkZahl > 0) blinkZahl--;
            schritt = Schritt::Start;
          }
      }
    }

    void init()
    {
      pinMode(ledPin, OUTPUT);
    }
};

enum Gruppe : byte {BEACON, NAVI, TORPEDO};
// Pins and timings (On / Off) for Blinking Sequence
Blinker blinkGruppe[] = {
  // {pin,hellzeit,dunkelzeit}
  {13, 20, 1000},  // Beacon Light
  {12, 700, 1000}, // Navigation Lights
  {11, 10, 150},   // Torpedo Light
};

// to which Pin is the Button for the Torpedo attached?
const byte ButtonPin = 2;

void setup()
{
  // Debug Console Output
  //Serial.begin(115200);
  //Serial.println(F("Start..."));

  pinMode(ButtonPin, INPUT_PULLUP);
  // Init Blinking lights
  for (Blinker &blinker : blinkGruppe) blinker.init();
}

void loop()
{
  // Run Main Blinking lights immediately after startup
  byte tmp = 1;
  blinkGruppe[BEACON].run(tmp);
  tmp = 1;
  blinkGruppe[NAVI].run(tmp);

  // Torpedo Launch Sequence
  // TBD: Should only be triggered three times and then quit.
  static byte blinkZahl = 0;
  if (!digitalRead(ButtonPin))
  {
    blinkZahl = 3;
  }
  blinkGruppe[TORPEDO].run(blinkZahl);
}

Beitrag #22 habe ich gerade erst gesehen :roll_eyes:

MoToSoftLed der Bibliothek MobaTools macht das auf allen Pins.

Danke für den Hinweis zu MoToSoftLed.
Das werde ich mir mal anschauen.

Das Bedeutet, ich könnte einfach die Bibliothek einbinden - und muss nur noch die gewünschten Parameter (Pin, etc.) übergeben... ?!

Was brauchst denn alles an Effekten? Bis jetzt habe ich nur blinken und faden gelesen, ... noch irgend etwas?

das sollten eigentlich erst einmal alle Effekte sein... vielleicht (aber da hab ich mich noch nicht entschlossen) sollen dann auch später passende Sound-Files mit ausgegeben werden... aber wie gesagt, das ist noch nicht Sicher - und kann auch außen vor bleiben.

Im Grunde sollen zu einem späteren Zeitpunkt ggf. noch einzelne Effekte über Taster - bzw. Touch-Sensoren aktiviert / deaktiviert werden können - aber das sollte dann das geringere Problem sein, wenn die Grundlagen für die Effekte gegeben sind.

Ach ja... btw:

Eine meiner Effekt-LEDs hängt an Pin13... wenn der Arduino startet, dann blitzt diese LED immer kurz auf - kann man das irgendwo für den Pin13 deaktivieren, oder sollte ich dann lieber einen anderen Pin nehmen?

[EDIT] - ok, das blitzen scheint vom Bootloader zu kommen... dann lasse ich das mal so stehen.
Entweder ich nehme das so hin - oder ändere den Pin für mein Projekt... ^^

Pins 11, 12 und 13 werden für Hardware-SPI verwendet, die solltest Du also freihalten, wenn Du diesen Bus mal nutzen möchtest. Da könnte man z. B. viele MCP23S17 anschließen.

ok, danke :slight_smile:
Werde ich versuchen im Hinterkopf zu behalten.

Ich glaube, für das Aktuelle Projekt ist das nicht notwendig... vielleicht irgendwann mal später, aber dann vermutlich auch mit einem neuen Board, da dieses hier fest in das Modell integriert wird.

ich hab jetzt nicht rauslesen können wo dein faden dran hängt, daher hab ich das mal auf pin 5 gelegt. Bei mir heißt das "heartbeat" und das dimmt dann von einem min Wert bis zu einem Max-Wert.
Paar sachen geklaut von Agmue, dann schauts so aus:

/*
  Blink LED and Heartbeat LED
  https://forum.arduino.cc/t/arduiono-projekt-fur-modellbau/953992/22
*/

#include <Noiasca_led.h>                     // download library from https://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm

enum Group : byte {BEACON, NAVI, TORPEDO};

BlinkPin blinkLed[]{13, 12, 10};
HeartbeatPin heartbeatLed {5};               // UNO PWM pins 3, 5, 6, 9, 10, 11 (on any other pin the LED will just blink)

void setup() {
  for (auto &i : blinkLed) i.begin();       // you have to call the .begin() method for the LED
  heartbeatLed.begin();   
  blinkLed[BEACON].setOnInterval(20);
  blinkLed[BEACON].setOffInterval(1000);
  blinkLed[NAVI].setOnInterval(700);
  blinkLed[NAVI].setOffInterval(1000);
  blinkLed[TORPEDO].setOffInterval(10);
  blinkLed[TORPEDO].setOnInterval(150);
  
  heartbeatLed.setInterval(10);         // you can modify the delay time for the pulsing. Default value is 50.
  heartbeatLed.setMinBrightness(50);    // you can limit the mininum brightness. Default value is 0
  //heartbeatLed.setMaxBrightness(240); // you can limit the maximum brightness. Default value is 255. Highest value on Arduino is 255. 
}

void loop() {
  for (auto &i : blinkLed) i.update();  // you have to call update() for the LED
  heartbeatLed.update();   
}

die Library findest du am Ende dieser Seite
https://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm und hat noch ein paar weitere Effekte.

Danke für die Mühe :slight_smile:
Ich werde das mal abspeichern - möchte es aber erst einmal ohne solche Bibliotheken versuchen :slight_smile:

Hallo Zusammen,

ich habe aktuell ein kleines Problem mit dem Programm, sowohl bei der Verwendung der Bibliothek von @noiasca wie auch mit dem ursprünglichen Code... :frowning:

Und zwar habe ich folgende "Blink-Sequenz" erstellt:

750 ms LED 1 und 2 ON
1500 ms LED 1 OFF 750ms LED 2 OFF
750 ms LED 2 ON

Es sollte also einmal beide LEDs aufleuchten, dann nur eine, dann wieder beide - dann nur eine.... immer im gleichen Rythmus.

Das Problem - nach einiger Zeit fangen die beiden LEDs an, außeinander zu laufen... ich vermute mal, das könnte das Problem sein, welches @my_xy_projekt in Reply #4 erwähnt hat?

Liegt es daran, dass millis() für beide LEDs nacheinander aufgerufen wird - und dadurch mit der Zeit einfach kleinere Differenzen aufaddiert werden?

Wie könnte ich dieses Problem angehen?

hier der aktuell verwendete Code dafür:

    // PWM functionallity not required
const byte NavigationLightsPin = 7;
const long NavigationLightsOnTime = 750;
const long NavigationLightsOffTime = 1500;

const byte PositionLightsPin = 2;
const byte PositionLightsOnTime = 750;
const byte PositionLightsOffTime = 750;

class Flasher {
  protected:
    const byte ledPin;
    const uint32_t onPhase;
    const uint32_t offPhase; 
    uint32_t timeMarker = 0; 
    
    enum _step : byte {Start, OnPhase, OffPhase};
    Flasher::_step step = _step::Start;

  public:
    Flasher(const byte ledPin, const unsigned long onPhase, const unsigned long offPhase): ledPin(ledPin), onPhase(onPhase), offPhase(offPhase) {}

    void run(byte & blinkAmount) {
      uint32_t now = millis();

      switch (step) {
        case _step::Start: 
          if (blinkAmount) {
            digitalWrite(ledPin, HIGH); 
            step = _step::OnPhase; 
            timeMarker = now;
          }
          break;
        case _step::OnPhase: 
          if (now - timeMarker >= onPhase) { 
            digitalWrite(ledPin, LOW);
            step = _step::OffPhase; 
            timeMarker = now;
          }
          break;
        case _step::OffPhase: 
          if (now - timeMarker >= offPhase) {  
            if (blinkAmount > 0) blinkAmount--; 
            step = _step::Start;  
          }
      }
    }

    void init() {
      pinMode(ledPin, OUTPUT);  
    }
};

enum FlashingGroup : byte {NAVI, TORPEDO, BEACON, POSITION};  
                         
Flasher flashingGroup[] = 
{                                          
  {NavigationLightsPin, NavigationLightsOnTime, NavigationLightsOffTime}, 
  {TorpedoLaunchLightPin, TorpedoLaunchOnTime, TorpedoLaunchOffTime},
  {BeaconLightPin, BeaconLightOnTime, BeaconLightOffTime}, 
 {PositionLightsPin, PositionLightsOnTime, PositionLightsOffTime},  
};

void setup()
{
  for (Flasher &flash : flashingGroup) flash.init();
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void loop()
{
  byte tmp = 1;
  flashingGroup[BEACON].run(tmp);
  flashingGroup[NAVI].run(tmp);
  flashingGroup[POSITION].run(tmp);
}

ja.
Wenn die 2 LEDs zu einander in Abhängigkeit stehen, würde ich sie nicht in separaten Klassen behandeln sondern in einer und stufenweise das Muster durchschalten.

probier mal diesen Ansatz - ohne Klasse: https://werner.rothschopf.net/microcontroller/202109_millis_led_sequence_en.htm

das pattern sieht lt. Deiner Beschreibung imho dann so aus:

const patternType pattern[] PROGMEM {
//  FEDCBA9876543210    // the LED pattern to be displayed, each bit defines one LED state, "paint" your pattern/sequence
  0b0000000000000011,   // LED A und LED B
  0b0000000000000000,   // all off
  0b0000000000000000,   // all off
  0b0000000000000010,   // nur LED B
  0b0000000000000000,   // all off
  0b0000000000000000,   // all off
  0b0000000000000001,   // nur LED A
  0b0000000000000000,   // all off
  0b0000000000000000,   // all off  
};

ist zwar overkill, aber so bekommst den sync sicher hin wenn du deine LEDs auf einer Zeitleiste gemeinsam betrachtest.

Wenn man in Klassen bleibt will weil man faden will fällt mir noch ein, dass man evtl. ein callback definiert und eine Master LED den anderen einen "Sync" sendet oder dass man der Runmethode die Millis übergibt. Also einmal millis() ermitteln und allen Run-Methoden übergeben.

Das wird nichts.
Mach mal: DATEI - VOREINSTELLUNGEN und setz den Haken bei: Ausführliche Ausgabe während [x] Kompilierung und darunter Compiler-Warnungen: ALLE

Las den mal laufen.

// PWM functionallity not required
const byte NavigationLightsPin = 7;
const unsigned long NavigationLightsOnTime = 750;
const unsigned long NavigationLightsOffTime = 1500;
unsigned long lastNavigationTime;

const byte PositionLightsPin = 2;
const unsigned long PositionLightsOnTime = 750;
const unsigned long PositionLightsOffTime = 750;
unsigned long lastPositionTime;

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  lastNavigationTime = millis();
  lastPositionTime = lastNavigationTime;
}

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void loop()
{
  unsigned long istTime = millis();
  if (istTime - lastNavigationTime >= NavigationLightsOnTime && digitalRead(NavigationLightsPin))
  {
    lastNavigationTime += NavigationLightsOnTime;
    digitalWrite(NavigationLightsPin, LOW);
    Serial.print(F("Navi an: ")); Serial.println(istTime);
  }
  if (istTime - lastNavigationTime >= NavigationLightsOffTime && !digitalRead(NavigationLightsPin))
  {
    lastNavigationTime += NavigationLightsOffTime;
    digitalWrite(NavigationLightsPin, HIGH);
    Serial.print(F("Navi aus: ")); Serial.println(istTime);
  }
  if (istTime - lastPositionTime >= PositionLightsOnTime && digitalRead(PositionLightsPin))
  {
    lastPositionTime += PositionLightsOnTime;
    digitalWrite(PositionLightsPin, LOW);
    Serial.print(F("Posi an: ")); Serial.println(istTime);
  }
  if (istTime - lastPositionTime >= PositionLightsOffTime && !digitalRead(PositionLightsPin))
  {
    lastPositionTime += PositionLightsOffTime;
    digitalWrite(PositionLightsPin, HIGH);
    Serial.print(F("Posi aus: ")); Serial.println(istTime);
  }
}

alternativ:
Ein Timer und durch eine FSM durchackern, nur mals schnell runtergeschrieben:

edit: nur Kommentare richtig gestellt:

/*
  Two Smooth LED in Sync

  Kombination of LED effects with a timer
  The two LEDs will 
  - smoothly blink in a pattern Both on - off - A on - off - B on - off

  you must connect the LED to a PWM enabled PIN

  by noiasca
  2022-02-14   used in https://forum.arduino.cc/t/arduiono-projekt-fur-modellbau/953992/35
*/

#include <Noiasca_led.h>               // download library from https://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm
#include <Noiasca_timer.h>

SmoothPin smoothLedA {10};               // UNO PWM pins 3, 5, 6, 9, 10, 11
SmoothPin smoothLedB {11};

// here you create your timer object
// you set one parameters: the intervall in milliseconds
LittleTimer littleTimer {750};

void setup() {
  Serial.begin(115200);
  smoothLedA.begin();   // you have to call the .begin() method for the LED pair
  smoothLedB.begin();   // you have to call the .begin() method for the LED pair

  //smoothLedA.on();                  // switch the LED on
  //smoothLedA.off();                 // switch the LED off
  //smoothLedA.setOnInterval(30);     // modify the delay time for the smothing
  //smoothLedA.setOffInterval(5);     // modify the delay time for the smothing
  //smoothLedA.setMaxBrightness(240); // limit the maximum brightness. Highest value on Arduino is 255. Default 255.
}

void loop() {
  // AB, off, off, A, off, off, B, off, off

  static byte state = 0;
  if (littleTimer.hasTriggered())
  {
    if (state < 8)
      state++;
    else
      state = 0;
    switch (state)
    {
      case 0 :
        smoothLedA.on();
        smoothLedB.on();
        break;
      case 1 :
      case 2 :
        smoothLedA.off();
        smoothLedB.off();
        break;
      case 3 :
        smoothLedA.on();
        break;
      case 4:
      case 5:
        smoothLedA.off();
        smoothLedB.off();
        break;
      case 6 :
        smoothLedB.on();
        break;
      case 7 :
      case 8:
        smoothLedA.off();
        smoothLedB.off();
        break;
    }
  }
  smoothLedA.update();   // you have to call update() for the LED
  smoothLedB.update();
}

evtl. muss das aufdimmen/updimmen etwas schneller machen, aber das muss dir halt gefallen.
Mit

setOnInterval(10)

kommt es gut.

Das wird nichts.
Mach mal: DATEI - VOREINSTELLUNGEN und setz den Haken bei: Ausführliche Ausgabe während Kompilierung und darunter Compiler-Warnungen: ALLE

Danke, hab das wohl von der falschen Stelle kopiert :frowning:

Habe ich jetzt mal korrigiert - und werde ich nochmal versuchen, aber ich glaube, dass das Problem dennoch nach einer gewissen Zeit auftreten dürfte...
Ansätze, welche ich noch probieren werde, wurden ja bereits genannt :slight_smile:

Vielen Dank an dieser Stelle schon mal :slight_smile:

Nein, ich kann Dir sagen, das ich in meinem Sketch oben zwar einen Fehler drin habe, aber der läuft nicht auseinander.

achso, sorry - misverständnis :slight_smile:

Ich habe aktuell nur die long / int debugged... alles weitere konnte ich z.Z. noch nicht testen und hatte mich daher gerade nur auf die Änderung bei der Variablen bezogen.
Soryy.

Dann schreib in meinen oben mal noch ins setup:

  pinMode(NavigationLightsPin, OUTPUT);
  pinMode(PositionLightsPin,OUTPUT);

Auf dem SerMon sehe ich dann unter anderem an einer Position, das es auseinanderlaufen will. Aber ich hole das im nächsten Umlauf wieder auf:

2:42:41.970 -> Posi an: 12000
12:42:42.699 -> Navi aus: 12750
12:42:42.699 -> Posi aus: 12750
12:42:43.460 -> Navi an: 13500
12:42:43.460 -> Posi an: 13500
12:42:44.222 -> Posi aus: 14251   <== Hier will er weglaufen
12:42:44.951 -> Navi aus: 15000
12:42:44.951 -> Posi an: 15000  <== Und hier ist er wieder im Takt
12:42:45.713 -> Navi an: 15750
12:42:45.713 -> Posi aus: 15750
12:42:46.475 -> Posi an: 16500