Irremote unvollständige Auswertung in while Schleife

Ich versuch hier gerade die rainbow func im Dauerbetrieb laufen zu lassen für den angeschlossenen LED-Strip. Um trotzdem während der while schleife auf neue Inputs über Infrarot reagieren zu können hab ich da n bisschen was geschrieben. Jetzt allerdings das Problem:

Wenn er in der While schleife ist, kann er gesendete Infrarotsignale nicht sinnvoll entschlüsseln. Im Normalen Modus schon. Und langsam fang ich echt an einen absoluten Spaghetti-Code zu produzieren bei der Fehlersuche und ich glaub ich hab mir echt einiges langsam überkompliziert dadurch.

Hier mal der "Code" (Will ihn wirklich nicht so nennen)

// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// Released under the GPLv3 license to match the rest of the
// Adafruit NeoPixel library

#include <Arduino.h>
#include <IRremote.hpp>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>  // Required for 16 MHz Adafruit Trinket
#endif

const int IR_RECEIVE_PIN = 7;
unsigned long key_value = 0;
unsigned long last_code = 0;
unsigned long key_value_old = 0;
unsigned long code_breaker = 0;
int breakOnUpdate = 0;

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN 8  // On Trinket or Gemma, suggest changing this to 1

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 256  // Popular NeoPixel ring size

// NeoPixel brightness, 0 (min) to 255 (max)
int BRIGHTNESS = 3;  // Set BRIGHTNESS to about 1/5 (max = 255)

// When setting up the NeoPixel library, we tell it how many pixels,
// and which pin to use to send signals. Note that for older NeoPixel
// pixelss you might need to change the third parameter -- see the
// strandtest example for more information on possible values.
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

#define DELAYVAL 50  // Time (in milliseconds) to pause between pixels

void setup() {
  Serial.begin(9600);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);  // Start the receiver
  // These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
  // Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  // END of Trinket-specific code.

  pixels.begin();  // INITIALIZE NeoPixel pixels object (REQUIRED)
  pixels.show();   // Turn OFF all pixels ASAP
  pixels.setBrightness(BRIGHTNESS);
}


void loop() {
  checkForUpdates();
}

void checkForUpdates() {
  if (IrReceiver.decode()) {
    Serial.println(IrReceiver.decode());
    // IrReceiver.printIRResultShort(&Serial);
    IrReceiver.resume();  // Enable receiving of the next value
    if (IrReceiver.decodedIRData.numberOfBits > 31) {
      Serial.println("32 Bit");
      key_value = IrReceiver.decodedIRData.decodedRawData;
      code_breaker = IrReceiver.decodedIRData.decodedRawData;
      // Serial.print("11Key Value am Start von Decode: ");
      // Serial.println(key_value, HEX);
      // Serial.print("12Key Value Old am Start von Decode: ");
      // Serial.println(key_value_old, HEX);
      ledCode(key_value);
    } else if (IrReceiver.decodedIRData.numberOfBits > 8 && IrReceiver.decodedIRData.numberOfBits < 31) {
      Serial.println("Unter 32 Bit");
      if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) {
      } else {
        IrReceiver.printIRResultShort(&Serial);
        code_breaker = IrReceiver.decodedIRData.decodedRawData;
      }
    } else {
      if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) {
        Serial.println("Repeat");

        ledCode(key_value);
      }
    }
  }
}

void checkForCodeBreaks() {
  if (IrReceiver.decode()) {
    if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) {
    } else {
      if (IrReceiver.decodedIRData.numberOfBits > 8) {
        code_breaker = IrReceiver.decodedIRData.decodedRawData;
      }
    }
    IrReceiver.resume();
  }
}

void ledCode(long key_value) {
  Serial.print("1Key Value am Start von ledCode: ");
  Serial.println(key_value, HEX);
  Serial.print("2Code Breaker am Start von ledCode: ");
  Serial.println(code_breaker, HEX);
  Serial.print("3key value old am Start von ledCode: ");
  Serial.println(key_value_old, HEX);
  if (key_value == 0xF807FF00) {  // Button An
    key_value = key_value_old;
    code_breaker = key_value_old;
    Serial.println("Breaker");
  }
  Serial.println(key_value, HEX);
  delay(100);
  switch (key_value) {
    case 0xF906FF00:  // Button Aus
      pixels.clear();
      pixels.show();
      break;
    case 0xF609FF00:  //Farbe Rot
      key_value_old = 0xF609FF00;
      colorSwitch(pixels.Color(255, 0, 0));
      break;
    case 0xF708FF00:  //Farbe Grün
      key_value_old = 0xF708FF00;
      colorSwitch(pixels.Color(0, 255, 0));
      break;
    case 0xF10EFF00:  // Farbe Blau Hell
      key_value_old = key_value;
      colorSwitch(pixels.Color(0, 0, 255));
      break;
    case 0xF40BFF00:
      key_value_old = key_value;
      colorSwitch(pixels.Color(128, 128, 128));
      break;
    case 0xF50AFF00:  // Farbe Blau Dunkel
      key_value_old = key_value;
      colorSwitch(pixels.Color(20, 0, 255));
      break;
    case 0xF20DFF00:  // Farbe Orange
      key_value_old = key_value;
      colorSwitch(pixels.Color(255, 147, 5));
      break;
    case 0xF30CFF00:  // Farbe Hellgrün
      key_value_old = key_value;
      colorSwitch(pixels.Color(118, 235, 2));
      break;
    case 0xEE11FF00:  // Farbe Gelb
      key_value_old = key_value;
      colorSwitch(pixels.Color(252, 244, 3));
      break;
    case 0xFA05FF00:  // Helligkeit hoch
      BRIGHTNESS++;
      pixels.setBrightness(BRIGHTNESS);
      pixels.show();
      break;
    case 0xFB04FF00:  // Helligkeit runter
      BRIGHTNESS--;
      if (BRIGHTNESS > 0) {
        pixels.setBrightness(BRIGHTNESS);
        pixels.show();
      }
      break;
    case 0xED12FF00:  //Farbe Pink
      key_value_old = 0xED12FF00;
      colorSwitch(pixels.Color(202, 21, 230));
      break;
    case 0xE41BFF00:  // Button Fade
      key_value_old = 0xE41BFF00;
      while (breakOnUpdate == 0) {  // Repeat 30 times...
        checkForUpdates();
        if (key_value == 0xE41BFF00 && code_breaker == 0xE41BFF00) {
          for (int h = 1; h < 32; h++) {
            checkForUpdates();
            pixels.setBrightness(h);
            delay(10);
            pixels.show();
          }
          for (int g = 32; g > 1; g--) {
            checkForUpdates();
            pixels.setBrightness(g);
            delay(50);
            pixels.show();
          }
        } else {
          breakOnUpdate = 1;
          key_value = IrReceiver.decodedIRData.decodedRawData;
        }
      }
      pixels.setBrightness(BRIGHTNESS);
      pixels.show();
      break;
    case 0xE817FF00:  // Button Strobe
      key_value_old = 0xE817FF00;
      IrReceiver.resume();
      theaterChaseRainbow(50);  // Rainbow-enhanced theaterChase variant
      break;
    case 0xEC13FF00:  // Button Smooth
      key_value_old = 0xEC13FF00;
      // IrReceiver.resume();
      rainbow(10);  //
      break;
    case -1935048056:
      pixels.clear();  // Set all pixel colors to 'off'
      for (int j = 0; j < 256; j++) {
        pixels.setPixelColor(j, pixels.Color(0, 150, 0));
      }
      pixels.show();  // Send the updated pixel colors to the hardware.
      break;
  }
  Serial.print("Key Value am Ende von ledCode: ");
  Serial.println(key_value, HEX);
  Serial.print("Code Breaker am Ende von ledCode: ");
  Serial.println(code_breaker, HEX);
  Serial.print("key value old am Ende von ledCode: ");
  Serial.println(key_value_old, HEX);
  breakOnUpdate = 0;
}


void colorSwitch(uint32_t color) {
  for (int i = 0; i < pixels.numPixels(); i++) {  // For each pixel in pixels...
    pixels.setPixelColor(i, color);               //  Set pixel's color (in RAM)
  }
  pixels.show();
}

void theaterChaseRainbow(int wait) {
  int firstPixelHue = 0;        // First pixel starts at red (hue 0)
  while (breakOnUpdate == 0) {  // Repeat 30 times...
    checkForUpdates();
    if (key_value == 0xE817FF00 || key_value == 0xF807FF00 && code_breaker == 0xE817FF00) {
      for (int b = 0; b < 3; b++) {  //  'b' counts from 0 to 2...
        pixels.clear();
        // 'c' counts up from 'b' to end of pixels in increments of 3...
        for (int c = b; c < pixels.numPixels(); c += 3) {
          // hue of pixel 'c' is offset by an amount to make one full
          // revolution of the color wheel (range 65536) along the length
          // of the pixels (pixels.numPixels() steps):
          int hue = firstPixelHue + c * 65536L / pixels.numPixels();
          uint32_t color = pixels.gamma32(pixels.ColorHSV(hue));  // hue -> RGB
          pixels.setPixelColor(c, color);                         // Set pixel 'c' to value 'color'
        }
        pixels.show();                // Update pixels with new contents
        delay(wait);                  // Pause for a moment
        firstPixelHue += 65536 / 90;  // One cycle of color wheel over 90 frames
      }
    } else {
      breakOnUpdate = 1;
    }
  }
  key_value = IrReceiver.decodedIRData.decodedRawData;
}

