Généralités et cahier des charges

m5stick.jpg, mai 2021

L'idée générale, avec ce périphérique est de permettre l'envoi de codes infrarouges, à partir d'une commande reçue du réseau local. Cela permet de déporter ce périphérique là ou il y en a besoin (et même de l'intégrer dans un appareil, comme un ampli audio vidéo par exemple), tout en gardant la capacité de l'interfacer avec un système de domotique ou basé sur Node Red.

Ici, j'ai choisi d'utiliser un ESP32, bien qu'un ESP8266 puisse également faire le job. Une attention toute particulière a été portée sur l'émission infrarouge, afin de pouvoir piloter les leds avec un niveau de puissance suffisant pour permettre de 'tirer loin". En effet, certaines de ces leds sont pilotables avec des courants pouvant aller jusqu'à 100ma, ce qui permet d'augmenter substantiellement la portée.

Les sorties de l'ESP32 étant en 3.3V et sous très faible puissance, il va donc nous falloir réaliser un booster pour permettre de sortie ces courants. Ceci va être fait avec un MOSFET.

Par ailleurs, disposant d'un ampli NAD avec une entrée "IR In", j'ai creusé la possibilité de connecter directement le signal IR directement sur l'ampli.

Nad indique à propos de ce connecteur qu'il permet de raccorder un signal modulé à 38khz ou non, en logique positive, et d'une amplitude de 5v max. Il est disponible sur un connecteur 3.5mm, avec le plus sur la pointe, et le 0V sur l'anneau. Après avoir fait quelques essais,il est apparu que l'impédance de l'entrée est relativement basse et qu'on ne peut pas y mettre directement une sortie de micro contrôleur, sinon le signal s'effondre et l'ampli ne comprend qu'une commande sur 10. Il faut l'amplifier avec un transistor.

Voici le synoptique résultant du dispositif souhaité :

synoptic.png, mai 2021

Réalisation matérielle

Les étages d'amplification (buffer) ont étés réalisés avec des N Channel FET 2N7000. En voici le schéma. La partie "NAD" peut être omise si vous n'en avez pas besoin.

sch.png, mai 2021

