O tym artykule
Zebrałem tutaj problemy, które faktycznie napotkałem przy pracy z urządzeniami przemysłowymi przez RS-485 i porty COM. Nie teoria — konkretne sytuacje, objawy i rozwiązania.
Port COM znika po restarcie
Objaw: Konwerter USB-RS485 był na COM8, po restarcie Windows przypisał mu COM12.
Przyczyna: Windows przypisuje numery COM dynamicznie. Przy każdym podłączeniu do innego portu USB lub po pewnych aktualizacjach numer może się zmienić.
Rozwiązanie:
- Menedżer urządzeń → Porty (COM i LPT) → prawy klik na urządzenie → Właściwości → Ustawienia portu → Zaawansowane
- Zmień "Numer portu COM" na stały (np. COM8) i zaznacz "Użyj tego portu COM, nawet jeśli jest zajęty"
- Alternatywnie: w kodzie nie hardcoduj numeru portu — daj użytkownikowi wybór z listy dostępnych portów
Urządzenie nie odpowiada — ale na pewno jest podłączone
Objaw: Port otwiera się, żądanie wysyłane, timeout — zero odpowiedzi.
Kolejność sprawdzania:
-
Baudrate — sprawdź w dokumentacji urządzenia. Domyślne 9600 to nie reguła. Spróbuj 19200, 38400, 115200.
-
Parity i stop bits — część urządzeń używa
8E1(8 data bits, Even parity, 1 stop bit) lub8N2. Złe ustawienie = brak odpowiedzi. -
Adres slave — sprawdź naklejkę lub dokumentację. Jeśli nieznany, zrób skan (1–32 to typowy zakres).
-
Terminator — brak terminatora 120Ω często objawia się brakiem odpowiedzi przy dłuższym kablu ale działaniem przy krótkim.
-
A/B zamienione — zamień przewody A i B między sobą.
-
Konwerter bez auto-DE — przy niektórych konwerterach USB-RS485 trzeba ręcznie sterować pinem DE (Driver Enable) przez RTS. W Pythonie:
rtscts=Truelubdsrdtr=True.
Odpowiedź urywana — dostaje część ramki
Objaw: Modbus zwraca błąd CRC albo za mało bajtów.
Przyczyny i rozwiązania:
Zbyt krótki timeout read:
# Za mało — przy wolnych urządzeniach może nie zdążyć
client = ModbusSerialClient(port="COM8", baudrate=9600, timeout=0.5)
# Bezpieczniej
client = ModbusSerialClient(port="COM8", baudrate=9600, timeout=2)
Garbage na początku bufora (echo konwertera):
# Przed wysłaniem żądania wyczyść bufor
serial_port.reset_input_buffer()
serial_port.reset_output_buffer()
Fragmentacja ramki na Windows: Windows grupuje bajty w buforze, co może powodować, że read() zwraca nie całą ramkę. Rozwiązanie: czytaj w pętli do momentu zebrania oczekiwanej liczby bajtów lub implementuj timeout między bajtami.
Działa na jednym komputerze, nie działa na drugim
Objaw: Ten sam kod, to samo urządzenie — na jednym PC działa, na drugim nie.
Przyczyny:
-
Sterowniki konwertera — CH340 wymaga sterownika na starszych Windows 10. Sprawdź czy w Menedżerze urządzeń nie ma żółtego wykrzyknika.
-
Inna wersja biblioteki — pymodbus 2.x i 3.x mają inny API.
pip show pymodbusna obu komputerach. -
Antywirus blokuje port — niektóre AV (szczególnie korporacyjne) mogą blokować dostęp do portów COM. Sprawdź logi AV lub wyłącz tymczasowo.
-
Uprawnienia — na Linux:
sudo usermod -a -G dialout $USER— bez tego normalny użytkownik nie ma dostępu do/dev/ttyUSB*.
Komunikacja działa, ale wartości są błędne
Objaw: Połączenie OK, rejestry odczytane, ale wartości nie mają sensu (np. temperatura -32768°C albo przepływ 99999).
Przyczyny:
-
Endianness float — 32-bitowy float składany z dwóch rejestrów Modbus może być w różnej kolejności:
- Big-endian: rejestr 0 = MSW, rejestr 1 = LSW
- Little-endian: rejestr 0 = LSW, rejestr 1 = MSW
- Byte-swap: bajty wewnątrz każdego rejestru zamienione
import struct r = result.registers # Wypróbuj wszystkie kombinacje jeśli wartość jest nonsensowna: struct.unpack(">f", struct.pack(">HH", r[0], r[1]))[0] # BE struct.unpack(">f", struct.pack(">HH", r[1], r[0]))[0] # LE words -
Offset adresów — dokumentacja podaje adres
40001, a w FC03 trzeba podać0. Zawsze odejmij 40001 (lub 30001 dla Input Registers). -
Skalowanie — wartość
1234może oznaczać12.34(× 100) albo123.4(× 10). Sprawdź dokumentację rejestru.
Port otwarty przez inny proces
Objaw: Serial Exception: [Errno 16] Device or resource busy (Linux) lub Access is denied (Windows).
Windows — znajdź który proces zajął port:
Windows nie ma wbudowanego sposobu na pokazanie który proces trzyma port COM. Najszybciej — handle.exe z pakietu Sysinternals:
# Numer urządzenia = numer portu COM minus 1
handle.exe \Device\Serial0 # COM1
handle.exe \Device\Serial7 # COM8
# Albo zbiorczo wszystkie porty:
handle.exe -a Serial
Najczęstsi sprawcy: Modbus Poll, inne instancje Python, LOGO! Soft Comfort, sterowniki drukarek z wbudowanym portem COM.
Linux:
fuser /dev/ttyUSB0 # pokaże PID procesu
lsof /dev/ttyUSB0 # więcej szczegółów