USB2DMX-Interface im Eigenbau

So, dann beginnen wir mal mit meinem ersten (öffentlichen) Arduino-Projekt. Ich beschäftige mich nebenher noch ein wenig mit Veranstaltungstechnik, also Licht und Musik. Ist so ein Hobby von mir. Da ich auch ein paar Geräte besitze, die DMX-steuerbar sind, ich allerdings (noch) kein DMX-Pult besitze, dachte ich mir, dass ich da doch gewiss etwas basteln könnte. Professionelle Software wie Freestyler sollte allerdings trotzdem nutzbar sein.

Zuerst kommt natürlich der Hardware-Teil. Daniel Lindenfelser hat in seinem Block eine schöne Bau-Anleitung für DMX-Steuerung per Arduino veröffentlicht: http://danlin.de/projekte/arduino-dmx/
Diesen Aufbau habe ich größtenteils übernommen, eingepasst wurde das ganze bei mir in eine einfache IP-Abzweigdose aus dem Baumarkt. Bilder folgen eventuell noch. Die pure Steuerung meiner Scheinwerfer durch Hardcoding im Arduino-Sketch funktioniert in diesem Punkt auch schon. Im Projekt von Daniel Lindenfelser wird dafür auf eine selbstgeschriebene Software für den PC zurückgegriffen, das wollen wir aber nicht. Wir wollen Programme wie Freestyler nutzen. Daher ist unser erklärtes Ziel, ein USB2DMX-Interface zu bauen. Kurz ein wenig gegooglet und auch gleich was gefunden: http://playground.arduino.cc/DMX/Opendmx

Da gibt es nur ein Problem… Wir schreiben mittlerweile das Jahr 2016, und der aktuelle Arduino UNO benutzt keinen FTDI-Chip mehr. Eine ähnliche Bemerkung findet sich auch auf der oben verlinkten Seite: „All this just works with an old arduino board, since the UNO there is a different chip used :(„

Doch wo ist eigentlich das Problem an der Sache? Was genau funktioniert bei aktuellen Arduino-Boards anders? Um diese Frage zu klären, schauen wir uns zunächst die Signalkette an.

Signalkette DMX-Projekt

Da muss unser Steuersignal also durch. Die größte Schwierigkeit besteht allerdings – und man glaubt es kaum – in der Kommunikation zwischen PC und Arduino! Wenn wir beide Seiten komplett selber programmieren würden, wäre das sicherlich nicht allzu aufwändig. Wenn wir allerdings irgendwann am PC eine Software wie Freestyler nutzen möchten, müssen wir entweder einen eigenen „Treiber“ für Freestyler schreiben oder ein vorhandenes Protokoll im Arduino-Teil umsetzen. Und da käme eigentlich der FTDI-Chip ins Spiel. Durch die spezielle Verdrahtung auf der Platine wäre dieser mit dem seriellen Ausgang des Arduinos verbunden gewesen. Der FTDI-Chip wird nämlich auch im OpenDMX-Projekt benutzt, daher können auch alle größeren DMX-Softwares mit diesem Chip kommunizieren. Bei den alten Arduinos ist dieses Signal also einfach durchgereicht worden und alles hat gepasst. Diesen Luxus haben wir heute nicht mehr, wir müssen uns also etwas einfallen lassen.

Da die Programmierung einer eigenen Interface-Definition für jedes größere DMX-Studio und die dazu anfallenden Eigenentwicklung eines kompletten Protokolls doch etwas aufwändig wäre, habe ich mich dazu entschieden, ein möglichst einfaches, bekanntes Protokoll zu übernehmen und auf den Arduino anzupassen. Am besten geeignet erschien mir miniDMX 2, zugehörige Dokumentation zu finden auf dieser Webseite: http://www.dzionsko.de/index.php?n=MiniDMX.Protokoll

Mit ein bisschen Fummelei, der DmxSimple-Bibliothek und viel Zeit ist es mir dann auch gelungen, dieses Protokoll relativ fehlerfrei zu implementieren. Sind zwar nur knapp 100 Zeilen, diese haben aber interessanterweise eine ziemlich komische Denkweise erfordert. Vielleicht gibt das mal einen Artikel zum psychologischen Aspekt von Programmieren. Auf jeden Fall funktioniert er, Freestyler läuft einwandfrei!

(Nutzung des folgenden Codes für kommerzielle Zwecke ist verboten. Nutzung für nicht-kommerzielle Zwecke ist mit Urheberangabe erlaubt. Schreibt mir doch, wenn ihr es irgendwo erfolgreich einsetzen konntet.)

#include <DmxSimple.h>

int mdmx_begin = 0x5A;
int mdmx_96c  = 0xA0;
int mdmx_256c = 0xA1;
int mdmx_512c = 0xA2;
int mdmx_end = 0xA5;
int mdmx_success = 0xC1;
int mdmx_failure = 0xC0;

int step_current = 0; //variabel
int step_begin = 0;
int step_size = 1;
int step_channels = 2;
int step_end = 3;

int channel = 0;
int maxchannel = 0;
int inByte = 0;


void setup() {
  DmxSimple.usePin(3);
  DmxSimple.maxChannel(512);

  DmxSimple.write(2, 255);
  DmxSimple.write(3, 255);
  
  Serial.begin(115200);
  //Serial.begin(9600);
  //DmxSimple.write(3, 255);
  //DmxSimple.write(8, 255);
}

void loop() {
  //Wenn keine Daten verfügbar sind, von vorne anfangen
  if (Serial.available() == 0)
  {
    return;
  }
  //Daten lesen
  inByte = Serial.read();

  //Blockstart
  if((inByte == mdmx_begin) && (step_current == step_begin))
  {
    //DmxSimple.write(8, 255);
    step_current = step_size;
    return;
  }

  //Protokollgröße (96, 256, 512) bestimmen
  if(step_current == step_size)
  //if(true)
  {
    if(inByte == mdmx_96c) //96-Kanal-Modus (miniDMX 1.0)
    {
      maxchannel = 96;
      //DmxSimple.write(6, 255);
    }
    if(inByte == mdmx_256c) //256-Kanal-Modus
    {
      maxchannel = 256;
      //DmxSimple.write(7, 255);
    }    
    if(inByte == mdmx_512c) //512-Kanal-Modus
    {
      maxchannel = 512;
      //DmxSimple.write(8, 255);
    }
    channel = 1;
    step_current = step_channels; //debug
    return;
  }

  //Kanäle lesen
  if((step_current == step_channels) && (channel != maxchannel))
  {
    DmxSimple.write(channel, inByte);
    channel++;
    if(channel == maxchannel)
    {
      step_current = step_end;
    }
    return;
  }

  if(step_current == step_end)
  {
    if(inByte == mdmx_end)
    {
      Serial.write(mdmx_begin);
      Serial.write(mdmx_success);
      Serial.write(mdmx_end);
    }
    else
    {
      Serial.write(mdmx_begin);
      Serial.write(mdmx_failure);
      Serial.write(mdmx_end);
    }
    step_current = step_begin;
  }
}