Accueil > MICROPYTHON > MicroPython : commande via un serveur web (esp8266 & esp32)

MicroPython : commande via un serveur web (esp8266 & esp32)

mercredi 22 avril 2020, par thebault

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 :


La connexion à un réseau wifi existant (mode station STA)

  1. import network
  2.  
  3. wifissid =" ... "
  4. pwd =" ..."
  5.  
  6. def connexion_wifi(ssid,password):
  7. wlan = network.WLAN(network.STA_IF)
  8. wlan.active(True)
  9. if not wlan.isconnected():
  10. print("connexion",ssid)
  11. wlan.connect(ssid,password)
  12. while not wlan.isconnected():
  13. pass
  14. print("Adresse IP :", wlan.ifconfig()[0])
  15. print("Masque réseau : ", wlan.ifconfig()[1])
  16. print("Gateway :", wlan.ifconfig()[2])
  17. print("Serveur DNS :", wlan.ifconfig()[3])
  18. return wlan.ifconfig()[0]
  19.  
  20. AdresseIP = connexion_wifi(wifissid,pwd)

Télécharger

PNG - 7.3 ko

Explications

On importe la bibliothèque network :

  1. 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.

  1. wifissid =" ... "
  2. pwd =" ..."

Télécharger

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)

  1. AdresseIP = connexion_wifi(wifissid,pwd)

Les cartes esp8266 &esp32 peuvent fonctionner suivant 2 modes :

  • en mode station (STA) : le carte rejoint un réseau wifi existant
  • en mode point d’accès (Access Point AP) la carte crée son propre réseau wifi et permet à un téléphone ou autre de se connecter dessus.

On configure la carte en mode station STA et on l’active :

  1. wlan = network.WLAN(network.STA_IF)
  2. wlan.active(True)

Télécharger

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.

  1. if not wlan.isconnected():
  2. print("connexion",ssid)
  3. wlan.connect(ssid,password)
  4. while not wlan.isconnected():
  5. pass

Télécharger

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 :

  1. print("Adresse IP :", wlan.ifconfig()[0])
  2. print("Masque réseau : ", wlan.ifconfig()[1])
  3. print("Gateway :", wlan.ifconfig()[2])
  4. print("Serveur DNS :", wlan.ifconfig()[3])

Télécharger

Enfin, on renvoie l’adresse IP (élément d’indice 0 du tuple)

  1. return wlan.ifconfig()[0]

Mise en place d’un serveur http minimaliste pour commander la broche GPIO 2

PNG - 8 ko

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 :

  • http://<Adresse IP de la carte>:8080/ ?etat=on
  • http://<Adresse IP de la carte>:8080/ ?etat=off
  1. import usocket as socket
  2. import network
  3. from machine import Pin
  4.  
  5. wifissid ="..."
  6. pwd ="..."
  7. brocheLed = Pin(2,Pin.OUT)
  8. Led = "ON"
  9.  
  10. htmlresponse = """HTTP/1.0 200 OK
  11. Content-Type: text/html
  12.  
  13. <!DOCTYPE html>
  14. <meta charset="UTF-8">
  15. <html>
  16. <head>
  17. <title>Serveur esp8266</title>
  18. </head>
  19. <body>
  20. <p>état de la LED : {} </p>
  21. <p> <a href = "/?etat=on" >Led ON </a> </p>
  22. <p> <a href = "/?etat=off" >Led OFF </a> </p>
  23. </body>
  24. </html>
  25. """
  26.  
  27. def connexion_wifi(ssid,password):
  28. wlan = network.WLAN(network.STA_IF)
  29. wlan.active(True)
  30. if not wlan.isconnected():
  31. print("connexion",ssid)
  32. wlan.connect(ssid,password)
  33. while not wlan.isconnected():
  34. pass
  35. print("Adresse IP :", wlan.ifconfig()[0])
  36. print("Masque réseau : ", wlan.ifconfig()[1])
  37. print("Gateway :", wlan.ifconfig()[2])
  38. print("Serveur DNS :", wlan.ifconfig()[3])
  39. return wlan.ifconfig()[0]
  40.  
  41. AdresseIP = connexion_wifi(wifissid,pwd)
  42. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  43. s.bind((AdresseIP,8080))
  44. s.listen(1)
  45.  
  46. while True :
  47. print("Attente connexion ...")
  48. connexion = s.accept()
  49. ClientAddress = connexion[1]
  50. ClientSocket = connexion[0]
  51. print("Adresse client connecté :",ClientAddress[0])
  52. request = ClientSocket.recv(2048)
  53. Lrequest = request.decode('utf-8').split(' ')
  54. if Lrequest[0] == 'GET' :
  55. url =Lrequest[1]
  56. if url[0:7] =="/?etat=":
  57. if url[7:] == 'on':
  58. brocheLed.value(0)
  59. Led = "ON"
  60. elif url[7:] == 'off':
  61. brocheLed.value(1)
  62. Led = "OFF"
  63. ClientSocket.send(htmlresponse.format(Led))
  64. ClientSocket.close()

Télécharger

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)

  1. import usocket as socket
  2. import network
  3. from machine import Pin

Télécharger

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).

  1. brocheLed = Pin(2,Pin.OUT)
  2. Led = "ON"

Télécharger

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é)

  1. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  2. s.bind((AdresseIP,8080))
  3. s.listen(1)

Télécharger

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])

  1. while True :
  2. print("Attente connexion ...")
  3. connexion = s.accept()
  4. ClientAddress = connexion[1]
  5. ClientSocket = connexion[0]
  6. print("Adresse client connecté :",ClientAddress[0])

Télécharger

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.

  1. 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 :

  1. ['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 :

  1. 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 :

  1. 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"

  1. if url[0:7] =="/?etat=":
  2. if url[7:] == 'on':
  3. brocheLed.value(0)
  4. Led = "ON"
  5. elif url[7:] == 'off':
  6. brocheLed.value(1)
  7. Led = "OFF"

Télécharger

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.

  1. htmlresponse = """HTTP/1.0 200 OK
  2. Content-Type: text/html
  3. <!DOCTYPE html>
  4. <meta charset="UTF-8">
  5. <html>
  6. <head>
  7. <title>Serveur esp8266</title>
  8. </head>
  9. <body>
  10. <p>état de la LED : {} </p>
  11. <p> <a href = "/?etat=on" >Led ON </a> </p>
  12. <p> <a href = "/?etat=off" >Led OFF </a> </p>
  13. </body>
  14. </html>
  15. """

Télécharger

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)

  1. ClientSocket.send(htmlresponse.format(Led))

Enfin, on ferme la connexion avec le client

  1. ClientSocket.close()

Un serveur web qui n’affiche pas de page html mais reçoit les ordres par requête GET + paramètre ’etat’

Ce qu’il faut modifier par rapport au précédent programme, c’est la réponse html. Dans ce cas elle sera :

  1. htmlresponse = "HTTP/1.0 204 No Content\r\n"
SPIP | | Plan du site | Suivre la vie du site RSS 2.0
Habillage visuel © digitalnature sous Licence GPL