Blog der Heimetli Software AG

Laufschrift ansteuern mit dem Raspberry

Kürzlich habe ich mir eine grosse Anzeige von Mc Crypt geleistet (anscheinend gibt es keine genauere Typenbezeichnung). Sie kann auf ca. 60x5cm eine Laufschrift anzeigen. Ich habe die günstigere Version nur mit roten LEDs gekauft. Das Display kann über eine serielle Schnittstelle oder per USB mit einem Computer verbunden werden.

Es gibt ein Windows-Programm und eine Fernbedienung dazu, aber für mich war die Sache klar: das muss an den Raspberry. Ein kurzer Test zeigte, dass Raspbian den USB-Seriell Chip erkennt und problemlos damit umgehen kann.

Erstaunlicherweise gibt es sogar eine recht genaue Dokumentation zur Schnittstelle, so dass es relativ einfach ist, ein Programm für den Raspberry dazu zu schreiben.

Der Code enthält nichts was spezifisch für den Raspberry ist, er müsste also auf jedem beliebigen Linux laufen. Das habe ich allerdings nicht getestet.

Das Display bietet viele Varianten für die Anzeige an, aber die meisten haben mich gar nicht überzeugt. Deshalb ist der Anzeigemodus im vorgestellten Code fest auf eine Laufschrift eingestellt. Wer einen anderen Modus will, kann die Initialisierung des Display-Befehls anpassen oder gleich parametrierbar gestalten.

Display-Initialisierung

Das Gerät wird über eine ID angesprochen die wie eine Adressierung wirkt. Meinem Display habe ich die ID 1 gegeben.

Bei den ersten Versuchen erhielt ich seltsame Zeichen anstelle der Umlaute. Ein Reset der Character-Table auf die Factory-Defaults hat dieses Problem behoben.

Diese Initialisierung erfolgte ad hoc mit minicom, deshalb sie ist hier nur in Textform dokumentiert.

Es gibt spezielle Escape-Sequenzen für Zeichen die nicht im ASCII-Bereich liegen. Ich hatte vorgesehen, diese Character beim kopieren in den Buffer durch Escapes zu ersetzen. Das ist in meinem Fall gar nicht nötig, weil die Umlaute auch ohne diese Sequenzen korrekt angezeigt werden.

Der Code für die Ansteuerung

Den eigentlichen Code für das Display finden Sie in der main-Funktion. Im Vergleich zum ganzen Programm ist er sehr klein...

Wenn das Display das Kommando akzeptiert, schickt es ACK zurück, ohne CR oder LF! Bei einem Fehler soll es NACK zurückgeben.

Im Erfolgsfall gibt das Programm 0 zurück. "Normale" Fehler werden nur durch den Returnwert angezeigt, während schwere Fehler zusätzlich eine Meldung auf stderr ausgeben.

/******************************************************************************/
/*                                                                            */
/*                                                          FILE: display.cpp */
/*                                                                            */
/*    Displays a scrolling text on a Mc CRYPT LED Display                     */
/*    ===================================================                     */
/*                                                                            */
/*    V0.01  30-JAN-2017   Te                                                 */
/*                                                                            */
/******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <time.h>

/*********************/
 bool SetRaw( int fd )
/*********************/

{
  struct termios tios ;

  if( tcgetattr(fd,&tios) == -1 )
     return false ;

  cfsetospeed( &tios, B9600 ) ;
  cfsetispeed( &tios, B9600 ) ;

  tios.c_iflag    &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
  tios.c_oflag    &= ~OPOST;
  tios.c_lflag    &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  tios.c_cflag    &= ~(CSIZE | PARENB);
  tios.c_cflag    |= CS8;
  tios.c_cc[VTIME] = 0 ;
  tios.c_cc[VMIN]  = 1 ;

  tcflush( fd, TCIFLUSH ) ;

  if( tcsetattr(fd,TCSANOW,&tios) == -1 )
     return false ;

  return true ;
}

/***************************/
 int CheckResponse( int fd )
/***************************/

{
   struct timeval tv ;
   fd_set         set ;

   time_t         now     = time( NULL ) ;
   time_t         timeout = now + 5 ;

   char           line[32] ;
   int            length  = 0 ;

   for( ;; )
   {
      FD_ZERO( &set ) ;
      FD_SET( fd, &set ) ;

      tv.tv_sec  = timeout - now ;
      tv.tv_usec = 0 ;

      int result = select( fd + 1, &set, NULL, NULL, &tv ) ;

      if( result == -1 )
      {
         fprintf( stderr, "error: select failed\n" ) ;
         return 4 ;
      }

      // Timeout
      if( result == 0 )
         return 3 ;

      int len = read( fd, line + length, (int)sizeof(line) - length - 1 ) ;

      if( len == -1 )
      {
         fprintf( stderr, "error: read failed\n" ) ;
         return 5 ;
      }

      if( len == 0 )
      {
         fprintf( stderr, "error: end of file\n" ) ;
         return 6 ;
      }

      length += len ;

      line[length] = '\0' ;

      // Display problem
      if( strstr(line,"NACK") != NULL )
         return 7 ;

      // Success
      if( strstr(line,"ACK") != NULL )
         return 0 ;

      if( length + 1 == (int)sizeof(line) )
      {
         fprintf( stderr, "error: buffer full\n" ) ;
         return 8 ;
      }

      now = time( NULL ) ;
   }
}

/**********************************/
 int main( int argc, char *argv[] )
/**********************************/

{
   if( argc != 2 )
   {
      fprintf( stderr, "usage: display text\n" ) ;
      return 1 ;
   }

   int fd = open( "/dev/ttyUSB0", O_RDWR ) ;

   if( fd == -1 )
   {
      fprintf( stderr, "error: can't open serial port\n" ) ;
      return 2 ;
   }

   SetRaw( fd ) ;

   const char *str       = argv[1] ;
   char        line[512] = "<ID01><L1><PA><FE><MQ><WB><FE>" ;
   int         index ;

   for( index = 30; index < ((int)(sizeof(line) - 6)) && (*str != '\0'); index++ )
      line[index] = *str++ ; 

   int check = 0 ;
   for( int i = 6; i < index; i++ )
      check ^= line[i] ;

   sprintf( line + index, "%02X<E>", check & 0xFF ) ;

   // Print the string for the display
   printf( "%s\n", line ) ;

   write( fd, line, strlen(line) ) ;

   int result = CheckResponse( fd ) ;

   close( fd ) ;

   return result ;
}

Download von display.cpp

Weitere Informationen zum Gerät und Ansteuerung mit Python

Nicht jeder hat einen C-Compiler zur Hand, und deshalb habe ich eine Python-Version des Programms geschrieben. In diesem Post finden sie auch weitere Informationen die im Laufe der Zeit aufgetaucht sind.