Yacht Devices Новости: Python Gateway 1.05: ШИМ, цифровые входы и управление светодиодной матрицей

03.02.2025

Представляем обновление, позволяющее использовать провода интерфейса NMEA 0183 для подключения оборудования с управлением широтно-импульсной модуляцией (ШИМ, PWM) или логическими уровнями.

Python Gateway привлекает внимание

На прошедшей выставке в Дюссельдорфе на нашем стенде работали три Python Gateway . Один имитировал GPS-приёмник и передавал текущие дату и время в NMEA 2000, чтобы датчики на стенде могли сохранять историю измерений. Второй выполнял наш классический пример: измерял напряжение на интерфейсе NMEA 2000 и передавал его в сеть как напряжение аккумулятора для отображения на многофункциональных дисплеях.

Третий Python Gateway получал из сети NMEA 2000 время, напряжение аккумулятора и данные окружающей среды от наших датчиков и выводил их на светодиодную матрицу WS2812B 8x32 (такие матрицы доступны в разных размерах, а также в виде ленты). Управление основано на последовательной передаче 24-битного значения цвета для каждого светодиода по одному проводу с помощью ШИМ. К этому Python Gateway мы также подключили кнопку, позволяющую останавливать и возобновлять смену данных на матрице.

На электрическом уровне NMEA 0183 представляет собой интерфейс RS422B. Когда на приёмопередатчик поступает логическая 1 или 0, на выходах TX+ и TX- появляются взаимно инвертированные сигналы. Аналогично работают входы RX+ и RX-: приёмопередатчик считывает 1 или 0 в зависимости от разности уровней. При этом RX+ или RX- можно соединить с землёй, чтобы считывать логический сигнал по одному проводу (подробнее см. раздел «II. NMEA Basics» в руководстве пользователя).

Таким образом, хотя современный интерфейс NMEA 0183 имеет пять проводов, программисту Python Gateway доступны только один выход и один вход. Для работы с ними можно использовать класс machine.Pin, предварительно отключив UART, который использует те же выводы:


   import machine
   uart_tx.deinit()
   p = machine.Pin("tx", machine.Pin.OUT)
   while True:
      p.on()
      time.sleep(0.1)
      p.off()
      time.sleep(0.1)

Этот код формирует на TX+ и TX- прямоугольный сигнал частотой 5 Гц с током до 100 мА, что позволяет непосредственно управлять маломощным реле. Но гораздо проще получить такой сигнал с помощью класса machine.PWM , не нагружая процессор:


   import machine
   uart_tx.deinit()
   p = machine.PWM("tx", freq=5, duty=50)
   while True:
      pass

Однако для светодиодной ленты WS2812B нужен более сложный сигнал. Логическая 1 кодируется высоким уровнем длительностью 0,8 мкс и следующим за ним низким уровнем длительностью 0,45 мкс. Логический 0 — высоким уровнем 0,4 мкс и низким уровнем 0,85 мкс. Для каждого пикселя нужно закодировать 24 бита цвета: по 8 бит для красного, зелёного и синего. А пикселей в нашем случае 32*8 = 256.

Кодирование битов WS2812B

Специально для этого в класс machine.PWM добавлен метод send(), принимающий массив байтов. Каждый байт (0..255) задаёт длительность сигнала в очередном периоде (0–100%). Для передачи 1 и 0 на WS2812B нужно передать функции два байта:


    { 255*0.8/(0.8+0.45), 255*0.4/(0.4+0.85) }

Вычисления несложные, но для оптимизации мы добавили функцию ydpg.bit2pwm(), позволяющую подготовить буфер для machine.PWM.send() за один вызов.

Вы можете скачать код бегущей строки , показанной на выставке, по этой ссылке (потребуется обновить прошивку устройства до версии 1.05).

Чтобы добавить в статью немного морской тематики, расскажем о недавней задаче, в которой порт NMEA 0183 устройства Python Gateway использовался по прямому назначению.

К нам обратились с просьбой подключить метеостанцию Lambrecht Weather Station WS6 к Transas Navi-Sailor 4100 ECDIS через NMEA 0183. Возникла проблема: станция передаёт влажность в предложении MHU, атмосферное давление — в MMB, а температуру воздуха — в MTA.

К сожалению, система 4100 ECDIS не поддерживает эти предложения и ожидает данные в MDA «Meteorological Composite». Поэтому между устройствами мы установили YDPG-01: его вход NMEA 0183 подключили к выходу метеостанции, а выход — ко входу 4100 ECDIS. Сеть NMEA 2000 в этом проекте не используется, поэтому соответствующий интерфейс YDPG-01 служит только для питания: к нему подключён тройник питания NMEA 2000 с двумя проводниками питания +12 В постоянного тока.

Следующая программа Python извлекает данные из входящих предложений MHU, MMB и MTA. После получения всех трёх она передаёт значения одним предложением MDA:


# buffer for MDA sentence
mda_data = [b''] * 20 # MDA contains 20 fields, make buffer of 20 strings
mda_mask = 0 # bit mask, indicates which messges were received {MTA, MHU, MMB} 

# parses received NMEA 0183 sentence
# with indication which data fields to extract
# when all MTA, MHU, MMB received, sends MDA
def clbk(index, line, mda_offset, field_count):
    global mda_data, mda_mask
    #split fields and remove talker ID 
    args = line.split(b'*')[0].split(b',')[1:]
    
    if len(args) == field_count:
        #copy received fields to MDA sentence fields
        mda_data[mda_offset:(mda_offset+field_count)] = args
        # mark received sentences [ MTA, MHU or MMB ]
        mda_mask |= 0x1 << index
        if mda_mask == 0b111:
            # all messages received, send combined MDA
            n0183.send(b'MDA,' + b','.join(mda_data))
            # reset MDA buffer
            mda_data = [b''] * 20
            mda_mask = 0

# callback: when air temperature received
def mta_clbk(n0183, line):
	clbk(0, line, 4, 2)
	return True

# callback: when humidity is received
def mhu_clbk(n0183, line):
	clbk(1, line, 8, 4)
	return True

# callback: when barometric pressure is received
def mmb_clbk(n0183, line):
	clbk(2, line, 0, 4)
	return True

# send 'MDA' with talker ID = 'WI'
n0183.talker(b"WI")

# forward received messages to TX port
n0183.forward(True)

# register RX callbacks
n0183.rxcallback(0, mta_clbk, b'$WIMTA')
n0183.rxcallback(1, mhu_clbk, b'$WIMHU')
n0183.rxcallback(2, mmb_clbk, b'$WIMMB')

Программа добавляет предложение MDA и формирует нужный поток на выходе Python Gateway:


$WIMWV,357.0,R,5.2,M,A*26
$WIMTA,-25.0,C*31
$WIMHU,100.0,,-40.0,C*15
$WIMDA,,,1050.0,B,-25.0,C,,,100.0,,-40.0,C,,,,,,,,*22
$WIMMB,,,1050.0,B*04

Эта небольшая и простая программа иллюстрирует несколько полезных приёмов разработки для YDPG-01:

  • перехват нужных предложений NMEA 0183 по маске через n0183.rxcallback()
  • двоичные флаги и побитовые операции (mda_mask)
  • формирование и отправка собственных предложений NMEA 0183, а также прямая пересылка полученных

Чтобы познакомиться с Python Gateway, перейдите на страницу устройства. Прошивка версии 1.05 доступна в разделе загрузок.