ComPort.WriteStr - dziwne zachowanie

0

Cześć,

Piszę program do obsługi robota przez USB w Delphi. Wykorzystuję bibliotekę ComPort. W laptopie podłączenie USB do robota poprawnie zgłasza się jako Virtual Port Com. Gdy wysyłam komendę:

ComPort.WriteStr('komenda');

to procesor w robocie w ogóle jej nie zauważa. Natomiast gdy wysyłam z programu Terminal to jest OK.

Ustawienia transmisji mam takie same.

Gdzie może być przyczyna tego problemu?

Pozdrawiam.

0
Markoni napisał(a):

Gdzie może być przyczyna tego problemu?

Brak terminatora?

0

O jaki terminator chodzi?
Podsłuchuję transmisję w drugim Terminalu i wszystkie znaki nadawane (z Delphi/ z Terminala) są takie same.

Podsłuchuje na zasadzie przejściówka USB-RS232 <-> RS232-USB. Ale do robota transmisja jest bez przejściówki, czyli bezpośrednio USB <-> USB. Może tu jest problem?

2
ComPort.WriteStr('komenda'#13#10);
0

Ja w swoim programie kończę komendy samym: #10. Tak samo wysyłam z Terminala do robota i nadawanie/odbiór jest OK.
ComPort.WriteStr('komenda'+#10);
Dlaczego wysyłanie z ComPortu komendy z zakończeniem #10 (z zapisem, tak samo jak z Terminala) powoduje problemy?

Czy ComPort ma jakieś ograniczenia w Windowsie?

1
Markoni napisał(a):

Czy ComPort ma jakieś ograniczenia w Windowsie?

Nie, nie ma ograniczeń jako takich. Ja u siebie obsługuję port COM bez problemów. Nie kończę żadnymi znakami #13#10 i jest ok. To, czy konieczny jest znak nowego wiersza zależy tylko i wyłącznie od urządzenia podłączonego pod port COM. Zatem należałoby sprawdzić w jego dokumentacji jakich komend oczekuje i takowe wysłać.

Dawno się nie bawiłem terminalem. Jednak takie modemy GSM wymagają podania na końcu znaku nowego wiersza. Dlatego wysyłając komendy AT miało to znaczenie. Jednak w przypadku np. drukarek fiskalnych takie znaki są wręcz niepożądane.

To, że komendy wysyłane z terminala działają, natomiast z programu nie może sugerować, ze urządzenie przetwarza komendy po odbiorze pełnego wiersza. Jednak 100% pewność da Ci dokumentacja danego urządzenia.

0

To urządzenie, to robot na którego napisałem oprogramowanie. Zatem wiem jakich komend oczekiwać :)
Tylko nie rozumiem, dlaczego z ComPortu/Delphi i Terminala jest inna reakcja?

W zasadzie z urządzenia/robota nie wychodzi COM, tylko USB bezpośrednio podłączone do procesora.

1
Markoni napisał(a):

To urządzenie, to robot na którego napisałem oprogramowanie. Zatem wiem jakich komend oczekiwać :)

Zatem musisz się pochylić bardziej nad softem wewnętrznym i sprawdzić w jaki sposób wczytujesz dane z portu. Czy wymagany jest znak entera czy nie. Nie znam się aż tak na embedded, ale to pewnie zależy od funkcji jakich używasz w kodzie wewnętrznym robota.

Markoni napisał(a):

Tylko nie rozumiem, dlaczego z ComPortu/Delphi i Terminala jest inna reakcja?

W zasadzie z urządzenia/robota nie wychodzi COM, tylko USB bezpośrednio podłączone do procesora.

Takie USB instaluje się w systemie jako wirtualny port COM który to tak naprawdę nie różni się od strony programisty niczym od fizycznego portu COM.

To może być kwestia ustawień, terminal może używać zupełnie innych ustawień portu COM niż Twój program. A żeby być pewnym co do znaków #13#10 posłuż się snifferem i podsłuchaj komunikację wraz z jej parametrami gdy działa program terminalu, oraz Twój. Wtedy zobaczysz jakie masz różnice.

0

Jaki jest sniffer pod Windowsa7/10?

2

Osobiście do sprawdzenia jakie parametry ustawia sobie obcy proces przed nawiązaniem połączenia z portem COM używałem tego http://www.rohitab.com/apimonitor Ustawianie parametrów podejrzałem w funkcji SetCommState Program ładnie zdekodował mi strukturę DCB https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-dcb

Natomiast do zwykłego podglądania tego co przesyła się po porcie COM używałem https://docs.microsoft.com/en-us/sysinternals/downloads/portmon

0
Markoni napisał(a):

Jaki jest sniffer pod Windowsa7/10?

https://www.eltima.com/products/serial-port-monitor/

Tylko po co Ci sniffer, skoro wiesz co wysłać terminalem i to działa.

2

Nie chcę się wypowiadać za bardzo w tym wątku, dlatego że doświadczenie w temacie komunikacji szeregowej mam praktycznie żadne (nie licząc Arduino, ale to inna bajka). Mimo wszystko – tak na logikę – komunikacja musi być oparta o jakieś metadane lub z góry narzucony stały rozmiar ramki. Urządzenie odbierające dane musi wiedzieć ile ich otrzyma (z góry określony rozmiar bufora), a jeśli nie wiadomo ile tych danych będzie, wymagane jest oznaczenie końca danych znakiem terminatora. Tak działają np. kasy fiskalne.

W sieci masz kupę przykładów i wątków dotyczących ComPort.WriteStr – wszystkie wykorzystują terminator.

2
furious programming napisał(a):

Mimo wszystko – tak na logikę – komunikacja musi być oparta o jakieś metadane lub z góry narzucony stały rozmiar ramki. Urządzenie odbierające dane musi wiedzieć ile ich otrzyma (z góry określony rozmiar bufora), a jeśli nie wiadomo ile tych danych będzie, wymagane jest oznaczenie końca danych znakiem terminatora. Tak działają np. kasy fiskalne.

Tak jest, większość urządzeń z jakimi miałem do czynienia właśnie tak działały jak piszesz. Ale.... raz się spotkałem z urządzeniem które nie miało takich ramek. Ot po prostu po każdym znaku analizowało komendę i jak była prawidłowa wykonywała ją. Dziwne to było, jednak trzeba było się dostosować. Nadmienię, że to był pomysł architektów systemów wewnętrznych i urządzenie nie wyszło poza firmę. Jednak musiałem gadać z nim i sama integracja trwała długo. Niemniej jednak terminatorem nie musi być znak nowej linii. W drukarkach fiskalnych które miałem obsługiwać taka ramka ma postać:

ESC komenda ESC \

Poza tym bardzo ważne są dokładne ustawienia portu COM, musi się zgadzać z tym co ustawione w urządzeniu.

1
Mr.YaHooo napisał(a):

Ale.... raz się spotkałem z urządzeniem które nie miało takich ramek. Ot po prostu po każdym znaku analizowało komendę i jak była prawidłowa wykonywała ją. Dziwne to było, jednak trzeba było się dostosować.

Może nie tyle dziwne, co nietypowe. Sam bym takiego sposobu nie wybrał. Raz, że trzeba by więcej kodu klepać, aby móc poprawnie interpretować nadchodzące dane, a dwa, że testowanie zawartości bufora po każdym otrzymanym znaku jest tak samo wydajne co sortowanie bąbelkowe. No bez sensu.

Nie mniej jednak terminatorem nie musi być znak nowej linii.

Nigdzie nie pisałem, że musi nim być konkretnie CR lub LF (albo sekwencja CRLF). :P

0

Tylko, że jak pisałem z Terminala odbiera, a z ComPortu NIE. Gdyby z Terminala nie odbierał, to błąd byłby po stronie urządzenia/robota.
Poza tym ramka ma zmienną długość, ale ma znaki początku i zakończenia ramki.

0

Zainstalowałem API Monitor i w funkcji SetCommState ustawienia dla Terminala i ComPort są te same. Różnica jest tylko w wReserved, XonLim, XoffLim.
Często mam też problemy z otwarciem portu: SetCommState=Error: 87.
Ale w Terminalu otwiera się zawsze.

Jak można usunąć problem z SetCommState=Error: 87?

0

@Markoni ten błąd niestety jest dość parszywy. Przyczyna może być różna od śmieci które się pojawiają podczas tworzenia struktury DCB poprzez błędne parametry których nie obsługuje port. Zatem osobiście próbowałbym ustawić dokładnie takie same parametry jak Terminal który nie ma problemu z połączeniem.

Swoją drogą błąd 87 masz podczas otwarcia portu, czy wykonania funkcji SetCommState Generalnie sprawdź czy masz mniej więcej podobnie jak tutaj https://docs.microsoft.com/pl-pl/windows/win32/devio/configuring-a-communications-resource?redirectedfrom=MSDN przepisz to na Delphi i sprawdź czy zadziała, podstawiając swoje parametry.

Osobiście nigdy podczas obsługi portu COM nie spotkałem się z tym błędem, więc trochę ciężko mi doradzić.

0

Błąd pojawia się podczas otwierania portu.

0

W takim razie być może źle tworzysz nazwę pliku który otwierasz jako port COM. Ciężko coś powiedzieć bez kodu. Fajnie by było abyś dał jakiś przykład jak otwierasz oraz ustawiasz parametry portu.

0

Prawdę mówiąc to otwieram klasycznie, przez wprowadzenie parametrów transmisji do TComPort, podania numeru portu i komendę ComPort.Open.
Jak powinno się "profesjonalnie" otwierać port COM?

1
Markoni napisał(a):

Prawdę mówiąc to otwieram klasycznie, przez wprowadzenie parametrów transmisji do TComPort, podania numeru portu i komendę ComPort.Open.

Wklej kod zamiast opisywać jak on wygląda…

0

Nie wstawiałem, ponieważ do otwarcia portu za dużo kodu to nie ma:

procedure TForm1.btnOpenClick(Sender: TObject);
begin
    if ComPort.Connected = false then
    begin
        if ComComboBox1.Text = '' then exit;
        ComPort.Port:= ComComboBox1.Text;
        ComPort.Open;
        btnOpen.Caption := 'Close';
    end else
    begin
        ComPort.Close;
        btnOpen.Caption := 'Open';
    end
end;
2

I to są właśnie uroki tworzenia aplikacji KLIKANIE+KODOWANIE
Jak wysyłasz taki kawałek kodu to nikt nie wie co jest w środku obiektu "ComPort" bo masz go WYKLINANEGO na FORMIE

Masz ustawione poprawne parametry transmisji ?
Zacznij moze od jakiego "terminal-a" i wyslij komendę przez coś co na 100% działa , potwierdzisz tym sposobem ze jest komunikacja WINDOWS -> robot

Ewentualnie zacznij od kodu 
var
  ComPort:TComport; 
begin
  ComPort:=TComport.create(...)
  // ustaw parametry 
1
Markoni napisał(a):

Błąd pojawia się podczas otwierania portu.

A czy ty przypadkiem nie masz otwartego tego portu w terminalu i próbujesz go równocześnie otworzyć w swoim programie? 🤔

0

Nie rozumiem tych ostatnich postów, przecież wcześniej napisałem, że bez problemu łącze się między Terminalem i ComPortem, przy pomocy przejściówek COM-USB .
Problem jest w łączności USB<->USB.

2
Markoni napisał(a):

Prawdę mówiąc to otwieram klasycznie, przez wprowadzenie parametrów transmisji do TComPort, podania numeru portu i komendę ComPort.Open.
Jak powinno się "profesjonalnie" otwierać port COM?

Klasycznie w stylu Delphi ;) Wziąć komponent, ustawić parametry i już. To jak robi się klasycznie widziałeś w linku który Ci przesłałem wcześniej https://docs.microsoft.com/pl-pl/windows/win32/devio/configuring-a-communications-resource?redirectedfrom=MSDN Plus CreateFile, WriteFile oraz ReadFile Niestety nie wiemy co komponent robi pod spodem i jak jest oprogramowany. Z drugiej strony podałeś też chyba nie do końca cały kod, ponieważ pisałeś w poprzednim poście

Markoni napisał(a):

Zainstalowałem API Monitor i w funkcji SetCommState ustawienia dla Terminala i ComPort są te same. Różnica jest tylko w wReserved, XonLim, XoffLim.
Często mam też problemy z otwarciem portu: SetCommState=Error: 87.
Ale w Terminalu otwiera się zawsze.

Jak można usunąć problem z SetCommState=Error: 87?

Zatem gdzie jest wywołanie tej metody i jak sprawdzasz wystąpienie błędu w wyniku czego wiesz, że to akurat błąd 87?

Markoni napisał(a):

Nie rozumiem tych ostatnich postów, przecież wcześniej napisałem, że bez problemu łącze się między Terminalem i ComPortem, przy pomocy przejściówek COM-USB .
Problem jest w łączności USB<->USB.

Nic bardziej mylnego. Skoro inny program łączy się poprawnie z robotem, to winą jest Twój program, a konkretnie podejrzewałbym komponent którego używasz TComPort. Osobiście nie znalazłem w miarę nowego komponentu/klasy o tej nazwie. A w swoim projekcie po prostu zakodowałem sam swoją klasę do wysyłania danych po porcie COM. Zajęło mi to 100 linijek (niestety piszę w C++) i takich problemów jak Ty nie miałem nigdy.

Zatem albo po prostu masz parę możliwości:

  • zrobisz jak ja i napiszesz własną krótką klasę
  • poszukasz innego, nowszego komponentu
  • przeanalizujesz kod źródłowy komponentu i znajdziesz błąd
  • zgłosisz ticketa u dostawcy komponentu

Oczywiście zakładam, że nie popełniasz głupiego błędu jak łączenie się do portu przez dwa programu na raz

0

Mr.YaHooo, przesłany przez ciebie kod jest do C++. Jak to zamienić do Delphiego (pascala)?

Co do błędu 87, to wyskakuje taki komunikat podczas otwierania portu.

To że błąd leży po stronie TComPortu to oczywiste i od dłuższego czasu próbuję to rozwiązać.

Napisać własną krótką klasę - na to jestem jeszcze za cienki ;)

Na jakich komponentach od COM-a pracujecie, na których nie macie problemów?

0
Markoni napisał(a):

Napisać własną krótką klasę - na to jestem jeszcze za cienki ;)

Nie umiesz napisać kodu otwierającego plik np. o nazwie COM1 oraz zapisujący do i odczytujący z niego dane? A później opakować go w jakąś małą klasę, żeby zamknąć całą logikę związaną z komunikacją w jednym kontenerku?

3

Synapse:

uses
  synaser;

procedure TForm1.Button1Click(Sender: TObject);
var
  com: TBlockSerial;
begin
  try
    com:=TBlockSerial.Create;
    com.Connect('COM1');
    com.Config(9600, 8, 'N', 1, False, False); 
    com.SendString('komenda'+#13#10);
  finally
    com.Free;
  end;
end;  
1
Markoni napisał(a):

Na jakich komponentach od COM-a pracujecie, na których nie macie problemów?

To jest na tyle proste, że sam oprogramowałem klasę. Zmieścisz się w mniej niż 100 liniach kodu.

Co do rozumienia C/C++. Jeśli poważnie traktujesz programowanie pod Windows to musisz nauczyć się czytać kod C/C++ ponieważ wszystkie przykłady WinAPI (które czasami jest niezbędne) są właśnie w tych językach.

Paweł Dmitruk napisał(a):
com.Connect('COM1');

A co w przypadku gdy mamy port większy niż 9? Synapse nie wymaga składni takiej jak funkcja https://support.microsoft.com/en-us/help/115831/howto-specify-serial-ports-larger-than-com9 Akurat w przypadku portów wirtualnych instalowanych po podłączeniu różnych urządzeń pod USB często ustawia się numer portu większy od 10 i można się nadziać w ten sposób.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.