- Python 99.4%
- Shell 0.6%
Le README garde ce qui a de la valeur hors du dépôt : les six pièges du protocole ED1, cités dans le firmware (port de réponse 60001, .ild à la racine de la carte SD, /ilda/<n>/play borné à 10, noms tronqués à 15 caractères, /ilda/pps sans effet sur un fichier de carte SD car le lecteur réimpose le débit de l'en-tête, liste UDP Arriv par rafales), et le format d'autoplay.txt. Corrections : l'arborescence omettait graph.py et EtherTool.app, le compte de tests annoncé (57) datait de la v0.1.0 (70 depuis la v0.2.0), la section interface listait quatre onglets pour cinq puces, et le CHANGELOG était dupliqué en fin de README. Les chemins absolus de la machine de développement sont retirés au profit de la racine du dépôt, et la recette pytest (il faut un Python avec _tkinter) est écrite. État assumé dans le README : le protocole est validé contre le firmware source et le faux DAC, jamais contre du matériel réel (D5 du devbook : le réseau du DAC passe par le VPN utun4, aucune réponse en ping, TCP 7765, UDP 7654 et UDP 60000). Un Ether Dream V2 n'est pas couvert. |
||
|---|---|---|
| docs | ||
| ethertool | ||
| EtherTool.app/Contents | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| pyproject.toml | ||
| README.md | ||
ethertool
Pilotage OSC des Ether Dream (ED1) du réseau local : détecter les DAC,
voir ce qu'ils jouent, et leur envoyer les commandes de l'User Guide
(lecture d'un .ild de la carte SD, cadence, géométrie, réseau).
Interface graphique Tkinter et ligne de commande, sans aucune dépendance Python tierce (l'OSC est encodé à la main).
ethertool/
├── ethertool/
│ ├── osc.py encodage / décodage OSC (UDP, sans dépendance)
│ ├── protocol.py décodage du broadcast ED1 (datagramme de 36 octets)
│ ├── dac.py les commandes OSC du firmware ED1
│ ├── discovery.py détection : broadcast UDP 7654 + sonde TCP 7765
│ ├── graph.py graphique Canvas de l'occupation du buffer
│ ├── app.py interface graphique Tkinter
│ └── cli.py ligne de commande
├── tests/ 70 tests, dont un faux DAC qui imite le firmware
├── EtherTool.app/ bundle macOS (double-clic = lancement)
└── docs/ devbook + journal
Installation
Aucun paquet à installer. Il faut Python 3.11 ou plus récent, et ce Python doit avoir Tkinter (l'interface graphique en dépend) :
# macOS, avec Homebrew (python@3.11 embarque tkinter)
brew install python@3.11
# ou avec uv
uv python install 3.11
État de la version
- Le protocole est validé contre le firmware source (
j4cDAC) et contre le faux DAC des tests, pas contre du matériel réel : aucune machine de ce dépôt n'avait d'Ether Dream joignable au moment du développement (le réseau du DAC passait par un VPN, sans réponse — voirD5dans le devbook). La première connexion réelle reste à faire. - Les commandes exposées sont celles du firmware ED1 (V1). Un Ether Dream
V2 (
etherdream-2) a d'autres adresses OSC : non testé ici.
Lancement
cd ethertool # à la racine du dépôt
# interface graphique
python3.11 -m ethertool.app
# ligne de commande
python3.11 -m ethertool.cli --help
Ligne de commande
# balayer une plage d'adresses (le broadcast peut etre filtre par le reseau)
python3.11 -m ethertool.cli scan 192.168.1. 1-254
# sonder une adresse precise sur le port TCP de controle
python3.11 -m ethertool.cli probe 192.168.1.4
# lister les .ild presents sur la carte SD
python3.11 -m ethertool.cli list 192.168.1.4
# jouer un fichier par son nom
python3.11 -m ethertool.cli play 192.168.1.4 maze.ild
# jouer le 3e fichier trouve sur la carte
python3.11 -m ethertool.cli play-index 192.168.1.4 3
# arreter, regler la cadence, la repetition
python3.11 -m ethertool.cli stop 192.168.1.4
python3.11 -m ethertool.cli pps 192.168.1.4 30000
python3.11 -m ethertool.cli fps 192.168.1.4 25
python3.11 -m ethertool.cli repeat 192.168.1.4 on
# geometrie
python3.11 -m ethertool.cli geom 192.168.1.4 size 0.5
python3.11 -m ethertool.cli geom 192.168.1.4 offset 0.4 -0.2
python3.11 -m ethertool.cli geom 192.168.1.4 corner tl -1 1
Interface graphique
Quatre onglets, une ligne par Ether Dream détecté, et un journal en bas.
- Contenu — la liste des
.ildde la carte SD (renvoyée par le firmware), lecture par index ou par nom exact,/ilda/repeat,/stop. - Cadence —
/ilda/fps(0 à 100) est mis en avant : c'est le seul levier de vitesse réel sur un.ild./ilda/ppsest aussi présent mais annoté comme sans effet sur un fichier de carte SD (le firmware réimpose le débit déclaré dans l'en-tête du fichier). Bouton « Relire » pour récupérer les valeurs rapportées par le DAC. - Buffer — graphique de la taille utilisée du buffer
(
buffer_fullness), un échantillon par seconde. Une courbe qui descend vers zéro signale un underflow (le DAC consomme plus de points qu'il n'en reçoit). - Géométrie —
/geom/size,/geom/offset, les quatre coins, le verrouillage des côtés, et par couleur le gain, l'offset et le décalage temporel. - Réseau — configuration IP statique (
/net/ipaddr,/net/netmask,/net/gateway) et diagnostics.
Les quatre pièges du protocole ED1
Ces points viennent de la lecture du firmware j4cDAC
(https://github.com/j4cbo/j4cDAC), pas de la documentation.
1. Les réponses arrivent sur le port 60001
Le firmware écoute les commandes sur le port UDP 60000, mais répond toujours sur le port 60001 de la machine d'origine :
/* firmware/net/osc.c */
udp_bind(&osc_pcb, IP_ADDR_ANY, 60000);
udp_sendto(&osc_pcb, p, osc_last_source, 60001);
Un client qui n'écoute pas sur 60001 ne recevra jamais les readouts
(/ilda/<n>/name, /ilda/ppsreadout, …). C'est la raison pour laquelle
ethertool lie explicitement son socket sur 60001.
2. Les .ild sont lus à la racine de la carte SD
Le firmware ouvre le répertoire racine et itère sur ses entrées :
/* firmware/net/ilda-osc.c, walk_fs() */
f_opendir(&dir, "");
...
if (finfo.fattrib & AM_DIR) continue; /* les sous-dossiers sont ignorés */
Un nom comme shows/maze.ild est donc refusé par le client.
3. /ilda/<n>/play s'arrête à 10
Le firmware n'enregistre que /ilda/1/play … /ilda/10/play. Au-delà, il
faut passer par /ilda/play <nom>.
De plus, les noms renvoyés dans la liste sont tronqués à 15 caractères (contrainte héritée de TouchOSC) :
if (strlen(fn) > 15) fn[15] = '\0';
L'interface signale les noms tronqués et propose un champ « nom exact ».
4. L'User Guide est trompeur sur un point
La documentation écrit : « Playing a file by name can currently only be done
via autoplay.txt, not via OSC ». Le firmware expose pourtant
{ "/ilda/play", PARAM_TYPE_S1, ... } : c'est bien possible en OSC.
5. /ilda/pps est sans effet sur un fichier de la carte SD
Le lecteur .ild appelle lui-même dac_set_rate() avec le débit déclaré
dans l'en-tête du fichier ILDA, donc le pps envoyé par OSC est écrasé à
chaque nouvelle frame. /ilda/fps est le vrai levier : le firmware
calcule ilda_points_per_frame = dac_current_pps / max_fps et répète
les frames pour atteindre ce ratio.
6. La liste des fichiers peut arriver incomplète
L'UDP est sans connexion : le firmware envoie les noms d'un seul trait (85
paquets pour 85 fichiers) et certains peuvent se perdre. ethertool
refait donc la collecte tant que les index reçus ne forment pas une suite
contiguë, et fusionne les lots successifs — la liste est complète même
avec 50 % de paquets perdus (testé dans tests/test_dac.py).
Le fichier autoplay.txt
Au démarrage, le firmware lit autoplay.txt à la racine de la carte SD
et exécute chaque ligne comme une commande OSC. C'est le moyen de faire
démarrer un show automatiquement au boot :
/net/ipaddr 192.168.1.4
/net/netmask 255.255.255.0
/net/gateway 192.168.1.1
/ilda/pps 30000
/ilda/fps 25
/ilda/repeat 1
/ilda/play maze.ild
Un répertoire de ressources (~/codesync/etherdream/) contient déjà 85 .ild
et un autoplay.txt de ce type.
Tests
cd ethertool # à la racine du dépôt
python3.11 -m unittest discover -s tests -t .
Septante tests, sans aucun matériel : tests/fake_dac.py simule un
Ether Dream (réponses sur le port 60001, troncature des noms à 15
caractères, index limité à 10, perte de paquets). Il sert aussi de
documentation exécutable du protocole.
$ python3.11 -m unittest discover -s tests -t .
Ran 70 tests in 16.9s
OK
Avec pytest : il faut un Python qui possède _tkinter (l'onglet Buffer
importe Tkinter). Le pytest installé via pipx sur un Python sans Tkinter
échoue à la collecte. Recette :
python3.11 -m venv .venv && .venv/bin/pip install -q pytest
.venv/bin/python -m pytest -q # 70 passed
Voir aussi
CHANGELOG.md— l'historique des versions.docs/devbook.md— la file des demandes (D…) et les décisions à trancher.docs/journal.md— le journal daté du développement, avec les causes racines et les mesures.