Télécommande universelle avec arduino. 1 – Mise en place du serveur

Introduction

Voici le premier billet d’une série (qui pourrait ne pas aboutir, si je n’arrive pas à faire ce que je veux) dans laquelle j’essaierai de construire une télécommande universelle.
Le principe est le suivant : j’aimerais me passer de toutes mes télécommandes à infrarouge. Parce que c’est encombrant, qu’il faut parfois recharger les piles, qu’elles risquent de se casser, et aussi parce que j’ai envie de m’amuser. La solution est donc de faire une télécommande universelle. Mais j’aimerais également que cette télécommande puisse être connectée au réseau local, afin d’envoyer des commandes depuis n’importe quel terminal connecté (surtout un smartphone, en fait).
Pour réaliser ce projet, j’ai décidé d’utiliser arduino.
Comme l’indique wikipedia :

Arduino est un circuit imprimé open-source sur lequel se trouve un microcontrôleur (calculateur) qui peut être programmé pour analyser et produire des signaux électriques, de manière à effectuer des tâches très diverses comme la charge de batteries, la domotique (le contrôle des appareils domestique (éclairage, chauffage…)), le pilotage d’un robot

L’avantage d’arduino, c’est que c’est libre (à part le nom), qu’il y a une forte communauté derrière, donc de nombreux exemples de codes et de circuits, qu’on peut brancher un nombre important de composants dessus, et que ça ne nécessite pas d’avoir des connaissances poussées en électronique.

Dans ce premier billet, je vais connecter mon arduino au réseau local, et faire tourner un petit serveur web dessus, afin d’écouter des instructions. Voilà exactement le déroulement des opérations : j’envoie une requête POST sur le serveur (arduino). Cette requête contient deux paramètres : un paramètre « device » permettant d’identifier la télécommande à utiliser, et un autre paramètre « command » identifiant le code à envoyer. En fonction de la télécommande à utiliser, le arduino charge un fichier de configuration (contenu dans une micro sd ; l’ethernet shield, utilisé pour la connexion ethernet, comporte un slot micro sd), et il va chercher la commande à envoyer dans ce fichier de configuration en fonction du paramètre « command ».

Les composants

Les composants dont j’aurai besoin pour ce projet sont :

  • Un module arduino. Pour l’instant, j’ai commandé et reçu ce kit, qui contient un module arduino et différents composants pour pouvoir réaliser ses premiers circuits.
  • Un « ethernet shield », qui est un module qui permet d’ajouter une connexion ethernet à l’arduino. Il a également un slot pour micro sd, ce qui est très pratique. J’en ai commandé un ici, je l’ai également reçu
  • Un émetteur et un récepteur infrarouge. Pour l’instant, je n’ai pas commandé. Le récepteur me servira à détecter les signaux envoyés par une télécommande, et l’émetteur me permettra de les reproduire
  • Une carte micro sd, achetée dans le supermarché du coin

Côté logiciel…

Les créateurs d’arduino ont créé un petit IDE propre à arduino. Pour l’installer sur debian et ses dérivées, il suffit de taper la commande :

apt-get install arduino

C’est tout ce dont on a besoin pour contrôler l’arduino. Il suffit ensuite de le brancher par USB sur le PC, de sélectionner le bon port USB dans Tools > Serial Monitor, et le tour est joué.

Le montage

Pour pouvoir connecter l’arduino au réseau, il faut monter l’ethernet shield sur le arduino. C’est une opération vraiment très simple, étant donné qu’il n’y a qu’à enficher l’ethernet shield dans l’arduino. L’opération est donc réversible. Une fois l’enfichage réalisé, il n’y a plus qu’à faire le programme qui va écouter les commandes.

Le code

Inutile de réinventer la roue, il existe des librairies qui permettent déjà de monter un serveur web. J’ai utilisé webduino pour ce projet.
Voici le code que j’ai utilisé (je l’expliquerai ensuite) :

#include "Ethernet.h"
#include <SPI.h>
#include <SD.h>
#include "WebServer.h"

//Adresse MAC
static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

//adresse ip
static uint8_t ip[] = { 192, 168, 1, 20 };

/* 
  L'url de post -> fail si différente
 */
#define PREFIX "/ws"
WebServer webserver(PREFIX, 80);

File myConfigFile;


String device = "";
String command = "";


/*
 * Parsing des requetes du serveur. La méthode utilisée doit etre le POST.
 * On prend en considération deux parametres : device et command.
 * device indique la telecommande a utiliser, et command la commande a envoyer
 */
void parseRequests(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{

  //on ne traite que les requetes de type POST
  if (type == WebServer::POST)
  {
    bool repeat;
    char name[16], value[16];
    do
    {
      /*
        Permet de continuer tant qu'il y a encore des paramètres
       */
      repeat = server.readPOSTparam(name, 16, value, 16);

      /* 
        Lit le paramètre device
       */
      if (strcmp(name, "device") == 0)
      {
        device = value;
      } 
      /*
        Lit le paramètre command
      */
      else if (strcmp(name, "command") == 0) 
      {
        command = value;
      }
    } while (repeat);
    
    processCommand(device,command);
    
  }

  /* succes */
  server.httpSuccess();

  

}

/*
 * Trouve la commande à envoyer en fonction de sa clé, dans une chaine
 * de caractère où les clés et les valeurs sont séparées par un '=' :
 * cle1=valeur1
 * cle2=valeur2
 *
 */
String findCommand(String str, String command) {
  int i;
  int previous = 0;
  int next = 0;

  for (int i = 0; i < str.length(); i++) {
    //on split aussi selon le caractere '\\n', sinon ça ne marche pas
    if(str.charAt(i) == '=' || str.charAt(i) == '\\n') {
     String s = str.substring(previous, i);
     previous = i + 1;
     if(next == 1) {
        return s; 
     }
     if(s == command) {
        next = 1; 
     }
    }
  }
  
  if(next == 1) {
    return str.substring(previous, i - 1);
  } else {
    return ""; 
  }
}

/*
 * Fonction qui traite les commandes à envoyer
 *
 */
void processCommand(String device, String command) 
{
   long time = millis();
   String fileContent = "";
   //si le device = 1, le fichier de configuration correspondant
   //dans la carte sd est : 1.cnf
   String filename = device + ".cnf";
   Serial.println("Lecture du fichier " + filename + ". Commande : " + command );
   char buf[filename.length()];
   filename.toCharArray(buf,filename.length() + 1);
   myConfigFile = SD.open(buf); 
   
   if(!myConfigFile) {
      Serial.println("Echec ouverture " + filename + ". Commande non prise en compte");
      return; 
   }
  
   while (myConfigFile.available()) {
    //Serial.write(myConfigFile.read());
     char c = myConfigFile.read();
     fileContent = fileContent + c;
   }
   myConfigFile.close();
   String commandToSend = findCommand(fileContent,command);
   if(commandToSend == "") {
      Serial.println("Commande non trouvee");
      return; 
   } else {
      Serial.println("Commande a envoyer : " + commandToSend); 
   }
   
   /*
     TODO Ici, on devra envoyer une commande via infrarouge
    */
  long totalTime = millis() - time;
  Serial.println(totalTime);
   
}


void setup()
{
  //Port série, pour le débuggage
  Serial.begin(9600);
  Serial.println("Initialisation ethernet");
  // configuration de la librairie ethernet
  //sur l'adresse mac et l'adresse ip déclarées
  Ethernet.begin(mac, ip);

  
  //fonction appelée lors d'une requete
  webserver.setDefaultCommand(&parseRequests);

  /* démarrage serveur */
  webserver.begin();
  Serial.println("Serveur web operationnel");
  Serial.println("Initialisation carte SD");
  //53 sur la mega, 10 sur les autres
  pinMode(53, OUTPUT);
  
  if (!SD.begin(4)) {
    Serial.println("Echec initialisation carte SD");
    return;
  }
  Serial.println("Initialisation OK");
  
}

void loop()
{
  
  webserver.processConnection();
}

Le code n’est sans doute pas optimisé, mais mes connaissances en C/C++ sont assez faibles, donc j’ai fait avec mes moyens.

Les 4 premières lignes du codes déclarent les librairies à utiliser : gestion de l’ethernet, de la carte sd, entre autres.
On déclare ensuite quelques variables : d’abord l’adresse MAC que l’on attribue à l’arduino. Puis son IP sur le réseau.
On définit ensuite l’url par laquelle on pourra joindre le serveur web. Ici, ce sera donc : http://192.168.1.20/ws.
Puis on déclare d’autres variables que je n’ai pas besoin d’expliquer.
La première fonction : parseRequests est la fonction qui va gérer les requêtes faites sur le serveur. Comme l’indique le premier if, on ne prend en compte que les requêtes POST. Cette fonction récupère les deux paramètres device et command de la requête POST.
Elle fait ensuite passer ces deux paramètres à la fonction processCommand. Cette fonction charge le contenu du fichier « device ».cnf.
Quand elle l’a chargé, elle fait appel à la fonction findCommand. Elle lui fait passer en paramètre le contenu du fichier, et la commande à trouver. Le fichier de configuration est de la forme :

cle1=valeur1
cle2=valeur2

La fonction trouve donc la valeur en fonction de la clé. C’est pour cette fonction qu’il y a sans doute des manières beaucoup plus élégantes de faire…

Les deux dernières fonctions sont les deux fonctions obligatoires dans un sketch pour arduino : setup, qui initialise les connexions. Et loop, qui est la fonction appelée en permanence par le module. Je n’expliciterai pas plus le contenu de ces fonctions, les commentaires sont assez explicites.

Test du montage

Pour tester le circuit, on doit formater la carte dans un format compatible (j’ai formaté en fat16), et mettre un fichier de configuration dessus. J’ai donc créé un fichier de configuration « 1.cnf », qui contient :

1=oriegnoerg
2=ergieorjre
3=zergpergr

Il suffit ensuite d’insérer la carte SD dans le slot de l’ethernet shield, de brancher un câble RJ45, et de brancher l’USB de l’arduino.
Ensuite, on peut uploader le code sur l’arduino (depuis l’IDE). On ouvre ensuite le moniteur de port série depuis l’IDE. On peut alors y voir :

Initialisation ethernet
Serveur web operationnel
Initialisation carte SD
Initialisation OK

Si tout se passe bien.
On peut alors ouvrir un terminal et y taper ceci :

curl -d « device=1&command=2 » http://192.168.1.20/ws

Et si tout se passe bien, on peut voir dans le moniteur du port série :

Lecture du fichier 1.cnf. Commande : 2
Commande a envoyer : ergieorjre

Conclusion

C’est donc bon, le arduino récupère la bonne valeur dans le fichier de configuration, en fonction de la requête qu’on lui fait.
La prochaine étape consistera tout d’abord à faire un montage qui permette de lire une commande envoyée par infrarouge par une télécommande, et de la sauvegarder dans un fichier de configuration sur la carte SD (ça, c’est dans le meilleur des scénarios).
Puis à utiliser un émetteur infrarouge pour reproduire le signal infrarouge. Ça semble être facile, mais visiblement, chaque constructeur fait son « standard » pour l’infrarouge des télécommandes, donc ça risque d’être compliqué (pas impossible, j’espère).
Et enfin, la dernière étape sera de faire des appli qui permettent d’envoyer des commandes au module.
Tout ça risque de prendre pas mal de temps. D’autant que je commence tout juste arduino (j’ai commencé hier soir, en fait), d’où le code pas terrible et peut-être quelques approximations.

La suite dans un prochain épisode (j’espère).

Vus : 4619
Publié par mael : 17