February 3, 2025  Python Gateway 1.05 Update

We are happy to introduce an update that allows you to use NMEA 0183 interface wires to connect equipment controlled by pulse width modulation (PWM) or logic levels.

Python Gateway is very attractive

At the last trade fair in Dusseldorf, three Python Gateways were installed at our company's booth. One emulated a GPS receiver and sent real time and date to the NMEA 2000 network so that the sensors at our booth could accumulate measurement history. The second performed our classic example: measuring voltage on the NMEA 2000 interface and sending the measurements to the network as battery voltage data, making them available for display on multifunction displays.

The third Python Gateway took data from the NMEA 2000 network (time, battery voltage, environmental data from our sensors) and displayed it on a WS2812B 8x32 LED matrix (the matrix is available in different sizes, and also as a strip). The control feature is the serial transmission of a 24-bit color value for each LED using pulse width modulation over a single wire. A button was also connected to this Python Gateway to stop and start the data change on the matrix.

The NMEA 0183 interface is electrically an RS422B interface, which means that the TX+ and TX- outputs will show an inverse signal when a logic "1" or "0" is sent to the transceiver. Similarly with the RX+ and RX- inputs: the transceiver reads "1" or "0" depending on the difference between the signal levels, but RX+ or RX- can be connected to ground to read a single wire logic signal (see "II. NMEA Basics" in the User's Manual for more details).

Thus, although the modern NMEA 0183 interface has five wires, only one output and one input are available to the Python Gateway programmer. To access them, you can use the machine.Pin class, having previously deinitialized the UART, which also uses these pins:


   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)

The code above provides on TX+ and TX- outputs a rectangular signal with frequency 5 Hz and current up to 100 mA, which allows direct control of a low-power relay. But it is much easier to get such a signal using the machine.PWM class without loading the processor:


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

However, for the WS2812B LED strip, the output signal is more complex. A logic "1" is encoded by a high level of 0.8 us duration followed by a low level of 0.45 us. A logic "0" is encoded with a high of 0.4 us and a low of 0.85 us. For each pixel we need to encode 24 bits of color, 8 bits each for Red, Green and Blue. And pixels in our case are 32*8 = 256.

WS2812B bits

Especially for this purpose, in the class machine.PWM was added method send(), which accepts an array of bytes, where each byte (0..255) means the duration of the signal in consecutive cycles (0-100%). To send a "1" and a "0" for the WS2812B, we would need to pass two bytes to the function:


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

Such calculations are not difficult, but for optimization we added the function ydpg.bit2pwm(), which allows us to prepare a buffer for machine.PWM.send() in one call.

You can download the code for the running line used at the expo at this link (requires updating the device firmware to version 1.05).

To add something nautical to this article, we will share with you a recent case study where the NMEA 0183 port was used in a Python Gateway for its intended purpose.

We have received a request to connect an NMEA-0183 capable Lambrecht Weather Station WS6 to Transas Navi-Sailor 4100 ECDIS via NMEA 0183 port. However, there is a problem: the Weather Station outputs humidity data via NMEA 0183 sentence MHU, air pressure via MMB and air temperature via MTA.

Unfortunately these sentences are not supported by the 4100 ECDIS system, it expects these data to be sent via MDA "Meteorological Composite" sentence instead. So we have inserted YDPG-01 in between: YDPG-01 NMEA 0183 listener port to the Weather Station talker port and YDPG-01 NMEA 0183 talker port back to the 4100 ECDIS system listener port. Since NMEA 2000 is not used in this project, the YDPG-01 NMEA 2000 interface was used only to power the Gateway: a NMEA 2000 power TEE with only two power rails (+12 V DC) was connected.

The following Python program was written to extract data from incoming MHU, MMB, and MTA sentences and, when all three are received, to send all data values via a single MDA sentence:


# 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')

This program produces the desired output on the Python Gateway talker port by adding the MDA sentence:


$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

This program is rather simple and short, but it well illustrates several important techniques that can be very useful in YDPG-01 program development, like:

  • interception of certain received NMEA 0183 sentences by "match mask" via n0183.rxcallback()
  • binary flags and bitwise operations (mda_mask)
  • crafting and sending custom and direct forwarding of received NMEA 0183 sentences

To start getting familiar with the Python Gateway, go to its page. The firmware update 1.05 is available at the Download section.

 

Next articles:

Previous articles:

See also: recent news, all news...