====== ESP-OSC ======
* Porteur du projet : reso-nance Laurent
* Date : 29/01/2020
* Licence : libre !
* Contexte : INIT'
* Support : [[http://reso-nance.org/revealJS/INIT_ESP_OSC.html#/ | présentation]]
* Lien : [[https://github.com/reso-nance/INITs/tree/master/ESP8266-OSC | repo github]]
===== Description =====
Réalisation d'un contrôleur analogique basé sur un ESP8266 communicant en OSC via wifi avec un ordinateur
9===== Matériaux =====
* [[http://fr.aliexpress.com/item/32631693796.html?spm=a2g0o.productlist.0.0.759b1e0840A8RR&algo_pvid=afa47c34-5d1d-434d-84c5-e6095c439b41&algo_expid=afa47c34-5d1d-434d-84c5-e6095c439b41-5&btsid=03e72130-c5bf-41e2-9825-546a7e235329&ws_ab_test=searchweb0_0,searchweb201602_2,searchweb201603_53|ESP8266]]
* [[https://fr.aliexpress.com/item/32324914059.html?spm=a2g0s.13010208.99999999.262.wzaVwn|Batterie]]
* capteur analogique 3,3V ou 5V
* router wifi
* ordinateur
===== Tutoriel =====
==== UDP & BROADCAST====
L'OSC peut circuler sur le réseau au protocole TCP ou UDP. Le TCP permet de vérifier que le destinataire à reçu le message et que son contenu n'est pas corrompu. L'UDP envoie le message sans se préoccuper de sa réception. En conséquence, le TCP ne peut s'utiliser qu'entre deux machines tandis que l'UDP peut être **broadcasté** (envoyé sur un grand nombre de machines simultanément). C'est la raison pour laquelle on préfèrera envoyer de l'OSC au protocole UDP dans la plupart des cas.
Pour envoyer des données en UDP depuis un ESP8266, on peut utiliser le code suivant, commenté ligne par ligne. Il est également disponible sur le [[https://github.com/reso-nance/INIT-ESP8266-OSC | repo github de l'init]]
++++ envoiUDP.ino|
#include // fonctions wifi : connection en station ou point d'accès, addresses IP ...
#include // création de paquets UDP et envoi sur le réseau
static char* nomDuReseau = "malinette"; // attention, sensible à la casse
static char* motDePasse = "malinette666"; // idem
static const int portUDP = 8000; // port auquel les paquets seront envoyés, doit être identique à celui du récepteur
static IPAddress IPcible = IPAddress({10,0,0,255});// addresse à laquelle les messages sont envoyés, ici en broadcast
WiFiUDP UDP;
void setup() {
Serial.begin(115200); // ouverture du port série
while (true) { // tant que la connection n'est pas établie, on restera coincé ici
Serial.println("\n\nConnection à " + String(nomDuReseau) + " ...");
WiFi.mode(WIFI_STA); // mode station : se connecte à un point d'accès existant
WiFi.begin(nomDuReseau, motDePasse); // tentative de connection
ESP.wdtFeed(); // évite de rebooter l'ESP si l'opération le bloque trop longtemps
yield(); // rends la main à l'ESP
if ( WiFi.waitForConnectResult() == WL_CONNECTED ) {break;} // si la connection est établie, on sort de la boucle infinie
}
Serial.print("connecté, addresse IP : ");
Serial.println(WiFi.localIP()); // addresse IP attribuée à l'ESP par le routeur wifi auquel il se connecte
UDP.begin(portUDP);
}
void loop() {
Serial.println("coucou !");
UDP.beginPacket(IPcible, portUDP);
UDP.write("coucou !");
UDP.endPacket();
yield(); // rends la main à l'ESP
delay(1000); // le paquet sera envoyé toutes les secondes
}
++++
Pour afficher l'UDP reçu, un script python écoutant l'UDP entrant est également fourni
++++ receptionUDP.py|
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
port = 8000
# little trick to get the local ip address.
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
IP = s.getsockname()[0]
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((IP, port))
print(" écoute l'UDP entrant à l'addresse %s sur le port %i" %(IP, port))
while True:
try :
data, addr = sock.recvfrom(1024)
print ("paquet UDP reçu : '%s'" % data.decode("utf-8"), "depuis", *addr)
except KeyboardInterrupt :
print("\n bye !")
raise SystemExit
++++
==== OSC ====
L'OSC est un format de donnée constitué d'une adresse et éventuellement d'arguments.
{{ :ateliers:esp-osc:exosc.png?direct&400 |}}
L'**adresse** adopte le format d'une URL en commençant par un **/**
Les **arguments** peuvent être aussi nombreux que nécessaire et contenir des entiers, des chaînes de caractères, des nombres à virgule flottantes ou tout autre type de données (appelées //blob//). Cette souplesse d'utilisation est la raison du succès de l'OSC qui s'adaptera aisément à tout type d'utilisation.
Pour créer un message OSC, on utilisera la bibliothèque OSC, disponible dans le gestionnaire de bibliothèque d'Arduino ou sur le [[https://github.com/reso-nance/INIT-ESP8266-OSC | repo de cet INIT]] Dans cet exemple, nous lirons la valeur d'un potentiomètre câblé sur l'entrée analogique de l'ESP8266 {{ :ateliers:esp-osc:montage.png?direct&400 |}}
Pour éviter de surcharger le réseau, nous n'enverrons cette valeur que si elle à changée de plus de 2. Pour s'en assurer, on stocke dans une variable sa valeur précédente (au tour de boucle précédente) et on la compare à la valeur actuelle. Si la différence est supérieure à la tolérance, un message OSC est envoyé. Ce message aura pour addresse ///monOSC// et un seul argument, la valeur du potentiomètre. On peut ajouter d'autres arguments au même message en répétant la ligne message->add(VALEUR_A_AJOUTER)
avant l'envoi du message. Le reste du code est commenté ligne par ligne.
++++ envoiOSC.ino|
#include // fonctions wifi : connection en station ou point d'accès, addresses IP ...
#include // création de paquets UDP et envoi sur le réseau
#include // gestion du protocol OSC en envoi et réception
static char* nomDuReseau = "malinette"; // attention, sensible à la casse
static char* motDePasse = "malinette666"; // idem
static const int portOSC = 8000; // port auquel les paquets seront envoyés, doit être identique à celui du récepteur
static IPAddress IPcible = IPAddress({10,0,0,255});// addresse à laquelle les messages sont envoyés, ici en broadcast
static int tolerance = 2; // différence minimale entre la la valeur actuelle et la précédente à partir de laquelle les messages seront envoyés
int valeurPrecedente = 0; // stockera la valeur précédente
WiFiUDP UDP;
void setup() {
Serial.begin(115200); // ouverture du port série
while (true) { // tant que la connection n'est pas établie, on restera coincé ici
Serial.println("\n\nConnection à " + String(nomDuReseau) + " ...");
WiFi.mode(WIFI_STA); // mode station : se connecte à un point d'accès existant
WiFi.begin(nomDuReseau, motDePasse); // tentative de connection
ESP.wdtFeed(); // évite de rebooter l'ESP si l'opération le bloque trop longtemps
yield(); // rends la main à l'ESP
if ( WiFi.waitForConnectResult() == WL_CONNECTED ) {break;} // si la connection est établie, on sort de la boucle infinie
}
Serial.print("connecté, addresse IP : ");
Serial.println(WiFi.localIP()); // addresse IP attribuée à l'ESP par le routeur wifi auquel il se connecte
}
void loop() {
int valeurActuelle = analogRead(0); // lecture du port analogique
if (valeurActuelle < valeurPrecedente-tolerance || valeurActuelle > valeurPrecedente+tolerance) { // si la valeur à changée significativement
envoieOSC(valeurActuelle);
valeurPrecedente = valeurActuelle; // la tendance d'hier est le vintage d'aujourd'hui
}
yield(); // rends la main à l'ESP
delay(2); // une courte sieste lui permettra de travailler plus longtemps
}
void envoieOSC(int valeur) {
Serial.print("envoie la valeur "); Serial.println(valeur);
static char* addresseOSC = "/monOSC"; // l'addresse OSC doit correspondre à ce que le destinataire attends
OSCMessage* message = new OSCMessage(addresseOSC); // création d'un nouveau message vide
message->add((float) valeur/1024.0f);// on ajoute la valeur sous forme d'un float entre 0 et 1
UDP.beginPacket(IPcible, portOSC); // création d'un paquet UDP vide
message->send(UDP); // envoi du message OSC en UDP
UDP.endPacket(); // le paquet est refermé (sans ruban)
delete(message); // on nettoie derrière soi pour éviter de mauvaises surprises
ESP.wdtFeed();
yield();
}
++++
Un exemple de réception d'OSC est fourni sous forme d'un script python utilisant la bibliothèque [[http://das.nasophon.de/pyliblo/ | pyliblo]] Un script d'installation de cette bibliothèque et de ses dépendances pour debian/ubuntu/raspbian est également disponible sur le [[https://github.com/reso-nance/INIT-ESP8266-OSC | repo de cet INIT]]
++++ receptionOSC.py|
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import liblo, sys
defaultPort=8000
portNumber = defaultPort
if len(sys.argv) != 2 : print("on utilise le port par défaut "+str(defaultPort))
else :
try :
portGiven = int(sys.argv[1])
if portGiven >= 1025 and portGiven <= 65535 : portNumber = portGiven
except :
print("le premier argument doit être le numéro de port (1025~65535)")
raise SystemExit
try:
server = liblo.Server(portNumber)
print("écoute l'OSC entrant sur "+server.url)
except liblo.ServerError as err:
print(str(err))
raise SystemExit
def printOSC(path, args, types, src):
print("reçu le message OSC '%s' de '%s'" % (path, src.url))
for a, t in zip(args, types):
print (" argument de type '%s': %s" % (t, a))
print("\n")
server.add_method(None, None, printOSC)
# loop and dispatch messages every 100ms
while True:
try : server.recv(100)
except KeyboardInterrupt :
print("\n bye !")
raise SystemExit
++++
==== ARDUINO====
* Gestion des preférences arduino pour l'[[materiel:esp8266:accueil|ESP8266]]
* Code, libs et exemples ESP/OSC Arduino [[https://github.com/reso-nance/INIT-ESP8266-OSC|https://github.com/reso-nance/INIT-ESP8266-OSC]]
===== Photos =====
Autres photos, galerie, ...
Les mots clés (tags) représentant votre travail
{{tag>[tag1 tag2 tag3]}}