DMX Steuerung Selbstbau? Teil 7 Vom Browser zum Licht

Jetzt gibt es schöne Fader. Nun muss der eingestellte Wert aber auch wirklich zum gewünschten Ergebnis führen. Bisher gibt es noch keine Funktion. HTML und PHP alleine können das nicht vernünftig lösen. Damit die Seite nicht bei jeder Änderung neu geladen werden muss, muss Java Script verwendet werden.

Der Fader sieht in HTML folgendermaßen aus:

<input type="range" name="rangeInput" min="0" max="255" onchange="WriteSingleDMXValue(this.value,0);"

Über den Namen muss man später noch einmal nachdenken. Aktuell Spielt er keine Rolle. Der Maximalwert liegt bei 2 hoch 8. Was einem 8 Bit Wert entspricht. Also 256 Abstufungen pro Kanal.

Wenn der Wert sich geändert hat, wird die Funktion WriteSingleDMXValue aufgerufen. Der erste Parameter ist der Wert des Faders. Also ein Wert zwischen 0 und 255. Der Zweite Wert ist die Kanalnummer des DMX Kanals. Der Parameter „onchange“ verhält sich bei verschiedenen Browsern unterschiedlich. Bei Firefox funktioniert es so wie es soll. Mit abgeschaltetem Java Script funktioniert es gar nicht.

Das aufgerufene Script sieht erst einmal sehr einfach aus, hat es aber doch in sich. Es wird ein unabhängiger Zugriff auf eine Adresse gestartet. Dabei gibt es einige Einschränkungen des Browser die man beachten muss. Prinzipiell wird ein Zugriff auf eine PHP Datei mit angehängten Parametern „simuliert“. Ein offener Punkt ist, ob man die Verbindung jedes mal mit einem Close schließen muss, oder ob man es so lassen kann.

<script>
  'use strict'
  function WriteSingleDMXValue(wert,kanal){
  var phpzugriff = new XMLHttpRequest();
  phpzugriff.open("GET","EinzelWert.php?wert=" + wert + "&kanal=" + kanal ,wert);
  phpzugriff.send();
  }

</script>

Die Aufgerufene PHP Datei ist wirklich simpel, und reicht die Werte einfach an eine C Funktion weiter. Der Aufruf erfolgt wie ein Konsolen Aufruf.

<?php

$wert = $_GET['wert'];
$kanal = $_GET['kanal'];

$Kommando = "./EinzelWert ";
$Kommando .=  $kanal;
$Kommando .= " ";
$Kommando .=  $wert;
$Kommando .= "\n";

$output = system($Kommando);

?>

In der C Funktion wird zuerst die Anzahl der Parameter überprüft:

      //***************************************************
      // Parameter Überprüfen
      //***************************************************
      if(iAnzahlParameter != 3)
      {
         printf("Anzahl der Argumente nicht korrekt.\n");
         printf("Befehl: EinzelWert [Kanalnummer] [Kanalwert]\n");
      }

Dann müssen die Werte in „vernünftige“ Zahlen konvertiert werden. Zum Schluss werden Hex-Werte benötigt. Die werden hier erzeugt:

         iKanalnummer=atoi(argv[1]);
         iKanalwert=atoi(argv[2]);
         if(iKanalnummer >= 512)
         {
            printf("DMX Kanalnummer zu groß! Wert ist:%d\n",iKanalnummer);
            return(0);
         }
         if(iKanalwert > 255)
         {
            printf("DMX Kanalwert zu groß! Wert ist:%d\n",iKanalwert);
            return(0);
         }

Zum Glück gibt es schon eine fertige Funktion (atoi) dafür. Selber konvertieren ist in C etwas lästig.

Jetzt kommt noch das altbekannte auslesen der Sende Datei. Dann muss ein einziger Wert geändert werden:

Buffer[DATEN_OFFSET + iKanalnummer]=iKanalwert;

Und zum Schluss wird wieder geschrieben.

Auch in dieser Funktion fehlt noch eine vernünftige Fehlerbehandlung beim Dateizugriff. Sollte duch Nebenläufigkeiten ein Zugriffsfehler auftreten, geht der Wert zum schreiben einfach verloren.

Zum Schluss noch die komplette C Datei zum schreiben eines einzelnen Wertes:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


#define ANZAHL_BYTES 518
#define DATEN_OFFSET 5

int main(int iAnzahlParameter, char *argv[])
{
  //***************************************************
  // Variablen
  //***************************************************
  char Buffer[ANZAHL_BYTES];     //Puffer für Daten
  int i;                         //Zählvariable für Schleife
  FILE *fDaten_Datei;            //Datei Handle für DMX Werte
  int iKanalnummer;
  int iKanalwert;

  //***************************************************
  // Code
  //***************************************************
      //***************************************************
      // Parameter Überprüfen
      //***************************************************
      if(iAnzahlParameter != 3)
      {
         printf("Anzahl der Argumente nicht korrekt.\n");
         printf("Befehl: EinzelWert [Kanalnummer] [Kanalwert]\n");
      }
      else
      {
         iKanalnummer=atoi(argv[1]);
         iKanalwert=atoi(argv[2]);
         if(iKanalnummer >= 512)
         {
            printf("DMX Kanalnummer zu groß! Wert ist:%d\n",iKanalnummer);
            return(0);
         }
         if(iKanalwert > 255)
         {
            printf("DMX Kanalwert zu groß! Wert ist:%d\n",iKanalwert);
            return(0);
         }


      }


      //***************************************************
      // Hole Daten vom Dateisystem
      //***************************************************
      fDaten_Datei = fopen("/var/www/html/Config/SendeDaten.dmx","r");
      if(fDaten_Datei == 0)
      {
         printf("SendeDaten.dmx fehlt! \n");
      }
      else
      {
        fread(Buffer,ANZAHL_BYTES,1,fDaten_Datei);
        //***************************************************
        // Schließe Datei
        //***************************************************
        fclose(fDaten_Datei);
      }


      //***************************************************
      // Ändern eines einzelnen Wertes
      //***************************************************
      Buffer[DATEN_OFFSET + iKanalnummer]=iKanalwert;

      //***************************************************
      // Öffne Datei zum Schreiben
      // Und schreibe zeilenweise konvertierte Daten
      //***************************************************
      fDaten_Datei = fopen("/var/www/html/Config/SendeDaten.dmx","w+");
      if(fDaten_Datei == 0)
      {
         printf("Kann SendeDaten.dmx nicht öffnen! \n");
      }
      else
      {
        fwrite(Buffer,ANZAHL_BYTES,1,fDaten_Datei);
        fclose(fDaten_Datei);
      }


  return(1);
}

DMX Steuerung Selbstbau? Teil 6 Werte auf Weboberfläche anzeigen

Es sollen Fader bereitstehen, um die Helligkeit der einzelnen Kanäle anzuzeigen. Und dann natürlich auch einzustellen. Leider ist es in PHP sehr unkomfortabel mit .hex Dateien zu arbeiten. Deshalb gibt es zuerst ein kleines Programm das die Hex Datei in eine Datei mit ASCII Zeichen umwandelt.

In einer Schleife werden alle Werte in eine Datei geschrieben. Jeder Wert kommt auf eine neue Zeile:

      {
        sprintf(cKBuffer, "%d\n", (unsigned char)Buffer[i]);
        fputs(cKBuffer,fDaten_Datei);
      }

Eine Fehlerbehandlung fehlt hier übrigens. Die kommt später noch.

In PHP wird die Hilfsfunktion aufgerufen:

  echo system('./Einlesen_Konverter');

Die erzeugte Datei wird in ein Array eingelesen und der Zeilenumbruch sofort entfernt:

  $LeseDatei = fopen('Config/KLDaten.dmp',r);
  for($i=0;$i<518;$i++)
  {

    $Wert[$i] = fgets($LeseDatei);
    $Wert[$i] = str_replace("\n", "",$Wert[$i]);

Und nun können die einzelnen Werte als Fader angezeigt werden.

    echo("<input type=\"range\" name=\"Pegel1\" min=\"0\" max=\"255\" step=\"1.0\" ");
    echo("value=\"");
    echo $Wert[$i];
    echo ("\" onchange=\"WriteSingleDMXValue(this.value,");
    echo $i-5;
    echo(")\"> <label> DMX Kanal ");
    echo $i-4;
    echo ("</label>");
    echo '<br>';
    echo "\n";

Da sind noch einige Werte Dabei, die zum Übertragungsprotokoll gehören. Und alle 512 Kanäle braucht man im Normalfall nicht. Deshalb werden hier nur die ersten paar angezeigt:

    if(($i > 4) && ($i < 21))
    {

Jetzt kann man beim Seitenaufruf direkt die eingestellten Werte sehen. Einziger Nachteil, wenn ein anderer Nutzer die Werte ändert, muss man die Seite neu Laden um die Änderung zu sehen. Mit PHP und HTML ist das Dynamisch nicht möglich. Dazu benötigt man Java Script.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


#define ANZAHL_BYTES 518

int main()
{
  //***************************************************
  // Variablen
  //***************************************************
  char Buffer[ANZAHL_BYTES];     //Puffer für Daten
  int i;                         //Zählvariable für Schleife
  FILE *fDaten_Datei;            //Datei Handle für DMX Werte
  unsigned char cKBuffer[10];    //Buffer zum Schreiben der 
                                 //Konvertierten Werte.
                                 //Max 255 Dezimal plus
                                 //Zeilenumbruch

  //***************************************************
  // Code
  //***************************************************
      //***************************************************
      // Hole Daten vom Dateisystem
      //***************************************************
      fDaten_Datei = fopen("/var/www/html/Config/SendeDaten.dmx","r");
      if(fDaten_Datei == 0)
      {
         printf("SendeDaten.dmx fehlt! \n");
      }
      else
      {
        fread(Buffer,ANZAHL_BYTES,1,fDaten_Datei);
        //***************************************************
        // Schließe Datei
        //***************************************************
        fclose(fDaten_Datei);
      }

      //***************************************************
      // Öffne Datei zum Schreiben
      // Und schreibe zeilenweise konvertierte Daten
      //***************************************************
      fDaten_Datei = fopen("/var/www/html/Config/KLDaten.dmp","w+");
      for(i=0;i<ANZAHL_BYTES;i++)
      {
        sprintf(cKBuffer, "%d\n", (unsigned char)Buffer[i]);
        fputs(cKBuffer,fDaten_Datei);
      }


      fclose(fDaten_Datei);



  return(1);
}
<?php
  $Wert[]=518;  
  echo system('./Einlesen_Konverter');

  $LeseDatei = fopen('Config/KLDaten.dmp',r);
  for($i=0;$i<518;$i++)
  {

    $Wert[$i] = fgets($LeseDatei);
    $Wert[$i] = str_replace("\n", "",$Wert[$i]);
    
    if(($i > 4) && ($i < 21))
    {
    echo("<input type=\"range\" name=\"Pegel1\" min=\"0\" max=\"255\" step=\"1.0\" ");
    echo("value=\"");
    echo $Wert[$i];
    echo ("\" onchange=\"WriteSingleDMXValue(this.value,");
    echo $i-5;
    echo(")\"> <label> DMX Kanal ");
    echo $i-4;
    echo ("</label>");
    echo '<br>';
    echo "\n";
    }

  }
?>

DMX Steuerung Selbstbau? Teil 5 Konfiguration mit Weboberfläche Laden

Im letzten Beitrag wurden die bereits erstellten Konfigurationsdateien von einem C-Programm „verwaltet“. Das Sende-Programm liest immer aus einer Datei. Um andere Daten zu senden, wird die Datei überschrieben.

Die Lösung mit C hat einige Nachteile. Zum Schluss soll die Steuerung über andere Geräten aus dem Netzwerk erfolgen. Und die beste Lösung ist ein Webserver, der eine Website bereitstellt. Wenn man sich nicht zu dumm anstellt, kann man eine Oberfläche erzeugen, die auf allen gängigen Geräten funktioniert. Also Android, iOS, Windows, Linux und selbst auf FirefoxOS.

Zuerst wird eine PHP Datei benötigt. Diese heißt hier ganz kreativ 001.php.

Dann wird eine Oberfläche benötigt, die die entsprechende Information an den Webserver sendet.

<form action="001.php" method="post">
<button type="submit" name="KonfigNummer" value="1"> Konfig 1</button>
<button type="submit" name="KonfigNummer" value="2"> Konfig 2</button>
<button type="submit" name="KonfigNummer" value="3"> Konfig 3</button>
<button type="submit" name="KonfigNummer" value="4"> Konfig 4</button>
<button type="submit" name="KonfigNummer" value="5"> Konfig 5</button>
<button type="submit" name="KonfigNummer" value="6"> Konfig 6</button>
<button type="submit" name="KonfigNummer" value="7"> Konfig 7</button>
<button type="submit" name="KonfigNummer" value="8"> Konfig 8</button>
<button type="submit" name="KonfigNummer" value="9"> Konfig 9</button>
</form>

Hier handelt es sich um ein HTML Element Namens „form“. Es ruft die Datei 001.php auf.

method="post"

„post“ bedeutet, dass die Informationen nicht in der URL übergeben werden. Als „value“ werden die Werte übergeben. Hier ganz kreativ Zahlen von 1 bis 9.

Mit diesen Zahlen wird die Adresse und der Name der gewünschten Konfigurationsdatei zusammengebaut:

  $KonfigNumberVar = $_POST["KonfigNummer"];
  $KonfigPfad = 'Config/KonfigDaten';
  $KonfigPfad .= $KonfigNumberVar;
  $KonfigPfad .= '.dmx';

Anschließend werden die Sendedatei mit der Konfigurationsdatei Überschrieben.

$Return = copy($KonfigPfad , 'Config/SendeDaten.dmx');

Zum Schluss wird noch ausgegeben, welche Konfiguration geladen wurde.

  if($Return == 1)
  {
     echo 'Konfiguration ',$KonfigNumberVar, ' geladen. <br>';
  }

Die Website sieht natürlich noch nicht schön aus, aber sie funktioniert.

Und Komplett:

<html>
<head>
    <title>Datei Kopieren</title>
</head>
  <body>
<?php

  $KonfigNumberVar = $_POST["KonfigNummer"];
  $KonfigPfad = 'Config/KonfigDaten';
  $KonfigPfad .= $KonfigNumberVar;
  $KonfigPfad .= '.dmx';

  $Return = copy($KonfigPfad , 'Config/SendeDaten.dmx');

  if($Return == 1)
  {
     echo 'Konfiguration ',$KonfigNumberVar, ' geladen. <br>';
  }
?>


<form action="001.php" method="post">
<button type="submit" name="KonfigNummer" value="1"> Konfig 1</button>
<button type="submit" name="KonfigNummer" value="2"> Konfig 2</button>
<button type="submit" name="KonfigNummer" value="3"> Konfig 3</button>
<button type="submit" name="KonfigNummer" value="4"> Konfig 4</button>
<button type="submit" name="KonfigNummer" value="5"> Konfig 5</button>
<button type="submit" name="KonfigNummer" value="6"> Konfig 6</button>
<button type="submit" name="KonfigNummer" value="7"> Konfig 7</button>
<button type="submit" name="KonfigNummer" value="8"> Konfig 8</button>
<button type="submit" name="KonfigNummer" value="9"> Konfig 9</button>
</form>

  </body>
</html>

DMX Steuerung Selbstbau? Teil 4 Voreingestellte Konfigurationen Laden

Habe noch eine IR Fernbedienung für den Computer gefunden. Diese wird auch als solche unter Linux erkannt. Habe aber keine Lust mich mit dem Input Stream eine IR-Devices zu beschäftigen. Deshalb muss folgende Zeile reichen:

  //***************************************************
  // Abfrage Input
  //***************************************************
  cInput = getchar();
  iGueltigerWert = 0;

Hier wird alles, was eine Tastatureingabe ist, zurückgegeben. Und zwar jedes einzelne Zeichen. Der Tread, bzw. Prozess hält an und wartet auf eine Eingabe. Man muss das gewüschte Zeichen eingeben und dann Enter drücken. Bei der Auswertung wird zuerst das Zeichen verarbeitet. Im zweiten Durchlauf kommt dann die Entertaste. Diese Eingabe muss Ignoriert werden.

Der Einfachheit halber werden hier nur Zahlen von 0 bis 9 aktzeptiert. Diese Zahlen beginnen bei ASCII Zeichen mit 0x30 plus den Wert der Zahl:

  if((iInput & 0x30)==0x30)
  {
    iInput = iInput & 0x0F;

    if(iInput > 0x09)
    {
       //Kein Gültiger Wert
       printf("Kein gültiger Wert:%x \n",iInput);
    }
    else
    {
      iGueltigerWert = 1;
    }
  }

Wenn jetzt ein gültiger Wert vorhanden ist:

  if(iGueltigerWert == 1)
  {...

Wir eine zugehörige Konfigurationsdatei geöffnet und deren Daten in die Sende Datei kopiert.

Zuerst wird der Dateiname in ein Array geschrieben und die entsprechende Datei geöffnet.

      //***************************************************
      // Hole Daten vom Dateisystem
      //***************************************************
      sprintf(cDateiName,"KonfigDaten%x.dmx",iInput);
      fKonfig_Datei = fopen(cDateiName,"r");

Dann wird überprüft ob die Datei vorhanden ist, die Sende Datei geöffnet und die Daten kopiert.

  if(iGueltigerWert == 1)
  {
    
 
      //***************************************************
      // Hole Daten vom Dateisystem
      //***************************************************
      sprintf(cDateiName,"KonfigDaten%x.dmx",iInput);
      fKonfig_Datei = fopen(cDateiName,"r");


      printf(cDateiName);

      //fKonfig_Datei = fopen("KonfigDaten.dmx","r",iInput);
      if(fKonfig_Datei == 0)
      {
         printf("KonfigDaten%x.dmx fehlt! \n",iInput);
         Buffer[DATEN_OFFSET]=0xFF;
      }
      else
      {
        fread(Buffer,ANZAHL_BYTES,1,fKonfig_Datei);
        //***************************************************
        // Schließe Datei
        //***************************************************
        fclose(fKonfig_Datei);

        //***************************************************
        // Schreibe Konfig
        //***************************************************
        fSende_Datei = fopen("SendeDaten.dmx","w+");
        if(fKonfig_Datei == 0)
        {
           printf("SendeDaten.dmx Nicht Schreibbar! \n");
        }
        else
        {
          printf("SendeDaten.dmx Schreiben \n");
          fwrite(Buffer,ANZAHL_BYTES,1,fSende_Datei);
          fclose(fSende_Datei);
        }
      }
  }

Das ist nicht wirklich schön, und lässt sich sicherlich eleganter lösen. Aber es entkoppelt die verschiedenen Prozesse und macht das Leben einfacher. Kollisionen beim Zugriff können vorkommen. Das muss bei der Entwicklung bedacht werden.

Ich mache mir übrigens keine Sorgen das ich mir den Speicher „Kaputtschreiben“ könnte. Bis das passiert funktioniert der Computer wahrscheinlich schon lange nicht mehr.

DMX Steuerung Selbstbau? Teil 3 Mit C DMX Daten Senden

Im letzten Beitrag ging es um das Protokoll zum ansteuern des eurolite Interfaces. Jetzt soll das natürlich auch in ein C Programm gegossen werden. Auch hier hat mich der Linux Treiber fast zum verzweifeln gebracht. Aber der Reihe nach.

Puffer anlegen und Initialisieren

Zuerst muss ein Puffer angelegt werden. Dieser soll später gesendet werden. Eigentlich ist dies in dieser Funktion nicht notwendig. Später wird diese Funktionalität in eine andere Funktion kommen. Aber immerhin können wir so eine Datei zum Testen erzeugen.

  //***************************************************
  // Init Buffer;
  //***************************************************
  for(i=0;i<ANZAHL_BYTES;i++)
  {
    Buffer[i]=0;
  }
  Buffer[0]=0x7E; //Startbyte
  Buffer[1]=0x06; //Protokollkennung
  Buffer[2]=0x01; //Anzahl Bytes niederwertiger Teil
  Buffer[3]=0x02; //Anzahl Bytes höherwertiger Teil
  Buffer[4]=0x00; //???
  Buffer[ANZAHL_BYTES-1]=0xE7; //Endbyte

Es wird der Nachrichten „Header“ erstellt. Das Endbyte wird eingetragen und alle Werte auf 0 gesetzt.

Puffer in eine Datei Schreiben

Wir erfinden Kurzerhand einen eigenen Dateityp für unser Interface und nennen es .dmx. Darin ist die Struktur aus unserem Puffer abgelegt. Und nichts anderes.

fDaten_Datei = fopen("SendeDaten.dmx","w+");
fwrite(Buffer,ANZAHL_BYTES,1,fDaten_Datei);
fclose(fDaten_Datei);

Jetzt gibt es die Datei „SendeDaten.dmx“. Im Hexeditor sieht das folgendermaßen aus:

Daten in Struktur zurücklesen

Jetzt können die Daten in die Struktur zurückgelesen werden. Damit gibt es eine einfache Möglichkeit des Datenaustausches. Ein anderes Programm kann die Datei Modifizieren. Dabei kann es es zu „Kollisionen“ kommen. Die sind aber kein Problem. Wenn der Zugriff nicht geklappt hat, wird es einfach später wieder versucht.

      //***************************************************
      // Hole Daten vom Dateisystem
      //***************************************************
      fDaten_Datei = fopen("SendeDaten.dmx","r");
      if(fDaten_Datei == 0)
      {
         printf("SendeDaten.dmx fehlt! \n");
      }
      else
      {
        fread(Buffer,ANZAHL_BYTES,1,fDaten_Datei);
        //***************************************************
        // Schließe Datei
        //***************************************************
        fclose(fDaten_Datei);
      }

Das Programm sendet einfach immer die letzten Daten die es ausgelesen hat.

Öffnen der Schnittstelle

Unter Linux sieht die Schnittstelle wie eine Datei aus. Das System weist dem Interface einen Namen zu. Ich kenne den Namen bei mir genau, kann ihn deshalb direkt angeben. Das funktioniert aber nicht überall! Unter Umständen muss man sich etwas mehr Gedanken darüber machen.

  //***************************************************
  // Öffne Schnittstellendatei
  //***************************************************
  iPortFD = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY);
  if (iPortFD <= 0)
  {
    printf("Kann Serielle Schnittstelle nicht öffnen\n");
  }

Nach diesem Öffnen hat man exklusiven zugriff auf die Schnittstelle.

Konfigurieren der Schnittstelle

Unter Linux kann man nicht einfach 250000Baud als Geschwindigkeit einstellen. In den „höheren“ Softwareschichten sind nur Standardbaudraten erlaubt. Ich habe ein Variante gefunden die Funktioniert. Das aber aber auch Zufall sein. Mit den zu konfigurierenden Flags habe ich mich nicht tiefgehend auseinandergesetzt. Gefunden ahbe ich die Konfiguration hier: https://gist.github.com/lategoodbye/f2d76134aa6c404cd92c#file-anybaud-c Das die Werte bei mir fest Kodiert sind, ist ein schlechter Stil, aber mein Programm muss immer mit der gleichen Baudrate, der gleichen Länge und dem gleichen Header funktionieren.

  //***************************************************
  // Erstelle Konfigurationsstruktur
  //***************************************************
  ioctl(iPortFD, TCGETS2, config);
  config.c_cflag &= ~CBAUD;
  config.c_cflag |= BOTHER;
  config.c_ispeed = 250000;
  config.c_ospeed = 250000;


  //***************************************************
  // Schreibe Konfiguration
  //***************************************************
  iReturn = ioctl(iPortFD,TCSETS2, &config);
  if(iReturn != 0)
  {
     printf("Fehler ioctl: %x",iReturn);
  }

Diese Funktionalität setzt übrigens folgende Header voraus:

#include "/usr/include/asm-generic/termbits.h"
#include "/usr/include/asm-generic/ioctls.h"

Senden der Daten

Um die Daten senden zu können, muss die Schnittstelle erfolgreich geöffnet und korrekt Konfiguriert sein. Ebenfalls sollten gültige Daten verfügbar sein. Dieses Programm verfügt aber über keinerlei Kontrolle, ob die Daten wirklich gültig sind.

  //***************************************************
  // Sende Daten
  //***************************************************
    intSendBytes = write (iPortFD, Buffer, ANZAHL_BYTES);
    if(intSendBytes != ANZAHL_BYTES)
    {
      printf("Daten konnten nicht geschrieben werden. Rückgabe: %x \n",intSendBytes);
    }

    sleep(1);

Zum Schluss kann noch überprüft werden, ob die Daten wirklich übertragen wurden. Es reicht für das Interface ein mal pro Sekunde zu senden. Wenn die Daten korrekt sind, leuchtet die LED grün.

Übersetzen

Unter Linux ist das Übersetzen ziemlich einfach (Wenn die richtigen Pakete installiert sind). Man benötigt eine Datei mit dem Quelltext. Zum Beispiel dmx_senden.c. Mit dem Befehl „gcc dmx_senden.c -o dmx_senden “ wird sie übersetzt. Und mit dem Befehl „./dmx_senden“ gestartet.

Außerdem ist es einfacher, eine Mainfunktion für den Einsprung zu erzeugen. Deshalb gesellt sich zu unsere Schreibfunktion, noch eine Zweite.

Programm Ausführen

Kanal 1 hat Wert 255
Kanal 3 hat wert 255

Mit einem Hex-Editor Werte einzustellen ist nicht toll, aber als Zwischenschritt schön, weil man sieht das etwas passiert.

Der komplette Quelltext

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include "/usr/include/asm-generic/termbits.h"
#include "/usr/include/asm-generic/ioctls.h"

#define ANZAHL_BYTES 518
#define DATEN_OFFSET 5

void vSchreibProzess (void);



int main()
{
  int i;
  int iReturn;

  //************************************************************************/
  //* 
  //************************************************************************/


  //************************************************************************/
  //* 
  //************************************************************************/
int pid; 
pid=fork();

  if(pid == -1)
  {
    //Fehler. Fork nicht möglich
    printf("Schreibprozess konnte nicht angelegt werden \n");
  }
  else if(pid == 0)
  {
    //Lese Daten Prozess
    printf("Erzeuge Schreibprozess\n");
    vSchreibProzess();
    exit(0);
  }

for(;;)
  {
    printf("Mainprozess\n");
    sleep(10);
  }
}


//***************************************************
//
//***************************************************


//************************************************************************/
//* Schreibprozess
//************************************************************************/
void vSchreibProzess(void){
  //***************************************************
  // Variablen
  //***************************************************
  int intSendBytes,intReadBytes; //Rückgabeparameter von Funktionen
  int iPortFD;                   //Datei Handle für Serielle Schnittstelle
  struct termios2 config;        //Konfiguration für Serielle Schnittstelle
  int iReturn;                   //Rückgabewert Konfiguration
  char Buffer[ANZAHL_BYTES];     //Puffer für Daten
  int i;                         //Zählvariable für Schleife
  FILE *fDaten_Datei;            //Datei Handle für DMX Werte

  //***************************************************
  // Code
  //***************************************************
  //***************************************************
  // Öffne Schnittstellendatei
  //***************************************************
  iPortFD = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY);
  if (iPortFD <= 0)
  {
    printf("Kann Serielle Schnittstelle nicht öffnen\n");
  }

  //***************************************************
  // Erstelle Konfigurationsstruktur
  //***************************************************
  ioctl(iPortFD, TCGETS2, config);
  config.c_cflag &= ~CBAUD;
  config.c_cflag |= BOTHER;
  config.c_ispeed = 250000;
  config.c_ospeed = 250000;


  //***************************************************
  // Schreibe Konfiguration
  //***************************************************
  iReturn = ioctl(iPortFD,TCSETS2, &config);
  if(iReturn != 0)
  {
     printf("Fehler ioctl: %x",iReturn);
  }
 
  //***************************************************
  // Schleife für Ausführung
  //***************************************************
  for(;;)
  {

      //***************************************************
      // Hole Daten vom Dateisystem
      //***************************************************
      fDaten_Datei = fopen("SendeDaten.dmx","r");
      if(fDaten_Datei == 0)
      {
         printf("SendeDaten.dmx fehlt! \n");
      }
      else
      {
        fread(Buffer,ANZAHL_BYTES,1,fDaten_Datei);
        //***************************************************
        // Schließe Datei
        //***************************************************
        fclose(fDaten_Datei);
      }
  //***************************************************
  // Sende Daten
  //***************************************************
    intSendBytes = write (iPortFD, Buffer, ANZAHL_BYTES);
    if(intSendBytes != ANZAHL_BYTES)
    {
      printf("Daten konnten nicht geschrieben werden. Rückgabe: %x \n",intSendBytes);
    }

    sleep(1);
  }
return;
}

DMX Steuerung Selbstbau? Teil 2 Das Protokoll

Die LED leuchtet rot, das Interface bekommt keine gültigen Daten.

Im letzten Beitrag habe ich das Interface von Eurolite auseinandergebaut, um zu sehen wie es funktioniert.

Es wird über USB an den Computer angeschlossen. Dort wird es als USB zu Seriell Wandler erkannt. Unter Windows als COM-Port. Unter Linux als Serielle Schnittstelle.

~$ lsusb
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 005 Device 002: ID 046d:c534 Logitech, Inc. Unifying Receiver
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 004: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

Hier ist also unser Interface:

Future Technology Devices International, Ltd FT232 Serial (UART) IC

In meinem Fall ist der Anschluss unter ttyUSB0 zu finden.

Als normaler Benutzer hat man keine Zugriffsrechte.

Am besten ist es, sich selber als Benutzer für die Gruppe „dialout“ einzutragen.

sudo usermod -aG dialout BENUTZERNAME

//oder als Root
usermod -aG dialout BENUTZERNAME

Jetzt kann man ein Terminalprogramm verwenden, um dem Interface Daten zu schicken. Und an dieser Stelle bin ich fast verzweifelt. Da es sich ja um eine Serielle Schnittstelle handelt, muss man die Baudrate kennen. Ich habe alle Standard Baudraten probiert. Nichts hat funktioniert. Nach einigen Tagen kam mir die Idee 250k Baud zu probieren. Und siehe da, es hat funktioniert. Die 250kBaud werden ja für die DMX übertragung verwendet, aber für die Verbindung zwischen FTDI und ATMEL wären sie eigentlich nicht notwendig.

Konfiguration für Kommunikation mit Eurolite USB-DMX512-PRO

Die zu übertragenden Daten müssen folgende Form haben:

  • Byte 0: 0x7E Startbyte
  • Byte 1: 0x06 Protokoll / Mode
  • Byte 2: 0x01 Nachrichtenlänge Höherwertiges Byte
  • Byte 3: 0x02 Datenlänge niederwertiges Byte
  • Byte 4: 0x00 Zweck ist mir unbekannt.
  • Byte 5-516 Daten. Ein Byte pro Kanal.
  • Byte 517: 0xE7 Stopbyte

Zum Glück gibt es keine Checksumme die man berechnen muss. Es reicht das jeweilige Byte für einen Kanal zu modifizieren.

Und wenn man die richtigen Daten sendet, wird die LED grün:

Es reicht scheinbar aus, einmal gültige Daten zu senden, das Interface sendet diese Informationen dann bis der Strom abgeschaltet wird, oder es neue gültige Daten gibt. Auch wenn die LED wieder Rot wird, scheint das Interface weiter zu senden.

7e 06 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e7

Quelle meines Wissens und Ansatzpunkt für weitere Nachforschungen ist übrigens: https://forum.dmxcontrol-projects.org/index.php?thread/8130-serielles-protokoll-zum-eurolite-usb-dmx512-pro-interface/

DMX Steuerung Selbstbau?

Ich benötige eine DMX Steuerung für feste Lichteinstellungen. Es soll entweder ein paar Knöpfe zum drücken geben, oder einen Webserver mit ein paar „Buttons“. Für ein komplett eigenes System habe ich keine Zeit, deshalb soll ein Linux verwendet werden. Als Hardware habe ich bereits ein „Eurolite USB-DMX512-PRO INTERFACE“. Mit dem bin ich auch sehr zufrieden. Unter Windows funktioniert es völlig problemlos mit Freestyler.

Nur unter Linux wollte es einfach nicht funktionieren. Ich wollte schon aufgeben, bin dann aber auf diese Seite gestoßen.

Es ist also ein FTDI Chip verbaut. Das kommt mir sehr entgegen, die sind nämlich weit verbreitet und mein Linux benötigt keinen zusätzlichen Treiber.

Die Platine sieht folgendermaßen aus:

Der USB Anschluss geht auf den FTDI Chip. Der Atmel MEGA32 scheint die Busskommunikation zu machen. Das ist für mich sehr gut, denn das DMX Timing nach DIN 56930-2 muss mein PC nicht sicherstellen. Die Norm ist bestimmt nicht mehr aktuell, aber die lag halt noch rum.

Die Platine passt einfach nicht auf den Objektträger meines Mikroskopes, deshalb musste der Fotoapparat mit Macro-Fotografie ran.

Atmel MEGA324PA MV 1431
FTDI FT232RL

VW Touran IQ.DRIVE

Hatte dieses Auto leihweise für einen Tag. Was soll man dazu sagen. Es ist ein VW Familienauto. Unaufgeregt abgestimmt. Ziemlich langweilig. Ist für die Zielgruppe aber OK. Bin extra auf die volle dreispurige Autobahn gefahren um Abstandsassistent und Spurwechselwarnung zu testen.

Was hat mir dennoch nicht so gefallen?

  • Die Sitze hatten einen schlechten Seitenhalt. Bin im Kreisverkehr immer gerutscht.
  • Der Spurhalteassistent wollte immer weiter nach Links als ich. Hatte die Befürchtung das er in den Gegenverkehr will.
  • Das Auto war ziemlich unübersichtlich. Aber das liegt wohl am Zeitgeist. Die meisten Konkurrenten machen es noch schlechter.
  • Warum muss IQ.DRIVE beim Türöffnen auf die Straße projiziert werden? Bei einem Audi R8. OK. Wer es da braucht, meinetwegen. Aber bei einem Turan?
  • Der Abstandsassistent funktioniert ganz gut. Manchmal ruckelt er jedoch etwas. Vor einigen Jahren schon, hatten wir uns Szenarien überlegt, bei denen das System versagt und sogar gefährlich werden könnte. Und siehe da, man muss sich gar nicht so viel Mühe geben um in eine solche Situation zu kommen.