Source file: /~heha/mb-iwp/Bergwerk/Muldenkipper.zip/usm.cpp

/* Daten vom Ultraschall-Entfernungsmesser "SR04T" lesen.
Diese Implementierung richtet sich _nur_ am Inter-Character-Timeout
obwohl es auch möglich wäre, sich am 0xFF zu orientieren.
Und kommt mit wechselnder Systemlast zurecht,
sichtbar an gelegentlichen 2-Byte-Reads.
Sowie mit unvollständigen Telegrammen (dann wird auf die nächste Lücke gewartet).
poll() ist hier ohne (globales) Timeout, das Abstecken des Sensors
wird nicht erkannt, es wird ewig gewartet.
Läuft stundenlang wie am Schnürchen.

 220928	erstellt
 */
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h>

typedef unsigned char byte;

unsigned GetTickCount() {	// wie unter Windows in ms
 timespec t;
 clock_gettime(CLOCK_MONOTONIC_RAW,&t);
 return t.tv_sec*1000 + t.tv_nsec/1000000;
}

bool checksum_ok(byte b[4]) {
 if (b[0]!=0xFF) return false;
 byte cs=0;
 for (size_t i=0; i<3; i++) cs+=b[i];
 if (b[3]!=cs) fprintf(stderr,"%02X!=%02X",b[3],cs);
 return b[3]==cs;
}

int main(int argc, char**argv) {
 if (argc<2) {
  perror("Schnittstelle muss angegeben werden!");
  exit(1);
 }
 setbuf(stdout,0);	// Kein Puffern der Standardausgabe (wie Windows-Konsole)
 int fd=open(argv[1],O_RDWR|O_NOCTTY|O_SYNC|O_NONBLOCK);	// Asynchron (Windows: OVERLAPPED) ist IMHO immer von Vorteil
 if (fd<0) {
  perror("Kann Schnittstelle nicht öffnen!");
  exit(1);
 }
 termios tty;
 if (tcgetattr(fd,&tty)) {
  perror("tcgetattr() versagt!");
  exit(2);
 }
 tty.c_iflag = IGNBRK|IGNPAR|IGNCR;	// kein BREAK
 tty.c_oflag = 0;
 tty.c_cflag = CS8|CREAD|CLOCAL;	// 8 Bit, Lesen erlauben
 tty.c_lflag = 0;	// nicht-kanonisch (!!)
 tty.c_cc[VMIN]=1;	// Minimale Anzahl Bytes bevor read() zurückkehrt (bei O_NONBLOCK egal ob 1 oder 4)
 tty.c_cc[VTIME]=2;	// Inter-Character-Timeout in 0,1 s (hier nicht nutzbar weil 90 ms)
 cfsetospeed(&tty,B9600);
 cfsetispeed(&tty,B9600);	// Linux kann nur vordefinierte Baudraten (= krank)
 if (tcsetattr(fd,TCSANOW,&tty)) {
  perror("tcsetattr() versagt!");
  exit(2);
 }
 unsigned tic = GetTickCount();
 byte buf[4],fill=0;
 for(;;) {
  byte b[4];
  int rb=read(fd,b,sizeof b);	// Zumeist kommt hier 1 Byte, manchmal 2 Byte
  if (rb<0) {
   if (errno==EWOULDBLOCK) {
    pollfd p={fd,POLLIN};	// Windows: WaitForSingleObject, WaitForMultipleObjects
    if (poll(&p,1,-1)!=1) {	// (-1 == unendlich) warten bis es etwas zu lesen gibt
     perror("poll() versagt!");
     exit(3);
    }
   }else{
    perror("read() versagt!");	// seltsamerweise nicht mal beim Abstecken des USB-Seriell-Konverters
    exit(4);
   }
  }else{
   if (!rb) break;		// Das passiert beim Abstecken des Konverters
   unsigned toc = GetTickCount();
   bool gap=unsigned(toc-tic)>=40;	// Klappt manchmal nicht (gap==false),
// aber Linux ist (wie Windows) nun mal kein Echtzeitbetriebssystem:
// Zwischen read() und GetTickCount() kann beliebig Zeit verstreichen
// Man müsste dazu zeitgestempeltes read/poll haben.
   tic = toc;
   printf("%c",gap ? '\n' : ' ');	// Bei mehr als 40 ms Pause neue Zeile
   if (gap) fill=0;
   for (int i=0; i<rb; i++) {
    printf("%02X",b[i]);
    if (fill<4) buf[fill++]=b[i];	// Byte in Puffer kopieren
   }
   if (fill==4 && checksum_ok(buf)) {
    printf("%6d mm",buf[1]<<8|buf[2]);
    fill=0;
   }
  }
 }
 printf("\n");
 close(fd);
}
Detected encoding: UTF-80