Dieser Code empfängt und dekodiert NEC-IR-Signale ohne externe Bibliothek.  
Er dient dazu, das Prinzip der Pulslängen-Modulation zu veranschaulichen und das Verständnis der Infrarot-Signalübertragung zu vertiefen. |
Messprinzip
Die Zeiterfassung erfolgt mit micros(), das die Dauer von LOW- und HIGH-Pulsen misst (Pulslängen-Modulation). 
Das Signal wird erfasst, indem measurePulse(state) die Zeit für jeden Zustand speichert.  
Ein Timeout (60 ms) verhindert endlose Messungen.
Zur Identifikation eines NEC-Signals:
 
Quellcode (engl. Sourcecode)
 Listing 1: infraredreceiver.ino
 
/**********************************************************************
   IR-Decoder-Beispiel: NEC-Protokoll mit  Pulstabelle
   - Erfasst Pulse (LOW/HIGH) mit micros().
   - Zeigt eine formatierte Pulstabelle mit Bit-Spalte.
   - Dekodiert NEC-Signale auf zwei Arten:
       1) decodeRawNEC        (MSB-first)
       2) decodeBitReverseNEC (LSB-first)
 **********************************************************************/
#define IR_PIN 7            // Pin für den IR-Empfänger
#define MAX_PULSES 70       // Max. Anzahl erfassbarer Pulse-Paare
#define TIMEOUT_US 60000UL  // 60 ms Sicherheitszeit
#define HIGH_PAUSE 10000UL  // 10 ms: Abbruchkriterium (Ende des IR-Signals)
bool USE_REGISTER = true;   // true = direkten Registerzugriff!
unsigned long pulseArray[MAX_PULSES][2];
int pulseIndex = 0;                    // Anzahl erfasster Pulse-Paare
bool capturing = false;                // Flag: Erfassung läuft?
void setup() {
  Serial.begin(115200);
  pinMode(IR_PIN, INPUT);
  Serial.println("IR-Decoder gestartet.");
}
void loop() {
  // Messung starten?
  if (!capturing && isIrSignalDetected()) {
    
    capturing = true;
    pulseIndex = 0;
    capturePulses();        // Pulse erfassen
    printPulses();          // Tabelle ausgeben
    decodeRawNEC();         // Dekodierung (MSB-first)
    decodeBitReverseNEC();  // Dekodierung (LSB-first)
    capturing = false;
  }
}
// ---------------------------------------------------------------------
// Erfasst abwechselnd LOW- und HIGH-Dauer, bis eine lange HIGH-Pause
// (HIGH_PAUSE) oder das Array voll ist.
// ---------------------------------------------------------------------
void capturePulses() {
  while (pulseIndex < MAX_PULSES) {
    unsigned long lowDuration  = measurePulse(LOW);
    unsigned long highDuration = measurePulse(HIGH);
    pulseArray[pulseIndex][0] = lowDuration;
    pulseArray[pulseIndex][1] = highDuration;
    pulseIndex++;
    if (highDuration > HIGH_PAUSE) {
      break; // Ende des Signals
    }
  }
}
bool isIrSignalDetected() { 
  if (USE_REGISTER)
    return !(PIND & (1 << IR_PIN));
  return digitalRead(IR_PIN) == LOW;
}
// ---------------------------------------------------------------------
// Misst, wie lange der Pin im angegebenen Zustand (LOW/HIGH) bleibt,
// bricht nach TIMEOUT_US ab (z.B. 60 ms).
// ---------------------------------------------------------------------
unsigned long measurePulse(bool state) {
  bool checkState = (state == LOW);
  
  unsigned long startTime = micros();
  while (isIrSignalDetected() == checkState) {
    if (micros() - startTime > TIMEOUT_US) {
      break;
    }
  }
  return micros() - startTime;
}
// ---------------------------------------------------------------------
// Gibt die aufgenommenen Pulse als Tabelle im Serial Monitor aus.
// Spalten: | Label      |  LOW(µs) | HIGH(µs) | Bit |
// Startbit = Zeile 0, Bits 1..32, danach Stopbit?
// Zur Bit-Bestimmung wird ein simpler Schwellwert (HIGH > 1000 µs) genutzt.
// ---------------------------------------------------------------------
void printPulses() {
  Serial.println("Infrarot Signal empfangen:");
  Serial.println("");
  Serial.println("| Label      |  LOW(µs) | HIGH(µs) | Bit |");
  Serial.println("|------------|---------:|---------:|-----|");
  for (int i = 0; i < pulseIndex; i++) {
    String label;
    if (i == 0) {
      label = "Startbit";
    } else if (i <= 32) {
      label = "Bit " + String(i);
    } else {
      label = "Stopbit";
    }
    char bitVal = '-';
    if (i >= 1 && i <= 32) {
      if (pulseArray[i][1] > 1000) {
        bitVal = '1';
      } else {
        bitVal = '0';
      }
    }
    char lineBuffer[80];
    sprintf(
      lineBuffer,
      "| %-10s | %8lu | %8lu |  %c  |",
      label.c_str(),
      pulseArray[i][0],
      pulseArray[i][1],
      bitVal
    );
    Serial.println(lineBuffer);
  }
  Serial.println();
}
// ---------------------------------------------------------------------
// Dekodiert das Signal MSB-first
// (klassischer Ansatz: data << 1, dann Bit rein).
// ---------------------------------------------------------------------
void decodeRawNEC() {
  if (pulseIndex < 34) {
    Serial.println("[decodeRawNEC] Zu wenige Pulse für NEC (mind. 34)!");
    return;
  }
  unsigned long startLow  = pulseArray[0][0];
  unsigned long startHigh = pulseArray[0][1];
  if (startLow < 8000 || startLow > 10000 ||
      startHigh < 4000 || startHigh > 5500) {
    Serial.println("[decodeRawNEC] Kein gültiges Startbit!");
    return;
  }
  uint32_t data = 0;
  for (int i = 1; i <= 32; i++) {
    unsigned long highDur = pulseArray[i][1];
    if (highDur > 1000) {
      data = (data << 1) | 1;  // Bit 1
    } else {
      data = (data << 1);      // Bit 0
    }
  }
  Serial.println("[decodeRawNEC] Ergebnis (MSB-first):");
  printHex32(data);
  printNECBytes(data);
  Serial.println("");
}
// ---------------------------------------------------------------------
// Dekodiert das Signal LSB-first
// (bitweises Umkehren durch data >> 1 | (bit << 31)).
// ---------------------------------------------------------------------
void decodeBitReverseNEC() {
  if (pulseIndex < 34) {
    Serial.println("[decodeBitReverseNEC] Zu wenige Pulse für NEC (mind. 34)!");
    return;
  }
  unsigned long startLow  = pulseArray[0][0];
  unsigned long startHigh = pulseArray[0][1];
  if (startLow < 8000 || startLow > 10000 ||
      startHigh < 4000 || startHigh > 5500) {
    Serial.println("[decodeBitReverseNEC] Kein gültiges Startbit!");
    return;
  }
  uint32_t data = 0;
  for (int i = 1; i <= 32; i++) {
    unsigned long highDur = pulseArray[i][1];
    int bitVal = (highDur > 1000) ? 1 : 0;
    data = (data >> 1) | ((uint32_t)bitVal << 31);
  }
  Serial.println("[decodeBitReverseNEC] Ergebnis (LSB-first):");
  printHex32(data);
  printNECBytes(data);
  Serial.println("");
}
// ---------------------------------------------------------------------
// Gibt den 32-Bit-Wert stets als achtstellige HEX-Zahl aus (mit 0ern).
// ---------------------------------------------------------------------
void printHex32(uint32_t val) {
  char buf[9];
  for (int i = 7; i >= 0; i--) {
    uint8_t nibble = val & 0xF;
    val >>= 4;
    buf[i] = (nibble < 10)
             ? (char)('0' + nibble)
             : (char)('A' + (nibble - 10));
  }
  buf[8] = '\0';
  Serial.print("32-Bit Hex: 0x");
  Serial.println(buf);
}
// ---------------------------------------------------------------------
// Zerlegt die 32-Bit-Daten in 4 Bytes (Addr, ~Addr, Cmd, ~Cmd)
// und gibt sie aus. Prüfung, ob ~Addr und ~Cmd jeweils Inversionen sind.
// ---------------------------------------------------------------------
void printNECBytes(uint32_t data) {
  uint8_t addr    = (data >> 24) & 0xFF;
  uint8_t addrInv = (data >> 16) & 0xFF;
  uint8_t cmd     = (data >>  8) & 0xFF;
  uint8_t cmdInv  =  data        & 0xFF;
  Serial.print("Addr = 0x");
  Serial.print(addr, HEX);
  Serial.print(", ~Addr = 0x");
  Serial.print(addrInv, HEX);
  Serial.print(", Cmd = 0x");
  Serial.print(cmd, HEX);
  Serial.print(", ~Cmd = 0x");
  Serial.println(cmdInv, HEX);
  bool verify = ((uint8_t)(addr ^ addrInv) == 0xFF) &&  ((uint8_t)(cmd ^ cmdInv) == 0xFF);
  if (!verify) {
    Serial.println("→ WARNING: Keine typische Inversion!\n");
  }
}
Erklärungen zum Quellcode
 loop - Hauptprogrammschleife
