1

I am developing a controller for Mobitec Bus destination panel. The intended communication layer is SBC -> USB 2 RS485 [FTDI] -> Mobitec.

This is also the first time for me working with serial and RS485.

While developing, though, my workstation is a Mac OS - if that may change anything.


How do I know it's a FTDI device?

$ system_profiler SPUSBDataType
USB:

    USB 3.0 Bus:

      Host Controller Driver: AppleUSBXHCIWPT
      PCI Device ID: 0x9cb1
      PCI Revision ID: 0x0003
      PCI Vendor ID: 0x8086

        FT230X Basic UART:

          Product ID: 0x6015
          Vendor ID: 0x0403  (Future Technology Devices International Limited)
          Version: 10.00
          Serial Number: DN05Q09Q
          Speed: Up to 12 Mb/s
          Manufacturer: FTDI
          Location ID: 0x14100000 / 8
          Current Available (mA): 500
          Current Required (mA): 90
          Extra Operating Current (mA): 0

Using node-serialport library, I have written the following code:

const SerialPort = require("serialport");

const port = new SerialPort("/dev/tty.usbserial-DN05Q09Q", {
  autoOpen: false,
  baudRate: 4800
});

port.on("open", () => {
  console.log("Opened", port.path);
});

port.open((error) => {
  if (error) {
    return void console.error(`Error opening port`, port.path, error);
  }

  port.write(/* Buffer with a length of 23 bytes (proprietary structure) */, (error) => {
    if (error) {
      return void console.error(
        "Error while writing...",
        port.path,
        error
      );
    }

    console.log(
      "Written successfully.",
      port.path
    );
  });
});
$ node script.js
Opened /dev/tty.usbserial-DN05Q09Q
Written successfully. /dev/tty.usbserial-DN05Q09Q

But sadly, while the logs have no errors and I see the activity LED blinking on my USB 2 RS485 device, it appears that the target device does not receive the message as it does not respond as expected (display isn't updating the LEDs).


Though, if I am to add a timeout after the write, like so:

port.write(/* Buffer with a length of 33 bytes (proprietary structure) */, (error) => {
  if (error) {
    return void console.error(
      "Error while writing...",
      port.path,
      error
    );
  }

  console.log(
    "Written successfully.",
    port.path
  );

  console.log("Starting a timeout after write...");
  setTimeout(() => {
    console.log("Timeout after write expired.");
  }, 1000)
});
$ node script.js
Opened /dev/tty.usbserial-DN05Q09Q
Written successfully. /dev/tty.usbserial-DN05Q09Q
Starting a timeout after write...
Timeout after write expired.

It works.

Obviously, the device will respond faster than the specified second.
But given that, if I change the timeout to 10ms, it won't work. If I change it to 50ms, it seems to be working at all times, but I have noticed some rare exceptions1. 40ms seems to work ~5% of the time.

1 The exceptions occur when changing the payloads. As if some state has been left in the serial port.


I also attempted to SerialPort.drain after the write in the write callback. Also tried to drain in parallel.

Neither did help.


Sadly, I do not have the production intended SBC to test with that, as it will run Linux and that may change something.

In my debugging efforts, I have tried to watch -n .1 node script.js to see if it's related to some internal buffers of the device, but it works flawlessly with the 50ms. The 10ms one won't register a single time. 40ms produces the ~5% result.


Is the timeout a known thing in serial world and there are known patterns to work around that? Maybe this is a MacOS issue? Maybe the RS485 adapter requires extra treatment?

Really out of clues here...

tomsseisums
  • 13,168
  • 19
  • 83
  • 145
  • When it "doesn't work", is your script exiting? I'm wondering if your port is getting unref'ed, and your app finishing before a buffer gets a chance to get transmitted. As far as serial port state, I'm assuming whatever is downstream from the converter has received a partial command/buffer/whatever, and is responding appropriately. – Brad Apr 14 '20 at 20:30
  • Yes, at all times, the app exits as soon as possible with this script. If no timeout, then immediately after write succeds/errors (it always succeeds though), if used with timeout - then after the timeout expires. – tomsseisums Apr 14 '20 at 20:39
  • Alright, well, that sounds like the issue. Maybe that API is doing this intentionally, but I'd expect the script to keep running as long as the port is opened, assuming you didn't unref it. If you actually want to script to exit as soon as possible, I'm not sure what to suggest... there's undoubtedly some underlying buffer. I'd recommend opening up an issue on GitHub for this Serial Port package. There's nothing inherently wrong with your code, as far as I can see. – Brad Apr 14 '20 at 20:42
  • @Brad What about FTDI protocol - is the communication acknowledged? Meaning, the write callback - is it callback from the target device or it's only a callback from the OS layer that it has received the message to be sent over? – tomsseisums Apr 14 '20 at 22:02
  • The whole FTDI side is transparent. Your Node.js app doesn't know or care that there is a USB adapter in between. The specifics of the callback behavior are probably dependent on the underlying OS, but you should check with the authors of that package... they'd know better. My guess is that your callback will be fired when the data is enqueued to be sent... and some lower layer handles after that. This doesn't generally change things for you, as you'll generally want to listen for a response from the device anyway. Otherwise, there is no guarantee that it received the message. – Brad Apr 14 '20 at 22:16
  • 1
    (Even with something as reliable as RS485 with differential signalling... always good to get some sort of ACK back from the device when the protocol allows.) – Brad Apr 14 '20 at 22:16

0 Answers0