La réalisation ne pose pas de problème particulier à part le brochage du 2N7000 et les tests car les leds infrarouge n'émettent pas de lumière visible. Vous pouvez utiliser une webcam ou la caméra d'un smartphone pour visualiser la lumière infrarouge. Attention, car les iphones sont munis de filtres IR qui ne permettent pas de visualiser les leds IR... Donc android ou webcam... Il suffit de mettre l'entrée à 3.3V pour que les leds s'allument. Si ce n'est pas le cas, c'est que vous avez un problème de câblage (leds à l'envers par exemple..). A noter que la résistance de R1peut être recalculée en fonction du courant souhaité avec la formule R = (5- (2x1.7) ) / I ou I est en courant souhaité. Sur certaines led IR on peut monter jusqu'à 0.1A (100ma). 0.05A peut être une valeur médiane acceptable.

Le logiciel

Le logiciel s'appuie en grande partie sur la bibliothèque IRremoteESP8266 qui fournit tout ce qu'il faut pour gérer de multiples formats infrarouge.

Après avoir inclus la bibliothèque :

#include <IRremoteESP8266.h>
#include <IRsend.h>

Il faut initialiser l'objet irsend quelque par dans la fonction setup :

irsend.begin();

Ensuite on peut utiliser les fonctions d'envoi dédiées à chaque protocole. Par exemple pour envoyer une commande au format NEC

irsend.sendNEC (0xE13E01FE,32,3);

Ici, on envoie 3 fois le code NEC étendu (adresse sur 16 bits + données sur 8 bits + complément de données sur 8 bits) avec appareil = E1, sous fonction= 3E, t.... Le mot total fait donc 32 bits.

N'oubliez pas que dans le format NEC, on envoie le LSB en premier pour chaque octet (voir article sur le décodage).

Autre exemple pour un format Philips RC6

irsend.sendRC6(0x000CU + 0x10000U*rc6Toggle, 20U);

A noter que pour le format RC6, un bit doit changer d'état à chaque transmission (0 ou 1), ce qui justifie le "rc6Toggle" sur la ligne. La longueur de la trame fait 20 bits.

Ce code est un exemple, adapté à mes périphérique et il faudra que vous l'adaptiez aux vôtres. Pour cela il vous faudra certainement consulter la documentation de IRremoteESP8266 ici: https://github.com/crankyoldgit/IRremoteESP8266/blob/master/SupportedProtocols.md car la bibliothèque inclut de nombreux protocoles .

/* NADRemote32
 * (c) 05/2021 JP CIVADE. Licence GPL 3.
 * 
 * Pour ESP32, permet d'envoyer des codes de télécommande NAD AVR2 (par exemple pour ampli T742) sur appel réseau.
 * Nécessite la bibliothèque IRremoteESP8266 . Utilise le format pronto pour définir les codes de la télécommande.
 * 
 * Sur M5stick, nécessite aussi les bibliothèques M5stick et u8g2.
 * 
 * Il suffit de lui envoyer la commande http://nad-esp32.local/ir?code=MYCODE
 * MYCODE doit être remplacé par l'un des codes supportés :
 * 
 * POUR LE NAD:
 * ---------------------------------------------------------------------------------------------------------------------
 * RCVR_POWER, AM, FM/AM, KEY_SLEEP, KEY_DVD, KEY_SAT, KEY_VCR, KEY_VIDEO_4, KEY_VIDEO_5, EXT._5.1, KEY_CD, KEY_TAPE, FM,
 * RCVR_POWER, AM, FM/AM, KEY_SLEEP, KEY_DVD, KEY_SAT, KEY_VCR, KEY_VIDEO_4, KEY_VIDEO_5, EXT._5.1, KEY_CD, KEY_TAPE, FM, 
 * FM/AM, RCVR_1, RCVR_2, RCVR_3, RCVR_4, RCVR_5, RCVR_6, RCVR_7, RCVR_8, RCVR_9, RCVR_0, RCVR_SURR, RCVR_DYN.R, RCVR_TEST,
 * RCVR_LEVEL, RCVR_VOLUME_UP, RCVR_VOLUME_DOWN, RCVR_TUNE_DOWN, RCVR_TUNE_UP, RCVR_MUTE_ENTER, RCVR_DISPLAY, RCVR_TUNE_MODE 
 * 
 * POUR LA TV PHILIPS
 * ----------------------------------------------------------------------------------------------------------------------
 * RC6_POWER RC6_VOLPLUS RC6_VOLMINUS RC6_PROGPLUS RC6_PROGMINUS RC6_OK RC6_LEFT RC6_RIGHT RC6_UP RC6_DOWN RC6_ANDROIDRETURN 
 * RC6_NETFLIX RC6_ANDROIDPLUS RC6_ANDROIDHOME RC6_MUTE RC6_1 RC6_2 RC6_3 RC6_4 RC6_5 RC6_6 RC6_7 RC6_8 RC6_9 RC6_0 
 * RC6_RED RC6_GREEN RC6_YELLOW RC6_BLUE RC6_STOP RC6_PAUSE RC6_RECORD RC6_REWIND RC6_PLAY RC6_FORWARD RC6_SUBTITLE RC6_TEXT 
 * RC6_AMBILIGHT RC6_SOURCES RC6_SETTINGS RC6_TOPPICKS RC6_SEARCH RC6_EXIT RC6_TVGUIDE RC6_RIGHTMENU RC6_LIST 
 * 
 * A chaque réception de trame et envoi de code, la led intégrée à l'ESP clignote. Le code IR pour NAD est envoyé 3 fois pour plus de sureté.
 * 
 * Une LED IR doit être branchée sur la broce 4 (D2), idéalement pilotée par un ampli (FET).
 * Le courant dans la led peut aller de 40 ma à 100ma sur certaines LED.
 * Si mises en série et alimentées en 5V et commutées par un FET, le calcul de la résistance s'effectue comme suit ;
 * R = (5 - 1.7 - 1.7) / 0.04 = 40 ohms pour 40 ma.
 * R = (5 - 1.7 - 1.7) / 0.05 = 32 ohms pour 50 ma.
 * R = (5 - 1.7 - 1.7) / 0.10 = 16 ohms pour 100 ma.
 * 
 * Sur M5stick, les broches sont adaptées pour utiiser la led interne, l'émetteur IR interne, et le plus grand bouton (B1). 
 * Le bouton B1 permet d'envoyer la commande Power du NAD.
 * L'afficheur LCD  affiche l'adresse IP ainsi que chaque commande émise en IR.
 * 
 * Pour télécommander un ampli NAD, l'entrée IR IN peut également être utilisée. Dans ce cas, il faut
 * bufferiser la sortie (avec un 7407 en collecteur ouvert et pullup de 1K) avant de l'utiliser.
 * En effet, l'entrée IR du NAD consomme beaucoup de courant.
 * 
 * Peut être mis à jour à la volée soit en browsant la page http://nad-esp32.local et en sélectionnant le fichier à envoyer ou
 * par le terminal avec curl -F "image=@firmware.bin" nad-esp32.local/update
 *  
*/

#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#ifdef ARDUINO_M5Stick_C
#include <M5StickC.h>
#endif

// Configure Wifi and device name here
const char *ssid = "mon-ssid";
const char *password = "mon_password";
const char* host = "nad-esp32";
static unsigned int rc6Toggle = 0;
// End config.

#ifdef ARDUINO_M5Stick_C
  #warning M5 Stick version
  // wiring here https://github.com/m5stack/M5StickC
  const uint16_t kIrLed = 9;  // M5stickC is 9
  #define LEDON 0
  #define LEDOFF 1
  const int led = GPIO_NUM_10; // 10 
#else // Standard arduino
  #warning Arduino version
  const uint16_t kIrLed = 4;  // ESP32 GPIO pin to use. Recommended: 4 (D2). 
  #define LEDON 1
  #define LEDOFF 0
  const int led = LED_BUILTIN; 

#endif

IRsend irsend(kIrLed);      // Set the GPIO to be used to sending the message.
WebServer server(80);

void handleRoot() {
  digitalWrite(led, LEDON);
  char temp[400];
  int sec = millis() / 1000;
  int min = sec / 60;
  int hr = min / 60;

// for automatic refresh, add on headers 
//     <meta http-equiv='refresh' content='5'/>\

  snprintf(temp, 400,
           "<html>\
  <head>\
    <title>ESP32 NAD Remote</title>\
    <style>\
      body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
    </style>\
  </head>\
  <body>\
    <h1>Hello from ESP32!</h1>\
    <p>Uptime: %02d:%02d:%02d</p>\
    <form method='POST' action='/update' enctype='multipart/form-data'>\
      <input type='file' name='update'><input type='submit' value='Update'>\
    </form>\
  </body>\
</html>",
           hr, min % 60, sec % 60
          );
  server.send(200, "text/html", temp);
  digitalWrite(led, LEDOFF);
}

void handleIR() {
  digitalWrite(led, LEDON);
  String message = "OK\n\n";
  String command = "";
  String displaymsg = "";

  // Get params
  for (uint8_t i = 0; i < server.args(); i++) {
    if ( server.argName(i) == "code" ) {
      command = server.arg(i);
      message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
      }
  }
  if (command == "RCVR_POWER") {
    irsend.sendNEC (0xE13E01FE,32,3);
    displaymsg = "RCVR_POWER";
    }   

  if (command == "AM") {
    irsend.sendNEC (0xE13EBB44,32,3);
    displaymsg = "AM";
    }   
  if (command == "FM/AM") {
    irsend.sendNEC (0xE13EBB44,32,3);
    displaymsg = "FM/AM";
    }   
  if (command == "KEY_SLEEP") {
    irsend.sendNEC (0xE13E04FB,32,3);
    displaymsg = "KEY_SLEEP";
    }   
  if (command == "KEY_DVD") {
    irsend.sendNEC (0xE13E43BC,32,3);
    displaymsg = "KEY_DVD";
    }   
  if (command == "KEY_SAT") {
    irsend.sendNEC (0xE13E03FC,32,3);
    displaymsg = "KEY_SAT";
    }   
  if (command == "KEY_VCR") {
    irsend.sendNEC (0xE13E837C,32,3);
    displaymsg = "KEY_VCR";
    }   
  if (command == "KEY_VIDEO_4") {
    irsend.sendNEC (0xE13E0CF3,32,3);
    displaymsg = "KEY_VIDEO_4";
    }   
  if (command == "KEY_VIDEO_5") {
    irsend.sendNEC (0xE13E8C73,32,3);
    displaymsg = "KEY_VIDEO_5";
    }   
  if (command == "EXT._5.1") {
    irsend.sendNEC (0xE13E748B,32,3);
    displaymsg = "EXT._5.1";
    }   
  if (command == "KEY_CD") {
    irsend.sendNEC (0xE13EA15E,32,3);
    displaymsg = "KEY_CD";
    }   
  if (command == "KEY_TAPE") { 
    irsend.sendNEC (0xE13EB14E,32,3);
    displaymsg = "KEY_TAPE";
    }   
  if (command == "FM") {
    irsend.sendNEC (0xE13EBB44,32,3);
    displaymsg = "FM";
    }   
  if (command == "FM/AM") { 
    irsend.sendNEC (0xE13EBB44,32,3);
    displaymsg = "FM/AM";
    }   
  if (command == "RCVR_1") {
    irsend.sendNEC (0xE13E51AE,32,3);
    displaymsg = "";
    }   
  if (command == "RCVR_2") {
    irsend.sendNEC (0xE13E718E,32,3);
    displaymsg = "RCVR_2";
    }   
  if (command == "RCVR_3") {
    irsend.sendNEC (0xE13E49B6,32,3);
    displaymsg = "RCVR_3";
    }   
  if (command == "RCVR_4") {
    irsend.sendNEC (0xE13E6996,32,3);
    displaymsg = "";
    }   
  if (command == "RCVR_5") {
    irsend.sendNEC (0xE13ED12E,32,3);
    displaymsg = "RCVR_5";
    }   
  if (command == "RCVR_6") {
    irsend.sendNEC (0xE13EF10E,32,3);
    displaymsg = "RCVR_6";
    }   
  if (command == "RCVR_7") {
    irsend.sendNEC (0xE13EC936,32,3);
    displaymsg = "RCVR_7";
    }   
  if (command == "RCVR_8") {
    irsend.sendNEC (0xE13EE916,32,3);
    displaymsg = "RCVR_8";
    }   
  if (command == "RCVR_9") {
    irsend.sendNEC (0xE13E19E6,32,3);
    displaymsg = "RCVR_9";
    }   
  if (command == "RCVR_0") {
    irsend.sendNEC (0xE13EE31C,32,3);
    displaymsg = "RCVR_0";
    }   
  if (command == "RCVR_SURR") {
    irsend.sendNEC (0xE13E33CC,32,3);
    displaymsg = "RCVR_SURR";
    }   
  if (command == "RCVR_DYN.R") {
    irsend.sendNEC (0xE13EF40B,32,3);
    displaymsg = "RCVR_DYN.R";
    }   
  if (command == "RCVR_TEST") {
   irsend.sendNEC (0xE13EB34C,32,3);
    displaymsg = "RCVR_TEST";
    }   
  if (command == "RCVR_LEVEL") {
    irsend.sendNEC (0xE13ED42B,32,3);
    displaymsg = "RCVR_LEVEL";
    }   
  if (command == "RCVR_VOLUME_UP") {
    irsend.sendNEC (0xE13E11EE,32,3);
    displaymsg = "RCVR_VOLUME_UP";
    }   
  if (command == "RCVR_VOLUME_DOWN") {
    irsend.sendNEC (0xE13E31CE,32,3);
    displaymsg = "RCVR_VOLUME_DOWN";
    }   
  if (command == "RCVR_TUNE_DOWN") {
    irsend.sendNEC (0xE13E8B74,32,3);
    displaymsg = "RCVR_TUNE_DOWN";
    }   
  if (command == "RCVR_TUNE_UP") {
    irsend.sendNEC (0xE13E4BB4,32,3);
    displaymsg = "RCVR_TUNE_UP";
    }   
  if (command == "RCVR_MUTE_ENTER") { 
    irsend.sendNEC (0xE13E29D6,32,3);
    displaymsg = "RCVR_MUTE_ENTER";
    }   
  if (command == "RCVR_DISPLAY") {
    irsend.sendNEC (0xE13E649B,32,3);
    displaymsg = "RCVR_DISPLAY";
    }   
  if (command == "RCVR_TUNE_MODE") { 
    irsend.sendNEC (0xE13ECC33,32,3);
    displaymsg = "RCVR_TUNE_MODE";
    } 
  //*************************************************************
  //  Philips TV Commmands
  if (command == "RC6_POWER") { 
    irsend.sendRC6(0x000CU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_POWER";
    } 
  if (command == "RC6_VOLPLUS") { 
    irsend.sendRC6(0x0010U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_VOLPLUS";
    } 
  if (command == "RC6_VOLMINUS") { 
    irsend.sendRC6(0x0011U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_VOLMINUS";
    } 
  if (command == "RC6_PROGPLUS") { 
    irsend.sendRC6(0x0020U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_PROGPLUS";
    } 
  if (command == "RC6_PROGMINUS") { 
    irsend.sendRC6(0x0021U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_PROGMINUS";
    } 
  if (command == "RC6_OK") { 
    irsend.sendRC6(0x005CU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_OK";
    } 
  if (command == "RC6_LEFT") { 
    irsend.sendRC6(0x005AU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_LEFT";
    } 
  if (command == "RC6_RIGHT") { 
    irsend.sendRC6(0x005BU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_RIGHT";
    } 
  if (command == "RC6_UP") { 
    irsend.sendRC6(0x0058U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_UP";
    } 
  if (command == "RC6_DOWN") { 
    irsend.sendRC6(0x0059U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_DOWN";
    } 
  if (command == "RC6_ANDROIDRETURN") { 
    irsend.sendRC6(0x000AU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_ANDROIDRETURN";
    } 
  if (command == "RC6_NETFLIX") { 
    irsend.sendRC6(0x0076U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_NETFLIX";
    } 
  if (command == "RC6_ANDROIDPLUS") { 
    irsend.sendRC6(0x0040U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_ANDROIDPLUS";
    } 
  if (command == "RC6_ANDROIDHOME") { 
    irsend.sendRC6(0x0054U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_ANDROIDHOME";
    } 
  if (command == "RC6_MUTE") { 
    irsend.sendRC6(0x000DU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_MUTE";
    } 
  if (command == "RC6_1") { 
    irsend.sendRC6(0x0001U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_1";
    } 
  if (command == "RC6_2") { 
    irsend.sendRC6(0x0002U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_2";
    } 
  if (command == "RC6_3") { 
    irsend.sendRC6(0x0003U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_3";
    } 
  if (command == "RC6_4") { 
    irsend.sendRC6(0x0004U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_4";
    } 
  if (command == "RC6_5") { 
    irsend.sendRC6(0x0005U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_5";
    } 
  if (command == "RC6_6") { 
    irsend.sendRC6(0x0006U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_6";
    } 
  if (command == "RC6_7") { 
    irsend.sendRC6(0x0007U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_7";
    } 
  if (command == "RC6_8") { 
    irsend.sendRC6(0x0008U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_8";
    } 
  if (command == "RC6_9") { 
    irsend.sendRC6(0x0009U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_9";
    } 
  if (command == "RC6_0") { 
    irsend.sendRC6(0x0000U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_0";
    } 
  if (command == "RC6_RED") { 
    irsend.sendRC6(0x006DU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_RED";
    } 
  if (command == "RC6_GREEN") { 
    irsend.sendRC6(0x006EU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_GREEN";
    } 
  if (command == "RC6_YELLOW") { 
    irsend.sendRC6(0x006FU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_YELLOW";
    } 
  if (command == "RC6_BLUE") { 
    irsend.sendRC6(0x0070U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_BLUE";
    } 
  if (command == "RC6_STOP") { 
    irsend.sendRC6(0x0031U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_STOP";
    } 
  if (command == "RC6_PAUSE") { 
    irsend.sendRC6(0x0030U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_PAUSE";
    } 
  if (command == "RC6_RECORD") { 
    irsend.sendRC6(0x0037U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_RECORD";
    } 
  if (command == "RC6_REWIND") { 
    irsend.sendRC6(0x002BU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_REWIND";
    } 
  if (command == "RC6_PLAY") { 
    irsend.sendRC6(0x002CU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_PLAY";
    } 
  if (command == "RC6_FORWARD") { 
    irsend.sendRC6(0x0028U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_FORWARD";
    } 
  if (command == "RC6_SUBTITLE") { 
    irsend.sendRC6(0x004BU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_SUBTITLE";
    } 
  if (command == "RC6_TEXT") { 
    irsend.sendRC6(0x003CU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_TEXT";
    } 
  if (command == "RC6_AMBILIGHT") { 
    irsend.sendRC6(0x008FU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_AMBILIGHT";
    } 
  if (command == "RC6_SOURCES") { 
    irsend.sendRC6(0x0038U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_SOURCES";
    } 
  if (command == "RC6_SETTINGS") { 
    irsend.sendRC6(0x00C0U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_SETTINGS";
    } 
  if (command == "RC6_TOPPICKS") { 
    irsend.sendRC6(0x0074U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_TOPPICKS";
    } 
  if (command == "RC6_SEARCH") { 
    irsend.sendRC6(0x00B4U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_SEARCH";
    } 
  if (command == "RC6_EXIT") { 
    irsend.sendRC6(0x009FU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_EXIT";
    } 
   if (command == "RC6_TVGUIDE") { 
    irsend.sendRC6(0x00CCU + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_TVGUIDE";
    } 
   if (command == "RC6_RIGHTMENU") { 
    irsend.sendRC6(0x0057U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_RC6_RIGHTMENU";
    } 
   if (command == "RC6_LIST") { 
    irsend.sendRC6(0x00D2U + 0x10000U*rc6Toggle, 20U);
    rc6Toggle = 1U - rc6Toggle;
    displaymsg = "RC6_LIST";
    } 
  if (displaymsg!=""){
    #ifdef ARDUINO_M5Stick_C
    M5.Lcd.println(displaymsg);
    #endif
    }
  else {
    // default message : unknown
    displaymsg="Unknown command";
    }

  server.send(200, "text/plain", message);
  digitalWrite(led, LEDOFF);
}

void handleNotFound() {
  digitalWrite(led, LEDON);
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }

  server.send(404, "text/plain", message);
  digitalWrite(led, LEDOFF);
}



void setup() {
  #ifdef ARDUINO_M5Stick_C
  M5.begin();
  M5.Lcd.fillScreen(BLACK);
  M5.Axp.ScreenBreath(12);
   
  #endif

  irsend.begin();
  Serial.begin(115200, SERIAL_8N1);
  
  pinMode(led, OUTPUT);
  digitalWrite(led, LEDOFF);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  #ifdef ARDUINO_M5Stick_C
  M5.Lcd.println(WiFi.localIP());
  #endif

  if (MDNS.begin(host)) {
    Serial.println("MDNS responder started");
  }

  // Home page
  server.on("/", handleRoot);

  // /inline for tests
  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });

  // manage IR codes
  server.on("/ir", handleIR);    

  // updater : /update
  server.on("/update", HTTP_POST, []() {
      server.sendHeader("Connection", "close");
      server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
      ESP.restart();
    }, []() {
      HTTPUpload& upload = server.upload();
      if (upload.status == UPLOAD_FILE_START) {
        Serial.setDebugOutput(true);
        Serial.printf("Update: %s\n", upload.filename.c_str());
        if (!Update.begin()) { //start with max available size
          Update.printError(Serial);
        }
      } else if (upload.status == UPLOAD_FILE_WRITE) {
        if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
          Update.printError(Serial);
        }
      } else if (upload.status == UPLOAD_FILE_END) {
        if (Update.end(true)) { //true to set the size to the current progress
          Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
        } else {
          Update.printError(Serial);
        }
        Serial.setDebugOutput(false);
      } else {
        Serial.printf("Update Failed Unexpectedly (likely broken connection): status=%d\n", upload.status);
      }
    });

  server.onNotFound(handleNotFound);
  server.begin();
  
  MDNS.addService("http", "tcp", 80);

  Serial.printf("Ready! Open http://%s.local in your browser\n", host);
}

void loop() {
  server.handleClient();
  #ifdef ARDUINO_M5Stick_C
  if ( M5.BtnA.wasPressed() ){
      // send power if bigest button pressed.
      digitalWrite(led, LEDON);
      irsend.sendPronto(NadPower, 76, 3);
      M5.Lcd.println("RCVR_POWER");  
      digitalWrite(led, LEDOFF);  
    }
  M5.update();
  #endif
  delay(1);
}

Alternative matérielle avec un M5StickC

m5stick.jpg, mai 2021

Il y a quelques mois, j'avais fait un petit article sur un tout petit périphérique sympa : le M5StickC (cliquer pour voir l'article) . Comme celui-ci a le bon gout d'implémenter un émetteur infrarouge intégré, le programme ci dessous était facile à modifier pour :

  • Utiliser l'émetteur infrarouge intégré
  • Utiliser a led intégrée pour acquitter une transmission infrarouge
  • Utiliser l'afficheur intégré pour afficher l'adresse ip du périphérique et le code Infrarouge recu
  • Gérer le bouton pour envoyer un code local (ici, NAD Power On/Off)

La portée n'est vraiment pas terrible, il faut que cela soit à moins d'un mètre. En tout cas, cela permet de tester et débugger le code. Pratique !

Sommaire des articles de la série: