WS2182b Arduino Steuerung Modelbahn

Hallo,
ich möchte auf meiner Modelbahn die Häuser mit WS2182b Ledstrips beleuchten. Das ganze soll über einen Arduino uno gesteuert werden.
Ich habe jedoch das Problem, dass ich die jeweiligen Häuser gerne einzeln programmieren möchte, diese aber im Loop gleichzeitig ablaufen sollen. Also Haus 1 und Haus 2 beginnen ihre Schleife zur gleichen Zeit.
Meine Beispielprogrammierung (2 LEDs) sieht bis jetzt folgendermaßen aus:


#include <Adafruit_NeoPixel.h>
#include <FastLED_NeoPixel.h> 
#define PIN         6
#define NUMPIXELS   60
#define BRIGHTNESS 50
#define BLINK_TIME 1000
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


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

void loop() {
 Haus1();
 Haus2();
}


void Haus1() {
  pixels.setPixelColor(1, pixels.Color(100,0,0));
  pixels.show();
  delay(1000);
  pixels.setPixelColor(1, pixels.Color(0,0,0));
  pixels.show();
  delay(1000);
}

void Haus2(){
  pixels.setPixelColor(2, pixels.Color(0,50,0));
    pixels.show();
    delay(1000);
  pixels.setPixelColor(2, pixels.Color(0,0,0));
    pixels.show();
    delay(1000);
}

Leider arbeitet der Loop logischerweise die Schleifen nacheinander ab und nicht zur selben Zeit. Gibt es hierfür eine Lösung?

Herzliche Grüße
Johannes

Ja, blockadearme Programmierung mit millis() anstelle delay().

1 Like

löse deine blockierenden delay() auf.
Mach das mal am Beispiel discreter LEDs so wie in "BlinkWithoutDelay".

Wenn du das für 2, 3 LEDs hast, dann kannst du das auch einfach auf die Neopixel übertragen.

Ähnliches Thema haben wir hier öfters, aber ich befürchte, das ist noch etwas zu schwierig für dich und daher eher mal den Weg über BlinkWithout Delay gehen.

https://forum.arduino.cc/t/2-sketche-verbinden/639965/12
so sieht ersters aus: https://youtu.be/d2wVjrqa-Ok

oder
LED Programmierbar Lauflicht - #46 by noiasca

es geht immer darum einen Strip zu haben und darauf mehrere "Objekte" anzulegen und dann jedem Objekt seine eigene Zeitsteuerung zu geben.

Umgelegt für deiner Häuser:
Jedes Haus ist ein Objekt und es weis selber wenn es Zeit ist das Licht aufzudrehen.

1 Like

Hallo,
mit delay wird das nichts, da legt der Arduino eine Pause ein. Schau dir mal millis() an und den Nachtwächter. Ein Beipiele in der IDE dazu ist blinkWithoutDelay.
Heinz

1 Like

Es gibt eine Lib , die MobaLedLib , die exakt für diese Anwendung geschrieben wurde.

Edit: hat sogar eine eigene wiki-Seite

1 Like

Dein Programm in blockadearmer Ausführung:

#include "FastLED.h"

const int HAEUSER = 2;
CRGB leds[HAEUSER];

#define LED_TYPE   WS2812
#define COLOR_ORDER   GRB
#define DATA_PIN       12

void setup()
{
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, HAEUSER);
}

enum {HAUS1, HAUS2};

void loop() {
  if ( animation_haus(HAUS1) || animation_haus(HAUS2) ) FastLED.show();
}

bool animation_haus( const byte haus) 
{
  uint32_t jetzt = millis();
  const CRGB farbe[HAEUSER] = {CRGB(100, 0, 0), CRGB(0, 50, 0)};
  const uint32_t zeit[HAEUSER] = {1000, 555};
  static uint32_t vorhin[HAEUSER] = {0, 0};
  static bool ANaus[HAEUSER];
  bool neu = false;

  if (jetzt - vorhin[haus] >= zeit[haus])
  {
    if (ANaus[haus])
    {
      leds[haus] = CRGB::Black;
    } else {
      leds[haus] = farbe[haus];
    }
    vorhin[haus] = jetzt;
    ANaus[haus] = !ANaus[haus];
    neu = true;
  }
  return neu;
}
2 Likes

vielen vielen Dank! ich bin noch neu auf dem Gebiet und kann mit Vielem noch nichts anfangen. Das hilft mir sehr um es nachzuvollziehen!

Bei Fragen fragen, manchmal gibt es gute Antworten :slightly_smiling_face:

Danke ich muss die Komponenten glaube ich erstmal nachvollziehen. Es sollen am Ende ja dann mehrere LEDs für ein Haus sein. kann man das wie in meinem Beispiel auch in einzelne Überblicksmäßige Abschnitte untergliedern also ein Bereich zu Haus 1 dann einen seperaten zu Haus 2?

ich würde da einfach zwei Parameter vorsehen, das "Startpixel" für das Häuschen und die Anzahl der Pixel des Häuschen.

Alle Häuschen dann als Array anlegen und dann ist das nur jeweils ein ein einzige neue Zeile je Haus.

1 Like

Der Klassiker für die Modellbahn, das belebte Haus.

Ja, geht selbstverständlich. Der µC kann es locker, ob Du es kannst, wird sich herausstellen :rofl:

Sorry für die Offenheit, wollte es nur mal deutlich gemacht haben.

1 Like

ja danke erstmal soweit. ich probiere mal mein Glück :smiley:

Hallo,
noch eine Idee , Du könntest die ein und aus Zeiten per Zufall entstehen lassen, ist noch ein bisschen realistischer.
Heinz

Oder etwas diese Idee erweitert: Die grundlegenden Zeiten lassen, aber darauf eine kleine Verschiebung per Zufall überlagern. Auf Startzeit- und/oder Länge.

Gruß Tommy

vielen Dank ich denke damit muss ich mich später befassen. Ich frage mich noch wie man in meinem Beispiel bei Haus 1 das Delay durch eine millis Funktion ersetzen kann und das gleiche dann in dem Void für Haus 2.

das hat dir agmue doch schon in #6 gezeigt. dort ist es die Zeile

if (jetzt - vorhin[haus] >= zeit[haus])

wenn du das mit dem IDE Beispiel "BlinkWithoutDelay" vergleichst, dann ist das genau das wie du deine delay los wirst.

Und hier boch was für 3 Häuschen und die Häuschen individuell mit mehreren Räumen:

/*
  Use pixels in a strip as separate objects
  by noiasca

  2020-03-02 https://forum.arduino.cc/index.php?topic=668289.15
             https://forum.arduino.cc/index.php?topic=668125
  2020-12-31 https://forum.arduino.cc/index.php?topic=720583.0
  2022-01-08 houses: https://forum.arduino.cc/t/ws2182b-arduino-steuerung-modelbahn/944662/3
*/
#include <Adafruit_NeoPixel.h>

const byte ledPin = 12;                // Which pin on the Arduino is connected to the NeoPixels?
const uint16_t ledCount = 128;

Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800);

class House {
  private:
    byte state = 1;                // 0 Effect off, 1 Effect on
    unsigned long previousMillis;  // used for timekeeping
    uint16_t on = 200;             // intervall
    uint32_t colorOn = 0xAAFF44;
    uint32_t colorOff = 0x000000;
    int8_t actual = 0;
    uint16_t rnd = 10;             // the higher the less changes
    Adafruit_NeoPixel& obj;
    const byte id;                 // first pixel of this group
    const byte leds;               // how many leds

  public:
    House(Adafruit_NeoPixel& obj, byte id, byte leds):
      obj(obj),
      id(id),
      leds(leds)
    {}

    void setInterval(uint16_t _on)  // modify on/off times during runtime
    {
      on = _on;
    }

    void setRandom(uint16_t _rnd)   // frequency of changes
    {
      rnd = _rnd;
    }

    void setState(byte _state)            // 0 switch off Effect; 1 Switch on effect;
    {
      state = _state;
      if (state == 0)
      {
        for (int i = 0; i < leds; i++)
          obj.setPixelColor(id + i, colorOff);
      }
    }

    void setColor(uint32_t newColorOn, uint32_t newColorOff = 0x000000)
    {
      colorOn = newColorOn;
      colorOff = newColorOff;
    }

    void update()
    {
      if (state)
      {
        uint32_t currentMillis = millis();
        if (currentMillis - previousMillis >= on) {
          previousMillis = currentMillis;  // save the last time we changed some LED
          if (random(rnd) == 0)
          {
            if (obj.getPixelColor(id + actual) == colorOff)
              obj.setPixelColor(id + actual, colorOn);
            else
              obj.setPixelColor(id + actual, colorOff);
          }
          actual++;
          if (actual >= leds) actual = 0;
          obj.show();
        }
      }
    }
};

House house[]
{
  // strip,start, used pixels
  {strip, 0, 1},  // house starting at pixel 0, with one room/LED
  {strip, 1, 6},  // house starting at pixel 1, with 6 rooms/LEDs
  {strip, 7, 4}   // house starting at pixel 7, with 4 rooms/LEDs
};

void setup() {
  Serial.begin(115200);
  Serial.println(F("\nStart"));
  randomSeed(A7);
  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)

  house[0].setInterval(300);       // speed of changes
  house[0].setRandom(10);          // frequence of changes
  //house[0].setState(0);          // switch off
  house[1].setColor(0xFFFFFF);     // cold white
  house[2].setColor(0xFFFF33);     // white
}

void loop() {
  for (auto &i : house) i.update();
}

Das System ist wieder das gleiche: es ist ein "BlinkWithoutDelay".

Sollte ich es noch nicht erwähnt haben:
Schau dir das Beispiel "BlinkWithoutDelay" an - es ist die Mutter aller deiner künftigen Sketche.

Chapeau!

Da komme ich mir mit meinen zwei Häuschen etwas popelig vor:

#include "FastLED.h"

const int ALLEZIMMER = 8;
CRGB leds[ALLEZIMMER];

#define LED_TYPE   WS2812
#define COLOR_ORDER   GRB
#define DATA_PIN       12

void setup()
{
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, ALLEZIMMER);
}

void loop() {
  if ( haus1(0, 3) || haus2(3, 5) ) FastLED.show();
}

bool haus1( const byte start, const byte anzahl)
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  static uint32_t zeit = 0;
  static uint8_t schritt = 1;
  bool neu = false;
  enum {KUECHE, WOHN, SCHLAF};

  if (jetzt - vorhin >= zeit)
  {
    switch (schritt)
    {
      case 1:
        zeit = random(0, 3000);
        break;
      case 2:
        leds[start + KUECHE] = CRGB::White;
        zeit = random(1000, 3000);
        break;
      case 3:
        leds[start + WOHN] = CRGB::White;
        zeit = random(1000, 5000);
        break;
      case 4:
        leds[start + KUECHE] = CRGB::Black;
        zeit = random(3000, 5000);
        break;
      case 5:
        leds[start + WOHN] = CRGB::Black;
        leds[start + SCHLAF] = CRGB::White;
        zeit = random(1000, 2000);
        break;
      case 6:
        leds[start + SCHLAF] = CRGB::Black;
        zeit = random(5000, 10000);
        break;
      default:
        schritt = 0;
    }
    schritt++;
    vorhin = jetzt;
    neu = true;
  }
  return neu;
}

bool haus2( const byte start, const byte anzahl)
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  static uint32_t zeit = 0;
  static uint8_t schritt = 1;
  bool neu = false;
  enum {KUECHE, WOHN, SCHLAF, KIND1, KIND2};

  if (jetzt - vorhin >= zeit)
  {
    switch (schritt)
    {
      case 1:
        zeit = random(0, 3000);
        break;
      case 2:
        leds[start + KUECHE] = CRGB::White;
        zeit = random(1000, 3000);
        break;
      case 3:
        leds[start + WOHN] = CRGB::White;
        zeit = random(1000, 5000);
        break;
      case 4:
        leds[start + KUECHE] = CRGB::Black;
        leds[start + KIND1] = CRGB::White;
        leds[start + KIND2] = CRGB::White;
        zeit = random(3000, 5000);
        break;
      case 5:
        leds[start + KIND1] = CRGB::Black;
        leds[start + KIND2] = CRGB::Black;
        zeit = random(1000, 2000);
        break;
      case 6:
        leds[start + WOHN] = CRGB::Black;
        leds[start + SCHLAF] = CRGB::White;
        zeit = random(1000, 2000);
        break;
      case 7:
        leds[start + SCHLAF] = CRGB::Black;
        zeit = random(5000, 10000);
        break;
      default:
        schritt = 0;
    }
    schritt++;
    vorhin = jetzt;
    neu = true;
  }
  return neu;
}

brauchst 'nen Fernseher dazu?

class TV {
  private:
    byte state = 1;                // 0 Effect off, 1 Effect on
    unsigned long previousMillis;  // used for timekeeping
    uint16_t on = 200;             // intervall
    uint32_t colorOn = 0xAAFF44;
    uint32_t colorOff = 0x000000;
    int8_t actual = 0;
    uint16_t rnd = 5;              // the higher the less changes
    Adafruit_NeoPixel& obj;
    const byte id;                 // first pixel of this group

  public:
    TV(Adafruit_NeoPixel& obj, byte id):
      obj(obj),
      id(id)
    {}

    void setInterval(uint16_t _on)  // modify on/off times during runtime
    {
      on = _on;
    }

    void setRandom(uint16_t _rnd)   // frequency of changes
    {
      rnd = _rnd;
    }

    void setState(byte _state)            // 0 switch off Effect; 1 Switch on effect;
    {
      state = _state;
      if (state == 0)
      {
        obj.setPixelColor(id, colorOff);
      }
    }

    void setColor(uint32_t newColorOn, uint32_t newColorOff = 0x000000)
    {
      colorOn = newColorOn;
      colorOff = newColorOff;
    }

    void update()
    {
      if (state)
      {
        uint32_t currentMillis = millis();
        if (currentMillis - previousMillis >= on) {
          previousMillis = currentMillis;  // save the last time we changed some LED
          if (random(rnd) == 0)
          {
            byte r, g, b;
            r = random(255);
            g = random(255);
            b = random(63);
            obj.setPixelColor(id, obj.Color(r, g, b));
          }
          obj.show();
        }
      }
    }
};

:wink:

@jost2619 : du siehst - auch hier findet sich ein "BlinkWithoutDelay" wieder.

Vielen Dank. Ich dachte das Ganze wäre für unerfahrene etwas leichter in der Aneignung. Das Beispiel Blinkwithout Delay kann ich soweit nachvollziehen. Da es jedoch mit einer LED funktioniert und nicht mit einem WS 2812 habe ich Schwierigkeiten die if (ledState == LOW) Bedingung auf den LED Strip zu übertragen und in euren fertigen Programmierungen nachzuvollziehen. Danke trotzdem für eure Bemühungen.

Das entspricht meinem

if (ANaus[haus])