Seriali reali e simulate su Linux

Su linux è possibile simulare coppie di porte seriali interconnesse tra di loro

Installare il package socat

sudo apt-get install socat

Il comando seguente crea due porte seriali interconnesse tra di loro con un link virtuale:

sudo socat PTY,link=/dev/ttyS10 PTY,link=/dev/ttyS11

poichè i due devices si trovano sotto la directory /dev per poterli accedere occorre avere i privilegi di superutente. Tuttavia non vi' è nessun vincolo che imponga di usare la /dev (e anche i nomi sono in realtà liberi), quindi, per evitare il fastidio del sudo, basta creare i devices sotto /tmp. Oltretutto, essendo sotto /tmp, i nuovi devices non sopravviveranno al prossimo reboot.

Con un qualunque programma (screen minicom lanciati in due terminali virtuali separati) e' possibile effettuare un test di funzionalità:

sudo screen /dev/ttyS10
sudo screen /dev/ttyS11

Come accedere ad una seriale da un programma scritto in C

Una porta seriale virtuale è vista come un semplice device a carattere, quindi non richiede speciali considerazioni quando è acceduta da un programma in C. Il programma va bene anche una porta seriale fisica, al netto del settaggio dei parametri di velocita', bit di start/stop e parità: come si vede il bit rate è comunque stabilito da un costante

#define BAUDRATE    B9600

anche se per le porte virtuali questo valore è ininfluente.

Programma di esempio


/**
 * Serial port example.
 * Compile with: g++ serialtest.cpp -lpthread -o serialtest
 *
 * taken from Cymait http://cymait.com
 **/

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>
#include <termios.h>

#define BUFFER_SIZE 128
#define BAUDRATE    B9600

struct Td {
    int fd;
    char buf[128];
    bool exit;
};

void usage(char* cmd) 
{
    std::cerr << "usage: " << cmd << " slave|master [device, only in slave mode]" << std::endl;
    exit(1);
}

void* reader_thread(void* pointer)
{
    Td *d = (Td *) pointer;
    long fd = d->fd;
    char inputbyte;
    unsigned n = 0;
    while (d->exit != true) {
        if (read(fd, &inputbyte, 1) == 1) {
            fprintf (stderr, "[%d]: %02x\n", n, inputbyte);
            n++;
            if (inputbyte == 'X') break;
        } else
            break;
    }
    std::cerr << "Terminating thread..."   << std::endl;
    d->exit = true;
    close (0);
    return 0;
}

int main (int argc, char** argv)
{
    if (argc < 2) usage(argv[0]);

    int fd = 0;
    
    fd = open(argv[1], O_RDWR);
    if (fd == -1) {
        perror ("error opening file");
        return 1;
    } else
    std::cerr << argv[1] << " successfully opened." << std::endl;

    /* serial port parameters */
    struct termios newtio = {0};
    struct termios oldtio;
    tcgetattr(fd, &oldtio);

    // remove local echo
    newtio = oldtio;
    newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
    newtio.c_iflag = 0;
    newtio.c_oflag = 0;
    newtio.c_lflag = 0;
    newtio.c_cc[VMIN] = 1;
    newtio.c_cc[VTIME] = 0;
    newtio.c_lflag &= ~(ICANON | ECHO); // disable local echo

    tcflush(fd, TCIFLUSH);
    cfsetispeed(&newtio, BAUDRATE);
    cfsetospeed(&newtio, BAUDRATE);
    tcsetattr(fd, TCSANOW, &newtio);

    Td data;
    data.fd = fd; data.exit = false;
    /* start reader thread */
    pthread_t thread;
    pthread_create(&thread, 0, reader_thread, (void*) &data);

    /* read from stdin and send it to the serial port */
    unsigned n = 0;
    while (data.exit != true) {
        //std::cin >> c;
        char x = getchar();

        if (write(fd, &x, 1) != 1) {
            perror ("writing");
            break;
        } else {
            fprintf (stderr, "[%d]: %02x\n", n, x);
        }
        n++;
    }

    close(fd);
    return 0;
}

Link interessanti sull'argomento

Andrea Montefusco
Currently employed as network architect, always Internet working man, real C/C++ programmer in the past, network and Unix system engineer as needed, HAM Radio enthusiast (former IW0RDI, now IW0HDV), aeromodeller (a person who builds and flies model airplanes) since 1976 (ex FAI10655).
http://www.montefusco.com - https://github.com/amontefusco - https://github.com/IW0HDV - andrew@montefusco.com