====== Serveur multimedia ArtNet ======
===== Matériel =====
* 4 x Raspberry Pi 3 :
* 4 x Disque dur WD [[https://www.wdc.com/fr-fr/products/wdlabs/wd-pidrive-foundation-edition.html#WD2500LMCW|PiDrive]]
* 4 x [[https://www.wdc.com/fr-fr/products/wdlabs/wd-pidrive-power-kit-3a.html#WDLB029RNE|Alimentations 3A]]
* 4 x [[https://www.wdc.com/fr-fr/products/wdlabs/wd-pidrive-enclosure-square.html#WDLB024RNN|Boitiers]]
* 1 x Borne d'accès WIFI Netgear WAC104
* 2 x Câbles ethernet Cat 5
* 1 x Node OnPC
* 2 x Adaptateurs USB-C / Ethernet
* 1 x Ordinateur tournant sous Windows
===== Switch WIFI =====
[[https://www.pcastuces.com/pratique/materiel/pont_wifi/page1.htm|Créer un pont WiFi]]
[[https://www.amazon.fr/TP-Link-TL-WA901ND-passive-antenne-démontable/dp/B002YETVXC/?tag=hardwfr-21|TP-Link TL-WA901ND]]
[[http://www.netgear.fr/business/products/wireless/essentials-wireless/WAC104.aspx|Nergear WAC104]]
[[https://kb.netgear.com/20927/How-do-I-change-my-NETGEAR-router-to-AP-mode-after-I-ve-already-run-setup|How do I change my NETGEAR router to AP mode after I’ve already run setup?]]
[[https://support.actlighting.com/knowledgeBase/7374771|Network DMX: Using Art-Net with grandMA2]]
[[https://support.actlighting.com/knowledgeBase/13194057|Outputting Art-Net from onPC]]
Mettre à jour le firmware
==== Connecter l'ordinateur au point d'accès ====
Sur MAC
Dans Préférences système > Réseaux, choisissez le port ethernet puis dérouler la liste Configurer IPV4 > Manuellement
Ensuite dans remplir les champs suivants :
Adresse IP : 192.168.0.210
Sous-réseau : 255.255.255.0
Cliquer sur Appliquer
Allumer la borne d'accès et attendre que le voyant wifi s'allume en vert de manière permanente.
Brancher avec un câble RJ45 l'ordinateur et la borne d'accès.
Dans un navigateur entrer l'adresse suivante
192.168.0.100
Entrer les valeurs suivantes dans les champs correspondants :
Admin : admin
Password : password
La fenêtre de configuration s'ouvre.
Dans Setup > Wireless Setup
Changer le SSID et le mot de passe pour le 2,4 gHz.
Changer le canal de diffusion choisir le 1 car le 12 et le 13 peuvent poser des problèmes.
Dans les options de sécurité choisir :
WPA2-PSK [AES]
Cliquer sur Apply pour sauvegarder la configuration.
Dans Advaced Setup > IP Settings, cocher la case :
Use fixed IP Address (not recommended):
Vous pouvez changer l'adresse IP
IP address : 192.168.1.200
Netmask : 255.255.255.0
Gateway IP address : 192.168.1.1
Primary DNS : 192.168.1.1
Cliquer sur Apply
===== Installer un disque dur =====
Pour pouvoir utiliser des fichiers vidéos conséquents, nous allons utiliser le système de fichier exFat. Pour cela, il faut installer le gestionnaires sur la PI
sudo apt-get update
sudo apt-get install exfat-fuse
Une fois le disque branché sur un port USB de la Raspberry, taper :
sudo fdisk -l
Vous verrez ainsi que le disque de 250 Go a bien été détecté. Vous devez avoir un résultat semblable :
Disk /dev/sda: 232.9 GiB, 250025607168 bytes, 488331264 sectors
Préparer la partition du disque
sudo cfdisk /dev/sda
Il faut d'abord supprimer la partition existante, si besoin utiliser les flèches Haut et Bas pour vous déplacer et choisir la bonne partition à effacer. Une fois celle-ci sur-lignée, allez sur [ Delete ] avec les flèches Droite et Gauche, puis valider avec Enter.
=== Créer une nouvelle partition ===
Allez sur [ New ] et validez. Comme nous n'en créons qu'une seule elle prendra la totalité de l'espace disque, donc valider la taille proposée. Et validez la création d'une partition Primaire.
Votre partition st créée et elle s'affiche. Nous allons modifier le type de la partition en allant sur [ Type ] et validez. dans le menu déroulant choisissez le type
7 HPFS/NTFS/exFAT
Pour finaliser, aller sur [ Write ] et validez et confirmez avec '''yes'''
Le message suivant s'affiche :
The partition table has been altered.
L'opération s'est donc bien déroulée.
Pour quitter l'application allez sur [ Quit ] et validez.
Vous pouvez vérifier le résultat avec :
sudo fdisk -l
=== Formater la partition ===
Une fois la pétition crée, il faut la formater avec :
sudo mkfs.exfat -n RaspiHDD /dev/sda1
=== Monter le disque dur ===
Créer le répertoire dans lequel le disque sera monté par exemple pour qu'il soit facilement accessible, le nom du répertoire sera par exemple PIHDD :
sudo mkdir /home/pi/PIHDD
Monter le disque au point voulu :
sudo mount /dev/sda1 /home/pi/PIHDD
Vérifier que l'opération s'est bien déroulée. Aucune erreur ne devrait apparaître :
ls /home/pi/PIHDD
=== Monter les disque automatiquement au démarrage ===
Repérer en premier lieu l'identifiant de la partition (UUID) :
sudo blkid
Par exemple '''5C24-1453'''
Il faut ensuite modifier le fichier fstab
sudo nano /etc/fstab
Ajouter à la fin du fichier la ligne suivante :
UUID=5C24-1453 /home/pi/PIHDD exfat defaults,auto,umask=000,users,rw 0 0
Ctrl + O validez et Ctrl + X pour finaliser l'opération.
=== Sources ===
* [[https://www.raspberrypi.org/documentation/configuration/external-storage.md|External storage configuration]]
* [[http://www.framboise314.fr/donnez-de-lespace-a-votre-framboise314-un-disque-dur-pour-le-raspberry-pi/|Un disque dur pour le Raspberry Pi]]
* [[https://doc.ubuntu-fr.org/exfat|exFAT]]
===== Installer OLA =====
[[https://www.openlighting.org/ola/linuxinstall|Installer OLA]]
Installer les dépendances :
sudo apt-get install autoconf uuid-dev pkg-config libncurses5-dev libtool libcppunit-dev libmicrohttpd-dev automake g++ protobuf-compiler libprotobuf-lite10 python-protobuf libprotobuf-dev libprotoc-dev zlib1g-dev bison flex make libftdi-dev libftdi1 libusb-1.0-0-dev liblo-dev libavahi-client-dev python-numpy
Avec Git
sudo apt-get install git
git clone https://github.com/OpenLightingProject/ola.git ola
cd ola
Pour installer les fichiers manquants :
autoreconf -i
Pour configurer l'installation
./configure --help
Vous donnera la liste des éléments possible à inclure. Ici nous allons faire simple, nous n'utiliserons que la partie ArtNet
./configure --disable-all-plugins --enable-artnet --enable-http
Compiler en utilisant les quatre coeurs du processeur (durée 25 minutes):
make -j4
make check
sudo make install
Pour pouvoir utiliser les nouvelles libraires installées :
sudo ldconfig
Pour lancer le serveur lad
olad -l 3
Vous pouvez à présent vérifier que vous pouvez accéder au serveur en tapant dans votre navigateur préféré :
raspberrypi.local:9090
Une page s'affiche. Il va falloir activer un univers. Cliquer sur le bouton [Add Universe]. Pour notre affaire, il faut remplir les champs
Universe Id : 0
Universe Name : Raspberry0
Et cocher la case
ArtNet [192.168.1.x] Input
Puis valider la création en cliquant sur le bouton [Add Universe]. Désormais il apparait dans les univers actifs.
Pour vérifier qu'il reçoit bien des informations, dans le menu de gauche cliquer sur Raspberry0, puis sur l'onglet [DMXMonitor]
Dans cette page vous pouvez voir toutes les valeurs reçues par le serveur OLA.
=== Configurer OLA ===
[[https://www.openlighting.org/ola/advanced-topics/patch-persistency/|Patch Persistency]]
===== Installer OMXPlayer =====
Si git n'est pas installé :
sudo apt-get install git
Ensuite :
git clone https://github.com/popcornmix/omxplayer.git
Une fois le téléchargement fini
cd omxplayer
Installer les dépendances :
sudo apt-get update && sudo apt-get install git-core libasound2-dev libva1 libpcre3-dev libboost-dev libssl1.0-dev libssh-dev libsmbclient-dev
Le script prépare les fichiers nécessaire à l'installation. S'i manque des dépendances, ce script demande de les installer.
./prepare-native-raspbian.sh
Compiler avec
make ffmpeg
make -j4
Puis installer avec :
sudo make install
[[https://github.com/turingmachine/omxplayer-sync|Sources OMXPlayer-Sync]]
===== Programmation C++ =====
[[https://docs.openlighting.org/ola/doc/0.10.4/namespaceola_1_1client.html|ola::client Namespace Reference]]
g++ -o multi multi.cpp $(pkg-config --cflags --libs libola)
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// g++ -o multi multi.cpp $(pkg-config --cflags --libs libola)
//
///////////////////////////////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
// Variabes globales
static const string chemin = "/home/pi/PIHDD/";
vector listeFichiers;
int fichirEnLecture = -1;
void RegisterComplete(const ola::client::Result& result) {
if (!result.Success()) {
OLA_WARN << "Failed to register universe: " << result.Error();
}
}
void arreterVideo(int index) {
cout << "-------------------------------------------------" << endl;
cout << "Arrêt vidéo" << endl << endl;
system("killall omxplayer.bin");
}
void demarrerVideo(int index) {
cout << "-------------------------------------------------" << endl;
cout << "Video : " << listeFichiers[index] << endl << endl;
int pid = fork();
if(pid == 0) {
string fichierVideo = chemin + listeFichiers[index];
execlp("omxplayer", " ", fichierVideo.c_str(), NULL);
_exit(0);
}
}
// Appeler lors de la réception d'une trame DMX
void NewDmx(const ola::client::DMXMetadata &metadata,
const ola::DmxBuffer &data) {
if ((fichirEnLecture >= 0) && (data.Get(fichirEnLecture) < 128)) {
arreterVideo(fichirEnLecture);
fichirEnLecture = -1;
}
for (int i = 0; i < listeFichiers.size(); i++) {
if (data.Get(i) > 127) {
if (fichirEnLecture != i) {
arreterVideo(fichirEnLecture);
fichirEnLecture = i;
demarrerVideo(fichirEnLecture);
break;
}
}
}
}
void listerRepertoire(string chemin, vector &tableau) {
DIR* repertoire = opendir(chemin.c_str());
struct dirent* fichierLu;
if (repertoire != NULL) {
while ((fichierLu = readdir(repertoire)) != NULL) {
if (fichierLu->d_name[0] != '.') {
tableau.push_back(fichierLu->d_name);
}
}
sort (tableau.begin(), tableau.end());
}
closedir(repertoire);
}
unsigned int univers() {
unsigned int univers;
ifstream fichier("/home/pi/.ola/ola-port.conf", ios::in);
string ligne;
size_t position;
while(getline(fichier, ligne)) {
position = ligne.find("2-1-I-0 = ");
if (position == 0) {
ligne = ligne.substr(10);
univers = stoi(ligne);
cout << univers << endl;
}
}
fichier.close();
return univers;
}
int main() {
// Lister les fichiers vidéos présents
listerRepertoire("/home/pi/PIHDD", listeFichiers);
for (int i = 0; i < listeFichiers.size(); i++) {
cout << listeFichiers[i] << endl;
}
ola::InitLogging(ola::OLA_LOG_INFO, ola::OLA_LOG_STDERR);
ola::client::OlaClientWrapper wrapper;
if (!wrapper.Setup())
exit(1);
ola::client::OlaClient *client = wrapper.GetClient();
// Set the callback and register our interest in this universe
client->SetDMXCallback(ola::NewCallback(&NewDmx));
client->RegisterUniverse(
univers(), ola::client::REGISTER,
ola::NewSingleCallback(&RegisterComplete));
wrapper.GetSelectServer()->Run();
return 0;
}
===== Configuration supplémentaires =====
[[https://www.supinfo.com/articles/single/2442-raspbian-jessy-configuration-wifi-ligne-commande|Raspbian Jessy, configuration wifi en ligne de commande]]
[[https://wiki.openlighting.org/index.php?title=Raspberry_Pi_Media_Player|Raspberry Pi Media Player]]
=== Écran noir au démarrage ===
*modifier le fichier cmdline.txt
sudo nano /boot/cmdline.txt
et ajouter sur la même ligne à la fin
consoleblank=20
*source
[[https://www.raspberrypi.org/documentation/configuration/screensaver.md|Setting the screen saver/screen blanking]]
=== Accéder à la borne ===
Il faut éditer le fichier wpa_supplicant.conf avec la commande :
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
Ensuite ajouter :
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
network={
ssid="SSID de la borne"
psk="Mot de passe de la borne"
key_mgmt=WPA-PSK
}
=== Adresse IP fixe ===
Afin d'obtenir une IP fixe pour chaque Raspberry Pi, il faut éditer le fichier /etc/network/interfaces avec la commande :
sudo nano /etc/network/interfaces
Puis insérer le lignes suivantes à la fin du fichier :
auto wlan0
iface lo inet loopback
iface eth0 inet dhcp
allow-hotplug wlan0
iface wlan0 inet static
address 192.168.1.101
netmask 255.255.255.0
gateway 192.168.1.200
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
iface default inet dhcp
===== GrandMA2 =====
=== Configuration ArtNet ===
{{media_03:artnetconfig.png?600|}}
=== Configuration sACN ===
Mettre le node dans le même sous-réseau :
[Setup] > [Network Configuration] > [DMX Node]
Pour modifier l'IP du code faire un clic secondaire sur le champ IP Ethernet 1 (Eth0) et entrer
192.168.1.202
Configurer la sortie sACN
[Setup] > [Network Protocols] > [sACN]
Remplir les champs avec les valeurs indiquées ci-dessous :
{{media_12:sacn1.png?600|}}
Ne pas oublier d'activer la sortie sACN en cliquant sur
[Ouput sACN Active]
===== Version sACN =====
Avec l'utilisation des sACN, il n'est pas nécessaire d'installer le serveur OLA. Cela simplifie grandement l'installation et l'exploitation.
Les fichiers sont triés et lus dans l'ordre alphabétique qui correspondent au numéro de circuit.
Le circuite 512 éteint la Raspberry PI. Il est nécessaire de le faire pour ne pas détériorer la Pi.
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// g++ -Wall -o UDP UDP.cpp
// Documentation sACN : http://tsp.esta.org/tsp/documents/docs/E1-31-2016.pdf
// http://bousk.developpez.com/cours/reseau-c++/UDP/01-introduction-premiers-pas/
//
///////////////////////////////////////////////////////////////////////////////////////////////////
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include // stringstream
#include
#include
#include
#include
using namespace std;
// Variabes globales
static const string chemin = "/home/pi/PIHDD/";
static const int port = 5568; // Port par défaut du protocole sACN
vector listeFichiers;
unsigned int fichirEnLecture = -1;
string intToHexString (int n) {
ostringstream oss;
oss << hex << n;
return oss.str();
}
string intToString (int n) {
ostringstream oss;
oss << n;
return oss.str();
}
void arreterVideo(int index) {
cout << "-------------------------------------------------" << endl;
cout << "Arrêt vidéo" << endl << endl;
system("killall omxplayer.bin");
}
void demarrerVideo(int index) {
cout << "-------------------------------------------------" << endl;
cout << "Video : " << listeFichiers[index] << endl << endl;
int pid = fork();
if(pid == 0) {
string fichierVideo = chemin + listeFichiers[index];
execlp("omxplayer", " ", fichierVideo.c_str(), NULL);
_exit(0);
}
}
void arreterRaspberry() {
system("sudo halt");
exit(1);
}
void listerRepertoire(string chemin, vector &tableau) {
DIR* repertoire = opendir(chemin.c_str());
struct dirent* fichierLu;
if (repertoire != NULL) {
while ((fichierLu = readdir(repertoire)) != NULL) {
if (fichierLu->d_name[0] != '.') {
tableau.push_back(fichierLu->d_name);
}
}
sort (tableau.begin(), tableau.end());
}
closedir(repertoire);
}
int main(int argc, char *argv[]) {
// Variables ;
int yes = 1;
// Création du socket
int sckt = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if (sckt < 0) {
perror("socket");
exit(1);
}
// Ouverture du socket
sockaddr_in adresse;
adresse.sin_family = AF_INET; // L'adresse est IPv4
adresse.sin_port = htons(port); // Définit le port
adresse.sin_addr.s_addr = INADDR_ANY; // Permet d'écouter sur toutes les interfaces locales
// V2rifie si le socket n'est pas déjà ouvert
int res = setsockopt(sckt, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if(res == -1) {
perror("setsockopt");
exit(1);
}
res = bind(sckt, (struct sockaddr*) &adresse, sizeof(adresse));
if (res < 0 ) {
perror("bind");
exit(1);
}
// Lister les fichiers vidéos présents
listerRepertoire(chemin, listeFichiers);
for (unsigned int i = 0; i < listeFichiers.size(); i++) {
cout << listeFichiers[i] << endl;
}
// Boucle principale
while(1) {
char packet_data[638];
struct sockaddr_in cliaddr;
socklen_t taille = sizeof(cliaddr);
int received_bytes = recvfrom(sckt, packet_data, sizeof(packet_data), 0, (struct sockaddr*) &cliaddr, &taille);
if ( received_bytes > 0 ) {
// Traitement des données reçues
for (unsigned int i = 0; i < listeFichiers.size(); i++) {
if (packet_data[i + 126] > 127) { // 126 Début de la trame DMX
if (fichirEnLecture != i) {
arreterVideo(fichirEnLecture);
fichirEnLecture = i;
demarrerVideo(fichirEnLecture);
break;
}
}
}
// Le canal 512 éteint la Raspberry Pi
if (packet_data[637] > 127) {
arreterRaspberry();
}
/*
//Affichage des valeurs
string blip = "";
for (int i = 126; i < 638; i++) { // Les vameurs DMX commencent à partir de l'indice 126 voi p. 5
blip.append(intToString(packet_data[i]) + "/");
}
cout << blip << endl << endl;
*/
}
}
close(sckt);
return 0;
}
===== Améliorations =====
Il serait bien de réécrire le programme et d'utiliser la librairie : [[https://github.com/kr15h/ofxPiMapper|ofxPiMapper]]
{{tag>c++ sylvain realisations_logicielles}}