IR Fernbedienung - Anfänger

Guten Abend,

ich bastel jetzt seid ein paar Stunden an meinem ersten Arduino Projekt herum und bin schon auf ein paar Probleme gestoßen. Ich möchte mein Wohnzimmer über Alexa fernsteuern und dazu hab ich mir einen IR Sender gebaut mit dem NodeMCU ESP8266.

Ich orientiere mich an diesem Aufbau: - YouTube

Ich kann meine Anlage, Fernseher und Apple TV an-/ausschalten. Leider funktionieren noch keine Funktionen wie “Musik” , sprich Anlage an und den Eingang auf Bluethoot stellen. Alle nötigen IR Codes habe ich schon eingelesen, leider funktioniert es nicht diese hintereinander zu senden. Ich habe schon eine Verzögerung mit Delay von 10s eingebaut, damit die Anlage genügend Zeit zum anschalten hat. Selbes Problem mit einer weiteren Funktion “YouTube” (Fernseher an, Anlage an, AppleTV an, Eingänge schalten & AppleTV zu der YouTube App navigieren). Es werden immer nur Teile dieser Aktionen durchgeführt und nie die Gesamte Funktion.

else if (device_id == 3)      //YouTube
   { 
     Serial.println("AN");
     IRLED.sendRC6(0x1000C, 20);   //Fernseher an
     delay(500);
     IRLED.sendNEC(0x10E03FC, 32);   //Anlage an
     delay(500);
     IRLED.sendNEC(0x77E1FA80, 32);   //AppleTV an
     delay(10000);
     
     for(int i = 0; i < 2; i++)
     {
       IRLED.sendNEC(0x77E16080, 32); //Apple TV rechts 
       delay(1000);
     }

     IRLED.sendNEC(0x77E1A080, 32);   //Apple TV Bestätigen
     delay(500);
     IRLED.sendRC5(0x839, 12);   //Fernseher auf AppleTV Eingang
     delay(500);
     IRLED.sendNEC(0x10E0BF4, 32);   //Anlage auf DVD Eingang
     delay(500);
}

Mein nächstes Problem ist das senden eines Signals über längere Zeit da ein Button mehrere Funltionen hat. Wenn ich den IR Code auslese sind es die selben NEC Codes, nur das wenn ich die Taste gedrückt halte es einen NEC(repeat) Code nach dem Standard Code für die Taste ausgibt. Gibt es dafür eine extra Funktion oder kann ich es vllt so schrieben?

if(device_id == 0)            //Apple TV
   { 
     Serial.println("AUS");

     TimerA = millis();
     
     do
     {
       IRLED.sendNEC(0x77E1A080, 32);   //Apple TV aus
       delay(50);
     }while(millis()-TimerA >= 4000UL);
     
   }

Poste bitte den kompletten Sketch, damit wir alle Zusammenhänge erkennen können.
Und verwende Code-Tags, Schaltfläche </> oben links im Editorfenster, damit wir den Sketch richtig lesen können.

Meist liegt der Fehler im verborgenen Teil des Sketches.

Ich weiß leider nicht was mit Code-Tags gemeint ist, aber im Anhang ist schonmal der gesamte Sketch.

Sprachsteuerung_ZimmerV2.ino (5.86 KB)

RobertMarley:
Ich weiß leider nicht was mit Code-Tags gemeint ist, aber im Anhang ist schonmal der gesamte Sketch.

Das steht doch in meinem Post, bitte nur lesen.

Anhänge können von mobilen Geräten nicht gelesen werden.

Sorry! Mein Fehler…

#include <ESP8266WiFi.h>
#include <RCSwitch.h>
#include "fauxmoESP.h" 

#include <IRremoteESP8266.h> //by Ken Shirriff (C) NEU
#include <IRsend.h>

#define WIFI_SSID "Mr. NiceGuy (.Y.)"
#define WIFI_PASS "*********"

fauxmoESP alexawifi;
RCSwitch mySwitch = RCSwitch();

IRsend IRLED(4); // IRLED angeschlossen am D2 Pin (GPIO4) - NEU

unsigned long TimerA;

void wifiSetup() {

// -----------------------------------------------------------------------------
// WLAN SETUP
// -----------------------------------------------------------------------------
 
    WiFi.mode(WIFI_STA);
 
    Serial.printf("Verbindungs zu %s wird aufgebaut ", WIFI_SSID);
    
    WiFi.begin(WIFI_SSID, WIFI_PASS);
 
    while (WiFi.status() != WL_CONNECTED) 
    {
        Serial.print(">");
        delay(100);
    } 
    Serial.println("");
    Serial.printf("Verbunden! SSID: %s, IP Adresse: %s\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
}

void anfrage(uint8_t device_id, const char * device_name, bool state) 
{
  Serial.print("Gerät: "); 
  Serial.println(device_name); 
  Serial.print("Status: ");
  
  if (state) //Geräte anschalten
  {  
    if(device_id == 0)            //AppleTV
    {
      Serial.println("AN");
      IRLED.sendNEC(0x77E12080, 32);   //AppleTV an
      delay(50);
    }

    else if (device_id == 1)      //Fernseher
    { 
      Serial.println("AN");
      IRLED.sendRC6(0x1000C, 20);   //Fernseher an
      delay(50);
    }

    else if (device_id == 2)      //Anlage
    { 
      Serial.println("AN");
      IRLED.sendNEC(0x10E03FC, 32);   //Anlage an
      delay(50);
    }

    else if (device_id == 3)      //YouTube
    { 
      Serial.println("AN");
      IRLED.sendRC6(0x1000C, 20);   //Fernseher an
      delay(500);
      IRLED.sendNEC(0x10E03FC, 32);   //Anlage an
      delay(500);
      IRLED.sendNEC(0x77E1FA80, 32);   //AppleTV an
      delay(10000);
      
      for(int i = 0; i < 2; i++)
      {
        IRLED.sendNEC(0x77E16080, 32); //Apple TV rechts 
        delay(1000);
      }

      IRLED.sendNEC(0x77E1A080, 32);   //Apple TV Bestätigen
      delay(500);
      IRLED.sendRC5(0x839, 12);   //Fernseher auf AppleTV Eingang
      delay(500);
      IRLED.sendNEC(0x10E0BF4, 32);   //Anlage auf DVD Eingang
      delay(500);
    }

    else if (device_id == 4)      //Netflix
    { 
      Serial.println("AN");
      IRLED.sendRC6(0x1000C, 20);   //TV an
      delay(500);
      IRLED.sendNEC(0x10E03FC, 32);   //Anlage an
      delay(500);
      IRLED.sendNEC(0x77E1A080, 32);   //Apple TV an
      delay(10000);
      IRLED.sendNEC(0x77E13A80, 32);   //Apple TV Bestätigen
      delay(1000);
      IRLED.sendRC5(0x84C, 12);   //TV auf AppleTV Eingang
      delay(500);
      IRLED.sendNEC(0x10E23DC, 32);   //Anlage auf DVD Eingang
      delay(500);
    }

    else if (device_id == 5)      //PlayStation Modus
    { 
      Serial.println("AN");
      IRLED.sendRC6(0x1000C, 20);   //TV an
      delay(500);
      IRLED.sendNEC(0x10E03FC, 32);   //Anlage an
      delay(10000);
      IRLED.sendRC5(0xC4, 12);   //TV auf PS4 Eingang
      delay(500);
      IRLED.sendNEC(0x10E0BF4, 32);   //Anlage auf DVD Eingang
      delay(500);
    }

    else if (device_id == 6)      //Musik
    { 
      Serial.println("AN");
      IRLED.sendNEC(0x10E03FC, 32);   //Anlage an
      delay(10000);
      IRLED.sendNEC(0x10E23DC, 32);   //Anlage auf CD/Bluetooht
      delay(500);
    }
  }
  
  else //Geräte Ausschalten
  {
    
    if(device_id == 0)            //Apple TV
    { 
      Serial.println("AUS");

      TimerA = millis();
      
      do
      {
        IRLED.sendNEC(0x77E1A080, 32);   //Apple TV aus
        delay(50);
      }while(millis()-TimerA >= 4000UL);
      
    }

    else if (device_id == 1)      //Fernseher
    { 
      Serial.println("AUS");
      IRLED.sendRC6(0x1000C, 20);   //Fernseher aus
      delay(50);
    }
    
    else if (device_id == 2)      //Anlage
    { 
      Serial.println("AUS");
      IRLED.sendNEC(0x10EF906, 32);   //Anlage aus
      delay(50);
    }

    else if (device_id == 3)      //Youtube
    { 
      Serial.println("AUS");
      IRLED.sendRC6(0x1000C, 20);   //TV aus
      delay(500);
      IRLED.sendNEC(0x10EF906, 32);   //Anlage aus
      delay(500);
      IRLED.sendNEC(0x77E1C080, 32);   //AppleTV Home
      delay(500);
      
      for(int i = 0; i < 3; i++)
      {
        IRLED.sendNEC(0x77E19080, 32); //Apple TV links 
        delay(500);
      }

      //IRLED.sendNEC(0x77E1A080, 32);   //Apple TV aus
      //delay(500);
    }

    else if (device_id == 4)      //Netflix
    { 
      Serial.println("AUS");
      IRLED.sendRC5(0x84C, 12);   //TV aus
      delay(500);
      IRLED.sendRC5(0x84C, 12);   //Anlage aus
      delay(500);
      IRLED.sendNEC(0x77E1C080, 32);   //AppleTV Home
      delay(500);
      //IRLED.sendNEC(0x77E1A080, 32);   //Apple TV aus
      //delay(50);
    }

    else if (device_id == 5)      //Zocker Modus
    { 
      Serial.println("AUS");
      IRLED.sendRC6(0x1000C, 20);   //TV aus
      delay(500);
      IRLED.sendNEC(0x10EF906, 32);   //Anlage aus
      delay(500);
    }

    else if (device_id == 6)      //Music
    { 
      Serial.println("AUS");
      IRLED.sendNEC(0x10EF906, 32);   //Anlage aus
      delay(50);
    }
  }
}

void geraete(){
  
    alexawifi.addDevice("Apple TV");      //ID 0    
    alexawifi.addDevice("Fernseher");     //ID 1
    alexawifi.addDevice("Anlage");        //ID 2
    alexawifi.addDevice("YouTube");       //ID 3
    alexawifi.addDevice("Netlix");        //ID 4
    alexawifi.addDevice("PlayStation Modus");  //ID 5
    alexawifi.addDevice("Musik");         //ID 6
    
    
    
    alexawifi.onMessage(anfrage);
    

}

void setup() {

    Serial.begin(115200);
    Serial.println("Nach dem Verbinden, sage 'Alexa, schalte <Gerät> an' oder 'aus'");
    
    mySwitch.enableTransmit(5);
   
    IRLED.begin(); // NEU

    wifiSetup();
    geraete();
  
}

void loop() {
  alexawifi.handle();
}

Ok, soweit gut
Selber habe ich noch keine große Erfahrung mit IR-Senden aber meine gelesen zu haben, zwischen den einzelnen verschiedenen Code muss eine Pause von 5 Sek. sein.

Erstmal vielen Dank für deine Hilfe und Input. Ich werd mich morgen Abend mal genauer damit beschäftigen und mal googlen, ob es da eine feste Zeitangabe gibt. 5 sec scheinen mir da doch etwas lang.
Z.b die Schleife beim einschalten von YouTube schickt, in 0.5s Schritten, dreimal nach rechts und das wird auch durchgeführt, nur leider bleibt das bestätigen auf der Strecke.

RobertMarley:
Erstmal vielen Dank für deine Hilfe und Input. Ich werd mich morgen Abend mal genauer damit beschäftigen und mal googlen, ob es da eine feste Zeitangabe gibt. 5 sec scheinen mir da doch etwas lang.
Z.b die Schleife beim einschalten von YouTube schickt, in 0.5s Schritten, dreimal nach rechts und das wird auch durchgeführt, nur leider bleibt das bestätigen auf der Strecke.

Das ist in der Tat sehr lang.
Daher habe ich grad mal die Library und hier die IRSendDemo angeschaut.
Da werden 2 Sek. zwischen den Codes gesetzt.

Aus dem Beispiel:

void loop() { 
  Serial.println("NEC"); 
  irsend.sendNEC(0x00FFE01F, 36); 
  delay(2000); 
  Serial.println("Sony"); 
  irsend.sendSony(0xa90, 12); 
  delay(2000); 
}

Edit:
Und "IRSend" muss nicht nochmal initialisiert werden. Das wird schon in der Lib gemacht.

Ich würde das nur in (sehr) kleinen Schritten entwickeln.
Erst wenn (zum Beispiel) das Einschalten des Fernsehers total betriebssicher funktioniert den nächsten Schritt machen. Dann (zum Beispiel) Fernseher auf AppleTV umschalten. Erst wenn das völlig zuverlässig funktioniert der nächste Schritt und so weiter.
Im Endeffekt müssen von 100 Versuchen 99,8 erfolgreiche Versuche sein :slight_smile:

Bsonders achte auf:

  • kritisches Timing
  • sicherer IR-Empfang aller Komponenten
  • Besonderheiten der Code-Protokolle
    Kritisches Timing
    Möglicherweise ist das Timing kritisch.
    Besonders wenn du in Menüs navigieren musst (wie z.B. bei AppleTV), kann es sein, dass die Reaktionszeit "situationsabhängig" ist. So etwas hatte ich vor kurzem bei einem HDMI-Umschalter mit Reaktionszeiten zwischen 0,2 und 3(!) Sekunden - das war sehr frustrierend. Wann immer möglich würde ich deshalb Menünavigationen vermeiden :slight_smile:

sicherer IR-Empfang aller Komponenten
Wie sicher ist der IR-Empfang aller Geräte?
Die IR-Empfänger in den Geräten sind auf eine bestimmte IR-Wellenlänge und IR-Modulationsfrequenz optimiert. Meist empfangen sie zwar auch "danebenliegende" Wellenlängen und Frequenzen, aber niemals so betriebssicher wie die "Originalwerte".
Typische Wellenlängen liegen zwischen etwa 875 und 950nm.
Typische Modulations-Frequenzen sind 36, 38 oder 40kHz. Manche Fernbedienungen laufen aber auch mit viel höheren Frequenzen (z.B. manche Geräte von Bang & Olufsen: auf 455kHz).
Du musst sicherstellen, dass alle Geräte die Signale sehr gut empfangen können.
Eventuell brauchst du dazu ein "recht kräftiges" IR-Signal:
Du kannst versuchen mehrere IR-Sende-LEDs parallel zu betreiben und auch solche mit unterschiedlichen Wellenlängen verwenden (parallel). Du brauchst ziemlich sicher eine Ansteuerung über Transistor, weil der Arduino-Pin nicht genug Strom liefern kann.

Besonderheiten der Code-Protokolle
Du verwendest verschiedene IR-Code-Protokolle: NEC und RC6.
Diese sind sehr unterschiedlich (näheres siehe hier)
So kann es sein, dass manchmal ein Wiederholungscode (z.B. NEC), manchmal Bit-Toggling (z.B. RC6) nötig ist. Nicht immer kümmert sich die Library darum. Versuche also den "Originalcode" deiner Fernbedienungen möglichst aufmerksam zu anaysieren.
Manche Geräte reagieren erst nachdem ein bestimmter Code mehrmals hintereinander empfangen wurde (früher war das hauptsächlich bei Sony so, mittlerweile machen das aber auch andere Marken).

Dein Projekt ist durchaus komplex.
Ich wünsche viel Erfolg! :slight_smile:

Uxomm dir auch vielen Dank für deinen Input!

Ich befolge direkt deinen Rat und gehe ertsmal Schrittweise voran. Alle Geräte funktionieren einwandfrei beim ein- bzw ausschalten. Nun hab ich das Signal "Anlage auf CD Input" alleine getestet, funktioniert. Daraufhin bin ich wieder in die Aktion "Musik" rein und hab diese mit einem Delay von 10s versucht, aber nach 10s passiert nichts. Mein Verdacht liegt bei Delay, dass alle Signal gleichzeitig geschickt werden.
Dann hab ich nochmal die Aktion "YouTube" getestet, ohne die Delay Zeiten zu ändern. Alle Geräte gehen an, aber nichts weiter passiert. Ich sehe bei meinem Fernseher auch eine LED leuchten, wenn er ein Signal empfängt. Diese leutet aber nur einmal.
Nun hab ich die Anlage mit "Anlage an" eingeschaltet und danach die Aktion "Musik an" gestartet. Nun da der Receiver schon an ist, wird direkt auf den CD Eingang geschaltet (direkt nach dem Befehl, also keine 10s Verzögerung).
Benutze ich dealy falsch oder ist der interne Timer evtl kaputt?

Noch nebenbei erwähnt reicht erstmal eine LED, die ich mit nem Transistor schalte. Auf die Weise hab ich eine Reichweite von ca. 5m. Wenn das delay Problem behoben ist und es immer noch nicht funktioniert, versuch ich mal mehrere LEDs parallel.

Es tut mir leid, ich fürchte, ich verstehe nicht genau was du versuchst zu beschreiben.

Ich denke, du solltest versuchen den Fehler einzugrenzen.

Lass mal Alexa weg.
Funktionieren deine Infrarot-Codes bzw. die Kombinationen dann?
Schreib ein kurzes Programm, wo nur dein Programmzweig für (zum Beispiel) "Youtube" ausgeführt wird, sonst nichts.
Sollte das funktionieren, dann könnte das Problem in der Kommunikation mit Alexa liegen.

Oder lass mal den IR-Code weg und schau dir nur an, welche Daten du über Alexa bekommst.
Reiche die Daten, die du bekommst einfach an die serielle Schnittstelle weiter und schau es dir an.
Kommt das an was du vermutest?
Ich kenne die Library, die du mit #include "fauxmoESP.h" einbindest nicht.

Du bindest mehrere Librarys ein. Ich kenne nur ein paar davon.
Es kommt immer wieder vor, dass sich manche Librarys nicht "miteinander vertragen".
Um das alleine durch Code-Studium beurteilen zu können, muss man sich schon recht gut vor allem mit der Hardware auskennen. Auf mich trifft das, zumindest was den ESP betrifft, leider nicht zu, beim ESP da "diletiere" ich nur vor mich hin. :slight_smile:
Aber du kannst trotzdem versuchen es herauszufinden, indem du das Programm reduzierst und Librarys mal weglässt (natürlich musst du dann auch den Code weglassen, der sich darauf bezieht).

Das meine ich mit eingrenzen.

Wünsche gutes Gelingen!

Die Funktion delay() macht einfach nicht was sie soll, sie verzögert das Programm nicht.

Hier kurz mit der Println Funktion getestet:

else if (device_id == 6)      //Musik
    { 
      Serial.println("AN");
      IRLED.sendNEC(0x10E03FC, 32);   //Anlage an
      delay(10000);
      Serial.println("10s");
      IRLED.sendNEC(0x10E23DC, 32);   //Anlage auf CD/Bluetooht
      delay(500);
    }

Meine Ausgabe “10s” wird nicht nach 10s, sondern direkt nach “An” ausgegeben.

Trifft das auch zu für andere Werte von delay?

Versuche stattdessen eine andere (ähnliche) Anweisung:

_delay_ms(5000);

Das sollte 5 Sekunden verzögern.

Hi

Ist es möglich, daß eine der Librarys einen der Timer verbiegt?
(oder warst Du Der, Der den millis()-Wert nullen wollte?)
Du solltest herausfinden, warum delay() hier nicht funktioniert - und für die Zukunft drauf verzichten.
Ich konnte mir vorstellen, daß ein auslösender Interrupt Dein delay verwirft/Dessen Werte missbraucht ect.pp.

MfG

Leider bin ich noch ein Anfänger und erkenne nicht wirklich ob eine der Librarys den Timer versaut.

Gerade noch diese Variante getestet

Serial.println("Start");

Start = millis();

do
{
   if (millis() - Start >= 10000)
  {
    Serial.println("10s");
    nr = 2;
  }
}while(nr == 1);

Leider bricht dann das Board zusammen und startet neu :confused:

Das hab ich auch versucht. Also delay() mit pause() getauscht. Das Board stürzt wieder ab und startet neu…

void pause(int ms) 
{ 
    for (int i = 0; i < ms; i++) 
    {
        delayMicroseconds(1000);
    }
}

Den ESP darfst du nicht durch so eine do-while-Schleife blockieren, der muss sich ja ständig um WLAN etc. kümmern können. Und da ist kein Betriebssystem, das dafür sorgt, dass mehrere Sachen gleichzeitig erledigt werden (können).

RobertMarley:
Das hab ich auch versucht. Also delay() mit pause() getauscht. Das Board stürzt wieder ab und startet neu...

Wenn du delay() ersetzen möchtest, musst du es über eine Funktion mit millis() machen.

Sieh dir dazu "BlinkWithoutDelay" an.

Hi

Neustarten geht - nach dem gezeigten Code - eigentlich nicht.
Aufhängen ok (wenn millis() versaut ist, kommt die Bedingung halt nie), aber ein Reset kann bei dieser Schleife nicht kommen.

Außer: Hier spielt noch ein Watchdog-Timer mit.
Hat der ESP-dingenskirchen einen WDT, Der empfindlich auf ein langes Delay reagiert?
Wird das Delay überhaupt blockierend ausgeführt, gerade, wo ja WLan und so Zeug abgearbeitet werden soll?
Ist delay() dort überhaupt 'zulässig'? (gerade, weil Das den normalen Ablauf stört - funktionieren tut's ja scheinbar)

Bis zu welcher Länge musst Du mit dem delay runter gehen, daß Dir der Code nicht mehr abschmiert?
Dafür würde ich aber das Pferd anders herum aufzäumen und mit einem 1sek delay anfangen, bei Erfolg jeweils eine Anzeige, wie lang das Delay war, eine Sekunde drauf und erneut warten lassen.
Irgendwann bekommst Du keine Erfolgsmeldung, der µC resettet und fängt wieder bei 1Sek an - dann könnte man ggf. diese kritische Zeit irgendwo im Datenblatt wieder finden - vll. sogar direkt beim WatchDogTimer.

MfG

MfG

Das delay() auf dem ESP8266 beinhaltet den yield()-Aufruf.
Deine do-while schleife blockiert aber, deshalb bootet er neu. Setze da mal ein yield(); in die Schleife rein, wenn Du unbedingt bei dem Konstrukt bleiben willst, das es aber keinen Sinn ergibt.

Du hast doch schon eine große Schleife namens loop(). Warum nutzt Du sie nicht.

Gruß Tommya