[risolto] Comunicazione seriale senza IDE

Ciao a tutti!

Vorrei realizzare un software che “muove” uno shieldbot, e vorrei poterlo comandare via seriale.
Come arduino uso un arduino due.
Però, volendo fare tutto questo tramite una GUI dal mio laptop, non posso usare il monitor seriale che fornisce l’ide di arduino.

Quindi, il goal, è far comunicare (quindi inviare e ricevere numeri) arduino con il pc.

Lato arduino, non penso cambi qualcosa, credo di poter sempre usare la libreria Serial.h (giusto?)
Lato laptop, invece, non so come interagire con la usb affinchè parli con arduino

Il pc ha kubuntu, e il linguaggio che voglio usare è il C (no altri linguaggi, ho già la GUI in C)

Quindi, dal mio pc, come interagisco con la USB seriale affichè mandi e riceva dati su arduino?

Grazie!

Risolto!

Per leggere da seriale ho fatto la funzione:

void printFromSerial(int fd){
    char buf[100];
    while (1) {
        for (int i=0; i<100; i++) buf[i]='\0';
        int  n = read (fd, buf, sizeof(buf));
        if (n <= 0) break;
        printf("%s", buf);
    }
}

Se volete, ecco tutto il codice (che nel mio pc funziona!)
NOTA BENE! Lato arduino uso SOLO Serial.print e NON Serial.write

//#include <function.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h> 
#include <string.h>
#include <termios.h>
#include <unistd.h>

#define FFLUSH while(getchar()!='\n')


void readString(char s[], int dim){                                                                                                                                                                                                          
    int i = 0;                                                                                                                                                                                                                               
    for (i = 0; i<dim - 1; i++)                                                                                                                                                                                                              
        if ((s[i] = getchar()) == '\n') break;                                                                                                                                                                                               
    if (i == dim - 1) FFLUSH;                                                                                                                                                                                                                
    s[i] = '\0';                                                                                                                                                                                                                             
}                                                                                                                                                                                                                                            


int set_interface_attribs (int fd, int speed, int parity) {
    struct termios tty;
    memset (&tty, 0, sizeof tty);
    if (tcgetattr (fd, &tty) != 0){
        printf ("error %d from tcgetattr", errno);
        return -1;
    }

    cfsetospeed (&tty, speed);
    cfsetispeed (&tty, speed);

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
    // disable IGNBRK for mismatched speed tests; otherwise receive break
    // as \000 chars
    tty.c_iflag &= ~IGNBRK;         // disable break processing
    tty.c_lflag = 0;                // no signaling chars, no echo,
    // no canonical processing
    tty.c_oflag = 0;                // no remapping, no delays
    tty.c_cc[VMIN]  = 0;            // read doesn't block
    tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

    tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
    // enable reading
    tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
    tty.c_cflag |= parity;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;

    if (tcsetattr (fd, TCSANOW, &tty) != 0){
        printf ("error %d from tcsetattr", errno);
        return -1;
    }
    return 0;
}

void set_blocking (int fd, int should_block){
    struct termios tty;
    memset (&tty, 0, sizeof tty);
    if (tcgetattr (fd, &tty) != 0) {
        printf ("error %d from tggetattr", errno);
        return;
    }

    tty.c_cc[VMIN]  = should_block ? 1 : 0;
    tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

    if (tcsetattr (fd, TCSANOW, &tty) != 0)
        printf ("error %d setting term attributes", errno);
}

void printFromSerial(int fd){
    char buf[100];
    while (1) {
        for (int i=0; i<100; i++) buf[i]='\0';
        int  n = read (fd, buf, sizeof(buf));
        if (n <= 0) break;
        printf("%s", buf);

    }
}

char *portname = "/dev/ttyACM0";

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

    int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0){
        printf ("error %d opening %s: %s\nTry\nsudo chmod 777 %s\n", errno, portname, strerror (errno), portname);
        return -1;
    }

    set_interface_attribs (fd, B9600, 0);  // set speed to 96000 bps, 8n1 (no parity)
    set_blocking (fd, 0);                // set NO blocking
    char buf [100];

    printFromSerial(fd);

    while (1) {
        for (int i=0; i<100; i++) buf[i]='\0';

        printf("Scrivi comando:");
        readString(buf, 100);  
        write (fd, buf, sizeof(char) * strlen(buf));


        printFromSerial(fd);

        usleep ((7 + 25) * 100);             // sleep enough to transmit the 7 plus
    }
    return 0;
}

LATO ARDUINO, per leggere semplici caratteri uso questa funzione:

int getCommand(){
    while(1) {
        while(!Serial.available());
        char s = Serial.read();
        Serial.flush();
        return s;
    }
}

Grazie ancora a tutti!

... non ti so dire esattamente su kubuntu, ma sicuramnete, dal 'C', hai modo di aprire una porta seriale e comunicarci.

Se passa qualcuno che conosce Linux ti potrà dire qualche cosa di più ... :wink:

Guglielmo

Io uso moserial
Mi trovo bene

Ma su Ubuntu non hai gtkterm?

Ma lo avete letto che ... deve gestire la comunicazione da programma (linguaggio C) o no ? ? ? :o

Guglielmo

@gpb01 sei tu che hai imbeccato deviando su linux :wink: che non dovrebbe centrare nulla se si usa C

stefano_raptor:
Però, volendo fare tutto questo tramite una GUI dal mio laptop, non posso usare il monitor seriale che fornisce l'ide di arduino.

Secondo me questa frase significa con un programma grafico apposta
se invece deve anche scriversela lui la Grafical User Interface.... aprire la seriale sarebbe il meno

EDIT
vediamo se ho capito:
lo OP vuole scrivere un programma in C sotto Linux, ha lo IDE per farselo, ma non sa aprire la porta seriale emulata con una USB
così girerebbe tutto, e avrebbe senso
nemmeno io so farlo, ma sono interessato, è tanto che voglio imparare a programmare su un PC moderno, seguo con interesse
in via straordinaria sono disposto a collaborare con uno studente, in violazione alle mie regole

docdoc:
Ma su Ubuntu non hai gtkterm?

no, non "di serie"
ti tocca di sudare..........
sudo apt install gtkterm

Ti sei dimenticato la parola inglese che da sola compone metà del vocabolario di quella lingua

-get

non serve piu’
anzi, ricordavo che non c’era proprio piu’
ho appena controllato, si puo’ ancora scrivere apt-get, nemmeno lo sapevo
controllato adesso info apt è consigliato ma apt-get non e’ ancora deprecato

maubarzi:
@gpb01 sei tu che hai imbeccato deviando su linux :wink: che non dovrebbe centrare nulla se si usa C

... come no ? ? ? ... ogni OS ha il suo modo di definire le seriali e quindi di cosa specificare quando si fa la open del file da 'C' :smiley:

Guglielmo

Beh allora sotto windows serve un esperto diverso se cambi la presa da una porta usb all'altra :stuck_out_tongue:

Intanto grazie delle risposte!

gpb01:
... non ti so dire esattamente su kubuntu, ma sicuramnete, dal 'C', hai modo di aprire una porta seriale e comunicarci.
[...]

Si, questo è quello che voglio fare ma non so come!

Standardoil:
Io uso moserial
Mi trovo bene

Fornisce qualche API da usare in programmi C ad hoc o posso "solo" scansionare manualmente una seriale?
Comunque continuerò a documentarmi, grazie dell'info!

Standardoil:
Secondo me questa frase significa con un programma grafico apposta
se invece deve anche scriversela lui la Grafical User Interface.... aprire la seriale sarebbe il meno

EDIT
vediamo se ho capito:
lo OP vuole scrivere un programma in C sotto Linux, ha lo IDE per farselo, ma non sa aprire la porta seriale emulata con una USB
[...]

La GUI la scrivo io sempre in C usando la libreria Allegro (come IDE uso vim :D), e su quella non ho problemi.
Il problema è appunto la comunicazione seriale da fare tramite C
[EDIT: nel titolo per IDE intendo quello di arduino, nel senso che non posso usare il monitor seriale che offre l'IDE di arduino]

gpb01:
... come no ? ? ? ... ogni OS ha il suo modo di definire le seriali e quindi di cosa specificare quando si fa la open del file da 'C' :smiley:

Guglielmo

Si, per questo ho specifiato l'OS! :slight_smile:

Secondo edit: vedo solo ora questa risposta scusatemi

docdoc:
Ma su Ubuntu non hai gtkterm?

No, non lo conosco ma mi documento subito

Vi prego di scusarmi se vi ho citato "tagliando" alcune parti, non volevo fare un reply troppo lungo!

stefano_raptor:
Vi prego di scusarmi se vi ho citato "tagliando" alcune parti, non volevo fare un reply troppo lungo!

Tranquillo, è il modo più corretto di fare i "quote* :slight_smile:

Allora, se si cerca con Google "c linux open serial port" ... escono tanti bei risultati ... QUESTO mi sembra interessante per quello che devi fare tu ... ::slight_smile:

Guglielmo

stefano_raptor:
La GUI la scrivo io sempre in C usando la libreria Allegro (come IDE uso vim :D), e su quella non ho problemi.
Il problema è appunto la comunicazione seriale da fare tramite C

Quindi, per riassumere: stai chiedendo nel forum di Arduino come si fa sotto Linux ad aprire una porta seriale Linux in C?
Mi sa che è meglio se chiedi in un forum di programmazione C sotto Linux, o, meglio, basta cercare 30 secondi sul solito Stackoverflow, dove di suggerimenti del genere ce ne sono parecchi. tu devi solo "riconoscere" la seriale di Arduino oppure, per fare prima, metti la porta direttamente nella configurazione del tuo programma.
Se poi cerchi un poco più specificatamente risposte sulla gestione della seriale di Arduino, trovi anche cose come QUESTA.
Scusami, ma per un programmatore "serio" la prima cosa che si deve fare è cercare su Stackoverflow.

Per qualsiasi sistema operativo, la seriale è un file. Quindi in C apri e leggi da un file.

Grazie Guglielmo, vado a leggermi quel post!

docdoc:
Quindi, per riassumere: stai chiedendo nel forum di Arduino come si fa sotto Linux ad aprire una porta seriale Linux in C?[...]

Si, ma la seriale è di arduino. Non mi sembra una domanda off-topic. Se ti da fastidio la mia domanda potevi anche andare avanti e non rispondermi e/o segnalarmi ad un moderatore.
E non mi ritengo un programmatore serio, tant'è che ancora studio e cerco di imparare chiedendo a chi ne sa più di me.

nid69ita:
Per qualsiasi sistema operativo, la seriale è un file. Quindi in C apri e leggi da un file.

Me lo aspettavo più complicato sinceramente, meglio così! I dati lato arduino li mando semplicemente come "write"?
Grazie!

Lato Arduino non cambi nulla ... così come parli con il monitor seriale dell'IDE, così parli con qualsiasi altra applicazione che apra la porta seriale ed invii/riceva dati su di essa :slight_smile:

Guglielmo

stefano_raptor:
Me lo aspettavo più complicato sinceramente, meglio così! I dati lato arduino li mando semplicemente come "write"?
Grazie!

Come dice @Guglielmo.
aggiungo, se hai una variabile intera,

  1. write() lo spedisce come binary ovvero serie di byte, essendo intera due byte.
  2. print() lo spedisce come testo, ovvero "converte" numero a testo e spedisce ogni singola cifra nel corrispondente ascii, 123 => "123" => 3 byte 49,50 e 51

Ok, ho letto la guida che mi avete consigliato e sono riuscito a far comunicare arduino con il pc.
Arduino quindi manda stringhe al pc e il pc riesce a leggerle.

Il contrario, però, non mi sta riuscendo. Ovvero inviare stringhe di comandi ad arduino.

Vi mostro la bozza di codice che ho scritto (ripeto, è una bozza):

char *portname = "/dev/ttyACM0";

int main(int argc, char **argv){
    int fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0){
        printf ("error %d opening %s: %s\nTry: sudo chmod 777 %s", errno, portname, strerror (errno), portname);
        return -1;
    }

    set_interface_attribs (fd, B9600, 0);  // set speed to 9600 bps, 8n1 (no parity)
    set_blocking (fd, 1);                // set blocking

    while (1) {
        usleep ((7 + 25) * 100);             // sleep enough to transmit the 7 plus
        int n=4;
        char buf [100];
        char stringaLetta[100];
        int index=0;
        for (int i=0; i<100; i++) stringaLetta[i]='\0';

       //inizio lettura da seriale
        while (n==4) {
           for (int i=0; i<100; i++) buf[i]='\0';
            n = read(fd, buf, sizeof buf);
            for (int i=0; i<4; i++) {
                if (buf[i]=='\n') break;
                else stringaLetta[index++] = buf[i];
            }
        }
        
        printf("Leggo: >%s>\n", trim(stringaLetta));
        
        //inizio scrittura su seriale
        printf("[pc]scrivi>");
        for (int i=0; i<100; i++) buf[i]='\0';
        readString(buf, 100);
        sprintf(buf, "%s", buf);
        write (fd, buf, 7);           // send 7 character greeting
        
    }
    return 0;
}

Dove le due funzioni set_ prima del while sono state prese da questa discussione (da voi suggeritami)
La funzione “readString” legge appunto stringhe e la uso da anni e di lei mi fido

Avete consigli? Io non sono molto sicuro di stare andando nella direzione giusta :confused: