Trouble reading serial data from legacy device

Hi,
I am trying to interface an Arduino with a depth gauge sensor on my boat. The depth sensor is no longer supported and there are no specs or support for it (if you're curious, it's a Standard Horizon DS50). However, it has a 5-pin interface and using a multimeter I was able to identify the GND, VCC (12v) and TX pins, and by hooking up an FTDI board to my MacBook and using a terminal program, I was able to verify the device is transmitting data at 1200 baud 8N1. The device is transmitting well-formed 8-byte sentences that start with 0x38 and end with 0x00.

I wrote a python program to read the data:

port = serial.Serial(portName, baudrate=1200, bytesize=8, parity='N', stopbits=1, timeout=0)
print(port.name, file=sys.stderr)

word = ''
while True:
  data = port.read(1)
  if len(data) > 0:
    for c in data:
      if len(word) == 0 and c == 0x38:
        word = b'\x38'
      elif len(word) == 7 and c == 0:
        ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print("%-12s %4X %4X %4X %4X %4X %4X %4X" % (ts, word[0], word[1], word[2], word[3], word[4], word[5], word[6]))

        word = b''

      elif len(word) > 0:
        word = word + bytes([c])

Which produces output like this when connected as described with either a 5V or 3.3V FTDI adapter:

/dev/cu.usbserial-A603AXO7
2023-09-03 13:52:21   38   83   B9   28   3B   2B   32
2023-09-03 13:52:23   38   87   B9   28   3B   2B   35
2023-09-03 13:52:25   38   3B   B9   A9   83   2B   2A
2023-09-03 13:52:26   38   83   B9   A8   3B   2B   35
2023-09-03 13:52:26   38   3B   B9   A8   3B   2B   2A

And another python program to write simulated data:

port = serial.Serial(portDev, baudrate=1200, bytesize=8, parity='N', stopbits=1, timeout=0)

random.seed()
template = [56, 59, 185, 0, 0, 43, 42, 0]
while True:
  # change bytes 3 & 4 to random numbers
  template[3] = random.randint(1, 200)
  template[4] = random.randint(1, 200)
  print(' '.join(['{:02X}'.format(c) for c in template]), file=sys.stdout)
  port.write(template)
  time.sleep(0.75)

And of course, an Arduino program that essentially does the same thing as the first python program; I'm connecting the RX pin on the Arduino to the TX pin on the DS50 and GND to GND.

#include <LiquidCrystal.h>

const int rs = 9, en = 8, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
  Serial.begin(1200, SERIAL_8N1);

  // initialize the display
  lcd.begin(16, 2);
  lcd.clear();
}

unsigned char ds50Buf[16];
int ds50BufSize = 0;

void loop() {
  
  while( Serial.available() > 0 ) {
    process();
  }
}

void process() {
  int readByte;
  char buf[4];
  
  status("PROCESS");
  readByte = Serial.read();
  status_byte("RX", readByte);

  if( ds50BufSize + 1 >= sizeof(ds50Buf) ) {
    status("RESET");
    ds50BufSize = 0;
  }

  if( ds50BufSize == 0 && readByte == ds50Byte0 ) {
    status("BEGIN");
    ds50Buf[ds50BufSize++] = readByte;
  }
  else if( ds50BufSize == 7 ) {
    status("END");
    if( readByte == 0 ) {
      // legitimate ds50 sentence
      ds50Buf[ds50BufSize++] = readByte;
      logData();
    }
    ds50BufSize = 0;
  }
  else if( ds50BufSize > 0 ) {
    ds50Buf[ds50BufSize++] = readByte;
  }
}

void status(const char *s) {
   status(s, 3);
}

void status(const char *s, int pos) {
  int i, x, y;

  y = pos/2;
  x = (pos%2) * 8;

  lcd.setCursor(x, y);
  lcd.print(s);
  i = strlen(s);
  while( i++ < 8 )
    lcd.print(' ');
}

void status_byte(const char *s, int b) {
  char buf[12];

  strcpy(buf, s);
  sprintf(buf + strlen(buf), "%02x", b);
  status(buf, 0);
}

void logData() {
  unsigned long t = millis();
  char lcdBuf[20], logBuf[32], buf[4];
  int i;

  status("LOGGING");

  // sanity check
  if( ds50BufSize > 8 ) ds50BufSize = 8;

  sprintf(logBuf, "%06ld", (long) (t/1000));
  lcdBuf[0] = 0;
  
  for(i=0;i<ds50BufSize;i++) {
    sprintf(buf, "%02X", (int) ds50Buf[i]);

    // don't print the terminating 00 to screen
    if( i < 7 ) strcat(lcdBuf, buf);
    
    if( i == 2 || i == 4 ) {
      strcat(lcdBuf, " ");
    }
    
    strcat(logBuf, " ");
    strcat(logBuf, buf);
  }

  lcd.setCursor(0, 0);
  lcd.print(lcdBuf);
  lcd.setCursor(0, 1);
  lcd.print(millis()/1000);
}

I'm testing with both an Arduino Uno R3 (5V 16Mhz) and an Arduino Pro Mini (3.3v 8Mhz).

