OSC UDP : MicroOsc
Problématique
Nous voulons une méthode robuste, fiable et universelle pour l'intégration de l'Arduino à l'espace immersif dans un contexte de création multimédia. C'est-à-dire l’intégration de capteurs et actionneurs dans un grand espace où l'ordinateur multimédia, qui doit coordonner les aspects audiovisuels (les projections visuelles, les éclairages, les effets et les diffusions sonores), est distant.
Solution de connectique
La solution proposée essaie de rester peu onéreuse et accessible tout en demeurant fiable (en éliminant les sources d’erreurs potentielles des communications) et très compatible avec les outils de création multimédia.
Dans cette optique, nous choisissons d'utiliser le protocole OSC. La plupart des protocoles (COBS, JSON, FUDI, etc.) permettent une trop grande latitude dans la manière d'organiser les données, et s'appuient donc sur des architectures personnalisées spécifiques à chaque développeur. L'OSC impose une structure particulière de l'information que toutes les applications doivent supporter ce qui rend toute communication plus universelle : tout le monde parle la même langue!
Dans ce cas-ci, nous allons utiliser la version UDP de l'OSC. Pour établir cette connexion, vous devez :
- Établir une connexion UDP
- Intégrer MicroOsc UDP
Connexion UDP
Il est possible d'établir une connexion UDP de deux manières:
- Une connexion UDP par Ethernet, plus fiable et simple.
- Une connexion UDP par WiFi, moins fiable et plus complexe.
Connexion UDP Ethernet
Code à ajouter à l'espace global pour une connexion UDP Ethernet
Modifiez les adreses et les ports pour que cela corresponde à votre configuration réseau!
#include <SPI.h>
#include <Ethernet.h>
#include <esp_mac.h>
EthernetUDP myUdp;
IPAddress myDestinationIp(10, 1, 2, 3);
unsigned int myDestinationPort = 7001;
IPAddress myIp(10, 1, 2, 101);
unsigned int myPort = 7000;
Code à ajouter à setup() pour une connexion UDP Ethernet
Initialisation et configuration de la connexion Etheret UDP :
// CONFIGURE ETHERNET HARDWARE :
// THE NUMBERS ARE THE HARDWARE PINS FOR THE ATOM POE.
SPI.begin(22, 23, 33, 19);
Ethernet.init(19);
// GET FACTORY DEFINED ESP32 MAC :
uint8_t myMac[6];
esp_efuse_mac_get_default(myMac);
// START ETHERNET WITH STATIC IP
Ethernet.begin(myMac, myIp);
myUdp.begin(myPort);
Serial.println();
Serial.println(__FILE__);
Serial.print("myDestinationIp: ");
Serial.println(myDestinationIp);
Serial.print("myDestinationPort: ");
Serial.println(myDestinationPort);
Serial.print("myIp: ");
Serial.println(Ethernet.localIP());
Serial.print("myPort: ");
Serial.println(myPort);
MicroOsc UDP
Code à modifier ou ajouter à l'espace global pour MicroOsc UDP
Le code pour MicroOscUdp doit être ajouté dans l'espace global après l'initialisation des variables précédentes.
Si MicroOscSlip était utilisé, il faut commenter pour désactiver les lignes suivantes :
// #include <MicroOscSlip.h>
// MicroOscSlip<1024> myOsc(&Serial);
Il faut ajouter la bibliothèque MicroOscUdp qui fait partie de MicroOsc et initialiser une instance de MicroOscUdp :
#include <MicroOscUdp.h>
// The number 1024 between the < > below is the maximum number of bytes reserved for incomming messages.
// Outgoing messages are written directly to the output and do not need more reserved bytes.
MicroOscUdp<1024> myOsc(&myUdp, myDestinationIp, myDestinationPort);
Code à ajouter à setup() pour MicroOsc UDP
Créez une variable pour mesurer le temps pour contrôler la vitesse à laquelle nous envoyons les messages:
unsigned long myChronoStart = 0; // VARIABLE USED TO LIMIT THE SPEED OF THE SENDING OF OSC MESSAGES
Fonction personnalisée à ajouter avant loop()
Il est nécessaire de définir une fonction qui va être appelée lorsqu'un nouveau message OSC va être reçu :
// FUNCTION THAT WILL BE CALLED WHEN AN OSC MESSAGE IS RECEIVED:
void myOscMessageParser( MicroOscMessage& receivedOscMessage) {
// ADD MESSAGE PARSING CODE HERE
}
À l'intérieur de la fonction que vous venez de créer l'adresse du message peut être validée avec checkOscAddress() ainsi :
if ( receivedOscMessage.checkOscAddress("/address") ) {
// MESSAGE ADDRESS IS "/address"
}
Lorsque l'adresse du message a été validée, il est possible de récuprer les données. Par exemple, pour récupérer un entier:
int32_t intArgument = receivedOscMessage.nextAsInt();
Voici comment récupérer les autres types de données :
// PARSE AN INT
int32_t intArgument = receivedOscMessage.nextAsInt();
// PARSE A FLOAT
float floatArgument = receivedOscMessage.nextAsFloat();
// PARSE A STRING
const char * s = receivedOscMessage.nextAsString();
// PARSE A BLOB
const uint8_t* blob;
uint32_t length = receivedOscMessage.nextAsBlob(&blob);
// PARSE MIDI
const uint8_t* midi;
receivedOscMessage.nextAsMidi(&midi);
Code à intégrer dans loop()
Déclencher la réception des messages
Dans loop() nous devons déclencher la réception des messages OSC avec la méthode onOscMessageReceived() à laquelle nous passons le nom de la fonctione personnalisée créée précédemment:
myMicroOsc.onOscMessageReceived( myOscMessageParser );
Envoyer des messages
Avant d'envoyer des messages, il faut limiter la vitesse d'envoi selon un intervalle de temps :
// EVERY 50 MILLISECONDS :
if (millis() - myChronoStart >= 50) {
myChronoStart = millis(); // RESTART CHRONO
// SEND OSC MESSAGES HERE
}
À l'intérieur de la fonction que vous venez de créer vous pouvez envoyer des messages. Par exemple un entier ainsi :
int myIntToSend = 100;
myMicroOsc.sendInt("/address", myIntToSend);
Voici les autres méthodes pour envoyer les autres types de données :
// SEND AN INT(32)
myMicroOsc.sendInt(const char *address, int32_t i);
// SEND A FLOAT
myMicroOsc.sendFloat(const char *address, float f);
// SEND A STRING
myMicroOsc.sendString(const char *address, const char *str);
// SEND A BLOB
myMicroOsc.sendBlob(const char *address, unsigned char *b, int32_t length);
// SEND DOUBLE
myMicroOsc.sendDouble(const char *address,double d);
// SEND MIDI
myMicroOsc.sendMidi(const char *address,unsigned char *midi);
// SEND INT64
myMicroOsc.sendInt64(const char *address, uint64_t h);
// SEND A MIXED TYPE VARIABLE LENGTH MESSAGE
myMicroOsc.sendMessage(const char *address, const char *format, ...);
Exemple MicroOsc UDP Ethernet à adresse statique pour M5Stack Atom Lite
L'exemple microosc_ethernet_static_m5stack_atom_poe dans les exemples de MicroOs pour M5Stack Atom démontre comment effectuer l'intégration de MicroOsc UDP à une connexion Ethernet avec une adresse statique.