Accueil > MICROPYTHON > MicroPython : commande via un serveur web (esp8266 & esp32)
mercredi 22 avril 2020, par
Les cartes esp8266 et esp32 sont nativement équipées d’une connexion Wifi. Cet article va permettre de mettre en place un serveur http minimaliste utilisant la bibliothèque usocket de micropython afin de commander une broche de sortie (la broche GPIO 2 qui commande la led dans ce cas). Les programmes sont fortement inspirés des documentations des bibliothèques :
import network wifissid =" ... " pwd =" ..." def connexion_wifi(ssid,password): wlan = network.WLAN(network.STA_IF) wlan.active(True) if not wlan.isconnected(): print("connexion",ssid) wlan.connect(ssid,password) while not wlan.isconnected(): pass print("Adresse IP :", wlan.ifconfig()[0]) print("Masque réseau : ", wlan.ifconfig()[1]) print("Gateway :", wlan.ifconfig()[2]) print("Serveur DNS :", wlan.ifconfig()[3]) return wlan.ifconfig()[0] AdresseIP = connexion_wifi(wifissid,pwd)
Explications
On importe la bibliothèque network :
import network
Les valeurs des variables wifissid et pwd doivent être complétées par la nom du réseau Wifi et le mot de passe.
wifissid =" ... " pwd =" ..."
La fonction connexion_wifi(ssid,password) prend en paramètre le nom du réseau wifi (ssid) et le mots de passe (password) puis renvoie la valeur de l’adresse IP de la carte esp en décimal pointé (xxx.xxx.xxx.xxx)
AdresseIP = connexion_wifi(wifissid,pwd)
Les cartes esp8266 &esp32 peuvent fonctionner suivant 2 modes :
On configure la carte en mode station STA et on l’active :
wlan = network.WLAN(network.STA_IF) wlan.active(True)
Si la carte n’est pas déjà connectée, on lui demande de se connecter au réseau wifi en utilisant le nom du réseau wifi (ssid) et le mot de passe qui ont été passés en paramètres à la fonction.
if not wlan.isconnected(): print("connexion",ssid) wlan.connect(ssid,password) while not wlan.isconnected(): pass
Les deux dernières lignes ci dessus permettent d’attendre que la connexion (qui prend quelques instant) soit bien effective avant de poursuivre l’exécution du programme.
La méthode wlan.ifconfig() renvoie un tuple comportant l’adresse IP, le masque de réseau, la passerelle (gateway) et le serveur DNS. On affiche ces différents éléments :
print("Adresse IP :", wlan.ifconfig()[0]) print("Masque réseau : ", wlan.ifconfig()[1]) print("Gateway :", wlan.ifconfig()[2]) print("Serveur DNS :", wlan.ifconfig()[3])
Enfin, on renvoie l’adresse IP (élément d’indice 0 du tuple)
return wlan.ifconfig()[0]
Ce serveur à différentes limitations :
il ne traite que la méthode GET lors d’une requête http
une seul connexion à la fois
il affiche la page web, peut importe l’url, tant que la requête pointe vers l’adresse IP. Pas de réponse http autre que le code réponse http 200 (OK)
le port d’écoute est le port 8080 (modifiable)
pas de gestion de sécurité (DOS...)
un seul paramètre passé lors de la requête http. Les requêtes pour allumer ou éteindre la led seront :
import usocket as socket import network from machine import Pin wifissid ="..." pwd ="..." brocheLed = Pin(2,Pin.OUT) Led = "ON" htmlresponse = """HTTP/1.0 200 OK Content-Type: text/html <!DOCTYPE html> <meta charset="UTF-8"> <html> <head> <title>Serveur esp8266</title> </head> <body> <p>état de la LED : {} </p> <p> <a href = "/?etat=on" >Led ON </a> </p> <p> <a href = "/?etat=off" >Led OFF </a> </p> </body> </html> """ def connexion_wifi(ssid,password): wlan = network.WLAN(network.STA_IF) wlan.active(True) if not wlan.isconnected(): print("connexion",ssid) wlan.connect(ssid,password) while not wlan.isconnected(): pass print("Adresse IP :", wlan.ifconfig()[0]) print("Masque réseau : ", wlan.ifconfig()[1]) print("Gateway :", wlan.ifconfig()[2]) print("Serveur DNS :", wlan.ifconfig()[3]) return wlan.ifconfig()[0] AdresseIP = connexion_wifi(wifissid,pwd) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((AdresseIP,8080)) s.listen(1) while True : print("Attente connexion ...") connexion = s.accept() ClientAddress = connexion[1] ClientSocket = connexion[0] print("Adresse client connecté :",ClientAddress[0]) request = ClientSocket.recv(2048) Lrequest = request.decode('utf-8').split(' ') if Lrequest[0] == 'GET' : url =Lrequest[1] if url[0:7] =="/?etat=": if url[7:] == 'on': brocheLed.value(0) Led = "ON" elif url[7:] == 'off': brocheLed.value(1) Led = "OFF" ClientSocket.send(htmlresponse.format(Led)) ClientSocket.close()
Explications :
Appel des bibliothèques usocket (connexions réseau bas niveau), network (connexion au réseau wifi vue précédemment), machine (gestion du GPIO)
import usocket as socket import network from machine import Pin
Configuration de la broche 2 en sortie (celle où il y a la led). on utilisera une variable d’état afin de connaître l’état de la LED. A la mise sous tension de la carte, la broche 2 est par défaut à 0 donc la led est allumée (ON).
brocheLed = Pin(2,Pin.OUT) Led = "ON"
On crée un socket réseau TCP/IP version 4 (AF_INET = Address Family de type Internet et SOCK_STREAM pour TCP) :
On rattache ce socket à l’adresse IP de la carte, sur le port 8080 (qui peut être modifié si besoin).
Le socket passe en écoute en autorisant seulement une seule connexion (1) (le serveur ne pourra avoir qu’un seul client à un instant donné)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((AdresseIP,8080)) s.listen(1)
On attend la connexion d’un client dans la boucle infinie. Lorsque le client se connecte, La méthode socket.accept() renvoie un tuple composé : du socket (connexion[0]) et de l’adresse IP du client (connexion[1])
while True : print("Attente connexion ...") connexion = s.accept() ClientAddress = connexion[1] ClientSocket = connexion[0] print("Adresse client connecté :",ClientAddress[0])
On va maintenant récupérer et analyser la requête http reçue.
On utilise un buffer de 2048 octets et la variable request contiendra la requête sous forme binaire.
request = ClientSocket.recv(2048)
On transforme cette requête http en chaîne de caractères par la méthode string.decode.
Si on l’affiche, on obtient ceci :
GET /?etat=off HTTP/1.1
Host: 192.168.1.27:8080
User-Agent: curl/7.52.1
Accept: */*
Ce qui nous intéresse ce trouve sur la première ligne de la requête. On a :
méthode | espace | url | espace | version du protocole http |
GET | / ?etat=off | HTTP/1.1 |
On sépare cette chaîne de caractères par les espaces avec split() et obtenir la liste Lrequest suivante :
['GET', '/?etat=off', 'HTTP/1.1\r\nHost:', '192.168.1.27:8080\r\nUser-Agent:', 'curl/7.52.1\r\nAccept:', '*/*\r\n\r\n']
On pourra obtenir la méthode de la requête http avec Lrequest[0] et l’url avec Lrequest[1].
Le code python pour décoder du binaire vers une chaîne de caractères puis séparer cette chaîne de caractères en htmlresponse = "HTTP/1.0 204 No Content\r\n" liste est :
Lrequest = request.decode('ascii').split(' ')
Le serveur ne va répondre que si la méthode de la requête html est bien de type GET :
if Lrequest[0] == 'GET' :
On va ensuite traiter l’url (Lrequest[1]). L’url pour commander la broche peut être soit ?etat=off soit ?etat=on .
Ces deux chaînes de caractères commencent par : ?etat=. Donc si les 6 premiers caractères de url[0:7] sont égaux à " ?etat=", on regarde la valeur du paramètre passé lors de la requête. La valeur du paramètre correspond à la fin de la chaîne de caractères est url[7 :].
Si cette valeur est on, on met la broche 2 à 0 pour allumer la led et on passe la variable d’état Led à "on"
Si cette valeur est off, on met la broche 2 à 1 pour éteindre la led et on passe la variable d’état Led à "off"
if url[0:7] =="/?etat=": if url[7:] == 'on': brocheLed.value(0) Led = "ON" elif url[7:] == 'off': brocheLed.value(1) Led = "OFF"
Reste à afficher la réponse http. Le protocole http indique que la réponse doit comporter un entête puis la réponse sous la forme :
<Version du protocole> <code réponse> <signification> CR LF
<Entête>
<corps de la réponse>
Afin d’afficher une page html dynamique qui indique l’état de la LED, on utilise la réponse suivante.
htmlresponse = """HTTP/1.0 200 OK Content-Type: text/html <!DOCTYPE html> <meta charset="UTF-8"> <html> <head> <title>Serveur esp8266</title> </head> <body> <p>état de la LED : {} </p> <p> <a href = "/?etat=on" >Led ON </a> </p> <p> <a href = "/?etat=off" >Led OFF </a> </p> </body> </html> """
On transmet la réponse http au client (la valeur de la variable d’état Led remplace la paire d’accolades dans la page html)
ClientSocket.send(htmlresponse.format(Led))
Enfin, on ferme la connexion avec le client
ClientSocket.close()
Ce qu’il faut modifier par rapport au précédent programme, c’est la réponse html. Dans ce cas elle sera :
htmlresponse = "HTTP/1.0 204 No Content\r\n"