La domotique chez soi

Je rentre d’une journée de boulot, et fatigué, je m’affale comme une masse dans le canapé. Me vient alors l’envie de regarder un épisode de ‘ The Big Bang Theory ‘, je sors donc mon portable, démarre l’application DomoGJ et appuie sur quelques boutons. Le volet se ferme, les lumières s’éteignent, le vidéo projecteur s’allume, la toile descend lentement et majestueusement, et l’épisode commence enfin. Bienvenue chez moi :-D

Et pour pas trop cher ! Je détaillerai dans cet article le prix, la liste des composants, où acheter, et enfin je donnerai mes sources. Comme d’habitude, ce projet s’articule autour de modules arduino et un peu d’Android. Le but étant de contrôler les lumières, les volets, tout ce qui vous passe par la tête en fait, à l’aide de votre portable Android – en réalité n’importe quel matériel qui a accès à internet – , où que vous soyez (Chez vous, ou pas :p).

Attention cependant avant de vous mettre à l’ouvrage, on touche à l’électricité, et on ne rigole pas avec ça, je ne suis pas responsable de l’utilisation que vous faites de ce tutorial. Veillez à toujours travailler avec le courant coupé.

Comment ça marche ? Un petit schéma :

Votre portable est donc connecté en wifi à votre box, ou bien à l’internet. Il appelle une URL qui passe par votre box et ai redirigé vers le module arduino connecté en ethernet. L’URL contient des variables, que l’arduino va extraire et analyser pour envoyer un signal RF dans tout l’appartement. Celui-ci va être capté par tous les modules arduino, et bien entendu seulement un seul d’entre eux est concerné, il va donc appliquer les ordres, généralement activer – ou pas – un relais. Un relais nous sert d’interrupteurs. En faisant passer un petit courant d’un côté (5v, celui de l’arduino), il laisse passer de l’autre les 220v du gros courant servant à allumer nos ampoules.

Module Principal

J’appelle module principal la partie connectée à votre box. Elle se compose des éléments suivant :

  • Arduino (Version Uno, pro, nano, vous avez l’embarras du choix, il y en a pour une vingtaine d’euros. Personnellement, je n’utilise pas les boards officiels, j’assemble tout à la main, c’est beaucoup moins cher (8 euros en tout), lire le tutorial)
  • Shield Ethernet pour Arduino (Vous pouvez acheter l’officiel, à 31 euros, ou bien celui que j’ai utilisé, à 15 euros, mais attention, peu de documentation et le code est un peu… Obscur ! Si j’avais à le refaire je prendrai l’officiel. On peut également ajouter qu’il est possible d’avoir un module arduino avec l’ethernet intégré, mais jamais testé)
  • RF Link Transmitter 434MHz (Pour envoyer sans fil les informations aux autres modules, chez Sparkfun à moins de $4 !!!)

Ce qui nous fait un total de 35 euros avec les composants que j’ai utilisé, on peut descendre à 25 en utilisant les solutions les moins coûteuse.

(Promettez-moi de ranger tout ça dans une belle boite, j’ai tout caché dans une boite noire qui s’accorde bien avec ma freebox Revolution :) )

Pour le cablage, il y a quatre pins sur l’émtteur RF : 5v, GND, Data et l’antenne. Dans le programme qui suivra, le pin Data est connecté au pin 3 de l’arduino.

Maintenant, il va falloir choisir comment on va envoyer les ordres aux différents récepteur. Comme décrit un peu plus tôt dans l’article, on utilise des URL dans lesquelles on fait passer des variables. Un ordre typique de mon installation :

http://(IP de l’arduino)/?cmd=A-1-3-0

La commande est «  A-1-3-0 « . Nous verrons un peu plus tard ce que celle-ci signifie, pour le moment notre module principal a juste à récupérer cette commande et à l’envoyer grâce à son module RF. Le code arduino :

#include <VirtualWire.h>
#include <etherShield.h>
#undef int
#undef abs
#undef double
#undef float
#undef round
 
static uint8_t mymac[6] = {0x54,0x55,0x58,0x10,0x00,0x24};
static uint8_t myip[4] = {192,168,1,15};
static char baseurl[]="http://192.168.1.15/";
static uint16_t mywwwport =80;
 
#define BUFFER_SIZE 500
#define STR_BUFFER_SIZE 40
static uint8_t buf[BUFFER_SIZE+1];
static char strbuf[STR_BUFFER_SIZE+1];
int cool = 0;
int tprecedent = 0;
 
EtherShield es=EtherShield();
uint16_t print_webpage(uint8_t *buf);
 
void setup(){
  Serial.begin(9600);
  vw_set_ptt_inverted(true);
  vw_setup(2000);
  vw_set_tx_pin(3);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  es.ES_enc28j60Init(mymac);
  es.ES_enc28j60clkout(2);
  delay(10);
  es.ES_enc28j60PhyWrite(PHLCON,0x880);
  delay(500);
  es.ES_enc28j60PhyWrite(PHLCON,0x990);
  delay(500);
  es.ES_enc28j60PhyWrite(PHLCON,0x880);
  delay(500);
  es.ES_enc28j60PhyWrite(PHLCON,0x990);
  delay(500);
  es.ES_enc28j60PhyWrite(PHLCON,0x476);
  delay(100);
  es.ES_init_ip_arp_udp_tcp(mymac,myip,80);
}
 
void loop(){
  uint16_t plen, dat_p;
  plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);
  if(plen!=0){
    if(es.ES_eth_type_is_arp_and_my_ip(buf,plen)){
      es.ES_make_arp_answer_from_request(buf);
      return;
    }
 
    if(es.ES_eth_type_is_ip_and_my_ip(buf,plen)==0){
      return;
    }
 
    if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V){
      es.ES_make_echo_reply_from_request(buf,plen);
      return;
    }
    if (buf[IP_PROTO_P]==IP_PROTO_TCP_V&&buf[TCP_DST_PORT_H_P]==0&&buf[TCP_DST_PORT_L_P]==mywwwport){
      if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V){
        es.ES_make_tcp_synack_from_syn(buf);
        return;
      }
      if(buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){
        es.ES_init_len_info(buf);
        dat_p=es.ES_get_tcp_data_pointer();
        if (dat_p==0){
          if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){
            es.ES_make_tcp_ack_from_any(buf);
          }
          return;
        }
        if (strncmp("GET ",(char *)&(buf[dat_p]),4)!=0){
          plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>200 OK</h1>"));
          goto SENDTCP;
        }
        if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){
          plen=print_webpage(buf);
          goto SENDTCP;
        }
 
         find_key_val((char *)&(buf[dat_p+5]),"cmd");
 
         if(cool == 1){
           plen=print_webpage(buf);
         }
         lum(strbuf);
 
         if(strbuf[0] == 'B'){ //Autre module qui contrôle directement des octocoupleurs faisant le lien entre l'arduino et une télécommande
           if(strbuf[2] == '0'){
             digitalWrite(4, HIGH);
             delay(100);
             digitalWrite(4, LOW);
           }else if(strbuf[2] == '1'){
             digitalWrite(5, HIGH);
             delay(100);
             digitalWrite(5, LOW);
           }else if(strbuf[2] == '2'){
             digitalWrite(6, HIGH);
             delay(100);
             digitalWrite(6, LOW);
           }
         }
 
SENDTCP:  es.ES_make_tcp_ack_from_any(buf); // send ack for http get
          es.ES_make_tcp_ack_with_data(buf,plen); // send data
      }
    }
  }
}
 
void lum(const char* test){
  if((millis()-tprecedent) > 200){ //Pour eviter deux ordres à la suite
    const char *msg = test;
    Serial.print(msg);
    Serial.println();
    vw_send((uint8_t *)msg, strlen(msg));
    vw_wait_tx();
    tprecedent = millis();
  }
}
 
uint8_t find_key_val(char *str,char *key){
  uint8_t found=0;
  uint8_t i=0;
  char *kp;
  kp=key;
  while(*str &&  *str!=' ' && found==0){
    if (*str == *kp){
      kp++;
      if (*kp == '\0'){
        str++;
        kp=key;
        if (*str == '='){
          found=1;
        }
      }
    }else{
      kp=key;
    }
    str++;
  }
  if (found==1){
    while(*str &&  *str!=' ' && *str!='&' && i<STR_BUFFER_SIZE){
      strbuf[i]=*str;
      i++;
      str++;
    }
    strbuf[i]='\0';
  }
  cool = 1;
  return(found);
}
 
uint16_t print_webpage(uint8_t *buf){
  uint16_t plen;
  plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<form METHOD=get action=\""));
  plen=es.ES_fill_tcp_data(buf,plen,baseurl);
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("\">"));
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<input type=submit name=\"cmd\" value=\"A-6-2-0-3-0-4-0-5-0-6-0-7-0\"><br><br>"));
  plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("</form>"));
 
  return(plen);
 }

 

Module Exécutant

J’appelle module exécutant les modules qui vont recevoir les ordres du module principal et les appliquer. Les modules exécutant peuvent être très différent suivant ce que vous voulez contrôler, mais en général ils sont composés de :

  • Arduino (Toujours le même, vous pouvez peut-être envisager la version nano, ou pro mini, car ils auront souvent à se loger dans un petit endroit, à côté de l’interrupteur)
  • RF Link Receiver 434MHz (Il s’agit cette fois-ci du récepteur RF, toujours chez Sparkfun à moins de $5 !)
  • Relais (Ils vont jouer le rôle de commutateur entre le faible courant de l’arduino et le 220v de votre électricité, j’utilise ces modèles là, il y’en a deux par shield et ils coûtent environ 4 euros)
  • Convertisseur 220v -> 5v (Pour alimenter l’arduino ! Je ne l’ai pas compté dans le module principale, car je sous entends qu’il est à côté de votre box disposant de port usb 5v, on en trouve pour 2 euros sur ebay)
  • De l’électronique diverses ! Je ne vais pas détailler mais n’oubliez pas que si vous ajoutez une commande à distance, il est préférable de laisser un interrupteur physique ! Imaginez qu’il vous arrive quelque chose dans votre appartement et que les gens voulant vous sauver perdent du temps à trouver la lumière… A méditer je crois ;)

Le prix total, et bien par exemple avec un module qui permet de gérer six lumières, le prix est de 30 euros (8 + (4*3) + 4 + Divers). Un module pour gérer un volet électrique vous coûtera 18 euros environ.

Un de mes modules de lumières (J’ai également refait le panneau des interrupteurs, un peu dans le genre cokpit d’avion) :

J'utilisais des leds pour symboliser les lumières, pendant les tests :)

 

Mes anciens interrupteurs... Pas très sexy (Le mur non plus d'ailleurs, la peinture m'attends depuis un bon bout de temps)

Pour rentrer tout ça là-dedans, je ne vous raconte pas la galère

Passons à la programmation ! Souvenez-vous en, on parlait tou à l’heure d’une commande envoyé par le module principal, qui ressemblait à :

A-1-3-0

Le A signifie que je souhaite exécuter l’ordre A (Ooooh… :p), et celui-ci dans mon cas commande 6 ampoules. Le 1 qui vient juste après nous donne le nombre d’instructions (Ici, 1 :p). Je lui dit donc de mettre à l’état 0 l’ampoule n°3. On pourrait traduire par : (Identifiant de l’ordre)-(Nombre d’instructions)-(Identifiant de l’amoule)-(Etat). Et on peut ajouter d’autres ordres ! Comme par exemple :

A-3-3-1-5-0-6-1 = Il y a trois ordres, allume l’ampoule 3, éteint l’amoule 5, allume l’ampoule 6.

Le code arduino (A adapter suivant vos modules, bien entendu) :

#include <VirtualWire.h>
#undef int
#undef abs
#undef double
#undef float
#undef round
 
int etat[] = {0, 0, 0, 0, 0, 0};
int diff[] = {0, 0, 0, 0, 0, 0};
 
int timer = 0;
int ancien = 0;
 
void setup(){
  Serial.begin(9600);
  vw_set_ptt_inverted(true);
  vw_setup(2000);
  vw_set_rx_pin(8);
  vw_rx_start();
 
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
 
  pinMode(12, INPUT); //Switch ON/OFF
  pinMode(9, OUTPUT); //LED
  pinMode(10, OUTPUT); //LED
 
  pinMode(14, INPUT); //Lumiere
  pinMode(15, INPUT); //Lumiere
  pinMode(16, INPUT); //Lumiere
  pinMode(17, INPUT); //Lumiere
  pinMode(18, INPUT); //Lumiere
  pinMode(19, INPUT); //Lumiere
 
  digitalWrite(12, HIGH); //Switch ON/OFF
 
  digitalWrite(14, HIGH); //Lumiere
  digitalWrite(15, HIGH); //Lumiere
  digitalWrite(16, HIGH); //Lumiere
  digitalWrite(17, HIGH); //Lumiere
  digitalWrite(18, HIGH); //Lumiere
  digitalWrite(19, HIGH); //Lumiere
}
 
void loop(){
  digitalWrite(9,HIGH);
  if(digitalRead(12) == LOW){
    timer = 0;
    digitalWrite(10,LOW);
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;
    char msg[30] = "";
    //vw_wait_rx();
    if(vw_get_message(buf, &buflen)){
      digitalWrite(10,HIGH);
      for (int i=0;i<buflen;i++) msg[i]=0; {
        int i;
        char vBuff[30] = "";
        for (int i = 0; i < buflen; i++){
          msg[i] = buf[i];
        }
        strcat(vBuff, msg);
 
        Serial.print(vBuff);
 
        const char separator[] = "-";
        char* accum;
        char* lecture = strtok_r(vBuff,separator,&accum);
 
        Serial.println();
        if(lecture[0] == 'A'){
          lecture = strtok_r(NULL,separator,&accum);
          int nbr = atoi(lecture);
          if(nbr == 10){
            int tps = atoi(strtok_r(NULL,separator,&accum));
            dodo(tps);
          }else{
            for(int j = 0; j < nbr; j++){
              int pin = atoi(strtok_r(NULL,separator,&accum));
              if(atoi(strtok_r(NULL,separator,&accum))){
                digitalWrite(pin, HIGH);
              }else{
                digitalWrite(pin, LOW);
              }
            }
          }
        }
      }
    }else{
      digitalWrite(10,LOW);
    }
    for(int k=14; k<20; k++){
 
      if(digitalRead(k) == HIGH && etat[k-14] == 0){
        diff[k-14] = 1;
      }else if(digitalRead(k) == LOW && etat[k-14] == 1){
        diff[k-14] = 1;
      }
 
      if(diff[k-14] == 1){
        digitalWrite(10,HIGH);
        delay(5);
        if(digitalRead(k) == HIGH){
          digitalWrite((k-12),LOW);
        }else{
          digitalWrite((k-12),HIGH);
        }
        digitalWrite(10,LOW);
        diff[k-14] = 0;
      }
 
      if(digitalRead(k) == HIGH){
        etat[k-14] = 1;
      }else{
        etat[k-14] = 0;
      }
    }
  }else{
    digitalWrite(10,LOW);
    if(timer == 10){
      for(int k=2; k<8; k++){
        digitalWrite(k,LOW);
      }
    }else{
      timer++;
      digitalWrite(10, HIGH);
      delay(20);
      digitalWrite(10, LOW);
      delay(980);
    }
  }
  delay(50);
}
 
void dodo(int time){
  ancien = millis();
  for(int i = 0; i<time; i++){
    digitalWrite(10, HIGH);
    delay(20);
    digitalWrite(10, LOW);
    delay(980);
  }
  for(int k=2; k<8; k++){
    digitalWrite(k,LOW);
  }
}

(Il y a dans ce code des choses  » en plus « , c’est à dire un switch qui mis sur off me laisse 10 secondes de lumières avant de tout éteindre (Très pratique quand on s’absente), ainsi qu’une fonction  » dodo « , l’ordre ressemble à  » A-10-30 « , où 10 signifie que l’on veut la fonction dodo, et 30 le nombre de secondes au bout duquel les lumières s’éteignent, ça laisse le temps de poser le portable et de se glisser dans son lit)

Module Android

Cette partie là est plutôt cadeau, il n’y a rien de vraiment très compliqué, il faut juste sous Android créer une application avec des boutons qui appellent des url. Libre à vous de vous compliquer un peu la tâche avec une interface graphique (Ce que je n’ai toujours pas eu le temps de faire :p).

L'appli magique, au look révolutionnaire.

 

Test lointain d'une interface graphique, que je finirai bien un jour, notez le système de navigation par onglet :)

 

Cet article touche à sa fin, j’ajouterai que bien entendu, les ajouts et variantes existent par millier, on peut imaginer le volet qui s’ouvre et se ferme en fonction de l’heure, de même même pour les lumières, et vous pouvez ajouter plein d’autre module, la toile du vidéo projecteur qui descend du plafond dans mon cas, gérer les télécommandes infrarouges pour allumer certain périphérique, la gestion d’une lumière multicolore, toujours bien pour les ambiances, la machine à café du matin, les arrosages automatique, etc. Tout cela pour une somme relativement modique, j’en ai eu pour 50 euros avec mon petit appartement (Bon, et quand même pas mal de temps de bricolage). Si il y a des câbles partout chez vous, le prix est dégressif :)

On pourrait aussi imaginer un système qui en plus de donner des ordres puisse nous donner l’état des lumières (Allumés, éteintes), pour par exemple pouvoir tout éteindre ou nous prévenir si vous êtes à plus de 50m de votre appartement. Tout est possible, et je vous souhaite vraiment d’avoir le temps de développer tout ça :)

Pour ceux qui se poserait la question, mon système est en place depuis plusieurs mois dans mon appartement, et depuis qu’il est en route il n’y a eu aucun soucis ! N’hésitez pas à commenter si vous avez des questions ;)

7 réflexions au sujet de « La domotique chez soi »

  1. Félicitation pour votre projet. J’ai l’intention de faire à peu près la même chose que vous, cependant un détail me gène quand a l’envoi d’ordre par internet.
    Le fait de passer des commandes dans une URL reste très dangereux. il suffit qu’on connaisse votre IP et toute votre maison va alors se mettre à s’allumer, s’éteindre. Avez vous trouvé un moyen de sécuriser cela.

    • Alors ce n’est pas forcément  » par internet « , ce que je décris ici (Même si cela se passe par url) reste local (Il faut être connecté en wifi à la box). Actuellement, mon système fonctionne également par internet, mais je passe par un serveur dédié avec un système de session ;)

  2. Génial le montage. Je cherche depuis un moment un montage de ce type . Merci pour les codes Arduino, en revanche je bloque un peu sur l’application sous Android. Est il possible de mettre en ligne l’application Android ?

    Merci et bravo pour le travail réalisé

  3. Bonjour GJ vraiment grand merci pour ce tuto très très utile je suis très content des tutos du genre sont rarissimes de nos jours.
    Par contre j’ai une question en terme de coût et autre est-il possible de mettre au point le même système en utilisant une carte arduino uno et un module wifi.De ce fait on pourra interagir directement avec la carte arduino à l’aide d’un portable sous android par exemple qui enverra directement les données via l’adresse de la carte arduino.Ainsi on aurra plus besoin du module principale

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>