void rainbow(int wait) {
  while (breakOnUpdate == 0) {  // Repeat 30 times...
    // Hue of first pixel runs 5 complete loops through the color wheel.
    // Color wheel has a range of 65536 but it's OK if we roll over, so
    // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
    // means we'll make 5*65536/256 = 1280 passes through this loop:
    if (key_value == 0xF807FF00) {
      key_value = key_value_old;
    }
    for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256) {
      checkForCodeBreaks();
      // Serial.print("Key Value am Start von rainbow: ");
      // Serial.println(key_value, HEX);
      // Serial.print("Code Breaker am Start von rainbow: ");
      // Serial.println(code_breaker, HEX);
      // Serial.print("key value old am Start von rainbow: ");
      // Serial.println(key_value_old, HEX);
      // Serial.print("buttonOn am Start von rainbow: ");
      // Serial.println(buttonOn);
      if (key_value == 0xEC13FF00 && code_breaker == 0xEC13FF00) {
        // pixels.rainbow() can take a single argument (first pixel hue) or
        // optionally a few extras: number of rainbow repetitions (default 1),
        // saturation and value (brightness) (both 0-255, similar to the
        // ColorHSV() function, default 255), and a true/false flag for whether
        // to apply gamma correction to provide 'truer' colors (default true).
        pixels.rainbow(firstPixelHue);
        // Above line is equivalent to:
        // pixels.rainbow(firstPixelHue, 1, 255, 255, true);
        pixels.show();  // Update pixels with new contents
      } else {
        breakOnUpdate = 1;
        IrReceiver.printIRResultShort(&Serial);
        Serial.println(key_value, HEX);
        break;
      }
    }
  }
}

Danke für eure Hilfe!

Hallo
Du hast in der while schleife ein delay drin um eine Pause zu machen. Das würd so nichts.
Schau dir das Beispiel "blinkwithoutDelay" an und suche hier im Forum nach der Nachtwächter Erklärung.

Mach das also dann konsequent mit millis() ohne Pause. Und frage die vergangene Zeit seit dem letzten Umlauf ab. Wenn die Zeit abgelaufen ist macht du was immer dann zu tun ist.

Danke für die Antwort, allerdings ist in der void rainbow kein delay drin, da hab ich das selbe Problem. Selbst wenn ich das Delay aus der theaterChaseRainbow raus mach, bleibt das Problem dass der IR Receiver trotzdem unter 32 Bit als Signal empfängt und ich die Taste zwei mal drücken muss bis er auf die richtige Farbe umschält

Das IST Spaghetti Code. Was mir aufgefallen ist:

unsigned long key_value = 0;  <- global
void ledCode(long key_value) in dieser Funktion case -1935048056:

Die Funktion checkForUpdates() verwendet das globale key_value (unsigend long)

Du solltest schon durchgängig usigned long oder uint32_t für den IR-Code verwenden.
Du erhältst, je nach Datentyp, unterschiedliche Werte. Auch, wenn das bei einer Ausgabe als HEX Wert nicht unbedingt auffällt.

Hallo
Ich hab das jetzt nicht nachgerechnet
256 Led benötigen schon ein Weilchen bis da die Daten raus sind. Led.show () ist da zuständig . Zudem werden währen der Zeit einige Interrupts von der lib gesperrt.

Während der Zeit werden keine Daten von der Fernbedienung erkannt.
Wenn der Sketch jetzt nur damit beschäftigt fast ständig led.show() zu machen dann würde ich erst mal nur 10 Led ansteuern um zu sehen ob das einen Einfluss hat und dazwischen die Fernbedienung einlesen. Aber das machst du ja glaube ich.

1 Like

Glaube ich eher nicht.
show() schreibt alle NUMLEDS-Pixeldaten raus, egal wie viele Du geändert hast.
Wenn Du das teilen willst, musst Du mehrere LED-Stränge an verschiedenen Pins nutzen, die Du dann versetzt ansteuern kannst.
Die show-Aufrufe würde ich auch aus den for-Schleifen raus nehmen und nur eins, wenn alle LED gefüllt sind.
Evtl. auch mal überlegen auf FastLED zu gehen, da die Adafruit-Libs nicht als besonders flink gelten.

Gruß Tommy

Hi @toastiman ,

Dein Verdacht mit dem "Spaghetti-Code" ist nicht von der Hand zu weisen, wobei bei die einzelnen "realen" Spaghetti jeweils nur einen Anfang und ein Ende haben ... :wink:

In der loop() rufst Du

checkForUpdates()

auf. Innerhalb dieser Funktion rufst Du an verschiedenen Stellen

ledCode(key_value);

auf. Und in der Funktion ledCode() wiederum mehrfach (auch in for-loops!) wiederum checkForUpdates().

Das ist ein kaum zu beherrschendes Konstrukt.

Besser:

  • Die Funktionalitäten "IR-Code empfangen" und "LED-Streifen ansteuern" getrennt realisieren
  • Die Funktionen der LED-Streifen als State Machine auslegen
  • Alle delays durch millis()-Funktionen ersetzen

Beide Funktionen werden dann in der loop() (schnell) nacheinander aufgerufen. Der Status der "LED-Maschine" ändert sich bei entsprechendem IR-Code.

Die Umstellung erfordert auf jeden Fall ein Umdenken bezüglich der for-loops in der Streifen-Ansteuerung. Die Funktion der for-loops wird dabei während des mehrfachen Durchlaufens der State Maschine erledigt...

Da das am Anfang für Dich nicht so ganz einfach sein könnte, wäre mein Vorschlag, dass Du zunächst die Funktion für den IR-Empfang angehst.

Wie @Rentner schon schrub: Das Konstrukt mit den Schleifen ist sehr Zeitaufwändig.
Dieser Teil hier tut mir unter anderem sehr weh:

    case 0xE41BFF00:  // Button Fade
      key_value_old = 0xE41BFF00;
      while (breakOnUpdate == 0)    // Repeat 30 times...
      {
        checkForUpdates();
        if (key_value == 0xE41BFF00 && code_breaker == 0xE41BFF00)
        {
          for (int h = 1; h < 32; h++)
          {
            checkForUpdates();
            pixels.setBrightness(h);
            delay(10);
            pixels.show();
          }
          for (int g = 32; g > 1; g--)
          {
            checkForUpdates();
            pixels.setBrightness(g);
            delay(50);
            pixels.show();
          }
        }
        else
        {
          breakOnUpdate = 1;
          key_value = IrReceiver.decodedIRData.decodedRawData;
        }
      }
      pixels.setBrightness(BRIGHTNESS);
      pixels.show();
      break;

Was sind die 32 da drin?
Und wenn Du 32 mal 50ms nimmst, bist Du schon bei mehr als 1,5 Sekunden.
Und dreist, wenn das IR-Update aufgenommen wird, wird erst der Codeteil zu Ende abgespielt.

Dann hast Du auch noch sowas hier:

    case 0xFA05FF00:  // Helligkeit hoch
      BRIGHTNESS++;
      pixels.setBrightness(BRIGHTNESS);
      pixels.show();
      break;

Geichzeitig auch noch das:

// NeoPixel brightness, 0 (min) to 255 (max)
int BRIGHTNESS = 3;  // Set BRIGHTNESS to about 1/5 (max = 255)

Ein int sind mehr als 255 und gehen zudem, u.a. beim Überlauf, ins negtive. Das führt möglicherweise zu weiteren unschönen Effekten.

Ich würde die Schleifen alle auflösen, oder zumindest die Funktion checkForUpdates() mit einem Rückgabecode versehen, sodass es möglich ist darauf zu reagieren und aus der laufenden Schleife auszubrechen.

Danke für die zahlreichen Antworten! Ich setz mich jetzt erstmal hin, bau mal die ganzen Tipps ein und entwirr das mal in zwei seperate Funktionen und schau dann nochmal

Ich hab das jetzt mal überarbeitet und siehe da, jetzt tritt der Fehler konstant auf. Also eine Verschlimmbesserung. Ich hab das mal kurzgefasst, damit das Problem ersichtlicher ist.

#include <Arduino.h>
#include <IRremote.hpp>
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>  // Required for 16 MHz Adafruit Trinket
#endif

const int IR_RECEIVE_PIN = 7;
unsigned long received_signal_code = 0;
unsigned long saved_code = 0;
unsigned long currentMillis;
unsigned long startMillis;
const unsigned long period = 100;
bool is_repeat = false;

#define PIN 8
#define NUMPIXELS 24
int BRIGHTNESS = 3;

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  Serial.begin(9600);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  pixels.begin();
  pixels.show();
  pixels.setBrightness(BRIGHTNESS);
  startMillis = millis();  //initial start time
}

void loop() {
  checkForUpdates();
  ledCode(received_signal_code);
}

void checkForUpdates() {
  if (IrReceiver.decode()) {
    is_repeat = false;
    IrReceiver.printIRResultShort(&Serial);
    Serial.println(IrReceiver.decodedIRData.protocol);
    if (IrReceiver.decodedIRData.numberOfBits > 31) {
      if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) {
        Serial.println("Hat Repeat erkannt");
      } else {
        received_signal_code = IrReceiver.decodedIRData.decodedRawData;
      }
    }
    IrReceiver.resume();
  }
}

void ledCode(long received_signal_code) {
  switch (received_signal_code) {
    case 0xF906FF00:  // Button Aus
      pixels.clear();
      pixels.show();
      break;
    case 0xF609FF00:  //Farbe Rot
      saved_code = 0xF609FF00;
      colorSwitch(pixels.Color(255, 0, 0));
      break;
    case 0xF708FF00:  //Farbe Grün
      saved_code = 0xF708FF00;
      colorSwitch(pixels.Color(0, 255, 0));
      break;
  }
}


void colorSwitch(uint32_t color) {
  for (int i = 0; i < pixels.numPixels(); i++) {  // For each pixel in pixels...
    pixels.setPixelColor(i, color);               //  Set pixel's color (in RAM)
  }
  pixels.show();
}


Hier noch das Output des Serial Monitors

23:06:56.637 -> Protocol=NEC Address=0x0 Command=0x9 Raw-Data=0xF609FF00 32 bits LSB first
23:06:56.702 -> 8
23:06:56.702 -> Protocol=UNKNOWN Hash=0x0 2 bits (incl. gap and start) received
23:06:56.768 -> 0
23:07:01.822 -> Protocol=UNKNOWN Hash=0xE1FFA1F8 27 bits (incl. gap and start) received
23:07:01.920 -> 0
23:07:01.920 -> Protocol=UNKNOWN Hash=0x0 2 bits (incl. gap and start) received
23:07:01.986 -> 0
23:07:05.234 -> Protocol=NEC Address=0x0 Command=0x8 Raw-Data=0xF708FF00 32 bits LSB first
23:07:05.332 -> 8
23:07:05.332 -> Protocol=UNKNOWN Hash=0x0 2 bits (incl. gap and start) received
23:07:05.401 -> 0

Das erste Signal erkennt er ohne Probleme. Beim zweiten Signal hat er dann Probleme und beim dritten gehts wieder. Das einzige was das Problem "behebt" ist wenn ich die LED Anzahl auf z.B. 12 reduziere. Aber das is ja auch irgendwie sinnbefreit.

Anscheinend hast du meinen Hinweis auf einen einheitlichen Datentyp nicht verstanden.

Aber egal.

Versuche doch mal das IrReceiver.resume() von IRRemote erst dann zu setzen, wenn die gewünschte Umschaltoperation fertig ist. Bis dahin werden dann natürlich keine IR-Anfragen mehr erfolgen.

Warum schaffst du mit den RAW Werten und nicht mit IrReceiver.decodedIRData.command?

Hallo,
ich habe leider keinen IR Empfänger mehr.
ich vermute du läufst standig durch die Functin colorSwich, bau da mal ein Serial.print () ein um das zu prüfen. Da solltest Du mit jedem erkannten und gültigen IR code nur einmal durchkommen.
Die Struktur sieht ja schon mal ganz gut aus :slight_smile: . Jetzt schau doch erst mal das du sauber erkennst wenn gültige Daten vom IR angekommen sind. Wenn Du das erkannt hast dann rufst Du Deine Funktion ledCode() auf. Bei einem falschen code machst Du nichts außer das anzuzeigen.

eventuell musst Du die Variable received_signal_code auch zurücksetzten damit sie nicht bestehen bleibt. Sonst ruft der Verteiler swich case immer weiter die selbe function auf, ich denke das passiert jetzt.

Hallo Tommy
dann hatten wir uns falsch verstanden, natürlich über die konstante NUMLEDS die Anzahl auf 10 geändert.

Ok, dann haben wir uns gründlich mistverstanden :wink:

Gruß Tommy

Also in der Simulation funktioniert das Ganze eigentlich ganz gut:

Code
#define DECODE_NEC            // DECODE_NEC
#include <IRremote.hpp>       // Do not change header order.
#include <Adafruit_NeoPixel.h>

enum class State : byte {irRemote, checkCode};

namespace gc {
constexpr uint8_t irReceivePin {7};
constexpr uint8_t neoPixelPin {8};
constexpr uint8_t numPixels {24};
}

Adafruit_NeoPixel pixels(gc::numPixels, gc::neoPixelPin, NEO_GRB + NEO_KHZ800);
uint8_t brightness {255};

uint16_t irReceive() {
  uint16_t received{0};
  
  if (IrReceiver.decode()) {
    IrReceiver.printIRResultShort(&Serial);
    if (IrReceiver.decodedIRData.protocol == UNKNOWN) {
      // We have an unknown protocol here, print more info
      IrReceiver.printIRResultRawFormatted(&Serial, true);
    }
    if (IrReceiver.decodedIRData.protocol == NEC) {
      received = IrReceiver.decodedIRData.command;
      Serial.print("Command: 0x");
      Serial.println(received, HEX);
    }
    IrReceiver.resume();
  }
  return received;
}

void ledCode(uint16_t rCode) {
  switch (rCode) {
    case 0x68:  // Ausschalten
      Serial.println("Button aus");
      pixels.clear();
      pixels.show();
      break;
    case 0x30:  //Farbe Rot
      Serial.println("Farbe Rot");
      colorSwitch(pixels.Color(255, 0, 0));
      break;
    case 0x18:  //Farbe Grün
      Serial.println("Farbe Grün");
      colorSwitch(pixels.Color(0, 255, 0));
      break;
  }
}

void colorSwitch(uint32_t color) {
  for (int i = 0; i < pixels.numPixels(); ++i) {  // For each pixel in pixels...
    pixels.setPixelColor(i, color);               //  Set pixel's color (in RAM)
  }
  pixels.show();
}

void fsm() {
  static State state {State::irRemote};
  static uint16_t irCode;  

  switch (state) {
    case State::irRemote:  
      irCode = irReceive();
      if (irCode) { state = State::checkCode; }
      break;
    case State::checkCode:
      ledCode(irCode);
      state = State::irRemote;
      break;
  }
}

void setup()
{
  Serial.begin(115200);

  IrReceiver.begin(gc::irReceivePin);
  Serial.print(F("Ready to receive IR signals at pin "));
  Serial.println(gc::irReceivePin);
  pixels.begin();
  pixels.setBrightness(brightness);
  pixels.show();
}

void loop()
{
  fsm();
}

Mal wieder danke für die ganzen Antworten.

Der Code funktioniert spitze, ich hab meinen auch ein wenig umgeschrieben gehabt, dass er auch nur die Funktion ledCode() nur dann ausführt wenn ein Signal kommt. Wo ich jetzt allerdings noch hänge ist wenn ich eine Schleife gleichzeitig laufen hab. Siehe:

https://wokwi.com/projects/421256550788568065

#define DECODE_NEC            // DECODE_NEC
#include <IRremote.hpp>       // Do not change header order.
#include <Adafruit_NeoPixel.h>

enum class State : byte {irRemote, checkCode};

namespace gc {
constexpr uint8_t irReceivePin {7};
constexpr uint8_t neoPixelPin {8};
constexpr uint8_t numPixels {24};
}

Adafruit_NeoPixel pixels(gc::numPixels, gc::neoPixelPin, NEO_GRB + NEO_KHZ800);
uint8_t brightness {255};

uint16_t irReceive() {
  uint16_t received{0};
  
  if (IrReceiver.decode()) {
    IrReceiver.printIRResultShort(&Serial);
    if (IrReceiver.decodedIRData.protocol == UNKNOWN) {
      // We have an unknown protocol here, print more info
      IrReceiver.printIRResultRawFormatted(&Serial, true);
    }
    if (IrReceiver.decodedIRData.protocol == NEC) {
      received = IrReceiver.decodedIRData.command;
      Serial.print("Command: 0x");
      Serial.println(received, HEX);
    }
    IrReceiver.resume();
  }
  return received;
}

void ledCode(uint16_t rCode) {
  switch (rCode) {
    case 0x68:  // Ausschalten
      Serial.println("Button aus");
      pixels.clear();
      pixels.show();
      break;
    case 0x30:  //Farbe Rot
      Serial.println("Farbe Rot");
      colorSwitch(pixels.Color(255, 0, 0));
      break;
    case 0x18:  //Farbe Grün
      Serial.println("Farbe Grün");
      colorSwitch(pixels.Color(0, 255, 0));
      break;
            case 0x52:
      Serial.println("Rainbow");
      rainbow();
      break;
  }
}

