В прошлой статье мы боролись с анемометром и чтобы применить его в хозяйстве, пришлось разрабатывать скрипт-сканер. Но, как оказалось, он совсем не универсальный и требует постоянной точечной настройки в самом коде Python практически под каждое новое устройство. Что ж, будем исправлять эту ситуацию.
Проблема: есть датчик, которым уже пользовались. Его сняли, положили на склад, прошли месяцы. И вот он понадобился. А какой у него адрес? Какая скорость? Где взять таблицу регистров? Документация утеряна, шильдик закрашен, штатная программа не видит устройство.
С такой ситуацией я столкнулся на днях. И поскольку это уже не первый раз, когда приходится плясать с бубном над каждым новым (или не очень новым) RS-485 устройством, то нужен универсальный сканер- один скрипт на Python, который сам перебирает все скорости на порту, находит устройство и сразу читает все его регистры.
Нам понадобится переходник RS485-USB
и понимание на каком COM порту появится наш датчик, посмотреть это можно в диспетчере устройств
Еще вам потребуется установленный Python и библиотека pyserial.
Что делает скрипт:
- Сканирует адреса от 1 до 247 на пяти скоростях: 4800, 9600, 19200, 38400, 115200
- Находит устройство и показывает его адрес, скорость и значение в регистре 0
- Сразу выводит полную таблицу всех доступных регистров (0...199)
Вам остаётся только прописать в коде номер COM-порта, на котором висит адаптер. Остальное скрипт сделает сам.
Код — full_scanner.py:
import serial
import time
def crc16(data):
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 1:
crc = (crc >> 1) ^ 0xA001
else:
crc >>= 1
return crc
PORT = 'COM4' # <-- УКАЖИТЕ СВОЙ ПОРТ
BAUD_RATES = [4800, 9600, 19200, 38400, 115200]
TIMEOUT = 0.15
REG_SCAN_LIMIT = 200
print("=" * 60)
print("УНИВЕРСАЛЬНЫЙ СКАНЕР MODBUS RTU v2.0")
print("=" * 60)
device_found = False
for baud in BAUD_RATES:
if device_found:
break
print(f"\nСканирую адреса на скорости {baud}...")
try:
ser = serial.Serial(PORT, baud, timeout=TIMEOUT)
time.sleep(0.2)
except:
print(f" Не могу открыть порт на {baud}")
continue
for addr in range(1, 248):
request = bytearray([addr, 3, 0, 0, 0, 1])
crc = crc16(request)
request.append(crc & 0xFF)
request.append((crc >> 8) & 0xFF)
try:
ser.write(request)
time.sleep(0.05)
if ser.in_waiting >= 5:
response = ser.read(ser.in_waiting)
if len(response) >= 5:
value = (response[3] << 8) | response[4]
print(f"\n [OK] Найдено устройство!")
print(f" Адрес: {addr}")
print(f" Скорость: {baud}")
print(f" Регистр 0 = {value}")
print(f"\n Сканирую регистры 0...{REG_SCAN_LIMIT-1}...")
found_regs = []
for reg in range(0, REG_SCAN_LIMIT):
req = bytearray([addr, 3, (reg >> 8) & 0xFF, reg & 0xFF, 0, 1])
c = crc16(req)
req.append(c & 0xFF)
req.append((c >> 8) & 0xFF)
try:
ser.write(req)
time.sleep(0.03)
if ser.in_waiting >= 5:
resp = ser.read(ser.in_waiting)
if len(resp) >= 5:
val = (resp[3] << 8) | resp[4]
found_regs.append((reg, val))
except:
pass
if found_regs:
print(f" Найдено регистров: {len(found_regs)}")
print(f" {'Регистр':>8} {'Hex':>8} {'Значение':>8}")
print(f" {'-'*8} {'-'*8} {'-'*8}")
for reg, val in found_regs:
print(f" {reg:8d} 0x{reg:04X} {val:8d}")
else:
print(" Регистры не найдены.")
device_found = True
break
except:
pass
ser.close()
if not device_found:
print("\nУстройств не найдено.")
print("Проверьте питание, подключение и номер COM-порта.")
print("\n" + "=" * 60)
print("Сканирование завершено.")
print("=" * 60)
***********************************************************************************
Ну что запускаем и вот наша потеряшка висит на 96 адресе, скорость 9600
Также мы получили таблицу регистров, и нас интересует блок, куда прописать новый адрес, а именно 0x001B.
Чтобы прописать адрес какому-либо устройству, универсального скрипта не придумал — он чуть переделанный с предыдущей статьи.
Скрипт для смены адреса устройства. Тут универсальности нет — нужно вручную прописать старый адрес, новый адрес, скорость и COM-порт. Работает через запись в регистр 0x1B (адрес Modbus). Проверил и на другом датчике..
но с другими устройствами может отличаться регистр хранения адреса возможно его нужно будет корректировать. Ниже скрипт для записи своего адреса в нужный регистр датчика..
import serial
import time
def crc16(data):
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 1:
crc = (crc >> 1) ^ 0xA001
else:
crc >>= 1
return crc
PORT = 'COM4' # свой порт
BAUD = 9600 # текущая скорость датчика
OLD_ADDR = 1 # текущий адрес
NEW_ADDR = 99 # новый адрес
print(f"Меняю адрес {OLD_ADDR} -> {NEW_ADDR}...")
ser = serial.Serial(PORT, BAUD, timeout=0.5)
time.sleep(0.1)
request = bytearray([OLD_ADDR, 6, 0, 0x1B, 0, NEW_ADDR])
crc = crc16(request)
request.append(crc & 0xFF)
request.append((crc >> 8) & 0xFF)
ser.write(request)
time.sleep(0.5)
ser.close()
print(f"Проверяю адрес {NEW_ADDR}...")
ser = serial.Serial(PORT, BAUD, timeout=0.3)
time.sleep(0.1)
request = bytearray([NEW_ADDR, 3, 0, 0, 0, 1])
crc = crc16(request)
request.append(crc & 0xFF)
request.append((crc >> 8) & 0xFF)
ser.write(request)
time.sleep(0.2)
if ser.in_waiting >= 5:
response = ser.read(ser.in_waiting)
if len(response) >= 5:
value = (response[3] << 8) | response[4]
print(f"ГОТОВО! Адрес {NEW_ADDR} ответил, регистр 0 = {value}")
ser.close()
Пользуйтесь. Работает на любом Python с библиотекой pyserial. Экономит часы времени и километры нервов.