Questo progetto IoT, realizzato con ESP8266, permette di comandare, tramite una pagina WEB, e quindi anche da cellulare o tablet, un qualsiasi dispositivo controllabile con un telecomando a infrarossi.
SchemaSchema
Il diodo LED infrarosso deve essere orientato verso gli apparati da controllare e non devono esserci ostacoli in mezzo.
Su alcuni telefoni, soprattutto in passato, era presente un LED emettitore a infrarossi, quindi si trovavano delle APP che funzionavano come telecomando.
Sui modelli recenti il LED non è più presente.
Questo progetto permette di superare la limitazione data dalla mancanza del LED.

Pagine WEB

Le schermate dell'applicativo, che possono essere richiamate dall'indirizzo http://192.168.0.50/, sono queste
SchermateSchermate

La prima pagina permette di controllare un televisore LG, la seconda un Home Teatre Onkio e l'ultima permette di iserire un qualsiasi codice per test.

Tramite swipe, destro o sinistro, è possibile cambiare schermata.
Attenzione per rilevare i codici corretti del vostro telecomando fate riferimento a questo post Come leggere i codici infrarosso di un telecomando TV con Arduino

Il circuito funziona implementando un web server che risponde a queste url:
L'indirizzo IP andrà personalizzato in base alle impostazione della rete WI-FI.

API

In particolare l'API http://<indirizzoIP>/ir vuole 4 parametri, ad esempio
URL
http://192.168.0.50/ir?mode=nec&code=0x4BB6C03F&bits=32&repeat=1&t=1625427666318
e ritrasmette il codice infrarosso.

I parametri sono:
  • mode: tipo di codifica da utiliizare per inviare i codici infrarosso
  • code: codice da inviare espresso in esadecimale
  • bits: numero di bit di cui è composto il codice
  • repeat: numero di ripetizioni del codice, solitamente sempre 1
  • t: timestamp opzionale (solo per essere sicuro che il browser non faccia cache delle chiamate)
La url può essere richiamata da qualunquu applicativo o script.

Realizzazione

Per la realizzazione del circuito serve:
  • nr. 1 ESP8266
  • nr. 1 LED emettitore all'infrarosso
  • nr. 1 transistor BC 548 C
  • nr. 1 resistenza da 22 ohm 1/4 W
  • nr. 1 resistenza da 2200 ohm 1/4 W
  • nr. 1 alimentatore a 5V d.c.
  • nr. 1 una breadboard
  • fili vari
Schema elettricoSchema elettrico
che in pratica diventa
Realizzazione praticaRealizzazione pratica

In alternativa è possibile usare il modulo IR, anche se dalle prove effettuate il raggio infrarossi sembra essere più direzionale e meno potente
Realizzazione pratica con modulo IRRealizzazione pratica con modulo IR

Programmazione

Il circuito va poi programmato, tramite l'IDE di Arduino, con il codice seguente che comprende la parte lato client HTML e JavaScript altre alla parte lato server per Arduino
Arduino: IRServer.ino
/*
   Sgart.it IR (infrared) Server
   Version 1.0 Jul 2018/2021
   Copyright 2018 Sgart.it
   https://www.sgart.it//IT/elettro/televisore-comandato-dal-cellulare-o-via-web-esp8266/post
   Board: 'Generic ESP8266 Module'
   Flash Mode: 'DIO'
   Flash Size: '1M (512KSPIFFS)'
   Debug Port: 'Disabled'
   Debug Level: 'None'
   Reset Method: 'nodemcu'
   Crystal frequency: '26MHz'
   Flash Frequency '40MHz'
   CPU Frequency '80 MHz'
   Upload Speed: '115200'
   Port: COM3
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <IRsend.h>

extern "C" {
#include "user_interface.h"
  uint16 readvdd33(void);
  bool wifi_set_sleep_type(sleep_type_t);
  sleep_type_t wifi_get_sleep_type(void);
}

/*
 * parametri da adeguare alla propria rete
 */
// impostazioni WI-FI
const String WIFI_SSID = "<SSID wi-fi>";
const String WIFI_PASSWORD = "<WI-FI password>";
// IP statico 
IPAddress ip(192, 168, 0, 50);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);

const String VERSION_STR = "HTTP IR Server v. 1.0 - 2018/2021 - Sgart.it";

ESP8266WebServer server(80);

//gestione pin trasmettitore infrarosso (ESP8266 GPIO4 - D2)
#define IR_LED 4
IRsend irsend(IR_LED);

//gestione pagina html iniziale (home)
void handleHome() {
  server.send(200, "text/html",
      "<html>"
      "<head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>IR Server - Sgart.it</title>" 
      "<link rel='shortcut icon' href='https://www.sgart.it/favicon.ico' type='image/x-icon'>"
      "<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>"
      "<meta name='application-name' content='IR - Sgart.it'><meta name='msapplication-TileColor' content='#500000'>"
      "<style>.navbar{margin-bottom:5px;} .navbar-header{width:100%;} .btn{display:block;width:100%;min-height:40px;} .row>div{padding:10px;} hr{margin: 10px 0}"
      "#btn-off{display:block;float:right;} #loading{position:fixed;top:0;left:0;right:0;bottom:0;;background-color:rgba(200,200,200,.5);z-index:99999;display:none;}"
      "</style></head><body>"
      "<div id='loading'></div>"
      
      "<nav class='navbar navbar-default'><div class='container0'><div class='navbar-header'>"
      "<a class='navbar-brand' href='https://www.sgart.it/?remote-control\'><img src='https://www.sgart.it/content/images/sgart32.png'></a>"
      "<button type='button' class='navbar-toggle btn-lg' id='btn-off' onclick='send(\"nec-20DF10EF,nec-4B36D32C\")'><i class='glyphicon glyphicon-off text-danger'></i></button>"
      "<button type='button' class='navbar-toggle' id='btn-tv-tuner' onclick='changeCard(1)' style='display:block'>Tuner</button>"
      "<h3 id='title'>Sgart.it</h3>"
      "</div></div></nav>"

      "<!-- BEGIN: CONTAINER --> <div class='container'>"
      
      "<!-- BEGIN: CARD TV --> <div id='card-0' data-title='TV' data-cmd-disabled='nec-4BB6906F,nec-20DF23DC'>"

      "<div class='row'>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4BB6A05F\")'><i class='glyphicon glyphicon-volume-off'></i><br><span>Mute</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF3EC1\")'><i class='glyphicon glyphicon-home'></i><br><span>Home</button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DFC23D\")'><i class='glyphicon glyphicon-cog'></i><br><span>Settings</span></button></div>"
      "</div>"

      "<div class='row'><!--vol +: 20DF40BF, vol -: 20DFC03F, mute: 20DF906F-->"
      "<div class='col-xs-6'><button class='btn btn-primary' onclick='send(\"nec-20DF40BF,nec-4BB640BF\")'><i class='glyphicon glyphicon-chevron-up'></i><br><span>Vol. +</span></button></div>"
      "<div class='col-xs-6'><button class='btn btn-primary' onclick='send(\"nec-20DF00FF\")'><i class='glyphicon glyphicon-chevron-up'></i><br><span>Prg. +</span></button></div>"
      "</div>"
      "<div class='row'>"
      "<div class='col-xs-6'><button class='btn btn-primary' onclick='send(\"nec-20DFC03F,nec-4BB6C03F\")'><i class='glyphicon glyphicon-chevron-down'></i><br><span>Vol. -</span></button></div>"
      "<div class='col-xs-6'><button class='btn btn-primary' onclick='send(\"nec-20DF807F\")'><i class='glyphicon glyphicon-chevron-down'></i><br><span>Prg. -</span></button></div>"
      "</div>"

      "<div class='row'>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF55AA\")'><i class='glyphicon glyphicon-info-sign'></i><br><span>Info</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-info' onclick='send(\"nec-20DF02FD\")'><i class='glyphicon glyphicon-menu-up'></i><br><span>Up</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DFCA35\")'><span>LIVE MENU</span></button></div>"
      "</div>"

      "<div class='row'>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-info' onclick='send(\"nec-20DFE01F\")'><i class='glyphicon glyphicon-menu-left'></i><br><span>Left</button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF22DD\")'><i class='glyphicon glyphicon-ok'></i><br><span>OK</button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-info' onclick='send(\"nec-20DF609F\")'><i class='glyphicon glyphicon-menu-right'></i><br><span>Right</span></button></div>"
      "</div>"

      "<div class='row'>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF14EB\")'><i class='glyphicon glyphicon-arrow-left'></i><br><span>Back</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-info' onclick='send(\"nec-20DF827D\")'><i class='glyphicon glyphicon-menu-down'></i><br><span>Down</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DFDA25\")'><i class='glyphicon glyphicon-remove'></i><br><span>Exit</span></button></div>"
      "</div>"

      "<hr> <!-- KEYS -->"
      "<div class='row'>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF8877\")'><span>1</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF48B7\")'><span>2</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DFC837\")'><span>3</span></button></div>"

      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF28D7\")'><span>4</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DFA857\")'><span>5</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF6897\")'><span>6</span></button></div>"

      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DFE817\")'><span>7</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF18E7\")'><span>8</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF9867\")'><span>9</span></button></div>"

      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DFD52A\")'><span>GUIDE</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF08F7\")'><span>0</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-20DF58A7\")'><span>Q.VIEW</span></button></div>"
      "</div>"

      "<hr>"
      "<div class='row'>"
      "<div class='col-xs-6'><button class='btn btn-xs btn-success' onclick='send(\"nec-20DF23DC\")'><span>ON</span></button></div>"
      "<div class='col-xs-6'><button class='btn btn-xs btn-danger' onclick='send(\"nec-20DFA35C\")'><span>OFF</span></button></div>"
      "</div>"

      "</div> <!-- END: CARD TV --> " \

      "<!-- BEGIN: CARD TUNER --> <div id='card-1'  data-title='Tuner' data-cmd-disabled='nec-4BB6D02F,nec-20DFA35C' style='display:none'>"

      "<div class='row'>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4BB6D02F\")'><span>AM / FM</span></button></div>"
      "<div class='col-xs-4'></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B36AA55\")'><span>DISPLAY</span></button></div>"
      "</div>"

      "<div class='row'><!--vol +: 20DF40BF, vol -: 20DFC03F, mute: 20DF906F-->"
      "<div class='col-xs-6'><button class='btn btn-primary' onclick='send(\"nec-4BB640BF\")'><i class='glyphicon glyphicon-chevron-up'></i><br><span>Vol. +</span></button></div>"
      "<div class='col-xs-6'><button class='btn btn-primary' onclick='send(\"nec-4BB600FF\")'><i class='glyphicon glyphicon-chevron-up'></i><br><span>Prg. +</span></button></div>"
      "</div>"
      
      "<div class='row'>"
      "<div class='col-xs-6'><button class='btn btn-primary' onclick='send(\"nec-4BB6C03F\")'><i class='glyphicon glyphicon-chevron-down'></i><br><span>Vol. -</span></button></div>"
      "<div class='col-xs-6'><button class='btn btn-primary' onclick='send(\"nec-4BB6807F\")'><i class='glyphicon glyphicon-chevron-down'></i><br><span>Prg. -</span></button></div>"
      "</div>"

      "<hr> <!-- KEYS -->"
      "<div class='row'>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B40AB54\")'><span>1</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B406B94\")'><span>2</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B40EB14\")'><span>3</span></button></div>"

      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B401BE4\")'><span>4</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B409B64\")'><span>5</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B405BA4\")'><span>6</span></button></div>"

      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B40DB24\")'><span>7</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B403BC4\")'><span>8</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B40BB44\")'><span>9</span></button></div>"

      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B40FB04\")'><span>D.TUN</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4B407B84\")'><span>0</span></button></div>"
      "<div class='col-xs-4'><button class='btn btn-xs btn-default' onclick='send(\"nec-4BB6BA45\")'><span>SLEEP</span></button></div>"
      "</div>"

      "</div> <!-- END: CARD TUNER -->"

      "<!-- BEGIN: CARD TEST CODE --> <div id='card-2'  data-title='Test code' data-cmd-disabled='' style='display:none'>"
      "<div class='row'>"
      "<div class='col-xs-9'>"
      "<span><select id='m' class='form-control'><option value='nec' selected>NEC</option><option value='rc5'>RC5</option><option value='rc6'>RC6</option></select></span>"
      "<span><input type='text' id='c' value='20DFCA35' placeholder='code' class='form-control'></span>"
      "<span><input type='text' id='b' value='32' placeholder='bits' class='form-control'></span>"
      "<span><input type='text' id='r' value='1' placeholder='repeat' class='form-control'></span>"
      "</div>"
      "<div class='col-xs-3'><button class='btn btn-xs btn-default' onclick='sendTest()'><i class='glyphicon glyphicon-play'></i><br><span>Test</span></button></div>"
      "</div> <!-- END: CARD TEST -->"

      "</div> <!-- END: CONTAINER --> "

      "<script> "
      "/* libreria per swipe */ "
      "!function(t,e){'use strict';'initCustomEvent'in e.createEvent('CustomEvent')&&(t.CustomEvent=function(t,n){n=n||{bubbles:!1,cancelable:!1,detail:void 0};var u=e.createEvent('CustomEvent');return u.initCustomEvent(t,n.bubbles,n.cancelable,n.detail),u},t.CustomEvent.prototype=t.Event.prototype),e.addEventListener('touchstart',function(t){if('true'===t.target.getAttribute('data-swipe-ignore'))return;l=t.target,i=Date.now(),n=t.touches[0].clientX,u=t.touches[0].clientY,a=0,o=0},!1),e.addEventListener('touchmove',function(t){if(!n||!u)return;var e=t.touches[0].clientX,i=t.touches[0].clientY;a=n-e,o=u-i},!1),e.addEventListener('touchend',function(t){if(l!==t.target)return;var e=parseInt(l.getAttribute('data-swipe-threshold')||'20',10),s=parseInt(l.getAttribute('data-swipe-timeout')||'500',10),r=Date.now()-i,c='';Math.abs(a)>Math.abs(o)?Math.abs(a)>e&&r<s&&(c=a>0?'swiped-left':'swiped-right'):Math.abs(o)>e&&r<s&&(c=o>0?'swiped-up':'swiped-down');''!==c&&(l.dispatchEvent(new CustomEvent(c,{bubbles:!0,cancelable:!0})),console&&console.log&&console.log(c+' fired on '+l.tagName));n=null,u=null,i=null},!1);var n=null,u=null,a=null,o=null,i=null,l=null}(this,document);"

      "/* main */ "
      "function getId(id){ return document.getElementById(id) }"

      "/* gestione swipe - card */ "
      "var cardIndex = 0, cardIndexMax = 2;"

      "document.addEventListener('swiped-left', function(e) { changeCard(1); }); document.addEventListener('swiped-right', function(e) { changeCard(-1); });"

      "function changeCard(moveIndex) { cardIndex += moveIndex; if(cardIndex < 0) cardIndex = cardIndexMax; else if (cardIndex > cardIndexMax) cardIndex = 0; showCard(); }"
      
      "function showCard(){"
      "/* card corrente */"
      "var objCard = getId('card-'+cardIndex); var title=objCard.getAttribute('data-title');"
      "getId('title').innerText=title; objCard.style.display='block';"
      "/* next button */"
      "var cardIndexNext = cardIndex >= cardIndexMax ? 0 : cardIndex + 1; objCardNext = getId('card-' + cardIndexNext); var titleNext = objCardNext.getAttribute('data-title'); getId('btn-tv-tuner').innerHTML = titleNext;"
      "/* disattivo schede */"
      "for(var i=0; i<=cardIndexMax; i++) { if(cardIndex!==i) getId('card-'+i).style.display='none'; }"
      "var cmd=objCard.getAttribute('data-cmd'); send(cmd);};"
      "showCard();"

      "/* test code */"
      "function sendTest(){ var m=getId('m').value; var c=getId('c').value; var b=getId('b').value; var r=getId('r').value; sendFull(m,c,b,r); }"

      "/* invia comandi */"
      "function send(cmds){ if(cmds===null) return; var cmd=cmds.split(','); for(var i=0;i<cmd.length;i++){ var m='',c='',b=null,r=null; var p=cmd[i].split('-');"
      "if(p.length>0) m=p[0]; if(p.length>1) c=p[1]; if(p.length>2) b=p[2]; if(p.length>3) r=p[3]; sendFull(m,c,b,r); }}"

      "function sendFull(m,c,b,r){ try{"
      "if(b===undefined || b==null || b==='' || b<=0) b=32; if(r===undefined || r==null || r==='' || r<=0) r=1;"
      "var url = '/ir?mode='+m.toLowerCase()+'&code=0x' + c + '&bits=' + b + '&repeat=' + r + '&t=' + (new Date().getTime());"
      "sendHttp(url); }catch(e){ console.log(e); } }"

      "/* chiamata http */ "
      "function sendHttp(url){ var loading=document.getElementById('loading'); loading.style.display='block';"
      "try{ var xhr = new XMLHttpRequest(); xhr.open('GET', url);"
      "xhr.onload = function() { loading.style.display='none'; if (xhr.status === 200) { console.log('OK'); } else { alert('Error: ' + xhr.status); }};"
      "xhr.onerror = function() { loading.style.display='none'; alert(\"Errore nell'invio del codice\"); };"
      "xhr.send(); }catch(e){ loading.style.display='none'; alert(e); } }"
    
      " </script> </body></html>");
}

// gestisce l'arrivo di una richiesta http (pagina /ir?mode=XX&code=0xXXXX&bits=XX&repeat=X
// e trasmette il corrispondente codice tramite un led infrarosso (IR)
void handleIR() {
  unsigned long code = 0;
  unsigned int bits = 32;
  unsigned int repeat = 1;
  String mode = "";

  //leggo i parametri dalla querystring
  for (uint8_t i = 0; i < server.args(); i++) {
    if (server.argName(i) == "code") {
      code = strtoul(server.arg(i).c_str(), NULL, 16);
    } else if (server.argName(i) == "mode") {
      mode = server.arg(i).c_str();
    } else if (server.argName(i) == "bits") {
      bits = strtoul(server.arg(i).c_str(), NULL, 10);
    } else if (server.argName(i) == "repeat") {
      repeat = strtoul(server.arg(i).c_str(), NULL, 10);
    }
  }

  // invio i codici
  String choose = "?";
  if (mode == "nec" && code != 0) {
    choose = "NEC";
    irsend.sendNEC(code, bits, repeat);
  } else if (mode == "rc5" && code != 0) {
    choose = "RC5";
    irsend.sendRC5(code, bits);
  } else if (mode == "rc6" && code != 0) {
    choose = "RC6";
    irsend.sendRC6(code, bits);
  }

  printCode(choose, mode, code, bits, repeat);

  // risposta
  if (choose == "?") {
    server.send(400, "text/html", "Bad Request: IR Protocol not found.");
  } else {
    server.send(200, "text/html", "OK");
    // piccola pausa
    delay(40);
  }
}

// visualizzo i parametri inviati in console
void printCode(String choose, String mode, unsigned long code, unsigned int bits, unsigned int repeat) {
  Serial.print("mode: ");
  Serial.print(mode);
  Serial.print(", code: ");
  Serial.print(code, HEX);
  Serial.print(", bits: ");
  Serial.print(bits);
  Serial.print(", repeat: ");
  Serial.print(repeat);
  Serial.print(", choose: ");
  Serial.println(choose);
}

// pagina non torvata
void handleNotFound() {
  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);
}

// setup iniziale
void setup(void) {
  Serial.begin(115200);
  Serial.println("");
  Serial.println("");

  // Wait for connection
  Serial.print("WI-FI '" + WIFI_SSID + "' connecting");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected to WI-FI: " + WIFI_SSID);

  // setto l'IP statico
  WiFi.config(ip, gateway, subnet);
  Serial.println("");
  Serial.println("IP set " + ip.toString());
  
  // evito che il wifi si disattivi per risparmiare energia
  wifi_set_sleep_type(NONE_SLEEP_T);

  irsend.begin();
  Serial.println("IR started");

  // url gestire
  server.on("/", handleHome); // pagina web
  server.on("/ir", handleIR); // api send IR
  server.on("/version", []() {  // pagina versione
    server.send(200, "text/plain", VERSION_STR);
  });
  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("Web server started http://" + WiFi.localIP().toString() + "/");

  //reset ???
  irsend.sendNEC(0, 32, 3);

  Serial.println(VERSION_STR);
}

//gestione loop
void loop(void) {
  server.handleClient();
}
Modificando le variabili:
  • WIFI_SSID: con l'SSID della rete WI_FI (2,4GHz)
  • WIFI_PASSWORD: la password di accesso alla rete
  • ip: l'indirizzo IP da assegnare staticamente
  • subnet: netmask della rete
  • gateway: gateway della rete (solitamente l'IP del router)
Vengono usate le librerie ESP8266WiFi.h, ESP8266WebServer.h e IRsend.h.

Dettaglio del codice

Per una migliore leggibilità riporto l'HTML generato
HTML
<div id='loading'></div>
<nav class='navbar navbar-default'>
	<div class='container0'>
		<div class='navbar-header'>
			<a class='navbar-brand' href='https://www.sgart.it/?remote-control'>
				<img src='https://www.sgart.it/content/images/sgart32.png'>
			</a>
			<button type='button' class='navbar-toggle btn-lg' id='btn-off' onclick='send("nec-20DF10EF,nec-4B36D32C")'>
				<i class='glyphicon glyphicon-off text-danger'></i>
			</button>
			<button type='button' class='navbar-toggle' id='btn-tv-tuner' onclick='changeCard(1)' style='display:block'>Tuner</button>
			<h3 id='title'>Sgart.it</h3>
		</div>
	</div>
</nav>
<!-- BEGIN: CONTAINER -->
<div class='container'>
	<!-- BEGIN: CARD TV -->
	<div id='card-0' data-title='TV' data-cmd-disabled='nec-4BB6906F,nec-20DF23DC'>
		<div class='row'>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4BB6A05F")'>
					<i class='glyphicon glyphicon-volume-off'></i>
					<br>
					<span>Mute</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF3EC1")'>
					<i class='glyphicon glyphicon-home'></i>
					<br>
					<span>Home
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DFC23D")'>
					<i class='glyphicon glyphicon-cog'></i>
					<br>
					<span>Settings</span>
				</button>
			</div>
		</div>
		<div class='row'>
			<!--vol +: 20DF40BF, vol -: 20DFC03F, mute: 20DF906F-->
			<div class='col-xs-6'>
				<button class='btn btn-primary' onclick='send("nec-20DF40BF,nec-4BB640BF")'>
					<i class='glyphicon glyphicon-chevron-up'></i>
					<br>
					<span>Vol. +</span>
				</button>
			</div>
			<div class='col-xs-6'>
				<button class='btn btn-primary' onclick='send("nec-20DF00FF")'>
					<i class='glyphicon glyphicon-chevron-up'></i>
					<br>
					<span>Prg. +</span>
				</button>
			</div>
		</div>
		<div class='row'>
			<div class='col-xs-6'>
				<button class='btn btn-primary' onclick='send("nec-20DFC03F,nec-4BB6C03F")'>
					<i class='glyphicon glyphicon-chevron-down'></i>
					<br>
					<span>Vol. -</span>
				</button>
			</div>
			<div class='col-xs-6'>
				<button class='btn btn-primary' onclick='send("nec-20DF807F")'>
					<i class='glyphicon glyphicon-chevron-down'></i>
					<br>
					<span>Prg. -</span>
				</button>
			</div>
		</div>
		<div class='row'>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF55AA")'>
					<i class='glyphicon glyphicon-info-sign'></i>
					<br>
					<span>Info</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-info' onclick='send("nec-20DF02FD")'>
					<i class='glyphicon glyphicon-menu-up'></i>
					<br>
					<span>Up</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DFCA35")'>
					<span>LIVE MENU</span>
				</button>
			</div>
		</div>
		<div class='row'>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-info' onclick='send("nec-20DFE01F")'>
					<i class='glyphicon glyphicon-menu-left'></i>
					<br>
					<span>Left
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF22DD")'>
					<i class='glyphicon glyphicon-ok'></i>
					<br>
					<span>OK
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-info' onclick='send("nec-20DF609F")'>
					<i class='glyphicon glyphicon-menu-right'></i>
					<br>
					<span>Right</span>
				</button>
			</div>
		</div>
		<div class='row'>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF14EB")'>
					<i class='glyphicon glyphicon-arrow-left'></i>
					<br>
					<span>Back</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-info' onclick='send("nec-20DF827D")'>
					<i class='glyphicon glyphicon-menu-down'></i>
					<br>
					<span>Down</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DFDA25")'>
					<i class='glyphicon glyphicon-remove'></i>
					<br>
					<span>Exit</span>
				</button>
			</div>
		</div>
		<hr>
		<!-- KEYS -->
		<div class='row'>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF8877")'>
					<span>1</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF48B7")'>
					<span>2</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DFC837")'>
					<span>3</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF28D7")'>
					<span>4</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DFA857")'>
					<span>5</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF6897")'>
					<span>6</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DFE817")'>
					<span>7</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF18E7")'>
					<span>8</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF9867")'>
					<span>9</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DFD52A")'>
					<span>GUIDE</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF08F7")'>
					<span>0</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-20DF58A7")'>
					<span>Q.VIEW</span>
				</button>
			</div>
		</div>
		<hr>
		<div class='row'>
			<div class='col-xs-6'>
				<button class='btn btn-xs btn-success' onclick='send("nec-20DF23DC")'>
					<span>ON</span>
				</button>
			</div>
			<div class='col-xs-6'>
				<button class='btn btn-xs btn-danger' onclick='send("nec-20DFA35C")'>
					<span>OFF</span>
				</button>
			</div>
		</div>
	</div>
	<!-- END: CARD TV -->
	<!-- BEGIN: CARD TUNER -->
	<div id='card-1' data-title='Tuner' data-cmd-disabled='nec-4BB6D02F,nec-20DFA35C' style='display:none'>
		<div class='row'>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4BB6D02F")'>
					<span>AM / FM</span>
				</button>
			</div>
			<div class='col-xs-4'></div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B36AA55")'>
					<span>DISPLAY</span>
				</button>
			</div>
		</div>
		<div class='row'>
			<!--vol +: 20DF40BF, vol -: 20DFC03F, mute: 20DF906F-->
			<div class='col-xs-6'>
				<button class='btn btn-primary' onclick='send("nec-4BB640BF")'>
					<i class='glyphicon glyphicon-chevron-up'></i>
					<br>
					<span>Vol. +</span>
				</button>
			</div>
			<div class='col-xs-6'>
				<button class='btn btn-primary' onclick='send("nec-4BB600FF")'>
					<i class='glyphicon glyphicon-chevron-up'></i>
					<br>
					<span>Prg. +</span>
				</button>
			</div>
		</div>
		<div class='row'>
			<div class='col-xs-6'>
				<button class='btn btn-primary' onclick='send("nec-4BB6C03F")'>
					<i class='glyphicon glyphicon-chevron-down'></i>
					<br>
					<span>Vol. -</span>
				</button>
			</div>
			<div class='col-xs-6'>
				<button class='btn btn-primary' onclick='send("nec-4BB6807F")'>
					<i class='glyphicon glyphicon-chevron-down'></i>
					<br>
					<span>Prg. -</span>
				</button>
			</div>
		</div>
		<hr>
		<!-- KEYS -->
		<div class='row'>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B40AB54")'>
					<span>1</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B406B94")'>
					<span>2</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B40EB14")'>
					<span>3</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B401BE4")'>
					<span>4</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B409B64")'>
					<span>5</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B405BA4")'>
					<span>6</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B40DB24")'>
					<span>7</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B403BC4")'>
					<span>8</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B40BB44")'>
					<span>9</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B40FB04")'>
					<span>D.TUN</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4B407B84")'>
					<span>0</span>
				</button>
			</div>
			<div class='col-xs-4'>
				<button class='btn btn-xs btn-default' onclick='send("nec-4BB6BA45")'>
					<span>SLEEP</span>
				</button>
			</div>
		</div>
	</div>
	<!-- END: CARD TUNER -->
	<!-- BEGIN: CARD TEST CODE -->
	<div id='card-2' data-title='Test code' data-cmd-disabled='' style='display:none'>
		<div class='row'>
			<div class='col-xs-9'>
				<span>
					<select id='m' class='form-control'>
						<option value='nec' selected>NEC</option>
						<option value='rc5'>RC5</option>
						<option value='rc6'>RC6</option>
					</select>
				</span>
				<span>
					<input type='text' id='c' value='20DFCA35' placeholder='code' class='form-control'>
				</span>
				<span>
					<input type='text' id='b' value='32' placeholder='bits' class='form-control'>
				</span>
				<span>
					<input type='text' id='r' value='1' placeholder='repeat' class='form-control'>
				</span>
			</div>
			<div class='col-xs-3'>
				<button class='btn btn-xs btn-default' onclick='sendTest()'>
					<i class='glyphicon glyphicon-play'></i>
					<br>
					<span>Test</span>
				</button>
			</div>
		</div>
	</div>
	<!-- END: CARD TEST -->
</div>
<!-- END: CONTAINER -->
e la parte JavaScript
JavaScript
/* libreria per swipe  OMESSA */

/* main */
function getId(id) {
	return document.getElementById(id)
}

/* gestione swipe - card */
var cardIndex = 0
	, cardIndexMax = 2;

document.addEventListener('swiped-left', function(e) {
	changeCard(1);
});

document.addEventListener('swiped-right', function(e) {
	changeCard(-1);
});

function changeCard(moveIndex) {
	cardIndex += moveIndex;
	if (cardIndex < 0)
		cardIndex = cardIndexMax;
	else if (cardIndex > cardIndexMax)
		cardIndex = 0;
	showCard();
}

function showCard() {
	/* card corrente */
	var objCard = getId('card-' + cardIndex);
	var title = objCard.getAttribute('data-title');
	getId('title').innerText = title;
	objCard.style.display = 'block';
	/* next button */
	var cardIndexNext = cardIndex >= cardIndexMax ? 0 : cardIndex + 1;
	objCardNext = getId('card-' + cardIndexNext);
	var titleNext = objCardNext.getAttribute('data-title');
	getId('btn-tv-tuner').innerHTML = titleNext;
	/* disattivo schede */
	for (var i = 0; i <= cardIndexMax; i++) {
		if (cardIndex !== i)
			getId('card-' + i).style.display = 'none';
	}
	var cmd = objCard.getAttribute('data-cmd');
	send(cmd);
}

;showCard();

/* test code */
function sendTest() {
	var m = getId('m').value;
	var c = getId('c').value;
	var b = getId('b').value;
	var r = getId('r').value;
	sendFull(m, c, b, r);
}

/* invia comandi */
function send(cmds) {
	if (cmds === null)
		return;
	var cmd = cmds.split(',');
	for (var i = 0; i < cmd.length; i++) {
		var m = ''
			, c = ''
			, b = null
			, r = null;
		var p = cmd[i].split('-');
		if (p.length > 0)
			m = p[0];
		if (p.length > 1)
			c = p[1];
		if (p.length > 2)
			b = p[2];
		if (p.length > 3)
			r = p[3];
		sendFull(m, c, b, r);
	}
}

function sendFull(m, c, b, r) {
	try {
		if (b === undefined || b == null || b === '' || b <= 0)
			b = 32;
		if (r === undefined || r == null || r === '' || r <= 0)
			r = 1;
		var url = '/ir?mode=' + m.toLowerCase() + '&code=0x' + c + '&bits=' + b + '&repeat=' + r + '&t=' + (new Date().getTime());
		sendHttp(url);
	} catch (e) {
		console.log(e);
	}
}

/* chiamata http */
function sendHttp(url) {
	var loading = document.getElementById('loading');
	loading.style.display = 'block';
	try {
		var xhr = new XMLHttpRequest();
		xhr.open('GET', url);
		xhr.onload = function() {
			loading.style.display = 'none';
			if (xhr.status === 200) {
				console.log('OK');
			} else {
				alert('Error: ' + xhr.status);
			}
		}
		;
		xhr.onerror = function() {
			loading.style.display = 'none';
			alert("Errore nell'invio del codice");
		}
		;
		xhr.send();
	} catch (e) {
		loading.style.display = 'none';
		alert(e);
	}
}

Modifica codici IR

Per modificare il codice inviato dei pulsanti, va cercato nel codice HTML la chiamata alla funzione JavaScript send(...) nell'evento onclick.

Questo è un esempio realativo al tasto mute
HTML
<button class='btn btn-xs btn-default' onclick='send("nec-4BB6A05F")'>
  <i class='glyphicon glyphicon-volume-off'></i>
  <br>
  <span>Mute</span>
</button>
La funzione send è quella che chiama l'API (/if) per l'invio dei codici a infrarosso.
Per rilevare i codici del telecomando vedi: Come leggere i codici infrarosso di un telecomando TV con Arduino.

Macro

La funzione supporta anche le macro, ovvero la possibilità di inviare più codici in sequenza.
Ad esempio per accendere contemporaneamente sia il televisore che il decoder.

Per far questo è sufficiente separare i codici con una virgola
HTML
<button class='btn btn-xs btn-default' onclick='send("nec-20DF10EF,nec-4B36D32C")'>
Non lasciare spazi tra un codice e l'altro.
Potrebbe interessarti anche: