Czytanie danych z portu RS232 i wyświetlenie cągu otrzymanych znaków

0

Witam szanownych kolegów i koleżanki. Niestety programiści na innym forum mi nie pomogli, wiec postanowiłem spróbować uzyskać pomoc tutaj. Próbuje napisać program, który zczytuję z portu COM, a następnie wyświetla ciąg otrzymanych znaków.
Urządzeniem podpiętym pod port jest odbiornik GPS i wysyła depesze NMEA w następującej formie: $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
Potrzebuje przerobić otrzymany szereg znaków na pozycje: 48 stopni 07,038 minuty N, 011 stopni 31,000 minuty E. Do tej pory udalo mi sie samo wyświetlanie ciągów znaków następującym programem.

 
#include "stdafx.h"
#include <stdio.h>
#include <conio.h>
#include <string.h>
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

void system_error(char *name) {
  WCHAR ptr[1024];
  FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM,
        0,
        GetLastError(),
        0,
        //(char *)&ptr,
        ptr,
        1024,
        NULL);
  fprintf(stderr, "\nError %s: %s\n", name, &ptr);
  LocalFree(ptr);
}


int _tmain(int argc, _TCHAR* argv[])
{


  int ch;
  char buffer[1];
  HANDLE file;
  COMMTIMEOUTS timeouts;
  DWORD read, written;
  DCB port;
  HANDLE keyboard = GetStdHandle(STD_INPUT_HANDLE);
  HANDLE screen = GetStdHandle(STD_OUTPUT_HANDLE);
  DWORD mode;
 
  LPCWSTR port_name = L"\\\\.\\COM5";
  char init[] = ""; // e.g., "ATZ" to completely reset a modem.

  if ( argc > 2 )
    swprintf_s((wchar_t *)&port_name, 128,L"\\\\.\\COM%c", argv[1][0]);


  // open the comm port.
  file = CreateFile(port_name,
            GENERIC_READ | GENERIC_WRITE,
            0, 
            NULL, 
            OPEN_EXISTING,
            0,
            NULL);

  memset(&port, 0, sizeof(port));
  port.DCBlength = sizeof(port);
 
  timeouts.ReadIntervalTimeout = 1;
  timeouts.ReadTotalTimeoutMultiplier = 1;
  timeouts.ReadTotalTimeoutConstant = 1;
  timeouts.WriteTotalTimeoutMultiplier = 1;
  timeouts.WriteTotalTimeoutConstant = 1;
  if (!SetCommTimeouts(file, &timeouts))
    system_error("setting port time-outs.");

  // basic terminal loop:
  do {
    // check for data on port and display it on screen.
    ReadFile(file, buffer, sizeof(buffer), &read, NULL);
    if ( read )
      WriteFile(screen, buffer, read, &written, NULL);

    // check for keypress, and write any out the port.
    if ( _kbhit() ) {
      ch = _getch();
      WriteFile(file, &ch, 1, &written, NULL);
    }
    // until user hits ctrl-backspace.
  } while ( ch != 127);

  // close up and go home.
  CloseHandle(keyboard);
  CloseHandle(file);

  return 0;
}
0

Ale z czym masz dokładnie problem? Ja na twoim miejscu bym to podzielił na jakieś sensowne funkcje:

  • OpenPort()
  • InitPort()
  • ClosePort()
  • Read()
  • Write()

Przy czym niech Read zwraca jakiegoś stringa, albo char*. Bo widzę, że piszesz to w czystym C. Nie dość, że program będzie czytelniejszy, to i będzie Ci łatwiej sparsować odpowiedź urządzenia, bo tylko do tego się sprowadza Twój problem.

Aha i wstawiaj tagi kolorujące kod, będzie łatwiej czytać go innym.

0
  1. stawiam na to, że problemem jest założenie, że przy każdym wywołaniu ReadFile dostajesz jedną pełną linię. Wcale tak nie musi być.
  2. doczytaj dokumentację odbiornika GPS. Większość tego typu urządzeń działa na zasadzie terminala. Czyli jeśli coś do nich wysyłasz to musisz wysłać znak końca linii by urządzenie przetworzyło to dane. Powinieneś mieć jakiś zestaw rozkazów do urządzenia. Najprostsze urządzenie po prostu wysyła co jakiś czas dane a wysłanie odpowiedniej komendy przyspiesza wysłanie danych.
  3. zainstaluj sobie "Hyper terminal" (kiedyś był domyślnie preinstalowany z Windows) połącz go z odpowiednim ortem i wykonaj testy manualne.

patrząc na ten wątek http://stackoverflow.com/questions/6036716/serial-comm-using-writefile-readfile rozumiem czemu na innym forum cię zignorowano. Wygląda na to, że sam nic nie zrobiłeś, znalazłeś kod w internecie, a teraz nawet nie próbujesz go przerobić go sam.
0

To prawda kod jest pobrany z internetu. Jestem za bardzo początkujący, żeby napisać coś takiego sam.

  do {
    // check for data on port and display it on screen.
    ReadFile(file, buffer, sizeof(buffer), &read, NULL);
    if ( read )
     cout << buffer << endl;

Próbuje sie dostać do bufora kodem powyżej. Myślałem, że łyka po jednym znaku a wyświetla mi coś takiego:

d101d9ce24.png

0

No jest tak jak pisałem odbierasz dane po jednym bajcie.
Drukują ci się śmieci bo brakuje kończącego zera (znak końca napisu) popraw tak:

  do {
    // check for data on port and display it on screen.
    ReadFile(file, buffer, sizeof(buffer)-1, &read, NULL); // -1 by zostało miejsce na dodatkowe zero
    buffer[read] = 0;
    if ( read )
     cout << buffer << endl; // ten endl chyba tu przeszkadza
0

Ok. Teraz zrobiłem tak:

 ReadFile(file, buffer, sizeof(buffer), &read, NULL);
    if ( read )
	tekst = buffer;
	tekst.erase( 1, 30 );
	tekst = literka;
    cout<<tekst;
   

Ładnie pokazuje mi całą depesze, z tym że jeden string to jeden znak, wiec ciągle nie wiem jak dojść do poszukiwanych wartości czyli danych pozycji, które są gdzieś wśród tych znaków.

1

@lukisp2 zamiast programować metodą prób i błędów przeczytaj sobie dokumentację funkcji której używasz https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx

Jasno tam jest napisane dlaczego odbiera Ci po 1 bajcie.

BOOL WINAPI ReadFile(
  _In_        HANDLE       hFile,
  _Out_       LPVOID       lpBuffer,
  _In_        DWORD        nNumberOfBytesToRead,
  _Out_opt_   LPDWORD      lpNumberOfBytesRead,
  _Inout_opt_ LPOVERLAPPED lpOverlapped
);

Ty natomiast masz

char buffer[1];
.
.
.
.

ReadFile(file, buffer, sizeof(buffer), &read, NULL);

Zmień sobie rozmiar bufora i będziesz miał cały komunikat. I moja uwaga z 1 posta jest jak najbardziej na miejscu. Skoro piszesz w C++ to czemu sobie nie utworzysz klasy np. CComPort, ba nawet takie coś jest już napisane wystarczy poszukać w internecie. Będzie to o wiele czytelniejsze, a i nauczysz się programować lepiej.

1 użytkowników online, w tym zalogowanych: 0, gości: 1