Results:

  1. I can connect my Macbook to the DS50 and run the 1st program (data receiver) and it produces the expected 8-byte sentences
  2. I can connect my Macbook running the 2nd program (data simulator) to an Arduino running the program shown and the Arduino parses and prints the same 8-byte sentences
  3. However, connecting the Arduino to the DS50 produces a string of unrecognized bytes, mostly 0x2B, 0x2E and 0x00.
  4. I've tried inserting a logic shifter on all of the scenarios described above with no effect; i.e., the Macbook can communicate with either the DS50 or the Arduino (suggesting the logic shifter is correctly connected) but the Arduino still doesn't read data correctly from the DS50. For the Uno I'm connecting VCC on the logic shifter to 5V on the Uno, and for the Pro/Mini I'm connecting to VCC (3.3v)
  5. The Macbook can read the DS50 with either a 3.3V or 5V FTDI adapter, with or without the logic converter.

Bottom line: all 3 devices are sending or receiving at 1200 baud, 8N1. The Macbook can read either Arduino or the DS50 just fine. Either Arduino can read the Macbook (sending simulated data) fine. But neither Arduino can read the DS50; instead it gets seemingly random data bytes. Logic shifters seem to have no effect.

Any ideas what could account for this? Perhaps a default in the serial setup that is different in the Arduino and Macbook/python environments?

could you upload a schmatic showing the circuit ?
you would probably get beter results using a device with hardware serial ports such as a Mega or ESP32

In case you don't already have a copy, here's the DS50 user manual:
DS50 Owner's Manual.pdf (828.3 KB)

The manual indicates that NMEA 0183 may be the serial comms protocol. If that is true, then the baud rate may be 4800 or 38400. NMEA sentences are ASCII text so should be easily viewable with a simple terminal program on your Mac.

If your sensor is speaking NMEA sentences, then I believe that there are libraries for Arduino that can extract the data for you.

You can run a software serial port with 4800 baud, but you will really need a hardware serial port for 38400 baud.

1 Like

The manual indicates that NMEA 0183 may be the serial comms protocol. If that is true, then the baud rate may be 4800 or 38400. NMEA sentences are ASCII text so should be easily viewable with a simple terminal program on your Mac.

It's not NMEA. 0183 consists of ASCII characters beginning with $ and ending with 0x0D0A. This is producing 8-byte sentences beginning with '&' and ending in 0x00. Higher baud rates would result in sentences that are too short for NMEA. It must be a proprietary format.

Just to clarify, my problem isn't parsing the output. I'll figure out the data format once I can get the Arduino to produce the same data from the DS50 as the Macbook does, even though the serial configurations are the same (Arduino and Macbook/python).

could you upload a schmatic showing the circuit ?

Here's the basics of it, leaving off power to the boards and connections to the LCD, which are not relevant. The connections to the Macbook (which works) are similar, except they connect to the TXO and GND pins of an FTDI adapter. I've also tried connecting directly, without the logic shifter in between, and leaving off the VCC connections. That always works fine connecting the Arduino to the simulator on the Macbook, or the Macbook to the DS50, but never the Arduino to the DS50.

you would probably get beter results using a device with hardware serial ports such as a Mega or ESP32

Sorry, I always understood that the Uno/Pro serial port was a hardware serial port, isn't it? I'm connecting to RX and not using the SoftwareSerial library.

The UNO does have a hardware serial port. It is hard wired to the USB-Serial interface. It's generally not a good idea to use the hardware serial port for debugging / printing as well as interfacing with other devices.

If you are sure that your data stream is running at 1200 baud, then that's well within the capability of a software serial port.

It's generally not a good idea to use the hardware serial port for debugging / printing as well as interfacing with other devices.

For sure. That's why I use the LCD for debugging/printing.

If you are sure that your data stream is running at 1200 baud, then that's well within the capability of a software serial port.

Actually, I've already tried using SoftwareSerial and a few of its alternatives with no better luck.

When you used the FTDI serial-USB board, did you also use the level shifter you show in your UNO picture?

It sounds like you are able to receive some repeatable data format. But have you established the signal level of the TX pin. I would have thought that it would be using RS-232 voltage levels.

When you used the FTDI serial-USB board, did you also use the level shifter you show in your UNO picture?

I've tried it with and without the level shifter, and it works both ways

It sounds like you are able to receive some repeatable data format. But have you established the signal level of the TX pin. I would have thought that it would be using RS-232 voltage levels.

I haven't confirmed the TX signal level. RS-232 is a strong possibility, in which case the level shifter wouldn't help me, right? Is it possible that the FTDI chip is doing the conversion by itself, and that's why the Macbook can read the device but the Arduino can't? The FTDI chip's datasheet suggests it can be used for RS232 applications.

there are a number of FTDI serial cables to meet different voltage ranges, e.g. TTL-232R-3v3 for 0 to 3.3V logic, TTL-232R-5V for 0 to 5V logic and USB to RS232 -12V to +12V (typical range) - see FTDI usb-rs232-cable-series
you have to know the voltages used by the target device (0 to 3.3V, 0 to 5V or -12 to +12V) to select the correct FTDI cable
suggest you get a multimeter and measure the voltages on the device Tx and Rx pins

1 Like

SOLVED: I took a fresh look with a logic analyzer, and it turns out I had the wrong baud rate AND the DS50 is sending inverted logic. The inverted logic was causing frequent framing errors, and apparently my Arduino was (rightly) tossing the "corrupt" bytes while the FTDI chip was sending them on. So now I just need to use SoftwareSerial or another option that supports inverted serial logic.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.