Televisore comandato dal Cellulare o via Web (ESP8266)
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.
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.
Il circuito funziona implementando un web server che risponde a queste url:
e ritrasmette il codice infrarosso.
I parametri sono:
In alternativa è possibile usare il modulo IR, anche se dalle prove effettuate il raggio infrarossi sembra essere più direzionale e meno potente
Modificando le variabili:
e la parte JavaScript
Questo è un esempio realativo al tasto mute
La funzione send è quella che chiama l'API (/if) per l'invio dei codici a infrarosso.
Ad esempio per accendere contemporaneamente sia il televisore che il decoder.
Per far questo è sufficiente separare i codici con una virgola
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.
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 questeLa 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:
- http://192.168.0.50/ (HTTP GET): ritorna la pagina web HTML con i pulsanti del telecomando
- http://192.168.0.50/ir (HTTP GET): web API per inviare i codici infrarosso richiamata dal codice JavaScript
- http://192.168.0.50/version (HTTP GET): visualizza la versione del codice
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 esempioURL
http://192.168.0.50/ir?mode=nec&code=0x4BB6C03F&bits=32&repeat=1&t=1625427666318
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
In alternativa è possibile usare il modulo IR, anche se dalle prove effettuate il raggio infrarossi sembra essere più direzionale e meno potente
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 ArduinoArduino: 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();
}
- 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)
Dettaglio del codice
Per una migliore leggibilità riporto l'HTML generatoHTML
<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 -->
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>
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.