void colorSwitch(uint32_t color) {
  for (int i = 0; i < pixels.numPixels(); ++i) {  // For each pixel in pixels...
    pixels.setPixelColor(i, color);               //  Set pixel's color (in RAM)
  }
  pixels.show();
}

void fsm() {
  static State state {State::irRemote};
  static uint16_t irCode;  

  switch (state) {
    case State::irRemote:  
      irCode = irReceive();
      if (irCode) { state = State::checkCode; }
      break;
    case State::checkCode:
      ledCode(irCode);
      state = State::irRemote;
      break;
  }
}

void rainbow() {
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    pixels.rainbow(firstPixelHue);
    pixels.show(); // Update strip with new contents
  }
}

void setup()
{
  Serial.begin(115200);

  IrReceiver.begin(gc::irReceivePin);
  Serial.print(F("Ready to receive IR signals at pin "));
  Serial.println(gc::irReceivePin);
  pixels.begin();
  pixels.setBrightness(brightness);
  pixels.show();
}

void loop()
{
  fsm();
}

Sobald man während der laufenden Schleife ein Signal sendet, dekodiert er es nicht komplett. Find ich ja zumindest geil dass es in der Simulation genausowenig funktioniert. Hätte ich gestern Abend gar nicht meine komplette Hardware überprüfen müssen

Ist interessant --- stört eigentlich auch nicht aber kannst Ja mal das probieren:

void fsm() {
  static State state {State::irRemote};
  static uint16_t irCode;  

  switch (state) {
    case State::irRemote:  
      irCode = irReceive();
      if (irCode) { state = State::checkCode; }
      break;
    case State::checkCode:
      ledCode(irCode);
      IrReceiver.resume();  // <- Einfügen
      state = State::irRemote;
      break;
  }
}

Ich habe Deinen Sketch vom Post 1 umgeschrieben und bei Wokwi eingestellt:

https://wokwi.com/projects/421249749008464897

Sketch
/*
   Forum: https://forum.arduino.cc/t/irremote-unvollstandige-auswertung-in-while-schleife/1346983
   Wokwi: https://wokwi.com/projects/421249749008464897

*/

// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// Released under the GPLv3 license to match the rest of the
// Adafruit NeoPixel library

#include <Arduino.h>
#include <IRremote.hpp>
#include <Adafruit_NeoPixel.h>

const int IR_RECEIVE_PIN = 7;
unsigned long key_value_old = 0;

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN 8  // On Trinket or Gemma, suggest changing this to 1
#define NUMPIXELS 24  // Popular NeoPixel ring size 256
int BRIGHTNESS = 192;  // Set BRIGHTNESS to about 1/5 (max = 255)

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

#define DELAYVAL 50  // Time (in milliseconds) to pause between pixels

/*
  const unsigned long buttonAn {0xF807FF00};
  const unsigned long buttonAus {0xF906FF00};
  const unsigned long buttonRot {0xF609FF00};
  const unsigned long buttonGruen {0xF708FF00};
  const unsigned long buttonHellblau {0xF10EFF00};
  const unsigned long buttonGrau {0xF40BFF00};
  const unsigned long buttonDunkelblau {0xF50AFF00};
  const unsigned long buttonOrange {0xF20DFF00};
  const unsigned long buttonHellgruen {0xF30CFF00};
  const unsigned long buttonGelb {0xEE11FF00};
  const unsigned long buttonHeller {0xFA05FF00};
  const unsigned long buttonDunkler {0xFB04FF00};
  const unsigned long buttonPink {0xED12FF00};
  const unsigned long buttonFade {0xE41BFF00};
  const unsigned long buttonStrobe {0xE817FF00};
  const unsigned long buttonSmooth {0xEC13FF00};
  const   signed long buttonIrgendwas {-1935048056};

*/

// Wokwi IR Codes
const unsigned long buttonAn {0x5DA2FF00};         // POWER
const unsigned long buttonAus {0x1DE2FF00};        // MENU
const unsigned long buttonRot {0xDD22FF00};        // TEST
const unsigned long buttonGruen  {0x9768FF00};     // 0
const unsigned long buttonHellblau {0x3DC2FF00};   // Backspace
const unsigned long buttonGrau {0x1FE0FF00};       // Fastrewind
const unsigned long buttonDunkelblau {0x57A8FF00}; // Play
const unsigned long buttonOrange {0x6F90FF00};     // Fastforward
const unsigned long buttonHellgruen {0x4FB0FF00};  // C
const unsigned long buttonGelb    {0xCF30FF00};    // 1
const unsigned long buttonHeller  {0xFD02FF00};    // +
const unsigned long buttonDunkler {0x6798FF00};    // -
const unsigned long buttonPink {0xE718FF00};       // 2
const unsigned long buttonFade {0x857AFF00};       // 3
const unsigned long buttonStrobe {0xEF10FF00};     // 4
const unsigned long buttonSmooth {0xC738FF00};     // 5
const unsigned long buttonIrgendwas {0xA55AFF00};  // 6

enum States   {sIDLE, sAUS, sROT, sGRUEN, sHELLBLAU,
               sGRAU, sDUNKELBLAU, sORANGE,
               sHELLGRUEN, sGELB, sHELLER, sDUNKLER,
               sPINK, sFADE, sSTROBE, sSMOOTH,
               sIRGENDWAS
              };

States state = sIDLE;
States prevState = sIDLE;
boolean newCmd = false;
unsigned long keyValue;
boolean isReady = false;
const int cHell = 7; // Für ursprüngliche Werte auf 1 setzen


void setup() {
  Serial.begin(115200);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
  pixels.begin();  // INITIALIZE NeoPixel pixels object (REQUIRED)
  pixels.show();   // Turn OFF all pixels ASAP
  pixels.setBrightness(BRIGHTNESS);
}



void loop() {
  checkForIRCodes();
  stateMachine();
}

void checkForIRCodes() {
  if (IrReceiver.decode()) {
    IrReceiver.resume();  // Enable receiving of the next value
    if (IrReceiver.decodedIRData.numberOfBits > 31) {
      keyValue = IrReceiver.decodedIRData.decodedRawData;
      // Serial.print(" 0x");
      // Serial.println(keyValue, HEX);
      ledCode(keyValue);
    }
  }
}

void storeKey(long key) {
  key_value_old = key;
}

void ledCode(long key_value) {
  if (key_value == buttonAn) {  // Button An
    key_value = key_value_old;
    Serial.println(F("An"));
  }
  switch (key_value) {
    case buttonAus:  // Button Aus
      Serial.println(F("Aus"));
      state = sAUS;
      break;
    case buttonRot:  //Farbe Rot
      Serial.println(F("Rot"));
      storeKey(key_value);
      state = sROT;
      break;
    case buttonGruen:  //Farbe Grün
      Serial.println(F("Grün"));
      storeKey(key_value);
      state = sGRUEN;
      break;
    case buttonHellblau:  // Farbe Blau Hell
      Serial.println(F("Hellblau"));
      storeKey(key_value);
      state = sHELLBLAU;
      break;
    case buttonGrau:
      Serial.println(F("Grau"));
      storeKey(key_value);
      state = sGRAU;
      break;
    case buttonDunkelblau:  // Farbe Blau Dunkel
      Serial.println(F("Dunkelblau"));
      storeKey(key_value);
      state = sDUNKELBLAU;
      break;
    case buttonOrange:  // Farbe Orange
      Serial.println(F("Orange"));
      storeKey(key_value);
      state = sORANGE;
      break;
    case buttonHellgruen:  // Farbe Hellgrün
      Serial.println(F("Hellgrün"));
      storeKey(key_value);
      state = sHELLGRUEN;
      break;
    case buttonGelb:  // Farbe Gelb
      Serial.println(F("Gelb"));
      storeKey(key_value);
      state = sGELB;
      break;
    case buttonHeller:  // Helligkeit hoch
      Serial.println(F("Heller"));
      state = sHELLER;
      break;
    case buttonDunkler:  // Helligkeit runter
      Serial.println(F("Dunkler"));
      state = sDUNKLER;
      break;
    case buttonPink:  //Farbe Pink
      Serial.println(F("Pink"));
      storeKey(key_value);
      state = sPINK;
      break;
    case buttonFade:  // Button Fade
      Serial.println(F("Fade"));
      storeKey(key_value);
      state = sFADE;
      fadingDone(false); // Startwerte setzen
      break;
    case buttonStrobe:  // Button Strobe
      Serial.println(F("Strobe"));
      storeKey(key_value);
      state = sSTROBE;
      theaterChaseRainbow(-1);  // Startwerte setzen durch Parameter < 0
      break;
    case buttonSmooth:  // Button Smooth
      Serial.println(F("Smooth"));
      storeKey(key_value);
      state = sSMOOTH;
      rainbowDone(-1);  // Startwerte setzen durch Parameter < 0
      break;
    case buttonIrgendwas:
      Serial.println(F("Irgendwas"));
      state = sIRGENDWAS;
      break;
    default:
      Serial.println(F("Unbekannt oder fehlerhaft"));
      state = sIDLE;
      pixels.clear();
      pixels.show();
      break;
  }
}

void stateMachine() {
  if (state != sFADE && prevState == sFADE) {
    pixels.setBrightness(BRIGHTNESS);
    pixels.show();
  }
  prevState = state;
  switch (state) {
    case sIDLE:
      break;
    case sAUS:  // Button Aus
      pixels.clear();
      pixels.show();
      state = sIDLE;
      break;
    case sROT:  //Farbe Rot
      colorSwitch(pixels.Color(255, 0, 0));
      state = sIDLE;
      break;
    case sHELLGRUEN:  //Farbe Grün
      colorSwitch(pixels.Color(0, 255, 0));
      state = sIDLE;
      break;
    case sHELLBLAU:  // Farbe Blau Hell
      colorSwitch(pixels.Color(0, 0, 255));
      state = sIDLE;
      break;
    case sGRAU:
      colorSwitch(pixels.Color(128, 128, 128));
      state = sIDLE;
      break;
    case sDUNKELBLAU:  // Farbe Blau Dunkel
      colorSwitch(pixels.Color(20, 0, 255));
      state = sIDLE;
      break;
    case sORANGE:  // Farbe Orange
      colorSwitch(pixels.Color(255, 147, 5));
      state = sIDLE;
      break;
    case sGRUEN:  // Farbe Grün
      colorSwitch(pixels.Color(118, 235, 2));
      state = sIDLE;
      break;
    case sGELB:  // Farbe Gelb
      colorSwitch(pixels.Color(252, 244, 3));
      state = sIDLE;
      break;
    case sHELLER:  // Helligkeit hoch
      if (BRIGHTNESS < 255) {
        BRIGHTNESS++;
        pixels.setBrightness(BRIGHTNESS);
        pixels.show();
      }
      state = sIDLE;
      break;
    case sDUNKLER:  // Helligkeit runter
      if (BRIGHTNESS > 0) {
        BRIGHTNESS--;
        pixels.setBrightness(BRIGHTNESS);
        pixels.show();
      }
      state = sIDLE;
      break;
    case sPINK:  //Farbe Pink
      colorSwitch(pixels.Color(202, 21, 230));
      state = sIDLE;
      break;
    case sFADE:  // Button Fade
      if (fadingDone(true)) {
        state = sIDLE;
      }
      break;
    case sSTROBE:  // Button Strobe
      theaterChaseRainbow(50);  // Bis ihn ein anderer IR Code beendet ...
      break;
    case sSMOOTH:  // Button Smooth
      if (rainbowDone(10)) {
        state = sIDLE;
      };
      break;
    case sIRGENDWAS:
      pixels.clear();  // Set all pixel colors to 'off'
      colorSwitch(pixels.Color(0, 150, 0));
      state = sIDLE;
      break;
  }
}

void colorSwitch(uint32_t color) {
  for (int i = 0; i < pixels.numPixels(); i++) {  // For each pixel in pixels...
    pixels.setPixelColor(i, color);               //  Set pixel's color (in RAM)
  }
  pixels.show();
}

boolean fadingDone(boolean run) {
  static int h = 1;
  static unsigned long lastAction = 0;
  static byte mode = 0;
  if (!run) {
    h =  1;
    lastAction = millis();
    mode = 0;
    return true;
  }
  switch (mode) {
    case 0:
      if (millis() - lastAction > 10) {
        lastAction = millis();
        pixels.setBrightness(h * cHell);
        pixels.show();
        h++;
      }
      if (h >= 32) {
        mode = 1;
      };
      break;
    case 1:
      if (millis() - lastAction > 50) {
        lastAction = millis();
        pixels.setBrightness(h * cHell);
        pixels.show();
        h--;
      }
      if (h <= 1) {
        mode = 2;
      };
      break;
    case 2:
      pixels.setBrightness(BRIGHTNESS);
      pixels.show();
      return true;
      break;
  }
  return false;
}


void theaterChaseRainbow(unsigned long wait) {  // call mit 50 ms
  static unsigned long lastAction = 0;
  static int firstPixelHue = 0;        // First pixel starts at red (hue 0)
  static int b = 0;
  if (wait < 0) {  // wait kleiner 0 setzt die Startwerte und kehrt direkt zurück
    lastAction = 0;
    firstPixelHue = 0;
    b = 0;
    return;
  }
  if (millis() - lastAction > wait) {
    lastAction = millis();
    pixels.clear();
    for (int c = b; c < pixels.numPixels(); c += 3) {
      int hue = firstPixelHue + c * 65536L / pixels.numPixels();
      uint32_t color = pixels.gamma32(pixels.ColorHSV(hue));  // hue -> RGB
      pixels.setPixelColor(c, color);                         // Set pixel 'c' to value 'color'
    }
    pixels.show();                // Update pixels with new contents
    firstPixelHue += 65536 / 90;  // One cycle of color wheel over 90 frames
    b++;
  }
  if (b > 2) {
    b = 0;
  }
}


boolean rainbowDone(unsigned long wait) {
  static long firstPixelHue = 0;
  static unsigned long lastAction = 0;
  if (wait < 0) {
    firstPixelHue = 0;
    lastAction = 0;
    return;
  }
  if (millis() - lastAction > wait) {
    lastAction = millis();
    pixels.rainbow(firstPixelHue);
    pixels.show();  // Update pixels with new contents
    if (firstPixelHue < 5 * 65536) {
      firstPixelHue += 256;
    } else {
      firstPixelHue = 0;
      return true;
    }
  }
  return false;
}

Allerdings habe ich die Schleifen, die ein Delay zum Anzeigen erfordern, in der StateMachine so umgesetzt, dass die loop() regelmäßig durchlaufen wird.

Da die Neo_Pixel-Ansteuerung den IR-Decoder beeinflusst, werden die IR-Codes während der schnell aufeinander folgenden Led-Aktionen i.d.R. fehlerhaft ausgewertet. Dem trägt der Sketch dadurch Rechnung, dass fehlerhafte und unbekannte Codes zum Abbruch der laufenden Led-Funktion führen und die Leds im Ring auf schwarz gesetzt werden. Mit einem zweiten Knopfdruck geht es dann weiter.

So kann man auch längerdauernde Aktivitäten per IR unterbrechen, auch wenn's nicht schön ist :wink:

Alternativ kann man

  • längere Aktionen durch einen Zähler begrenzen (n-mal durchlaufen lassen und dann erst wieder auf IR reagieren) oder bevorzugt
  • das IR-Empfangen und Decodieren auf einen zweiten Controller auslagern und die Kommandos per Hardware-Serial auf diesem Controller entgegennehmen. Hardware-Serial scheint durch Neo-Pixel (zumindest in der Simulation) nicht beeinträchtigt zu werden.

Gruß
ec2021

Jaaaa, ich denke ich werd einfach 2 Controller verwenden. Oder warten bis mein Raspberry Pi 5 da is :sweat_smile:

Nee, das brauchst Du nicht ...

Es gibt eine einfache Lösung!

Ersetze alle Aufrufe von pixels.show() durch folgende Funktion:

void pixelsShow(){
  if (IrReceiver.isIdle()){
    pixels.show();
  }
}

Das sorgt dafür, dass die blockierende Funktion show() nur aufgerufen wird, wenn der Decoder nicht beschäftigt ist.

Siehe https://wokwi.com/projects/421312361290532865

Sketch
/*
   Forum: https://forum.arduino.cc/t/irremote-unvollstandige-auswertung-in-while-schleife/1346983
   Wokwi: https://wokwi.com/projects/421312361290532865


   ec2021

*/

// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// Released under the GPLv3 license to match the rest of the
// Adafruit NeoPixel library

#include <Arduino.h>
#include <IRremote.hpp>
#include <Adafruit_NeoPixel.h>

const int IR_RECEIVE_PIN = 7;
unsigned long key_value_old = 0;

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN 8  // On Trinket or Gemma, suggest changing this to 1
#define NUMPIXELS 24  // Popular NeoPixel ring size 256
int BRIGHTNESS = 192;  // Set BRIGHTNESS to about 1/5 (max = 255)

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

#define DELAYVAL 50  // Time (in milliseconds) to pause between pixels

/*
  const unsigned long buttonAn {0xF807FF00};
  const unsigned long buttonAus {0xF906FF00};
  const unsigned long buttonRot {0xF609FF00};
  const unsigned long buttonGruen {0xF708FF00};
  const unsigned long buttonHellblau {0xF10EFF00};
  const unsigned long buttonGrau {0xF40BFF00};
  const unsigned long buttonDunkelblau {0xF50AFF00};
  const unsigned long buttonOrange {0xF20DFF00};
  const unsigned long buttonHellgruen {0xF30CFF00};
  const unsigned long buttonGelb {0xEE11FF00};
  const unsigned long buttonHeller {0xFA05FF00};
  const unsigned long buttonDunkler {0xFB04FF00};
  const unsigned long buttonPink {0xED12FF00};
  const unsigned long buttonFade {0xE41BFF00};
  const unsigned long buttonStrobe {0xE817FF00};
  const unsigned long buttonSmooth {0xEC13FF00};
  const   signed long buttonIrgendwas {-1935048056};

*/

// Wokwi IR Codes
const unsigned long buttonAn {0x5DA2FF00};         // POWER
const unsigned long buttonAus {0x1DE2FF00};        // MENU
const unsigned long buttonRot {0xDD22FF00};        // TEST
const unsigned long buttonGruen  {0x9768FF00};     // 0
const unsigned long buttonHellblau {0x3DC2FF00};   // Backspace
const unsigned long buttonGrau {0x1FE0FF00};       // Fastrewind
const unsigned long buttonDunkelblau {0x57A8FF00}; // Play
const unsigned long buttonOrange {0x6F90FF00};     // Fastforward
const unsigned long buttonHellgruen {0x4FB0FF00};  // C
const unsigned long buttonGelb    {0xCF30FF00};    // 1
const unsigned long buttonHeller  {0xFD02FF00};    // +
const unsigned long buttonDunkler {0x6798FF00};    // -
const unsigned long buttonPink {0xE718FF00};       // 2
const unsigned long buttonFade {0x857AFF00};       // 3
const unsigned long buttonStrobe {0xEF10FF00};     // 4
const unsigned long buttonSmooth {0xC738FF00};     // 5
const unsigned long buttonIrgendwas {0xA55AFF00};  // 6

enum States   {sIDLE, sAUS, sROT, sGRUEN, sHELLBLAU,
               sGRAU, sDUNKELBLAU, sORANGE,
               sHELLGRUEN, sGELB, sHELLER, sDUNKLER,
               sPINK, sFADE, sSTROBE, sSMOOTH,
               sIRGENDWAS
              };

States state = sIDLE;
States prevState = sIDLE;
boolean newCmd = false;
unsigned long keyValue;
boolean isReady = false;
const int cHell = 7; // Für ursprüngliche Werte auf 1 setzen


void pixelsShow() {
  if (IrReceiver.isIdle()) {
    pixels.show();
  }
}

void setup() {
  Serial.begin(115200);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
  pixels.begin();  // INITIALIZE NeoPixel pixels object (REQUIRED)
  pixelsShow();   // Turn OFF all pixels ASAP
  pixels.setBrightness(BRIGHTNESS);
}



void loop() {
  checkForIRCodes();
  stateMachine();
}

void checkForIRCodes() {
  if (IrReceiver.decode()) {
    IrReceiver.resume();  // Enable receiving of the next value
    if (IrReceiver.decodedIRData.numberOfBits > 31) {
      keyValue = IrReceiver.decodedIRData.decodedRawData;
      // Serial.print(" 0x");
      // Serial.println(keyValue, HEX);
      ledCode(keyValue);
    }
  }
}

void storeKey(long key) {
  key_value_old = key;
}

void ledCode(long key_value) {
  if (key_value == buttonAn) {  // Button An
    key_value = key_value_old;
    Serial.println(F("An"));
  }
  switch (key_value) {
    case buttonAus:  // Button Aus
      Serial.println(F("Aus"));
      state = sAUS;
      break;
    case buttonRot:  //Farbe Rot
      Serial.println(F("Rot"));
      storeKey(key_value);
      state = sROT;
      break;
    case buttonGruen:  //Farbe Grün
      Serial.println(F("Grün"));
      storeKey(key_value);
      state = sGRUEN;
      break;
    case buttonHellblau:  // Farbe Blau Hell
      Serial.println(F("Hellblau"));
      storeKey(key_value);
      state = sHELLBLAU;
      break;
    case buttonGrau:
      Serial.println(F("Grau"));
      storeKey(key_value);
      state = sGRAU;
      break;
    case buttonDunkelblau:  // Farbe Blau Dunkel
      Serial.println(F("Dunkelblau"));
      storeKey(key_value);
      state = sDUNKELBLAU;
      break;
    case buttonOrange:  // Farbe Orange
      Serial.println(F("Orange"));
      storeKey(key_value);
      state = sORANGE;
      break;
    case buttonHellgruen:  // Farbe Hellgrün
      Serial.println(F("Hellgrün"));
      storeKey(key_value);
      state = sHELLGRUEN;
      break;
    case buttonGelb:  // Farbe Gelb
      Serial.println(F("Gelb"));
      storeKey(key_value);
      state = sGELB;
      break;
    case buttonHeller:  // Helligkeit hoch
      Serial.println(F("Heller"));
      state = sHELLER;
      break;
    case buttonDunkler:  // Helligkeit runter
      Serial.println(F("Dunkler"));
      state = sDUNKLER;
      break;
    case buttonPink:  //Farbe Pink
      Serial.println(F("Pink"));
      storeKey(key_value);
      state = sPINK;
      break;
    case buttonFade:  // Button Fade
      Serial.println(F("Fade"));
      storeKey(key_value);
      state = sFADE;
      fadingDone(false); // Startwerte setzen
      break;
    case buttonStrobe:  // Button Strobe
      Serial.println(F("Strobe"));
      storeKey(key_value);
      state = sSTROBE;
      theaterChaseRainbow(-1);  // Startwerte setzen durch Parameter < 0
      break;
    case buttonSmooth:  // Button Smooth
      Serial.println(F("Smooth"));
      storeKey(key_value);
      state = sSMOOTH;
      rainbowDone(-1);  // Startwerte setzen durch Parameter < 0
      break;
    case buttonIrgendwas:
      Serial.println(F("Irgendwas"));
      state = sIRGENDWAS;
      break;
    default:
      Serial.println(F("Unbekannt oder fehlerhaft"));
      state = sIDLE;
      pixels.clear();
      pixelsShow();
      break;
  }
}

void stateMachine() {
  if (state != sFADE && prevState == sFADE) {
    pixels.setBrightness(BRIGHTNESS);
    pixelsShow();
  }
  prevState = state;
  switch (state) {
    case sIDLE:
      break;
    case sAUS:  // Button Aus
      pixels.clear();
      pixelsShow();
      state = sIDLE;
      break;
    case sROT:  //Farbe Rot
      colorSwitch(pixels.Color(255, 0, 0));
      state = sIDLE;
      break;
    case sHELLGRUEN:  //Farbe Grün
      colorSwitch(pixels.Color(0, 255, 0));
      state = sIDLE;
      break;
    case sHELLBLAU:  // Farbe Blau Hell
      colorSwitch(pixels.Color(0, 0, 255));
      state = sIDLE;
      break;
    case sGRAU:
      colorSwitch(pixels.Color(128, 128, 128));
      state = sIDLE;
      break;
    case sDUNKELBLAU:  // Farbe Blau Dunkel
      colorSwitch(pixels.Color(20, 0, 255));
      state = sIDLE;
      break;
    case sORANGE:  // Farbe Orange
      colorSwitch(pixels.Color(255, 147, 5));
      state = sIDLE;
      break;
    case sGRUEN:  // Farbe Grün
      colorSwitch(pixels.Color(118, 235, 2));
      state = sIDLE;
      break;
    case sGELB:  // Farbe Gelb
      colorSwitch(pixels.Color(252, 244, 3));
      state = sIDLE;
      break;
    case sHELLER:  // Helligkeit hoch
      if (BRIGHTNESS < 255) {
        BRIGHTNESS++;
        pixels.setBrightness(BRIGHTNESS);
        pixelsShow();
      }
      state = sIDLE;
      break;
    case sDUNKLER:  // Helligkeit runter
      if (BRIGHTNESS > 0) {
        BRIGHTNESS--;
        pixels.setBrightness(BRIGHTNESS);
        pixelsShow();
      }
      state = sIDLE;
      break;
    case sPINK:  //Farbe Pink
      colorSwitch(pixels.Color(202, 21, 230));
      state = sIDLE;
      break;
    case sFADE:  // Button Fade
      if (fadingDone(true)) {
        state = sIDLE;
      }
      break;
    case sSTROBE:  // Button Strobe
      theaterChaseRainbow(50);  // Bis ihn ein anderer IR Code beendet ...
      break;
    case sSMOOTH:  // Button Smooth
      if (rainbowDone(10)) {
        state = sIDLE;
      };
      break;
    case sIRGENDWAS:
      pixels.clear();  // Set all pixel colors to 'off'
      colorSwitch(pixels.Color(0, 150, 0));
      state = sIDLE;
      break;
  }
}

void colorSwitch(uint32_t color) {
  for (int i = 0; i < pixels.numPixels(); i++) {  // For each pixel in pixels...
    pixels.setPixelColor(i, color);               //  Set pixel's color (in RAM)
  }
  pixelsShow();
}

boolean fadingDone(boolean run) {
  static int h = 1;
  static unsigned long lastAction = 0;
  static byte mode = 0;
  if (!run) {
    h =  1;
    lastAction = millis();
    mode = 0;
    return true;
  }
  switch (mode) {
    case 0:
      if (millis() - lastAction > 10) {
        lastAction = millis();
        pixels.setBrightness(h * cHell);
        pixelsShow();
        h++;
      }
      if (h >= 32) {
        mode = 1;
      };
      break;
    case 1:
      if (millis() - lastAction > 50) {
        lastAction = millis();
        pixels.setBrightness(h * cHell);
        pixelsShow();
        h--;
      }
      if (h <= 1) {
        mode = 2;
      };
      break;
    case 2:
      pixels.setBrightness(BRIGHTNESS);
      pixelsShow();
      return true;
      break;
  }
  return false;
}


void theaterChaseRainbow(unsigned long wait) {  // call mit 50 ms
  static unsigned long lastAction = 0;
  static int firstPixelHue = 0;        // First pixel starts at red (hue 0)
  static int b = 0;
  if (wait < 0) {  // wait kleiner 0 setzt die Startwerte und kehrt direkt zurück
    lastAction = 0;
    firstPixelHue = 0;
    b = 0;
    return;
  }
  if (millis() - lastAction > wait) {
    lastAction = millis();
    pixels.clear();
    for (int c = b; c < pixels.numPixels(); c += 3) {
      int hue = firstPixelHue + c * 65536L / pixels.numPixels();
      uint32_t color = pixels.gamma32(pixels.ColorHSV(hue));  // hue -> RGB
      pixels.setPixelColor(c, color);                         // Set pixel 'c' to value 'color'
    }
    pixelsShow();                // Update pixels with new contents
    firstPixelHue += 65536 / 90;  // One cycle of color wheel over 90 frames
    b++;
  }
  if (b > 2) {
    b = 0;
  }
}


boolean rainbowDone(unsigned long wait) {
  static long firstPixelHue = 0;
  static unsigned long lastAction = 0;
  if (wait < 0) {
    firstPixelHue = 0;
    lastAction = 0;
    return;
  }
  if (millis() - lastAction > wait) {
    lastAction = millis();
    pixels.rainbow(firstPixelHue);
    pixelsShow();  // Update pixels with new contents
    if (firstPixelHue < 5 * 65536) {
      firstPixelHue += 256;
    } else {
      firstPixelHue = 0;
      return true;
    }
  }
  return false;
}

Quelle der Lösung:

http://www.notsodistantfuture.com/2016/12/04/resolving-timing-conflicts-with-neopixels-and-ir-signal-processing.html

Stammt aus 2016 ... :wink:

[Edit]: Und weil's so schön läuft, hier noch eine Version

  • mit einer "endlosen" Rainbow-Funktion auf Taste 7 und
  • serieller Eingabe

Bei Eingabe von 'd' oder auch 'D' wechselt die Variable "debug" zwischen false und true. Bei debug == true wird der empfangene IR-Code so per Serial ausgegeben, dass man ihn direkt in die Konstanten-Deklaration kopieren kann. Das switch()-Konstrukt kann bei Bedarf leicht durch weitere Funktionen ergänzt werden (z.B. das Auslösen bestimmter Funktionen per serieller Eingabe).

Die Funktionen

boolean rainbowDone(unsigned long wait)
void rainbowEndless(unsigned long wait)

zeigen eine Möglichkeit, wie man das, was ansonsten per Schleife läuft, in einer StateMachine umsetzen kann. Besser als die Verwendung globaler bzw. lokaler static Variablen wäre die Verwendung von Klassen für die umfangreicheren Led-Funktionen. Darauf habe ich aktuell verzichtet, um möglichst nahe bei den ursprünglichen Funktionen zu bleiben.

Siehe hier https://wokwi.com/projects/421313616968994817

Sketch IR_NEOPIXEL III
/*
   Forum: https://forum.arduino.cc/t/irremote-unvollstandige-auswertung-in-while-schleife/1346983
   Wokwi: https://wokwi.com/projects/421313616968994817


   ec2021

*/

// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// Released under the GPLv3 license to match the rest of the
// Adafruit NeoPixel library

#include <Arduino.h>
#include <IRremote.hpp>
#include <Adafruit_NeoPixel.h>

const int IR_RECEIVE_PIN = 7;
unsigned long key_value_old = 0;

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN 8  // On Trinket or Gemma, suggest changing this to 1
#define NUMPIXELS 24  // Popular NeoPixel ring size 256
int BRIGHTNESS = 192;  // Set BRIGHTNESS to about 1/5 (max = 255)

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

#define DELAYVAL 50  // Time (in milliseconds) to pause between pixels

/*
  const unsigned long buttonAn {0xF807FF00};
  const unsigned long buttonAus {0xF906FF00};
  const unsigned long buttonRot {0xF609FF00};
  const unsigned long buttonGruen {0xF708FF00};
  const unsigned long buttonHellblau {0xF10EFF00};
  const unsigned long buttonGrau {0xF40BFF00};
  const unsigned long buttonDunkelblau {0xF50AFF00};
  const unsigned long buttonOrange {0xF20DFF00};
  const unsigned long buttonHellgruen {0xF30CFF00};
  const unsigned long buttonGelb {0xEE11FF00};
  const unsigned long buttonHeller {0xFA05FF00};
  const unsigned long buttonDunkler {0xFB04FF00};
  const unsigned long buttonPink {0xED12FF00};
  const unsigned long buttonFade {0xE41BFF00};
  const unsigned long buttonStrobe {0xE817FF00};
  const unsigned long buttonSmooth {0xEC13FF00};
  const   signed long buttonIrgendwas {-1935048056};

*/

// Wokwi IR Codes
const unsigned long buttonAn {0x5DA2FF00};         // POWER
const unsigned long buttonAus {0x1DE2FF00};        // MENU
const unsigned long buttonRot {0xDD22FF00};        // TEST
const unsigned long buttonGruen  {0x9768FF00};     // 0
const unsigned long buttonHellblau {0x3DC2FF00};   // Backspace
const unsigned long buttonGrau {0x1FE0FF00};       // Fastrewind
const unsigned long buttonDunkelblau {0x57A8FF00}; // Play
const unsigned long buttonOrange {0x6F90FF00};     // Fastforward
const unsigned long buttonHellgruen {0x4FB0FF00};  // C
const unsigned long buttonGelb    {0xCF30FF00};    // 1
const unsigned long buttonHeller  {0xFD02FF00};    // +
const unsigned long buttonDunkler {0x6798FF00};    // -
const unsigned long buttonPink {0xE718FF00};       // 2
const unsigned long buttonFade {0x857AFF00};       // 3
const unsigned long buttonStrobe {0xEF10FF00};     // 4
const unsigned long buttonSmooth {0xC738FF00};     // 5
const unsigned long buttonIrgendwas {0xA55AFF00};  // 6
const unsigned long buttonRainbow {0xBD42FF00};    // 7

enum States   {sIDLE, sAUS, sROT, sGRUEN, sHELLBLAU,
               sGRAU, sDUNKELBLAU, sORANGE,
               sHELLGRUEN, sGELB, sHELLER, sDUNKLER,
               sPINK, sFADE, sSTROBE, sSMOOTH, sRAINBOW,
               sIRGENDWAS
              };

States state = sIDLE;
States prevState = sIDLE;
boolean newCmd = false;
unsigned long keyValue;
boolean isReady = false;
const int cHell = 7; // Für ursprüngliche Werte auf 1 setzen
boolean debug = false;


void pixelsShow() {
  if (IrReceiver.isIdle()) {
    pixels.show();
  }
}

void setup() {
  Serial.begin(115200);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
  pixels.begin();  // INITIALIZE NeoPixel pixels object (REQUIRED)
  pixelsShow();   // Turn OFF all pixels ASAP
  pixels.setBrightness(BRIGHTNESS);
}

void loop() {
  checkForIRCodes();
  stateMachine();
  getSerialCmd();
}

void getSerialCmd() {
  if (Serial.available()) {
    char c = Serial.read();
    switch (c) {
      case 'd':
      case 'D':
        debug = !debug;
        Serial.print(F("Debug is "));
        Serial.println((debug) ? F("On") : F("Off"));
        break;
    }
  }
}

void checkForIRCodes() {
  if (IrReceiver.decode()) {
    IrReceiver.resume();  // Enable receiving of the next value
    if (IrReceiver.decodedIRData.numberOfBits > 31) {
      keyValue = IrReceiver.decodedIRData.decodedRawData;
      if (debug) {
        Serial.print(" 0x");
        Serial.println(keyValue, HEX);
      }
      ledCode(keyValue);
    }
  }
}

void storeKey(long key) {
  key_value_old = key;
}

void ledCode(long key_value) {
  if (key_value == buttonAn) {  // Button An
    key_value = key_value_old;
    Serial.println(F("An"));
  }
  switch (key_value) {
    case buttonAus:  // Button Aus
      Serial.println(F("Aus"));
      state = sAUS;
      break;
    case buttonRot:  //Farbe Rot
      Serial.println(F("Rot"));
      storeKey(key_value);
      state = sROT;
      break;
    case buttonGruen:  //Farbe Grün
      Serial.println(F("Grün"));
      storeKey(key_value);
      state = sGRUEN;
      break;
    case buttonHellblau:  // Farbe Blau Hell
      Serial.println(F("Hellblau"));
      storeKey(key_value);
      state = sHELLBLAU;
      break;
    case buttonGrau:
      Serial.println(F("Grau"));
      storeKey(key_value);
      state = sGRAU;
      break;
    case buttonDunkelblau:  // Farbe Blau Dunkel
      Serial.println(F("Dunkelblau"));
      storeKey(key_value);
      state = sDUNKELBLAU;
      break;
    case buttonOrange:  // Farbe Orange
      Serial.println(F("Orange"));
      storeKey(key_value);
      state = sORANGE;
      break;
    case buttonHellgruen:  // Farbe Hellgrün
      Serial.println(F("Hellgrün"));
      storeKey(key_value);
      state = sHELLGRUEN;
      break;
    case buttonGelb:  // Farbe Gelb
      Serial.println(F("Gelb"));
      storeKey(key_value);
      state = sGELB;
      break;
    case buttonHeller:  // Helligkeit hoch
      Serial.println(F("Heller"));
      state = sHELLER;
      break;
    case buttonDunkler:  // Helligkeit runter
      Serial.println(F("Dunkler"));
      state = sDUNKLER;
      break;
    case buttonPink:  //Farbe Pink
      Serial.println(F("Pink"));
      storeKey(key_value);
      state = sPINK;
      break;
    case buttonFade:  // Button Fade
      Serial.println(F("Fade"));
      storeKey(key_value);
      state = sFADE;
      fadingDone(false); // Startwerte setzen
      break;
    case buttonStrobe:  // Button Strobe
      Serial.println(F("Strobe"));
      storeKey(key_value);
      state = sSTROBE;
      theaterChaseRainbow(-1);  // Startwerte setzen durch Parameter < 0
      break;
    case buttonSmooth:  // Button Smooth
      Serial.println(F("Smooth"));
      storeKey(key_value);
      state = sSMOOTH;
      rainbowDone(-1);  // Startwerte setzen durch Parameter < 0
      break;
    case buttonRainbow:
      Serial.println(F("Rainbow"));
      rainbowEndless(-1); // Startwerte setzen
      state = sRAINBOW;
      break;
    case buttonIrgendwas:
      Serial.println(F("Irgendwas"));
      state = sIRGENDWAS;
      break;
    default:
      Serial.println(F("Unbekannt oder fehlerhaft"));
      state = sIDLE;
      pixels.clear();
      pixelsShow();
      break;
  }
}

void stateMachine() {
  if (state != sFADE && prevState == sFADE) {
    pixels.setBrightness(BRIGHTNESS);
    pixelsShow();
  }
  prevState = state;
  switch (state) {
    case sIDLE:
      break;
    case sAUS:  // Button Aus
      pixels.clear();
      pixelsShow();
      state = sIDLE;
      break;
    case sROT:  //Farbe Rot
      colorSwitch(pixels.Color(255, 0, 0));
      state = sIDLE;
      break;
    case sHELLGRUEN:  //Farbe Grün
      colorSwitch(pixels.Color(0, 255, 0));
      state = sIDLE;
      break;
    case sHELLBLAU:  // Farbe Blau Hell
      colorSwitch(pixels.Color(0, 0, 255));
      state = sIDLE;
      break;
    case sGRAU:
      colorSwitch(pixels.Color(128, 128, 128));
      state = sIDLE;
      break;
    case sDUNKELBLAU:  // Farbe Blau Dunkel
      colorSwitch(pixels.Color(20, 0, 255));
      state = sIDLE;
      break;
    case sORANGE:  // Farbe Orange
      colorSwitch(pixels.Color(255, 147, 5));
      state = sIDLE;
      break;
    case sGRUEN:  // Farbe Grün
      colorSwitch(pixels.Color(118, 235, 2));
      state = sIDLE;
      break;
    case sGELB:  // Farbe Gelb
      colorSwitch(pixels.Color(252, 244, 3));
      state = sIDLE;
      break;
    case sHELLER:  // Helligkeit hoch
      if (BRIGHTNESS < 255) {
        BRIGHTNESS++;
        pixels.setBrightness(BRIGHTNESS);
        pixelsShow();
      }
      state = sIDLE;
      break;
    case sDUNKLER:  // Helligkeit runter
      if (BRIGHTNESS > 0) {
        BRIGHTNESS--;
        pixels.setBrightness(BRIGHTNESS);
        pixelsShow();
      }
      state = sIDLE;
      break;
    case sPINK:  //Farbe Pink
      colorSwitch(pixels.Color(202, 21, 230));
      state = sIDLE;
      break;
    case sFADE:  // Button Fade
      if (fadingDone(true)) {
        state = sIDLE;
      }
      break;
    case sSTROBE:  // Button Strobe
      theaterChaseRainbow(50);  // Bis ihn ein anderer IR Code beendet ...
      break;
    case sSMOOTH:  // Button Smooth
      if (rainbowDone(10)) {
        state = sIDLE;
      };
      break;
    case sRAINBOW:
      rainbowEndless(10); // Bis ihn ein anderer IR Code beendet ...
      break;
    case sIRGENDWAS:
      pixels.clear();  // Set all pixel colors to 'off'
      colorSwitch(pixels.Color(0, 150, 0));
      state = sIDLE;
      break;
  }
}

void colorSwitch(uint32_t color) {
  for (int i = 0; i < pixels.numPixels(); i++) {  // For each pixel in pixels...
    pixels.setPixelColor(i, color);               //  Set pixel's color (in RAM)
  }
  pixelsShow();
}

boolean fadingDone(boolean run) {
  static int h = 1;
  static unsigned long lastAction = 0;
  static byte mode = 0;
  if (!run) {
    h =  1;
    lastAction = millis();
    mode = 0;
    return true;
  }
  switch (mode) {
    case 0:
      if (millis() - lastAction > 10) {
        lastAction = millis();
        pixels.setBrightness(h * cHell);
        pixelsShow();
        h++;
      }
      if (h >= 32) {
        mode = 1;
      };
      break;
    case 1:
      if (millis() - lastAction > 50) {
        lastAction = millis();
        pixels.setBrightness(h * cHell);
        pixelsShow();
        h--;
      }
      if (h <= 1) {
        mode = 2;
      };
      break;
    case 2:
      pixels.setBrightness(BRIGHTNESS);
      pixelsShow();
      return true;
      break;
  }
  return false;
}


void theaterChaseRainbow(unsigned long wait) {  // call mit 50 ms
  static unsigned long lastAction = 0;
  static int firstPixelHue = 0;        // First pixel starts at red (hue 0)
  static int b = 0;
  if (wait < 0) {  // wait kleiner 0 setzt die Startwerte und kehrt direkt zurück
    lastAction = 0;
    firstPixelHue = 0;
    b = 0;
    return;
  }
  if (millis() - lastAction > wait) {
    lastAction = millis();
    pixels.clear();
    for (int c = b; c < pixels.numPixels(); c += 3) {
      int hue = firstPixelHue + c * 65536L / pixels.numPixels();
      uint32_t color = pixels.gamma32(pixels.ColorHSV(hue));  // hue -> RGB
      pixels.setPixelColor(c, color);                         // Set pixel 'c' to value 'color'
    }
    pixelsShow();                // Update pixels with new contents
    firstPixelHue += 65536 / 90;  // One cycle of color wheel over 90 frames
    b++;
  }
  if (b > 2) {
    b = 0;
  }
}


boolean rainbowDone(unsigned long wait) {
  static long firstPixelHue = 0;
  static unsigned long lastAction = 0;
  if (wait < 0) {
    firstPixelHue = 0;
    lastAction = 0;
    return;
  }
  if (millis() - lastAction > wait) {
    lastAction = millis();
    pixels.rainbow(firstPixelHue);
    pixelsShow();  // Update pixels with new contents
    if (firstPixelHue < 5 * 65536) {
      firstPixelHue += 256;
    } else {
      firstPixelHue = 0;
      return true;
    }
  }
  return false;
}

void rainbowEndless(unsigned long wait) {
  static long firstPixelHue = 0;
  static unsigned long lastAction = 0;
  if (wait < 0) {
    firstPixelHue = 0;
    lastAction = 0;
    return;
  }
  if (millis() - lastAction > wait) {
    lastAction = millis();
    pixels.rainbow(firstPixelHue);
    pixelsShow();  // Update pixels with new contents
    if (firstPixelHue < 5 * 65536) {
      firstPixelHue += 256;
    } else {
      firstPixelHue = 0;
    }
  }
}
2 Likes