LinuxQuestions.org
View the Most Wanted LQ Wiki articles.
Go Back   LinuxQuestions.org > Blogs > matiasar
User Name
Password

Notices

Rate this Entry

"Trigger" event button through serial rs-232 port

Posted 03-09-2011 at 07:32 AM by matiasar

Serial ports seems to be part of the past, but surely is one of the most straight ways to communicate a linux box with the external world.
Recently I need to use a single button which a user could push and trigger some event, in my case a bash script to scan a document.
I wanted to do a "headeless" print and scan box (of course running Linux), where a user could just print documents (through samba+cups) and scan documents, then the scanned documents are available to local network through a samba share.

The problem I found was that my printer's (HP F380 All-in-One) driver doesn't support the built-in "scan" button, and I didn't find a workaround for that issue.
So I decided to use an external button just to trigger the scan script. I could have used parallel port, but the serial port approach seemed to me straighter and there's no problem if the box doesn't have a rs-232, it could be used and usb/rs-232 adaptor too.

http://www.linuxquestions.org/questi...server-862892/

I decided using the RING bit to send HIGH signal (+12v) when the button was pressed. I could have used any other "control" input from rs-232, like DCD (carrier detection).
I soldered the push button, in order to turn "high" the RNG bit (pin number 9 whithin a DB-9 connector) when the button was pressed.
The complete rs-232 pinout, bouth DB-9 and DB-25 could be found here: http://www.lammertbies.nl/comm/cable/RS-232.html

Once the very simple "hardware" solution was ready I wrote a very simple C program, I called button daemon, and its main function was to run a loop reading RNG bit state. When the program found that bit went high, then trigger some event.
I copy the little code here. Any suggestions on improving its code are welcome. I'm really more confortable writing OOP code, but for this time I thought plain C was enough for what I wanted to do. And I needed to do it fast... So code could be improved in later versions.

Code:
    #include <stdlib.h>
    #include <stdio.h>   /* Standard input/output definitions */
    #include <string.h>  /* String function definitions */
    #include <unistd.h>  /* UNIX standard function definitions */
    #include <fcntl.h>   /* File control definitions */
    #include <errno.h>   /* Error number definitions */
    #include <asm/termios.h> /* POSIX terminal control definitions */


    int openport(char* Port);
    void runevent (char* event, char* playfile, unsigned short enabled);
    int openport(char* Port);


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

    int fd;
    int status;
    unsigned short EnablePlay = 0;
    char *Puerto = (char *)malloc(40 * sizeof(char));
    char *EventScript = (char*)malloc(200 * sizeof(char));
    char *SoundFile = (char *)malloc(140 * sizeof(char));

    /* configuracion del puerto */

     if ( argc == 3 )
          {
                strcpy(Puerto, argv[1]);
                strcpy(EventScript, argv[2]);
          }

     if ( argc == 4 )
          {
                strcpy(Puerto, argv[1]);
                strcpy(EventScript, argv[2]);
                strcpy(SoundFile, argv[3]);
                EnablePlay = 1;
          }
   if ( argc < 3 || argc > 4 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "/?") == 0 )
          {
               printf("%s: demonio de boton por seņal de RNG (pin 9 DB-9). Cuando pasa a High dispara eventos.\n\n", argv[0]);
               printf("\nUso: %s /dev/ttyNN /path/to/script_to_run [/path/to/soundfile]", argv[0]);
               printf("\nEl dev para el puerto y el script_to_run son obligatorios");
               printf("\nSi no se especifica archivo de sonido esa funcion queda deshabilitada\n");
               exit (0);
          }



    fd = openport(Puerto);
    if ( fd == -1 )
          {
               printf("Error al abrir el puerto. Abortado\n");
               exit (1);
          }
               // Constantes que sirven para identificar cada bit
               // ver http://www.easysw.com/~mike/serial/serial.html

              //printf("DSR: %d\n", TIOCM_LE);
              //printf("DTR: %d\n", TIOCM_DTR);
              //printf("RTS: %d\n", TIOCM_RTS);

              //printf("STX : %d\n", TIOCM_ST );
              //printf("SRX: %d\n", TIOCM_SR );
              //printf("CTS: %d\n", TIOCM_CTS);
              //printf("DCD : %d\n", TIOCM_CAR);
              //printf("...\n");
              //printf("RNG: %d\n", TIOCM_RNG);
              //printf("DSR: %d\n", TIOCM_DSR );

   /*************************************************************************/
   /*** Loop principal para verificar si se activan bits                  ***/
   /*************************************************************************/

     while (1)
          {

             ioctl(fd,TIOCMGET, &status);
               // status es un entero, que en realidad es un binario que habra que comparar AND para detectar cada bit, ver abajo
              if ( status & TIOCM_RNG )
                    {
                         //printf("Ring them bell!\n");
                         if ( EnablePlay == 0 )
                              runevent(EventScript, "", 0);
                         if ( EnablePlay == 1 )
                              runevent(EventScript, SoundFile, 1);
                    }
              if ( status & TIOCM_CAR )
                    {
                         printf("Portadora detectada!\n");
                    }
             usleep (70000);
          }

     // Libera memoria
     free(Puerto);
     free(SoundFile);
     free(EventScript);
     Puerto = NULL;
     SoundFile = NULL;
     EventScript = NULL;
}
    int openport(char* Port)
    {

       /*
       * 'open_port()' - Abre el puerto definido que se le pase al prog
       *
       * Retorna el descriptor del archivo o -1 si hubo error
       */

       int fd; /* Descriptor de archivo para el port */


       fd = open(Port, O_RDWR | O_NOCTTY | O_NDELAY);
       if (fd == -1)
       {
          /* No pudo abrirse el puerto  */
          printf ("open_port: error al intentar abrir %s - ", Port);
       }
       else
          fcntl(fd, F_SETFL, 0);
      return (fd);
   }


