WiFi-IR Blaster: DIY-project met ESP8266 en Arduino
Onderdelen en benodigdheden
| Espressif ESP8266 ESP-01 | Je kunt dit ook doen met een standaard ESP8266 of de NodeMCU development kit | 1 |
| Arduino Nano R3 | 1 | |
| Algemene NPN-transistor | 1 | |
| Adafruit IR-leds | 1 |
Benodigde tools en machines
| Solderbout (generiek) |
Apps en online diensten
| Arduino IDE | |
| NodeMCU firmware |
Over dit project
Twee stappen vooruit...
Grote telefoonfabrikanten stappen schijnbaar af van ingebouwde IR-blasters. Ik zoek een manier om mijn "domme" apparaten die niet met mijn IoT-infrastructuur geïntegreerd zijn, toekomstbestendig te maken. Ik heb een mesh-netwerk slimme thuis, maar ik heb een fysieke afstandsbediening nodig om mijn TV aan te zetten? Er moet een betere manier zijn.
Ik bouwde recent een IR-blaster voor losse airconditioners en een ander project was een verbonden HVAC-klep met een Android-webapp. Eigenlijk hoef ik deze twee dingen alleen te combineren tot een web-verbonden IR-blaster met een webapp-frontend, zodat ik op mijn telefoon knoppen kan indrukken en IR-signalen naar mijn mediacenters worden gestuurd. Deze kunnen in elke kamer worden geplaatst en vanaf één interface bediend worden, dus de toepassingen zijn extreem breed.
Snel prototypen
Ik pakte ongebruikte hardware en wirede alles wat ik nodig had om verbinding te maken met mijn WiFi, HTTP-verzoeken te accepteren en IR-signalen te sturen.
Dit werkte prima: ik kon een specifieke URL opvragen op het IP-adres van de ESP8266 en de Arduino zou een "Power"-signaal naar de IR-led sturen. Ik moest alleen uitvinden waar ik de webapp kon hosten, die de URLs naar de IR-blaster zou sturen en die verzoeken zou parseren naar specifieke IR-codes. Dit was zeer vervelend met AT-commando's op de ESP8266, en er was een vertraging van 1-2 seconden tussen het sturen van het verzoek en het knipperen van de led.
NodeMCU
Ik ging verder met het NodeMCU ESP8266 dev-board omdat het onboard spanningsregeling, USB-naar-seriële interface, en veel I/O-pinnen heeft in dit kleine formaat (en het is breadboard-vriendelijk). Er blijkt al een ESP8266 WebServer-bibliotheek te zijn die GET-verzoeken accepteert, die ik kon aanpassen voor het sturen van IR-codes, en de gewone IRremote-bibliotheek werkt out-of-the-box.
De ESP8266 op de NodeMCU is niet krachtig genoeg om de webapp zelf te hosten, dus ik draai hem op een onderbenutte C.H.I.P. $9-computer die al op mijn thuisnetwerk draait. Een andere beperking is dat de ESP8266 een 3,3V-bord is, dus IR-leds direct aangedreven vanaf een I/O-pin waren erg zwak en moesten dichtbij het apparaat. Ik gebruikte de I/O-pin om een NPN-transistor aan te sturen die de 5V-voeding aan/uit schakelt.
REST...ish
Na onderzoek vond ik een geweldige tutorial voor het implementeren van een REST API voor Arduino om LEDs te besturen op Adafruit. Ik gebruikte hun JavaScript en PHP-bestanden om de cURL-verzoeken te sturen en laadde dat op de CHIP die al Apache draaide, maar net zo goed in de cloud gehost kan worden. Na het maken van een basis HTML-pagina voor de afstandsbediening, voegde ik manifest.json en icoonbestanden toe zodat het als een native webapp op een Android-telefoon kan draaien.
Op de Arduino-kant stutte ik de webserver af tot het accepteren van een GET-verzoek, het parsen van de URL, het sturen van een 200 OK, en het verbreken van de verbinding. Op basis van de URL zou de Arduino de IR-code sturen die gemapt was op de ingedrukte knop.
Universele Remonster
Alles werkt flawless met nul vertraging. Ik ontdekte dat de IR-leds die ik gebruik een zeer smalle hoek hebben, dus ze moeten correct gericht zijn. Industriële velcro laat me het onder een plank monteren, onzichtbaar, en zorgt dat het gericht blijft op de IR-ontvangers. Ik voegde dikke draad toe aan de led-pootjes voor fijn afstellen.
Weerstanden voor de IR-leds maakten ze bijna onbruikbaar, dus de transistor voedt ze met ongeregelde 5V. Dit laat me IR van muren laten weerkaatsen en de blaster aan de overkant van de kamer monteren, maar zal zeker invloed hebben op de levensduur. Ik hoop dat ze, aangezien ze zelden gebruikt worden en de IR-pulsen superkort zijn (lagere duty cycle dan de meeste gedimde PWM-signalen), nog een tijdje meegaan. Ik heb al wide-angle en hogere output IR-leds besteld voor versie 2.
3-maanden update en toekomstige features
Ik gebruik dit elke dag en het werkt flawless. Ik heb zelfs een tweede apparaat in onze familiekamer toegevoegd voor onze raam-airco en een andere TV.
Plannen voor de toekomst:
- Web UI migreren naar een cloud-service
- Integreren met Amazon Echo voor stemcommando's (Gedaan)
- Volgende hardware-iteratie krijgt een DHT22-temperatuursensor en ik onderzoek manieren om RF te transmitteren voor mijn plafondventilatoren
- IR-ontvanger toevoegen voor een leer-functie zodat je de IR-blaster commando's van bestaande afstandsbedieningen kunt "leren"
- Integreren met mijn thuisautomatisering/beveiliging om apparaten uit te schakelen wanneer niet in gebruik
Deze kleine verbonden IR-blaster vult langzaam de kloof tussen mijn slimme thuis en legacy-apparaten.
Code
WiFi IR Blaster voor ESP8266 Arduino Sketch
Je hebt de ESP8266 wifi-bibliotheek en IRremote.h nodig. Je kunt er ook voor kiezen om de Raw IR-codes niet te gebruiken als je afstandsbediening in de bibliotheek ondersteund wordt. Ik heb deze sketch aangepast van een airconditioner-controller die non-standaard IR-codes gebruikte.
/*
* WiFi IR Blaster door Buddy Crotty
* Gebruik een ESP8266-module of dev-board om HTTP GET-verzoeken te ontvangen
* en dan IR-codes te sturen naar een aangesloten IR-led gebaseerd op die verzoeken.
* Werkt het beste met een andere webserver als frontend die
* cURL-verzoeken stuurt op basis van welke knoppen worden ingedrukt.
* cURL-formaat: https://ESP8266/IRcode
*/
#include <ESP8266WiFi.h>
#include <IRremoteESP8266.h>
#include <ESP8266mDNS.h>
const char* ssid = "AP_SSID";
const char* password = "AP_Pass";
MDNSResponder mdns;
int khz = 38; // 38kHz draaggolf voor zowel NEC als Samsung
IRsend irsend(4); // IR-led op GPIO4 (pin D2 op NodeMCU)
// Insert RAW IR signaal voor "TV Power"
unsigned int irTVpwr[] = {4650,4250, 700,1550, 650,1550, 700,1550, 650,450, 650,500, 600,500, 600,500, 600,550, 550,1700, 550,1650, 600,1650, 550,550, 600,500, 600,550, 550,550, 600,500, 600,550, 550,1650, 600,550, 550,550, 600,500, 600,550, 550,550, 600,500, 600,1650, 600,500, 600,1650, 550,1700, 550,1650, 600,1650, 550,1650, 600,1650, 600};
// SAMSUNG E0E040BF
// ... (overige IR-codes weggelaten voor beknoptheid, zie origineel voor volledige lijst)
WiFiServer server(80);
void setup() {
Serial.begin(115200);
delay(10);
irsend.begin();
// Verbinden met WiFi-netwerk
Serial.println();
Serial.println();
Serial.print("Verbinden met ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi Verbonden");
// Server starten
server.begin();
Serial.println("HTTP Server Gestart");
// IP-adres printen
Serial.print("IP-adres: ");
Serial.println(WiFi.localIP());
if (mdns.begin("IRBlasterLR", WiFi.localIP())) {
Serial.println("MDNS Responder Gestart");
}
Serial.println();
Serial.println();
}
void loop() {
// Check of een client verbonden is
WiFiClient client = server.available();
if (!client) {
return;
}
// Wachten tot client data stuurt
Serial.println("nieuwe client");
while(!client.available()){
delay(1);
}
// Lees eerste regel van verzoek
String req = client.readStringUntil('\r');
Serial.println(req);
client.flush();
// Verzoek matchen
if (req.indexOf("/irTVpwr") != -1){
irsend.sendRaw(irTVpwr, sizeof(irTVpwr) / sizeof(irTVpwr[0]), khz);
Serial.println("IRreq irTVpwr verstuurd");
} else if (req.indexOf("/irTVsrc") != -1){
irsend.sendRaw(irTVsrc, sizeof(irTVsrc) / sizeof(irTVsrc[0]), khz);
Serial.println("IRreq irTVsrc verstuurd");
} else if (req.indexOf("/irTVmute") != -1){
irsend.sendRaw(irTVmute, sizeof(irTVmute) / sizeof(irTVmute[0]), khz);
Serial.println("IRreq irTVmute verstuurd");
} else if (req.indexOf("/irTVvdn") != -1){
irsend.sendRaw(irTVvdn, sizeof(irTVvdn) / sizeof(irTVvdn[0]), khz);
Serial.println("IRreq irTVvdn verstuurd");
} else if (req.indexOf("/irTVvup") != -1){
irsend.sendRaw(irTVvup, sizeof(irTVvup) / sizeof(irTVvup[0]), khz);
Serial.println("IRreq irTVvup verstuurd");
} else if (req.indexOf("/irTVchup") != -1){
irsend.sendRaw(irTVchup, sizeof(irTVchup) / sizeof(irTVchup[0]), khz);
Serial.println("IRreq irTVchup verstuurd");
} else if (req.indexOf("/irTVchdn") != -1){
irsend.sendRaw(irTVchdn, sizeof(irTVchdn) / sizeof(irTVchdn[0]), khz);
Serial.println("IRreq irTVchdn verstuurd");
} else if (req.indexOf("/irALLpwr") != -1){
irsend.sendRaw(irRECpwrON, sizeof(irRECpwrON) / sizeof(irRECpwrON[0]), khz);
irsend.sendRaw(irTVpwr, sizeof(irTVpwr) / sizeof(irTVpwr[0]), khz);
delay(2000);
irsend.sendRaw(irRECsrc, sizeof(irRECsrc) / sizeof(irRECsrc[0]), khz);
Serial.println("IRreq irALLpwr verstuurd");
} else if (req.indexOf("/irRECpwr") != -1){
irsend.sendRaw(irRECpwr, sizeof(irRECpwr) / sizeof(irRECpwr[0]), khz);
Serial.println("IRreq irRECpwr verstuurd");
} else if (req.indexOf("/irRECpwrON") != -1){
irsend.sendRaw(irRECpwrON, sizeof(irRECpwrON) / sizeof(irRECpwrON[0]), khz);
Serial.println("IRreq irRECpwrON verstuurd");
} else if (req.indexOf("/irRECpwrOFF") != -1){
irsend.sendRaw(irRECpwrOFF, sizeof(irRECpwrOFF) / sizeof(irRECpwrOFF[0]), khz);
Serial.println("IRreq irRECpwrOFF verstuurd");
} else if (req.indexOf("/irRECmute") != -1){
irsend.sendRaw(irRECmute, sizeof(irRECmute) / sizeof(irRECmute[0]), khz);
Serial.println("IRreq irRECmute verstuurd");
} else if (req.indexOf("/irRECvdn") != -1){
irsend.sendRaw(irRECvdn, sizeof(irRECvdn) / sizeof(irRECvdn[0]), khz);
Serial.println("IRreq irRECvdn verstuurd");
} else if (req.indexOf("/irRECvup") != -1){
irsend.sendRaw(irRECvup, sizeof(irRECvup) / sizeof(irRECvup[0]), khz);
Serial.println("IRreq irRECvup verstuurd");
} else {
Serial.println("ongeldig verzoek");
client.stop();
return;
}
client.flush();
// Stuur response naar client
client.print("HTTP/1.1 200 OK\r\n");
delay(1);
Serial.println("Client Verbonden Gebroken");
Serial.println();
}
script.js
// Functie om IR-commando's te sturen
function buttonClick(clicked_id){
if (clicked_id == "irTVpwr"){
$.get("curl.php", { room: "192.168.1.62", button: "irTVpwr" });
}
if (clicked_id == "irTVsrc"){
$.get("curl.php", { room: "192.168.1.62", button: "irTVsrc" });
}
if (clicked_id == "irTVmute"){
$.get("curl.php", { room: "192.168.1.62", button: "irTVmute" });
}
if (clicked_id == "irTVvdn"){
$.get("curl.php", { room: "192.168.1.62", button: "irTVvdn" });
}
if (clicked_id == "irTVvup"){
$.get("curl.php", { room: "192.168.1.62", button: "irTVvup" });
}
if (clicked_id == "irTVchup"){
$.get("curl.php", { room: "192.168.1.62", button: "irTVchup" });
}
if (clicked_id == "irTVchdn"){
$.get("curl.php", { room: "192.168.1.62", button: "irTVchdn" });
}
if (clicked_id == "irRECpwr"){
$.get("curl.php", { room: "192.168.1.62", button: "irRECpwr" });
}
if (clicked_id == "irALLpwr"){
$.get("curl.php", { room: "192.168.1.62", button: "irALLpwr" });
}
if (clicked_id == "irRECpwrON"){
$.get("curl.php", { room: "192.168.1.62", button: "irRECpwrON" });
}
if (clicked_id == "irRECpwrOFF"){
$.get("curl.php", { room: "192.168.1.62", button: "irRECpwrOFF" });
}
if (clicked_id == "irRECmute"){
$.get("curl.php", { room: "192.168.1.62", button: "irRECmute" });
}
if (clicked_id == "irRECvdn"){
$.get("curl.php", { room: "192.168.1.62", button: "irRECvdn" });
}
if (clicked_id == "irRECvup"){
$.get("curl.php", { room: "192.168.1.62", button: "irRECvup" });
}
}
index.html
<html> <head> <LINK href="style.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="jquery-2.0.3.min.js"></script> <script type="text/javascript" src="script.js"></script> <link rel="manifest" href="manifest.json"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <link rel="icon" type="image/png" href="favicon.ico" /> </head> <body> <div align="center" class="div_text_shadow"> <button class="btn2" type="button" id="irALLpwr" onClick="buttonClick(this.id)">Alles Aan</button><BR><BR><BR> <H1>TV</H1> <button class="btn" type="button" id="irTVpwr" onClick="buttonClick(this.id)">Power</button><BR><BR> <button class="btn" type="button" id="irTVvup" onClick="buttonClick(this.id)">Vol +</button> <button class="btn" type="button" id="irTVsrc" onClick="buttonClick(this.id)">Source</button> <button class="btn" type="button" id="irTVchup" onClick="buttonClick(this.id)">Ch +</button><BR><BR> <button class="btn" type="button" id="irTVvdn" onClick="buttonClick(this.id)">Vol -</button> <button class="btn" type="button" id="irTVmute" onClick="buttonClick(this.id)"> Mute </button> <button class="btn" type="button" id="irTVchdn" onClick="buttonClick(this.id)">Ch -</button> <br><br><br> <H1>Receiver</H1> <button class="btn" type="button" id="irRECpwr" onClick="buttonClick(this.id)">Power</button><BR><BR> <button class="btn" type="button" id="irRECmute" onClick="buttonClick(this.id)">Mute</button> <button class="btn" type="button" id="irRECvdn" onClick="buttonClick(this.id)">Vol -</button> <button class="btn" type="button" id="irRECvup" onClick="buttonClick(this.id)">Vol +</button> <br><br> </div> </body> </html>
curl.php
<?php $room = $_GET['room']; $button = $_GET['button']; // Maak cURL-aanroep $service_url = 'https://' . $room . '/' . $button; $curl = curl_init($service_url); // Stuur cURL naar Yun-board curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); $curl_response = curl_exec($curl); curl_close($curl); // Print antwoord echo $curl_response; ?>
manifest.json
{
"name": "WiFi Remote",
"icons": [
{ "src": "remote_icon_36.png", "sizes": "36x36", "type": "image/png", "density": 0.75 },
{ "src": "remote_icon_48.png", "sizes": "48x48", "type": "image/png", "density": 1.0 },
{ "src": "remote_icon_128.png", "sizes": "128x128", "type": "image/png", "density": 1.0 },
{ "src": "remote_icon_192.png", "sizes": "192x192", "type": "image/png", "density": 1.0 }
],
"scope": "/remote/",
"start_url": "/remote/index.html",
"display": "fullscreen",
"orientation": "portrait"
}
Schema's
Eigenlijk niets aan de hand, alleen voeding en een enkele pin verbonden met een NPN-transistor om twee IR-leds in serie (zonder weerstanden) van de 5V-voeding aan te sturen.
Als je extra leds wilt aansturen of minder spanningsval van een 3,3V-bron nodig hebt, kun je een NPN-transistor gebruiken om meerdere PNP-transistoren (één per led) te schakelen. Deze configuratie laat je zoveel leds gebruiken als je bronstroom kan leveren.
Productieproces
- Naadloze MQTT Integratie: NodeMCU DHT22 Sensoren Met Raspberry Pi 3 B+ Als Broker
- Bouw een WiFi-gecontroleerde Robot met Raspberry Pi & Android Smartphone
- Bouw een Wi-Fi-gecontroleerde Raspberry Pi Robot met Python – Stap-voor-stap Gids
- Bouw een Slimme IoT Pot met ESP8266, Arduino & Ultrasonische Sensor – Real-time Monitoring
- Arduino Uno WiFi Webserver: LED aan/uit schakelen via browser
- WiFi-ingeschakeld slim slot met Arduino & ESP8266 – Ontgrendel via je telefoon
- WiFi Calling Uitgelegd: Hoe Het Werkt En Waarom Het Belangrijk Is
- Korrelstraalen uitgelegd: De ultieme gids voor gladde, gepolijste afwerkingen
- Belangrijkste beperkingen van Wi‑Fi Real-Time Location Systems
- Opkomende draadloze technologieën van 2015: Een gids voor M2M & IoT
- Een TLS-ingeschakelde Web Server Deployen op een $3 ESP8266 in Onder 30 Minuten