Skip to content
SedationLog
← Back to blog

Decoding the Mindray ePM 10M's DIAP protocol

We spent a week reverse-engineering the wire protocol on a USB-to-serial adapter so a dentist could plug in a Mindray and have vitals show up in the right case. Here's what we learned.

Michael Schaake
  • bridge
  • engineering
TODO: This is placeholder copy summarizing the SedationLog_Status_Summary.md §4.5 protocol notes. Rewrite to match the voice you want before public launch; lots of internal technical detail here that may not all be appropriate for marketing.

A working sedation record is only as good as the vitals it captures. We knew from the start that asking dentists to type heart rate / SpO2 / BP into a form every five minutes wasn’t going to work in practice. The product had to pull vitals directly from the monitor.

The most common monitor in U.S. general dentistry is the Mindray ePM 10M. It ships with a USB serial cable. Mindray’s XChart software talks to it over that cable. Nobody else does, because Mindray doesn’t publish the protocol.

So we reverse-engineered it.

What was on the wire

Two days with Wireshark, the Mindray USB sniffer mode, and XChart running against a live monitor gave us this:

  • Transport: USB-to-serial via a Prolific PL2303 chip. 38400 baud, 8N1.
  • Application layer: ASCII polling protocol, not binary. Mindray calls it DIAP.
  • Poll command (constant): DIAP000<allparam>43C1\n
  • Response: DIAP000<key=value;key=value;...>CRC\n
  • Missing values: runs of - characters with width matching the field’s normal width (e.g. --- for a 3-character HR field).

The CRC is ASCII hex (4 chars), not raw bytes. Our first prototype appended a raw 16-bit CRC and the monitor silently dropped every command. After we switched to ASCII hex the monitor started responding.

What surprised us

A few details that ate disproportionate amounts of debugging time:

  1. The poll interval is configurable, but XChart hard-codes 5-6 seconds. We tested at 1 Hz and it works fine, but ship at 5 s by default to match XChart’s behavior and avoid spooking the monitor.
  2. Windows enforces exclusive serial port access. If the SedationLog Bridge can’t open the COM port, it’s almost always because XChart (or another bridge instance) is already running. The error message is intentionally explicit about this.
  3. Vital readings come at 1 byte per USB URB. The bridge accumulates a line buffer and splits on \n. Most languages’ default serialport wrappers do the right thing here, but if you read raw URBs you’ll get one character at a time and wonder why your responses are empty.
  4. The “no value” sentinel is --- with width matching the normal field width. Detecting it with /^-+$/ works; checking value === "---" doesn’t, because BP fields can be ----- and respiratory rate can be --.

What we shipped

The bridge today is a small Node service that:

  • Polls the monitor at a configurable interval (5 s default, 1 Hz tested OK).
  • Parses the DIAP response into a typed VitalReading shape.
  • Buffers parsed readings to a local SQLite queue (WAL mode) before flushing to the cloud. Power loss + connectivity hiccups don’t lose data.
  • Auto-restarts on crash via a built-in watchdog supervisor.
  • Auto-discovers the operatory’s currently-active case via a polling endpoint, so the dentist doesn’t have to type a case UUID on the command line.

It installs via a standard MSI, registers as a Windows Service, and runs as LocalSystem on the operatory desktop. The API token is encrypted at rest under Windows DPAPI.

Why we wrote this up

Two reasons.

First, if you’re a dentist running a Mindray ePM 10M and you want SedationLog, the answer to “does this work with my monitor?” is now visible to Google.

Second, we’d love to expand monitor support. If you have a Mindray model that’s not ePM 10M, or a Welch Allyn, or a Philips, and you’re willing to let us sniff your COM port for an hour while you run a test case, email hello@sedationlog.com. We’ll write the next post about your monitor.