Sobald ein IR-Signal erkannt wird (isIrSignalDetected()), startet die Verarbeitung:
-  - capturePulses(): Erfasst die LOW-/HIGH-Pulse und speichert sie.
 
-  - printPulses(): Gibt die erfasste Pulstabelle im Serial Monitor aus.
 
-  - decodeRawNEC(): Dekodiert das Signal im MSB-first-Format.
 
-  - decodeBitReverseNEC(): Dekodiert das Signal im LSB-first-Format.
 
Danach wird capturing zurückgesetzt, um neue Signale zu empfangen.
pulseArray - zweidimensionales Array
pulseArray ist ein zweidimensionales Array pulseArray[MAX_PULSES][2], das die gemessenen Dauerwerte eines empfangenen IR-Signals speichert.
Jeder Eintrag i entspricht einem gemessenen Puls-Paar (LOW/HIGH), beginnend mit dem Startbit und gefolgt von den Datenbits des IR-Signals.
decodeRawNEC() – MSB-first Dekodierung
-  Prüft das Startbit (ca. 9000 µs LOW + 4500 µs HIGH). 
-  Liest die folgenden 32 Datenbits: - 
-  Bit 1, wenn HIGH-Dauer > 1000 µs. 
-  Bit 0, wenn HIGH-Dauer ≤ 1000 µs. 
-  Speichert das Bit von links nach rechts in - data:
 
 
-  Gibt das 32-Bit-Ergebnis als HEX-Wert und die Einzelbytes (Adresse, Kommando) aus. 
decodeBitReverseNEC() – LSB-first Dekodierung
-  Identisch zu - decodeRawNEC(), aber die Bits werden umgekehrt gespeichert:
 
-  Das Ergebnis entspricht einer spiegelbildlichen Darstellung der Binärdaten. 
Beispiel: Makeblock Fernbedienung
Der  Code infraredreceiver.ino erzeugt die folgende Ausgabe, wenn ‚A‘ mit der Makeblock-Fernbedienung gesendet wird. Diese wird von einem Arduino Nano über die serielle Schnittstelle ausgegeben. 
Infrarot Signal empfangen:
| Label      |  LOW(µs) | HIGH(µs) | Bit |
|------------|---------:|---------:|-----|
| Startbit   |     9116 |     4428 |  -  |
| Bit 1      |      624 |      524 |  0  |
| Bit 2      |      596 |      524 |  0  |
| Bit 3      |      596 |      520 |  0  |
| Bit 4      |      596 |      524 |  0  |
| Bit 5      |      572 |      548 |  0  |
| Bit 6      |      596 |      520 |  0  |
| Bit 7      |      600 |      516 |  0  |
| Bit 8      |      600 |      524 |  0  |
| Bit 9      |      592 |     1624 |  1  |
| Bit 10     |      624 |     1620 |  1  |
| Bit 11     |      624 |     1624 |  1  |
| Bit 12     |      624 |     1620 |  1  |
| Bit 13     |      624 |     1624 |  1  |
| Bit 14     |      624 |     1620 |  1  |
| Bit 15     |      624 |     1624 |  1  |
| Bit 16     |      620 |     1620 |  1  |
| Bit 17     |      624 |     1624 |  1  |
| Bit 18     |      628 |      520 |  0  |
| Bit 19     |      596 |     1620 |  1  |
| Bit 20     |      624 |      524 |  0  |
| Bit 21     |      596 |      520 |  0  |
| Bit 22     |      592 |      524 |  0  |
| Bit 23     |      596 |     1616 |  1  |
| Bit 24     |      628 |      524 |  0  |
| Bit 25     |      596 |      520 |  0  |
| Bit 26     |      596 |     1624 |  1  |
| Bit 27     |      624 |      520 |  0  |
| Bit 28     |      596 |     1624 |  1  |
| Bit 29     |      620 |     1624 |  1  |
| Bit 30     |      624 |     1616 |  1  |
| Bit 31     |      620 |      524 |  0  |
| Bit 32     |      596 |     1620 |  1  |
| Stopbit    |      624 |    60008 |  -  |
[decodeRawNEC] Ergebnis (MSB-first):
32-Bit Hex: 0x00FFA25D
Addr = 0x0, ~Addr = 0xFF, Cmd = 0xA2, ~Cmd = 0x5D
[decodeBitReverseNEC] Ergebnis (LSB-first):
32-Bit Hex: 0xBA45FF00
Addr = 0xBA, ~Addr = 0x45, Cmd = 0xFF, ~Cmd = 0x0
Analyse
Die Verwendung der micros()-Methode liefert präzisere und konsistentere Werte als beispielsweise eine auf delay()-basierten Messung, was zu einer zuverlässigeren Erfassung der IR-Signale führt.
	
	
		| Abschnitt | Erwartet (NEC) µs | Gemessen µs | Abweichung | Bewertung | 
	
	
		| Startbit LOW | 9000 | 9116 | +1.3% | ✅ Sehr gut | 
	
		| Startbit HIGH | 4500 | 4428 | -1.6% | ✅ Sehr gut | 
	
		| Bit 0 LOW | 560 | 624 | +11.4% | ⚠ Leichte Abweichung | 
	
		| Bit 0 HIGH | 560 | 524 | -6.4% | ✅ Gut | 
	
		| Bit 1 LOW | 560 | 592-628 | +5-12% | ⚠ Leichte Abweichung | 
	
		| Bit 1 HIGH | 1690 | 1616-1624 | -4.4% | ✅ Sehr gut | 
	
		| Stopbit LOW | 560 | 624 | +11.4% | ⚠ Leichte Abweichung | 
	
		| Stopbit HIGH | keine Angabe | 60008 | N/A | 🚩 Typische HIGH-Pause (Signalende) | 
-  Das Startbit liegt sehr nahe an den Sollwerten (~9000 µs LOW und ~4500 µs HIGH) mit einer minimalen Abweichung von +1.3 % (LOW) und -1.6 % (HIGH).   
-  Die Datenbits weichen typischerweise um 5–12 % ab, wobei die LOW-Phasen tendenziell etwas länger und die HIGH-Phasen für „0“ leicht kürzer als erwartet sind. Diese Abweichungen liegen im akzeptablen Bereich für IR-Signale.   
-  Beim Stopbit wird ein kurzer LOW-Puls (~624 µs) erfasst, der leicht über dem erwarteten Wert von 560 µs liegt (+11.4 % Abweichung). Danach folgt eine lange HIGH-Phase (~60008 µs), was das typische Signalende des NEC-Protokolls anzeigt.   
→ Fazit: Das Signal wird präzise erkannt und dekodiert, die Messergebnisse liegen im akzeptablen Toleranzbereich. 🚀  
 
MSB (Most Significant Bit) ist das höchstwertige Bit einer Zahl, während LSB (Least Significant Bit) das niederwertigste Bit ist. 
 
Beim Speichern oder Übertragen von Daten gibt es zwei mögliche Reihenfolgen:  
Je nach Protokoll kann eine der beiden Darstellungen verwendet werden, weshalb eine Umwandlung zwischen MSB-first und LSB-first manchmal erforderlich ist.
 
Was macht decodeBitReverseNEC() genau?
Die Funktion decodeBitReverseNEC() dekodiert das NEC-Signal LSB-first, also das niederwertigste Bit zuerst. Das bedeutet:
Beispiel
Original (MSB-first) Empfang
Angenommen, das empfangene Signal besteht aus 32 Bits:
MSB → 00000000  11111111  10100010  01011101 ← LSB
Hexadezimal als MSB-first:
  0x00FFA25D
Bitweise Spiegelung für LSB-first
Wenn wir LSB-first dekodieren, wird nicht nur die Reihenfolge der Bytes umgedreht, sondern auch die Bits in jedem Byte selbst:
	
	
		| MSB-first Byte | LSB-first umgekehrt | 
	
	
		| 00000000(0x00)↓ | 00000000(0x00) | 
	
		| 11111111(0xFF) | 11111111(0xFF) | 
	
		| 10100010(0xA2) | 01000101(0x45) | 
	
		| 01011101(0x5D) | 10111010(0xBA)↑ | 
Das ergibt Hexadezimal als LSB-first:
 0xBA45FF00
Warum werden nicht nur die Bytes, sondern auch die Bits umgedreht?
In  decodeBitReverseNEC() passiert folgendes:
uint32_t data = 0;
for (int i = 1; i <= 32; i++) {
  unsigned long highDur = pulseArray[i][1];
  int bitVal = (highDur > 1000) ? 1 : 0;
  data = (data >> 1) | ((uint32_t)bitVal << 31);
}
Erklärung:
-  - bitValwird als 0 oder 1 gesetzt, basierend auf der HIGH-Zeit.
 
-  A: Das neue Bit wird an die höchste Position (- « 31) geschrieben.
 
-  B: Der Rest der bereits gespeicherten Bits wird um eine Position nach rechts verschoben (- » 1).
 
-  Beide Ausdrücke (A und B) werden - oderverknüpft.
 
Das bedeutet:
Ergebnis: