J’ai récemment acheté un système d’éclairage LED avec télécommande Radio Fréquence. Celle-ci est composée de quatre boutons (On/Off, Synchronisation, Couleur, Variation). Nous allons voir comment connecter facilement cette télécommande à notre réseau et ainsi la piloter par n’importe quelle interface (iPhone dans mon cas).

Pour cela, nous allons utiliser un Arduino avec Shield Ethernet (j’ai choisi l’ENC28J60 car moins cher que l’officiel) et un composant électronique l’ULN2803 (merci Michaël pour la découverte).
Le principe et de programmer l’Arduino pour qu’il écoute les requêtes HTTP. S’il reçoit un GET qu’il comprend, alors il change l’état d’une de ses sorties. L’ULN2803, permet de simuler l’appui sur une touche de la télécommande en reliant la partie soudée à la masse (c.f. schéma).
Ainsi, avec un simple GET (exemple : http://192.168.1.11/bouton/onoff) vers l’Arduino, je suis capable d’allumer l’éclairage LED. En associant plusieurs combinaisons de touches et actions successives, je peux également choisir la couleur ou le mode de variation.

Schéma de l’ULN2803

Schéma de câblage

Les tests de câblage

Le résultat

Code source Arduino

#define DEBUG 1
//#undef DEBUG
#include "EtherCard.h"
 
#define pinO 2
#define pinC 3
#define pinP 4
 
boolean actionOnoff = false;
boolean actionBlanc = false;
boolean actionBleu = false;
boolean actionVert = false;
boolean actionJaune = false;
boolean actionRouge = false;
boolean actionViolet = false;
boolean actionVariation1 = false;
boolean actionVariation2 = false;
boolean actionVariation3 = false;
boolean actionVariation4 = false;
 
 
 
// please modify the following three lines. mac and ip have to be unique
// in your local area network. You can not have the same numbers in
// two devices:
 
static uint8_t mymac[6] = { 0x00,0x24,0x41,0x55,0xF6,0x53 };
//Static IP
//static byte myip[] = { 192,168,1,114 };
//static byte gwip[] = { 192,168,1,1 };
//
#define MYWWWPORT 80   
 
#define BUFFER_SIZE 700
byte Ethernet::buffer[BUFFER_SIZE];
BufferFiller bfill;
 
#define STR_BUFFER_SIZE 22
static char strbuf[STR_BUFFER_SIZE+1];
 
uint16_t http200ok(void)
{
  bfill = ether.tcpOffset();
  bfill.emit_p(PSTR(
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Pragma: no-cache\r\n"
    "\r\n"));
  return bfill.position();
}
 
// prepare the webpage by writing the data to the tcp send buffer
uint16_t print_webpage()
{
  bfill = ether.tcpOffset();
  bfill.emit_p(PSTR(
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Pragma: no-cache\r\n"
    "\r\n"
    "<html><body>Invalid option selected</body></html>"));
  return bfill.position();
}
 
 
#define CMDBUF 50
 
int16_t process_request(char *str)
{
  int8_t r=-1;
  int8_t i = 0;
  char clientline[CMDBUF];
  int index = 0;
  int plen = 0;
 
#ifdef DEBUG
  Serial.println( str );
#endif
 
  char ch = str[index];
  while( ch != ' ' && index < CMDBUF) {
    clientline[index] = ch;
    index++;
    ch = str[index];
  }
  clientline[index] = '\0';
 
 
 
  #ifdef DEBUG
  Serial.println( clientline );
  #endif
 
  // convert clientline into a proper
  // string for further processing
  String urlString = String(clientline);
 
 
  if(urlString == "/bouton/onoff"){
    Serial.println("Action ON/OFF");
    actionOnoff=true;
  }
  if(urlString == "/couleur/blanc"){
    Serial.println("Action BLANC");
    actionBlanc=true;
  }
  if(urlString == "/couleur/bleu"){
    Serial.println("Action BLEU");
    actionBleu=true;
  }
  if(urlString == "/couleur/vert"){
    Serial.println("Action VERT");
    actionVert=true;
  }
  if(urlString == "/couleur/jaune"){
    Serial.println("Action Jaune");
    actionJaune=true;
  }
  if(urlString == "/couleur/rouge"){
    Serial.println("Action ROUGE");
    actionRouge=true;
  }
  if(urlString == "/couleur/violet"){
    Serial.println("Action VIOLET");
    actionViolet=true;
  }
  if(urlString == "/mode/variation1"){
    Serial.println("Action VARIATION1");
    actionVariation1=true;
  }
  if(urlString == "/mode/variation2"){
    Serial.println("Action VARIATION2");
    actionVariation2=true;
  }
  if(urlString == "/mode/variation3"){
    Serial.println("Action VARIATION3");
    actionVariation3=true;
  }
  if(urlString == "/mode/variation4"){
    Serial.println("Action VARIATION4");
    actionVariation4=true;
  }
  return( http200ok() );
}
 
 
void setup(){
  pinMode(pinO, OUTPUT);
  pinMode(pinC, OUTPUT);
  pinMode(pinP, OUTPUT);
 
  Serial.begin(9600);
  Serial.println("ENC28J60/Nanode/EtherCard RESTduino");
 
  if (ether.begin(sizeof Ethernet::buffer, mymac, 10) == 0) {
    //#ifdef DEBUG
    Serial.println( "Failed to access Ethernet controller");
    //#endif
    while(1);
  }
 
  // Setup static IP address
  //ether.staticSetup(myip, gwip);
 
   if (!ether.dhcpSetup())
    Serial.println("DHCP failed");
 
  ether.printIp("My IP: ", ether.myip);
  ether.printIp("Netmask: ", ether.mymask);
  ether.printIp("GW IP: ", ether.gwip);
  ether.printIp("DNS IP: ", ether.dnsip);
 
  #ifdef DEBUG
  Serial.println("Ready");
  #endif
 
}
 
void loop(){
  uint16_t plen, dat_p;
  int8_t cmd;
 
  while(1) {
    // read packet, handle ping and wait for a tcp packet:
   dat_p=ether.packetLoop(ether.packetReceive());
 
    /* dat_p will be unequal to zero if there is a valid http get */
    if(dat_p==0){
      // no http request
      continue;
    }
 
    // tcp port 80 begin
    if (strncmp("GET ",(char *)&(Ethernet::buffer[dat_p]),4)!=0){
      // head, post and other methods:
      dat_p = print_webpage();
      goto SENDTCP;
    }
 
    // just one web page in the "root directory" of the web server
    if (strncmp("/ ",(char *)&(Ethernet::buffer[dat_p+4]),2)==0){
      #ifdef DEBUG
      Serial.println("GET / request");
      #endif
      dat_p = print_webpage();
      goto SENDTCP;
    }
    dat_p = process_request((char *)&(Ethernet::buffer[dat_p+4]));
 
SENDTCP:
      if( dat_p )
        ether.httpServerReply( dat_p);
 
       //Partie Action 
       if(actionOnoff==true){
         digitalWrite(pinO, HIGH);   
         delay(300);              
         digitalWrite(pinO, LOW);
         actionOnoff=false;
       }
 
       if(actionBlanc==true){
         digitalWrite(pinP, HIGH);   
         delay(300);              
         digitalWrite(pinP, LOW);
         delay(100);
         digitalWrite(pinC, HIGH);   
         delay(300);              
         digitalWrite(pinC, LOW);
         actionBlanc=false;
       } 
 
       if(actionBleu==true){
         digitalWrite(pinP, HIGH);   
         delay(300);              
         digitalWrite(pinP, LOW);
         for(int i=1;i<=2;i++){
           delay(100);
           digitalWrite(pinC, HIGH);   
           delay(300);              
           digitalWrite(pinC, LOW);
         }
         actionBleu=false;
       }
 
       if(actionVert==true){
         digitalWrite(pinP, HIGH);   
         delay(300);              
         digitalWrite(pinP, LOW);
         for(int i=1;i<=6;i++){
           delay(100);
           digitalWrite(pinC, HIGH);   
           delay(300);              
           digitalWrite(pinC, LOW);
         }
         actionVert=false;
       }
 
       if(actionJaune==true){
         digitalWrite(pinP, HIGH);   
         delay(300);              
         digitalWrite(pinP, LOW);
         for(int i=1;i<=8;i++){
           delay(100);
           digitalWrite(pinC, HIGH);   
           delay(300);              
           digitalWrite(pinC, LOW);
         }
         actionJaune=false;
       }
 
       if(actionRouge==true){
         digitalWrite(pinP, HIGH);   
         delay(300);              
         digitalWrite(pinP, LOW);
         for(int i=1;i<=10;i++){
           delay(100);
           digitalWrite(pinC, HIGH);   
           delay(300);              
           digitalWrite(pinC, LOW);
         }
         actionRouge=false;
       }
 
       if(actionViolet==true){
         digitalWrite(pinP, HIGH);   
         delay(300);              
         digitalWrite(pinP, LOW);
         for(int i=1;i<=11;i++){
           delay(100);
           digitalWrite(pinC, HIGH);   
           delay(300);              
           digitalWrite(pinC, LOW);
         }
         actionViolet=false;
       }
 
       if(actionVariation1==true){
         digitalWrite(pinC, HIGH);   
         delay(300);              
         digitalWrite(pinC, LOW);
         delay(100);
         digitalWrite(pinP, HIGH);   
         delay(300);              
         digitalWrite(pinP, LOW);
         actionVariation1=false;
       }
 
       if(actionVariation2==true){
         digitalWrite(pinC, HIGH);   
         delay(300);              
         digitalWrite(pinC, LOW);
         for(int i=1;i<=2;i++){
           delay(100);
           digitalWrite(pinP, HIGH);   
           delay(300);              
           digitalWrite(pinP, LOW);
         }
         actionVariation2=false;
       }
 
       if(actionVariation3==true){
         digitalWrite(pinC, HIGH);   
         delay(300);              
         digitalWrite(pinC, LOW);
         for(int i=1;i<=3;i++){
           delay(100);
           digitalWrite(pinP, HIGH);   
           delay(300);              
           digitalWrite(pinP, LOW);
         }
         actionVariation3=false;
       }
 
       if(actionVariation4==true){
         digitalWrite(pinC, HIGH);   
         delay(300);              
         digitalWrite(pinC, LOW);
         for(int i=1;i<=4;i++){
           delay(100);
           digitalWrite(pinP, HIGH);   
           delay(300);              
           digitalWrite(pinP, LOW);
         }
         actionVariation4=false;
       }    
  }
}

Résultat final (avant et après)

Bien sûr, ce principe s’applique à tout système composé de boutons. On peut alors facilement contrôler sa porte de garage, son alarme, ou tout autre objet.
Ça en devient presque trop facile ;-)