ubuntuserial-port

send a 300-400 ms pulse through serial port


As the title, I need to send a 300-400 ms pulse through the serial port. My sensor need such a pulse to wake up. The program that my sensor's producer provides can do so in Windows. However I need to develop a similar program in ubuntu since my project is using ubuntu. Any pointing is also appreciated.

regards,


Solution

  • First of all you will want to determine on what pin you want to give that pulse. Have a look at the Wikipedia page on RS232 to see which signals exist. Although it's doable to send a pulse on Tx (Transmit Data) that's fairly uncommon, so I assume you will need to send out the pulse either via RTS or via DTR.

    To do this you will need ioctl and this is how to do it, assuming you want to pulse DTR

    int pulseDTR(int fd)
    {
        int status;
    
        // get the current modem control lines status
        if (ioctl(fd, TIOCMGET, &status) == -1) {
            // handle the error
        }
         
        // Now set DTR high
        status |= TIOCM_DTR;
        // and apply  
        if (ioctl(fd, TIOCMSET, &status) == -1) {
            // handle the error
        }
        // wait a bit
        usleep (400*1000);
        // Now set DTR low
        status &= ~TIOCM_DTR;
        // and apply  
        if (ioctl(fd, TIOCMSET, &status) == -1) {
            // handle the error
        }
    
        return 0;
    }
    

    If you need to pulse RTS just replace TIOCM_DTR by TIOCM_RTS.

    For more information about programming the serial port I'd recommend the Serial Programming Guide for POSIX Operating Systems and the Linux serial Howto.

    EDIT: based on OP's comment sending a break is in fact what's needed. Sending a break means that the Tx line is pulled to logical 0 for a certain amount of time (and not a control signal). This is described in the Linux serial programming howto and is done as follows:

    Using ioctl:

       int ioctl( fd, TCSBRK, int arg );
    

    Using the tc* calls

       int tcsendbreak( fd, int arg );
    

    Please note the comment from the Linux serial programming howto

    Send a break: Here the action differs between the conventional
    ioctl() call and the POSIX call. For the conventional call, an arg of '0' sets the break control line of the UART for 0.25 seconds. For the POSIX command, the break line is set for arg times 0.1 seconds.

    EDIT2: the great thing about open source is that the sources are available :-) This is verbatim from the minicom source, and it's really great because it shows 3 ways to send a break:

    /*
     * Send a break
     */
    void m_break(int fd)
    {
    #ifdef USE_SOCKET
      if (portfd_is_socket)
        return;
    #endif
    #ifdef POSIX_TERMIOS
      tcsendbreak(fd, 0);
    #else
    #  ifdef _V7
    #    ifndef TIOCSBRK
      {
        struct sgttyb sg, ng;
    
        ioctl(fd, TIOCGETP, &sg);
        ioctl(fd, TIOCGETP, &ng);
        ng.sg_ispeed = ng.sg_ospeed = B110;
        ng.sg_flags = BITS8 | RAW;
        ioctl(fd, TIOCSETP, &ng);
        write(fd, "\0\0\0\0\0\0\0\0\0\0", 10);
        ioctl(fd, TIOCSETP, &sg);
      }
    #    else
      ioctl(fd, TIOCSBRK, 0);
      sleep(1);
      ioctl(fd, TIOCCBRK, 0);
    #    endif
    #  endif
    #endif
    } 
    
    1. I guess that Linux uses the first option, tcsendbreak(fd, 0);
    2. The third option is also pretty interesting as performing ioctl(fd, TIOCSBRK, 0); followed by some sleep call followed by ioctl(fd, TIOCCBRK, 0); should allow you to send a break signal with a finely tuned length.
    3. the second option is actually prettty nifty, because it show how to emulate a break by fiddling with the port settings and sending a string of zeroes. It can always be useful on systems that don't implement break.

    So, conclusion? Try first with tcsendbreak(fd, 0); as it's the easiest, and if that's not giving a good result try ioctl(fd, TIOCSBRK, 0); some sort of sleep ; ioctl(fd, TIOCCBRK, 0);. On a sane system the "emulated break" should not be necessary.