void runevent (char* event, char* playfile, unsigned short enabled)
{
     system(event);
     if (enabled == 1)
          {
              char *Play = (char *)malloc( ( strlen(playfile) + 10 )* sizeof(char));
              strcat(Play, "aplay ");
              strcat(Play, playfile);
              system(Play);
          }
Building the binary file is simple as: gcc buttondaem.c -o buttonrng

My main guide was Michael R. Sweet book available in web version here: http://www.easysw.com/~mike/serial/serial.html

termios.h header provides some constant which simplifies detecting signals. I left some examples like comments within the code:

Code:
              //printf("DSR: %d\n", TIOCM_LE);
              //printf("DTR: %d\n", TIOCM_DTR);
              //printf("RTS: %d\n", TIOCM_RTS);

              //printf("STX : %d\n", TIOCM_ST );
              //printf("SRX: %d\n", TIOCM_SR );
              //printf("CTS: %d\n", TIOCM_CTS);
              //printf("DCD : %d\n", TIOCM_CAR);
After opening the port the main loop is where the program keeps looking at RNG bit. The current state of the port is stored in status (integer). Then, status is "ANDED" with TIOCM_RNG constant. That operation is TRUE if RNG bit is high (+12v) otherwise is FALSE. Just to ilustrate I left in the code the same operation but for DCD (carrier detect: TIOCM_CAR).

Code:
     while (1)
          {

             ioctl(fd,TIOCMGET, &status);
               // status es un entero, que en realidad es un binario que habra que comparar AND para detectar cada bit, ver abajo
              if ( status & TIOCM_RNG )
                    {
                         //printf("Ring them bell!\n");
                         if ( EnablePlay == 0 )
                              runevent(EventScript, "", 0);
                         if ( EnablePlay == 1 )
                              runevent(EventScript, SoundFile, 1);
                    }
              if ( status & TIOCM_CAR )
                    {
                         printf("Portadora detectada!\n");
                    }
             usleep (70000);
          }
Other control signal constants can be found in Michael Sweet's book, see Table 11.

Finally, for the program to run it needs two arguments:


./serialbutton /dev/ttyNN /path/to/script_to_run [/path/to/soundfile]


The first one is a device node for the serial port we are using, for example, /dev/ttyS0, the second one is the full path to the script that will be run. A third argument is optional and is just a path to a sound file that will be played by the application "aplay" after the program trigger the event script.
I might do some research in order to check if there's any library which could be usefull to play a wav file in a direct way, without passing control to external application. Any suggestions are welcome too.

Well hope this little example could be usefull for some others applications. Please feel free to leave your comments.
Posted in Uncategorized
Views 2300 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 11:59 PM.

Main Menu

My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration