Kirjoittaja Aihe: ? Sarjaportin lukeminen C:llä ?  (Luettu 2569 kertaa)

Mistofelees

  • Käyttäjä
  • Viestejä: 641
    • Profiili
? Sarjaportin lukeminen C:llä ?
« : 11.11.21 - klo:21.36 »
Kirjoitin Bash:lla kevyen scriptin, joka kutsuu toistuvasti C:llä kirjoitettua ohjelmaa 'GPS'
'GPS lukee sarjaportissa olevan GPS:n lähettämää tekstivirtaa, poimii siitä $GPRMC-lauseen, tulostaa sen ja poistuu.

Scripti pyörii 5 - 20 kierrosta. jonka jälkeen se jämähtää.
Vika on kirjoittamassani 'GPS'-ohjelmassa ja tarkemmin sen lukulauseessa
int n = read(fd, &line, sizeof(line));

Onko jotain tapaa, millä näkisi, mikä on vikana ? muistin ylivuotoja, tms

Olen kirjoittanut tuon GPS ainakin sata kertaa uudestaan eri rakenteilla

Scripti on vain testausta varten:
Koodia: [Valitse]
#!/bin/bash
lkm=0
while true; do
   ./gps;   lkm=$((lkm+1));   echo "lkm=$lkm"
done

nm

  • Käyttäjä
  • Viestejä: 16232
    • Profiili
Vs: ? Sarjaportin lukeminen C:llä ?
« Vastaus #1 : 11.11.21 - klo:23.00 »
Scripti pyörii 5 - 20 kierrosta. jonka jälkeen se jämähtää.
Vika on kirjoittamassani 'GPS'-ohjelmassa ja tarkemmin sen lukulauseessa
int n = read(fd, &line, sizeof(line));

Onko jotain tapaa, millä näkisi, mikä on vikana ? muistin ylivuotoja, tms

valgrind-työkalulla on helppo etsiä muistivuotoja. Syy voisi löytyä myös koodia silmäilemällä, mutta sitä pitäisi nähdä enemmän kuin tuo yksi rivi.

Ainakin timeoutit kannattaa asettaa, jos sarjaporttia lukee blokkaavassa tilassa.

Mistofelees

  • Käyttäjä
  • Viestejä: 641
    • Profiili
Vs: ? Sarjaportin lukeminen C:llä ?
« Vastaus #2 : 12.11.21 - klo:18.08 »
Tuota timeouttia olen kasvatellut eri tavoin.
Tässä on tuon C-kielisen osan sorsat

Koodia: [Valitse]
#define TERMINAL    "/dev/ttyS2"
#define FALSE 0
#define TRUE !(FALSE)

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
/**********************/
int set_interface_attribs(int fd, int speed){
    struct termios tty;
    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }
    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 50;
    tty.c_cc[VTIME] = 3;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}
int main(){
    char *port = TERMINAL;
    int fd;
    unsigned char c;
    unsigned char line[120];
    unsigned char temp[120];
    unsigned char jatkuu=1;
    unsigned int len;

    fd=open("/dev/ttyS2",O_RDONLY);
    if (fd < 0) {
        printf("Error opening %s: %s\n", port, strerror(errno));
        return -1;
    }
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    do {
        len = read(fd, &line, sizeof(line));
            if(strncmp("$GPRMC",line,5)==0){
                jatkuu=0;
                // Leikataan alusta 7 merkkiä ja lopusta 8 merkkiä:
                memmove(temp, line + 7, len -7-5 + 1);
                printf("LEN=%d %s\n",len,temp);
            }
       
    } while (jatkuu);
    close(fd);
}

kamara

  • Käyttäjä
  • Viestejä: 2944
    • Profiili
Vs: ? Sarjaportin lukeminen C:llä ?
« Vastaus #3 : 13.11.21 - klo:08.10 »
En osaa kovin hyvin C:tä ohjelmoida, mutta eikö tuon koodin pitäisi olla jotenkin seuraavaa ????
Koodia: [Valitse]
...
    /*baudrate 9600, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B9600);
    do {
        len = read(fd, &line, sizeof(line));
            if((strncmp("$GPRMC",line,6)==0) && (len>=7+5-1)){
                jatkuu=0;
                // Leikataan alusta 7 merkkiä ja lopusta 8 merkkiä:
                memmove(temp, line + 7, len -7-5 + 1);
                printf("LEN=%d %s\n",len,temp);
            }
       
    } while (jatkuu);
    close(fd);
}

Edit - Kyllähän tuossa minun "korjauksessa" on edelleenkin bugi, mutta ymmärtänet ideani.
« Viimeksi muokattu: 13.11.21 - klo:08.13 kirjoittanut kamara »

Mistofelees

  • Käyttäjä
  • Viestejä: 641
    • Profiili
Vs: ? Sarjaportin lukeminen C:llä ?
« Vastaus #4 : 15.11.21 - klo:13.48 »
Kiitos korjauksesta. Käänsin ja pistin kestotestiin.
Itselleni C on vieras kieli.

10 min ajon aikana tuli neljä timeout:a ja sitten koko kone meni jumiin, eikä vastaa mihinkään. Ei edes ping tai ssh.
IVO-boot auttaa.

Vika ei ole tekemässäsi korjauksessa, vaan jossain muualla järjestelmässä.
Tämä kone ei muuten jumittele, mutta tämä sarjaportin luku on sille liikaa.

Tässä on itsellä sellainen ongelma, että käyttämäni GPS-moduli sylkee satunnaisesti röykkiöittäin roskaa.
Moduli olisi muuten nepea ja tarkka, joten en viitsisi luopua

Mielestäni pistin tälle palstalle toisen version ohjelmasta. Siinä sarjaporttia luetaan merkki kerrallaan. Sitä ei nyt kuitenkaan näy missään.
« Viimeksi muokattu: 15.11.21 - klo:14.01 kirjoittanut Mistofelees »

jarmala

  • Käyttäjä
  • Viestejä: 774
    • Profiili
Vs: ? Sarjaportin lukeminen C:llä ?
« Vastaus #5 : 15.11.21 - klo:14.48 »
joka kutsuu toistuvasti C:llä kirjoitettua ohjelmaa 'GPS'

Onko se lukuohjelma jotenkin pakko kirjoittaa juuri C:llä?

Minulla on ollut jo vuosikausia käytössä Pythonilla kirjoitettu sarjaportin lukuohjelma, joka lukee Arduinolta tulevaa dataa. Se on viimeisen, pari vuotta sitten tekemäni, korjauksen jälkeen toiminut kuin junan vessa.

Vaikka nimenomaan kysyttiin C:llä kirjoittua ohjelmaa, rohkenen kuitenkin laittaa tähän esimerkinomaisesti tämän Pythonilla väkerretyn koodin. Anteeksi, jos se näyttää kamalalta ja rumalta, mutta minä olen Pythonissa ihan aloittelija...

Koodia: [Valitse]
#! /usr/bin/python
import serial
from time import strftime, sleep
ser = serial.Serial('/dev/ttyACM0', 9600, timeout = 0.1)
sleep (0.5)
while 1:
sleep(5)
lin = ("")
if ser.inWaiting() >= 1:
# odotetaan vielä hetki, että mahdollisesti juuri kirjoitettavana oleva tietue tulee valmiiksi...
sleep(0.15)
lin = ser.readline()
from datetime import datetime
aika = datetime.now().strftime('%H:%M:%S-%d/%m/%Y ')
line = aika + '\t' + lin.decode('utf8')
# print len(line)
# tarkistetaan, että tietue on oikean pituinen ja vain silloin kirjoitetaan se talteen
if len(line) >= 66 and len(line) <= 69:
f = open( 'a', 'a')
f.write (line)
f.close()
# print(line)

Tuo siis lukee sarjaportista rivin. Tarkistaa sen pituuden, jotta se ei ole viallinen ja liittää siihen aikaleiman ja kirjoittaa koko rimpsun tiedoston "a" perään. Gnuplotilla sitten luetaan syntynyt tiedosto...
Ubuntu 18.04 LTS, Gnome Flashback Metacity, Xeon E3-1245 V2, 8 GB
Ubuntu 22.04 LTS, KDE Plasma, Celeron N5105, 8 GB

Mistofelees

  • Käyttäjä
  • Viestejä: 641
    • Profiili
Vs: ? Sarjaportin lukeminen C:llä ?
« Vastaus #6 : 16.11.21 - klo:12.22 »
Kiitos. Mielenkiintoista päästä tutkimaan Pythonilla kirjoitettua koodia, Siitä on akaa, kun viimeksi olen Pythonia käyttänyt.
Tämä C lähti liikkeelle siiitä, kun kirjoitin OrangePi:lle C-pätkän, joka tulostaa suoraan 4-riviselle I2C LCD-näytölle. Tuli himo kokeilla C:tä muutenkin.