Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Blaster infrarouge Wi-Fi

Composants et fournitures

Espressif ESP8266 ESP-01
Vous pouvez le faire avec un ESP8266 standard à la place ou le kit de développement NodeMCU
× 1
Arduino Nano R3
× 1
Transistor à usage général NPN
× 1
DEL IR Adafruit
× 1

Outils et machines nécessaires

Fer à souder (générique)

Applications et services en ligne

Arduino IDE
Micrologiciel NodeMCU

À propos de ce projet

Deux pas en avant...

Il semble que les principaux fabricants de téléphones s'éloignent des blasters IR intégrés, donc je cherche un moyen de pérenniser mes appareils « idiots » qui ne s'intègrent pas actuellement à mon infrastructure IoT. J'ai une maison intelligente en réseau maillé, mais j'ai besoin de trouver une télécommande physique pour allumer mon téléviseur ? Il doit y avoir une meilleure façon.

J'ai récemment construit un blaster IR qui contrôle les climatiseurs autonomes dans ma maison et un autre projet précédent était un évent CVC connecté avec une application Web connectée à Android. Essentiellement, tout ce que j'ai à faire est de combiner ces deux choses dans un blaster IR connecté au Web avec une interface d'application Web afin que je puisse appuyer sur les boutons de mon téléphone et envoyer des signaux IR à mes centres multimédias. Ceux-ci peuvent être déployés dans chaque pièce et contrôlés n'importe où à partir d'une seule interface, de sorte que les applications pour cela sont extrêmement larges.

Prototypage rapide

J'ai récupéré du matériel inutilisé et câblé tout ce dont j'avais besoin pour me connecter à mon WiFi, accepter les connexions HTTP et envoyer des signaux IR.

Cela a très bien fonctionné dans la mesure où je pouvais demander une URL spécifique sur l'adresse IP attribuée à l'ESP8266 et faire en sorte que l'Arduino envoie un signal "Power" à la LED IR. J'avais juste besoin de savoir où héberger l'application Web, d'envoyer les URL au blaster IR, puis d'analyser ces demandes en codes IR spécifiques. C'était très fastidieux à faire en utilisant les commandes AT avec l'ESP8266, et il y avait quelque chose qui causait un délai de 1 à 2 secondes entre l'envoi de la demande et le clignotement de la LED.

NodeMCU

J'ai opté pour la carte de développement NodeMCU ESP8266 car elle dispose d'une régulation de l'alimentation intégrée, d'une interface USB vers série et de nombreuses broches d'E/S dans ce petit boîtier (elle est également compatible avec la maquette). Il s'avère qu'il existe déjà une bibliothèque ESP8266 WebServer qui accepte les requêtes GET que j'ai pu adapter pour l'envoi de codes IR et que la bibliothèque IRremote standard fonctionne immédiatement.

L'ESP8266 sur le NodeMCU n'est pas assez puissant pour héberger l'application Web elle-même, je vais donc l'exécuter sur un C.H.I.P. sous-utilisé. ordinateur à 9 $ qui fonctionne déjà sur mon réseau domestique. Une autre limitation est que l'ESP8266 est qu'il s'agit d'une carte 3.3v, donc les LED IR pilotées directement à partir d'une broche d'E/S étaient très faibles et devaient être juste à côté de l'appareil auquel elle envoyait un signal. J'ai plutôt utilisé la broche I/O pour piloter un transistor npn pour activer/désactiver la tension d'alimentation 5v.

REPOS...ish

Après avoir fait quelques recherches, j'ai trouvé un excellent tutoriel pour implémenter une API REST pour Arduino afin de contrôler les LED sur adafruit. J'ai utilisé leurs fichiers JavaScript et PHP pour soumettre les requêtes cURL et les ai chargées sur le CHIP qui exécute déjà Apache mais pourrait tout aussi bien être hébergé dans le cloud. Après avoir créé une page HTML de base pour la télécommande, j'ai ajouté les fichiers manifest.json et icon afin qu'elle puisse s'exécuter en tant qu'application Web native sur un téléphone Android.

Côté Arduino, j'ai réduit le serveur Web pour simplement accepter une requête GET, analyser l'URL, envoyer un 200 OK, puis me déconnecter. Sur la base de l'URL, l'Arduino enverrait le code IR mappé au bouton enfoncé.

Remonster universel

Tout fonctionne parfaitement sans aucun retard. J'ai découvert que les LED IR que j'utilise ont un angle très étroit, je dois donc faire attention à ce qu'elles soient pointées correctement. Certains velcro industriels me permettent de le monter sous une étagère, hors de vue, et s'assure qu'il reste pointé vers les récepteurs IR à l'autre extrémité. J'ai ajouté du fil de gros calibre aux fils LED afin qu'ils puissent être réglés avec précision.

Le fait d'avoir des résistances pour les LED IR les rendait presque inutilisables, donc le transistor les alimente avec un 5v non régulé. Cela me permet de faire rebondir l'IR sur les murs et de monter le blaster à travers la pièce, mais aura certainement un effet sur la longévité. J'espère qu'étant donné qu'ils sont utilisés très rarement et que les signaux IR sont des impulsions très courtes (cycle d'utilisation inférieur à la plupart des signaux PWM faibles), je n'aurai pas besoin de remplacer les LED pendant un certain temps. Je me suis assuré de commander des LED IR grand angle et à sortie plus élevée pour la version 2, donc quand celles-ci mourront enfin, je les remplacerai avec une résistance en ligne.

Mise à jour de 3 mois et fonctionnalités futures

Je l'utilise tous les jours et il fonctionne parfaitement. J'ai même ajouté un deuxième appareil dans notre salle familiale pour contrôler notre unité de climatisation de fenêtre et un autre téléviseur.

Projets pour l'avenir :

  • Migrez l'interface utilisateur Web vers un service cloud
  • Intégrer avec Amazon Echo pour les commandes vocales (Terminé)
  • La prochaine itération matérielle aura également un capteur de température DHT22 et je recherche des moyens de transmettre des RF pour mes ventilateurs de plafond
  • Ajoutez un récepteur IR pour une fonction d'apprentissage afin que vous puissiez « enseigner » les commandes de blaster IR à partir de télécommandes existantes
  • Intégrer avec ma domotique/sécurité pour éteindre les appareils lorsqu'ils ne sont pas utilisés

Ce petit blaster IR connecté comble lentement le fossé entre ma maison intelligente et les appareils existants.

Code

  • WiFi IR Blaster pour ESP8266 Arduino Sketch
  • script.js
  • index.html
  • curl.php
  • manifest.json
WiFi IR Blaster pour ESP8266 Arduino SketchC/C++
Vous aurez besoin de la bibliothèque wifi ESP8266 et de IRremote.h. Vous devez également choisir de ne pas utiliser les codes IR bruts si votre télécommande est prise en charge dans la bibliothèque. J'ai adapté ce croquis à partir d'un contrôleur de climatiseur que j'ai construit qui utilisait des codes IR non standard.
/* * WiFi IR Blaster par Buddy Crotty * Utilisez un module ESP8266 ou une carte de développement pour recevoir la requête HTTP GET * puis envoyez des codes IR à une LED IR attachée en fonction de ces demandes. * Cela fonctionne mieux avec un autre serveur Web agissant comme un serveur frontal qui * envoie des requêtes cURL en fonction des boutons enfoncés. * format cURL :http://ESP8266/IRcode */#include #include #include const char* ssid ="AP_SSID";const char* mot de passe ="AP_Pass ";MDNSResponder mdns;int khz =38; // Fréquence porteuse de 38 kHz pour NEC et SamsungIRsend irsend(4) ; // une led IR est connectée à GPIO4 (broche D2 sur NodeMCU) // Insérez le signal IR RAW pour "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 // Insertion du signal IR RAW pour "Source TV" unsigned int irTVsrc[] ={4600,4300, 700,1550, 650,1550, 650,1600, 650,450, 650,450, 600,550, 550,550, 600,500, 600, 1650, 550,1650, 600,1650, 550,550, 600,500, 600,550, 550,550, 550,550, 600,1650, 550,550, 550,550, 600,500, 600,500, 600,550, 550,550, 600,500, 600,550, 550,1650, 550,1700, 550, 1650, 600,1600, 600,1650, 600,1600, 600,1650, 550} ; // SAMSUNG E0E0807F // Insertion du signal IR RAW pour "TV Mute" unsigned int irTVmute[] ={4650,4250, 700,1550, 650,1550, 700,1550, 650,450, 650,500, 600,500, 600,500, 600,500, 600, 1650, 600,1600, 600,1650, 550,550, 600,500, 600,550, 550,550, 600,500, 600,1650, 550,1650, 600,1650, 550,1650, 600,550, 550,550, 550,550, 600,500, 600,550, 550,550, 550,550, 600 500, 600 1650, 550 1650, 600 1650, 550 1650, 600} ; // SAMSUNG E0E0F00F // Insertion du signal IR RAW pour "TV Volume Down" unsigned int irTVvdn[] ={4650,4250, 700,1550, 650,1550, 700,1550, 650,450, 650,450, 650,450, 600,550, 550,550, 600 ,1650, 550,1650, 550,1650, 600,550, 550,550, 550,550, 600,500, 600,500, 600,1650, 600,1600, 600,500, 600,1650, 550,550, 600,500, 600,500, 600,550, 550,550, 600,500, 600,1650 , 550 550, 550 1650, 600 1650, 550 1650, 600 1650, 550} ; // SAMSUNG E0E0D02F // Insertion du signal IR RAW pour "TV Volume Up" unsigned int irTVvup[] ={4600,4300, 650,1600, 650,1550, 650,1600, 600,500, 600,550, 600,500, 600,550, 550,550, 550 ,1700, 550,1650, 600,1650, 550,550, 600,500, 600,550, 550,550, 600,500, 600,1650, 600,1650, 550,1650, 600,550, 550,550, 600,500, 600,550, 550,550, 600,500, 600,550, 550,550, 600 ,1600, 600,1650, 600,1650, 550,1650, 600,1650, 600} ; // SAMSUNG E0E0E01F // Insertion du signal IR RAW pour "TV Channel Up" unsigned int irTVchup[] ={4650,4250, 700,1550, 650.1600, 650,1550, 650,500, 600,500, 600,500, 650,500, 600,500, 600 ,1650, 550,1650, 600,1650, 600,500, 600,500, 600,550, 550,550, 600,550, 550,550, 550,1650, 600,550, 600,500, 600,1650, 550,550, 600,500, 600,550, 550,1650, 600,550, 550,1650 , 600 1650, 600 500, 600 1650, 600 1600, 600 1650, 600} ; // SAMSUNG E0E048B7 // Insertion du signal IR RAW pour "TV Channel Down" unsigned int irTVchdn[] ={4600,4350, 650,1550, 650,1600, 650,1600, 600,500, 600,500, 600,550, 550,550, 600,550, 550 ,1650, 600,1650, 550,1700, 550,550, 550,550, 600,500, 600,550, 550,550, 600,500, 600,550, 550,550, 550,550, 600,1650, 600,500, 600,500, 600,550, 550,1650, 600,1650, 600,1650 , 550 1650, 600 550, 550 1650, 600 1650, 600 1650, 550} ; // SAMSUNG E0E008F7 // Insérer le signal RAW IR pour "Receiver Power" unsigned int irRECpwr[] ={9050,4350, 650,500, 600,1600, 600,500, 650,500, 600,1600, 600,550, 600,1600, 600,1650, 550 550, 600 500, 600 1600, 650 1600, 600 500, 600 1650, 600 1600, 600 500, 600 1650, 600 1600, 600 550, 600 1600, 600 500, 600 550, 600 1600, 600 1600, 650 500, 600 500, 600 1600, 650 500, 600 1600, 600 1650, 600 500, 600 500, 600} ; // NEC 4B36D32C // Insertion du signal RAW IR pour "Receiver Power On" unsigned int irRECpwrON[] ={9000,4400, 600,550, 600,1600, 600,500, 600,550, 600,1600, 600,500, 600,1600, 650,1600 , 600 1600, 600 500, 650 1600, 600 1600, 600 500, 650 1600, 600 1600, 600 500, 600 550, 600 500, 600 1600, 600 550, 600 500, 600 500, 650 500, 600 500, 600 1600, 650 ,1600, 600,500, 600,1600, 650,1600, 600,1600, 600,1600, 600,1600, 650} ; // NEC 4BB620DF // Insertion du signal RAW IR pour "Receiver Power Off" unsigned int irRECpwrOFF[] ={9000,4400, 600,550, 550,1650, 600,550, 550,550, 600,1650, 550,550, 600,1650, 550,1650 , 600 550, 550 550, 550 1650, 600 1650, 600 550, 550 1650, 600 1650, 550 550, 600 1650, 550 1650, 600 1650, 600 500, 600 550, 550 550, 600 1650, 550 550, 600 500 , 600 550, 550 550, 550 1700, 550 1650, 600 1650, 550 550, 600 1650, 550} ; // NEC 4B36E21D // Insertion du signal IR RAW pour "Receiver Mute" unsigned int irRECmute[] ={9000,4400, 650,450, 650,1600, 600,500, 600,500, 650,1600, 600,500, 600,1650, 600,1600, 600 1600, 650 500, 600 1600, 650 1600, 600 500, 600 1600, 650 1600, 600 500, 600 1650, 600 500, 600 1600, 650 500, 600 500, 600 500, 600 500, 650 500, 600 500, 600, 1600, 650,500, 600,1600, 600,1600, 650,1600, 600,1650, 600,1600, 600} ; // NEC 4BB6A05F // Insertion du signal RAW IR pour "Receiver Volume Down" unsigned int irRECvdn[] ={9150,4250, 750,350, 700,1550, 700,400, 700,450, 650,1550, 700,450, 600,1600, 650,1600 , 600 1650, 600 500, 600 1650, 600 1600, 600 550, 600 1600, 600 1650, 600 500, 600 1650, 600 1600, 650 500, 600 500, 600 500, 650 500, 600 500, 600 500, 600 550, 600 500 , 600 1650, 600 1600, 600 1650, 600 1650, 600 1600, 600 1650, 600} ; // NEC 4BB6C03F // Insertion du signal IR RAW pour "Receiver Volume Up" unsigned int irRECvup[] ={9050,4400, 650,500, 600,1600, 600,550, 600,500, 600,1650, 600,500, 600,1600, 650,1600 , 600 1600, 600 550, 600 1600, 600 1600, 650 500, 600 1600, 650 1600, 600 500, 600 550, 600 1600, 600 550, 600 500, 600 550, 600 500, 600 550, 600 500, 600 1600, 650 500 , 600 1600, 600 1650, 600 1600, 600 1650, 600 1600, 600 1600, 600} ; // NEC 4BB640BF // Insertion du signal RAW IR pour "Receiver Source CBL/SAT" unsigned int irRECsrc[] ={8950,4450, 600,500, 600,1650, 600,500, 600,500, 600,1650, 600,500, 600,1600, 600 ,1650, 600,1600, 600,550, 600,1600, 600,1650, 600,500, 600,1600, 600,1650, 600,500, 600,500, 600,1650, 600,1600, 600,1650, 600,500, 600,500, 600,500, 650,500 , 600 1600, 600 500, 600 550, 600 500, 600 1600, 600 1650, 600 1600, 600 1650, 600} ; // NEC 4BB6708F// Crée une instance du serveur// spécifie le port sur lequel écouter comme argumentWiFiServer server(80);void setup() { Serial.begin(115200); retard(10) ; irsend.begin(); // Connexion au réseau WiFi Serial.println(); Serial.println(); Serial.print("Connexion à "); Serial.println(ssid); WiFi.begin(ssid, mot de passe); while (WiFi.status() !=WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi Connecté"); // Démarrer le serveur server.begin(); Serial.println("Serveur HTTP démarré"); // Imprimer l'adresse IP Serial.print("Adresse IP :"); Serial.println(WiFi.localIP()); if (mdns.begin("IRBlasterLR", WiFi.localIP())) { Serial.println("MDNS Responder Started"); } Serial.println(); Serial.println();}void loop() { // Vérifie si un client s'est connecté WiFiClient client =server.available(); if (!client) { return; } // Attendez que le client envoie des données Serial.println("nouveau client"); while(!client.available()){ delay(1); } // Lit la première ligne de la requête String req =client.readStringUntil('\r'); Serial.println(req); client.flush(); // Correspond à la requête if (req.indexOf("/irTVpwr") !=-1){ irsend.sendRaw(irTVpwr, sizeof(irTVpwr) / sizeof(irTVpwr[0]), khz); Serial.println("IRreq irTVpwr envoyé"); } else if (req.indexOf("/irTVsrc") !=-1){ irsend.sendRaw(irTVsrc, sizeof(irTVsrc) / sizeof(irTVsrc[0]), khz); Serial.println("IRreq irTVsrc envoyé"); } else if (req.indexOf("/irTVmute") !=-1){ irsend.sendRaw(irTVmute, sizeof(irTVmute) / sizeof(irTVmute[0]), khz); Serial.println("IRreq irTVmute envoyé"); } else if (req.indexOf("/irTVvdn") !=-1){ irsend.sendRaw(irTVvdn, sizeof(irTVvdn) / sizeof(irTVvdn[0]), khz); Serial.println("IRreq irTVvdn envoyé"); } else if (req.indexOf("/irTVvup") !=-1){ irsend.sendRaw(irTVvup, sizeof(irTVvup) / sizeof(irTVvup[0]), khz); Serial.println("IRreq irTVvup envoyé"); } else if (req.indexOf("/irTVchup") !=-1){ irsend.sendRaw(irTVchup, sizeof(irTVchup) / sizeof(irTVchup[0]), khz); Serial.println("IRreq irTVchup envoyé"); } else if (req.indexOf("/irTVchdn") !=-1){ irsend.sendRaw(irTVchdn, sizeof(irTVchdn) / sizeof(irTVchdn[0]), khz); Serial.println("IRreq irTVchdn envoyé"); } else if (req.indexOf("/irALLpwr") !=-1){ irsend.sendRaw(irRECpwrON, sizeof(irRECpwrON) / sizeof(irRECpwrON[0]), khz); irsend.sendRaw(irTVpwr, sizeof(irTVpwr) / sizeof(irTVpwr[0]), khz); retard (2000); irsend.sendRaw(irRECsrc, sizeof(irRECsrc) / sizeof(irRECsrc[0]), khz); Serial.println("IRreq irALLpwr envoyé"); } else if (req.indexOf("/irRECpwr") !=-1){ irsend.sendRaw(irRECpwr, sizeof(irRECpwr) / sizeof(irRECpwr[0]), khz); Serial.println("IRreq irRECpwr envoyé"); } else if (req.indexOf("/irRECpwrON") !=-1){ irsend.sendRaw(irRECpwrON, sizeof(irRECpwrON) / sizeof(irRECpwrON[0]), khz); Serial.println("IRreq irRECpwrON envoyé"); } else if (req.indexOf("/irRECpwrOFF") !=-1){ irsend.sendRaw(irRECpwrOFF, sizeof(irRECpwrOFF) / sizeof(irRECpwrOFF[0]), khz); Serial.println("IRreq irRECpwrOFF envoyé"); } else if (req.indexOf("/irRECmute") !=-1){ irsend.sendRaw(irRECmute, sizeof(irRECmute) / sizeof(irRECmute[0]), khz); Serial.println("IRreq irRECmute envoyé"); } else if (req.indexOf("/irRECvdn") !=-1){ irsend.sendRaw(irRECvdn, sizeof(irRECvdn) / sizeof(irRECvdn[0]), khz); Serial.println("IRreq irRECvdn envoyé"); } else if (req.indexOf("/irRECvup") !=-1){ irsend.sendRaw(irRECvup, sizeof(irRECvup) / sizeof(irRECvup[0]), khz); Serial.println("IRreq irRECvup envoyé"); } else { Serial.println("requête invalide"); client.stop(); retourner; } client.flush(); // Envoie la réponse au client //client.print(s); client.print("HTTP/1.1 200 OK\r\n"); retard(1) ; Serial.println("Client déconnecté"); Serial.println(); // Le client sera réellement déconnecté // lorsque la fonction revient et que l'objet 'client' est détruit}
script.jsJavaScript
javascript pour webapp (nécessite jquery)
// Fonction pour envoyer des commandes IRfunction buttonClick(clicked_id){ if (clicked_id =="irTVpwr"){ $.get( "curl.php", { room:"192.168.1.62" , bouton :"irTVpwr"} ); } if (clicked_id =="irTVsrc"){ $.get( "curl.php", { room :"192.168.1.62", bouton :"irTVsrc"} ); } if (clicked_id =="irTVmute"){ $.get( "curl.php", { room:"192.168.1.62", bouton:"irTVmute"} ); } if (clicked_id =="irTVvdn"){ $.get( "curl.php", { room :"192.168.1.62", bouton :"irTVvdn"} ); } if (clicked_id =="irTVvup"){ $.get( "curl.php", { room :"192.168.1.62", bouton :"irTVvup"} ); } if (clicked_id =="irTVchup"){ $.get( "curl.php", { room :"192.168.1.62", bouton :"irTVchup"} ); } if (clicked_id =="irTVchdn"){ $.get( "curl.php", { room :"192.168.1.62", bouton :"irTVchdn"} ); } if (clicked_id =="irRECpwr"){ $.get( "curl.php", { room :"192.168.1.62", bouton :"irRECpwr"} ); } if (clicked_id =="irALLpwr"){ $.get( "curl.php", { room :"192.168.1.62", bouton :"irALLpwr"} ); } if (clicked_id =="irRECpwrON"){ $.get( "curl.php", { room:"192.168.1.62", bouton:"irRECpwrON"} ); } if (clicked_id =="irRECpwrOFF"){ $.get( "curl.php", { room:"192.168.1.62", bouton:"irRECpwrOFF"} ); } if (clicked_id =="irRECmute"){ $.get( "curl.php", { room:"192.168.1.62", bouton:"irRECmute"} ); } if (clicked_id =="irRECvdn"){ $.get( "curl.php", { room :"192.168.1.62", bouton :"irRECvdn"} ); } if (clicked_id =="irRECvup"){ $.get( "curl.php", { room :"192.168.1.62", bouton :"irRECvup"} ); } }
index.htmlHTML
HTML de base pour afficher les boutons.
 



TV





     


Récepteur





curl.phpPHP
script php pour envoyer des requêtes GET à ESP8266
manifest.jsonJSON
Cela permettra à la page Web de s'exécuter en tant qu'application Web native dans Android.
{ "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":"plein écran", "orientation":"portrait"}

Schémas

Rien de vraiment à cela, juste l'alimentation et une seule broche connectée à un transistor NPN pour piloter deux LED IR en série (sans résistances) hors de la tension d'alimentation 5v. Si vous souhaitez utiliser des LED supplémentaires ou avez besoin de moins de chute de tension à partir d'une source de 3,3 v, vous pouvez utiliser un NPN transistor pour commuter plusieurs transistors PNP (un par LED). Cette configuration vous permet d'utiliser autant de LED que votre tension de source peut fournir du courant.

Processus de fabrication

  1. Les lacunes du WiFi RTLS
  2. Nouvelle technologie sans fil 2015
  3. Comment installer un serveur Web intégré sécurisé sur un appareil Wi-Fi à 3 $
  4. Communication MQTT entre NodeMCU et Raspberry Pi 3 B+
  5. ROBOT WIFI RASPBERRY PI CONTRLÉ À PARTIR D'UN SMARTPHONE ANDROID
  6. Robot contrôlé par Wi-Fi utilisant Raspberry Pi
  7. Qu'est-ce que les appels Wi-Fi ? Comment ça marche ?
  8. Serrure de porte intelligente utilisant la page de connexion WiFi par Arduino et ESP8266
  9. Qu'est-ce qu'un Bead Blast ? Guide du grenaillage