Librairie domotique: REST-like interface
[adsGrandRectangleTexte]
Pour communiquer avec les Arduino, j’utilise (en plus d’xPL) des requêtes HTTP « REST-like » de ce type :
http://ip_arduino/lumiere1/on
http://ip_arduino/add/lumiere2/1/13
http://ip_arduino/del/lumiere2
L’idée originale vient du projet RESTduino. La partie du code qui parse l’url à été en partie intégrée dans la librairie.
Si vous vous souvenez, la librairie permet d’ajouter des « pages » à servir par le serveur http via les méthodes AddPage() et AddDefaultPage()
Si nous regardons une fonction définissant le comportement d’une « page », on voit qu’elle reçoit en paramètre le premier argument de l’url demandée. Dans les exemples ci-dessus, cela correspond à lumiere1 et lumiere2. Add et del sont eux des noms de pages à servir.
On peut ensuite demander les arguments suivants via la méthode GetNextArg().
Ici on affiche tous les arguments de l’url demandée :
void showUrlArg(char* arg1) { int i = 0; while(arg1 != NULL) { server.Print("Argument "); server.Print(++i); server.Print(": "); server.Println(arg1); arg1 = server.GetNextArg(); } }
Implémentation
Les fonctions ParseUrl et GetNextArg de la classe HttpServeur qui servent à parser l’url demandée et à récupérer ses arguments :
char* HttpServer::ParseUrl(char *str) { int8_t r=-1; int8_t i = 0; int index = 4; int index1 = 0; int plen = 0; char ch = str[index]; char clientline[CMDBUF]; while( ch != ' ' && index < CMDBUF) { clientline[index1++] = ch; ch = str[++index]; } clientline[index1] = '\0'; // convert clientline into a proper // string for further processing String urlString = String(clientline); // extract the operation String op = urlString.substring(0,urlString.indexOf(' ')); // we're only interested in the first part... urlString = urlString.substring(urlString.indexOf('/'), urlString.indexOf(' ', urlString.indexOf('/'))); // put what's left of the URL back in client line urlString.toCharArray(clientline, CMDBUF); return clientline; } char* HttpServer::GetNextArg(char *str) { static char clientline[CMDBUF]; if(str != NULL) { sprintf(clientline,"%s",ParseUrl(str)); return strtok(clientline,"/"); } else { return strtok(NULL,"/"); } }
Un exemple concret
Ici c’est la page par défaut que j’utilise. Celle qui est appelée si aucune autre page ne correspond.
– Si aucun argument (ex : http://192.168.0.133/), j’affiche la configuration de tous les modules en json (via l’objet manager … j’en reparlerai dans un autre article, voir les sources pour les curieux)
– Si au moins un argument existe (ex : http://192.168.0.133/113/1), je recherche le module correspondant. Ensuite, en fonction de son type, j’effectue une action. Dans le cas d’une lumière je demande le deuxième argument et je modifie la valeur du module.
void Default(char* arg1) { // Si aucun argument, on affiche l'état des modules en Json if(arg1 == NULL) { server.Print(manager.ToJson()); } else { // Recherche du module demandé, arg1 étant l'id du module demandé Module* module = manager.FindModule(atoi(arg1)); if(module != NULL) { // En fonction du type de module on lance une action switch(module->GetType()) { case LIGHT: { Light* l = (Light*)module; // Retourne l'argument suivant: la valeur à paramétrer char* value = server.GetNextArg(); if(value != NULL) { l->Set(atoi(value)); } break; } case SHUTTER: { // traitement ... } // Gestion des autres cas ... } // Puis on affiche l'état du module en Json server.Print(module->ToJson()); } else { // Aucun module trouvé, on suppose que l'utilisateur veut forcer l'état d'une io. // On tente de basculer la pin correspondante char* value = server.GetNextArg(); digitalWrite(atoi(arg1), atoi(value)); server.Print("Force pin "); server.Print(arg1); server.Print(" to value "); server.Println(value); } } }
Voilà pour aujourd’hui :), bonne journée.
Bravo pour ces articles super interessants, je debute dans la domotique (mais pas en informatique ou en electronique) et je souhaite « domotiser » les equipements que je possede. arduino, Raspberrypi, volet radio nice, 1wire et sonde oregon.
j’attends avec impatience les prochains articles sur l’oregon 😉
je vais tester ta librairie dans les semaines qui suivent.
Grand MERCI pour ce partage.
Merci ça fait paisir 🙂
Ils sont presque prets mais j’ai encore deux trois détails à régler (une petite amélioration « cosmétique » sur le décodeur et des tests de portée). Etant en plein déménagement tout est en carton :/
N’hésite pas à me remonter les problèmes que tu rencontre sur la librairie. à bientot
C’est parti, je viens de mettre un peu a jour mes logiciels,
Arduino 1.0.1, le dernier ethercard et la librairie connectingstuff
mais des que je compile RESTLikeInterface j’obtiens ces messages d’erreur, il doit manquer une librairie !!!
RESTLikeInterface:3: error: ‘HttpServer’ does not name a type
RESTLikeInterface.cpp: In function ‘void setup()’:
RESTLikeInterface:18: error: ‘EthernetConf’ has not been declared
RESTLikeInterface:21: error: ‘server’ was not declared in this scope
RESTLikeInterface.cpp: In function ‘void loop()’:
RESTLikeInterface:38: error: ‘server’ was not declared in this scope
RESTLikeInterface.cpp: In function ‘void Default(char*)’:
RESTLikeInterface:49: error: ‘server’ was not declared in this scope
RESTLikeInterface:67: error: ‘server’ was not declared in this scope
RESTLikeInterface:80: error: ‘server’ was not declared in this scope
RESTLikeInterface:86: error: ‘server’ was not declared in this scope
RESTLikeInterface.cpp: In function ‘void AddModule(char*)’:
RESTLikeInterface:100: error: ‘server’ was not declared in this scope
RESTLikeInterface:113: error: expected `)’ before ‘;’ token
RESTLikeInterface.cpp: In function ‘void DelModule(char*)’:
RESTLikeInterface:122: error: ‘server’ was not declared in this scope
Merci
A priori je dirais qu’il ne trouve pas la lib connectingstuff ?
J’essai de faire un test rapidement et je reviens vers toi.
J’ai trouvé, je te corrige ça.
J’ai dû uploader la dernière version sans la tester. Je corrige deux trois erreurs
de syntaxe et c’est bon.
Pour le problème que tu as, en fait dans Kernel.h tu peux activer/désactiver des morceaux de code (la partie ethernet, xPL LCD …) et par défaut j’ai tout désactiver. Il faut que tu remplace #undef ETH par un #define ETH.
Je n’ai pas trouver comment le configurer directement dans le sketch, il y a un problème de visibilité des constantes entre l’environnement Arduino et les librairies. Donc pour le moment je suis obligé de le laisser dans la librairie, ce qui n’est pas fameux.
ok merci, j’attend les modifs, j’avais pas vu le fichier kernel.h avec ces fonctions.
Voila c’est corrigé, ça compile. Ça devrait fonctionner.
N’oublie pas que si tu travailles avec un Arduino Mega tu dois (ça dépend du shield ethernet je crois) modifier les pins utilisées pour le shield ethernet dans la lib ethercard.
ca compil sans erreur 🙂 plus qu’a tester en live.
Merci pour ce soutien hyper rapide.
Ok super. Il faudrait que je fasse une petite doc sur l’architecture interne de la lib pour expliquer globalement comment ça fonctionne et sur ce qui n’est pas totalement finalisé.
ah j’oubliais, j’utilise aussi l’enc28J60.