This guide describes how to add custom devices to Ohmni's multidrop serial bus. You can use this to add Arduinos or other microcontrollers easily and send commands to them during a call.

Architecture overview

Ohmni is designed to be extensible at various levels. The lowest level is designed to make it easy to attach high power devices that are controlled via a microcontroller (i.e. Arduino-class boards). This could be motors, servos, lights, pumps, etc.

The architecture is based on a 4-wire multidrop serial bus, with the serial running at 3.3V logic level. The wires are as follows:

  • Wire 1: GND
  • Wire 2: V+
    • Direct from battery power. Voltage: 15V - 17.5V depending on battery level.
    • Recommended draw: < 1A per added device.
  • Wire 3: Device TX serial line, 3.3V, OPEN-DRAIN
    • IMPORTANT: devices connected here must be set to open drain mode BEFORE being attached or they can permanently damage the serial bus. See the explanation section below for more details.
  • Wire 4: Device RX line, 3.3V
    • All devices/servos on the bus listen here (high-impedance) for commands from the host.

This architecture was designed based on asynchronous serial UART bus used by some of the common robotics servos in the market (Herkulex and Dynamixel). Note this is a full-duplex bus, unlike the lower end Dynamixels with a half-duplex bus.

Be very careful in how you refer to the TX/RX lines, as they are essentially reversed depending on the perspective you are using (host centric or device centric). Always prefix the "TX" or "RX" with the perspective. We use the following terminology:

  • Wire 3: Host RX = Device TX or Servo TX
  • Wire 4: Host TX = Device RX or Servo RX

The "servo" naming is just because it used to be only servos on this bus, so in some places our documentation may refer to it as the Servo TX or Servo RX line.

Communication over the multi-drop bus is currently at 115200 bps, 8N1.

There is a simple packet protocol that lets you send addressed commands to individual devices on the bus and you are free to add your own commands/etc. as you see fit. We'll get into more details later on in the doc.

Electrical pinout

There are four ethernet jacks in the base of the unit. One of these provides power and signal to the servo and peripherals in the robot's neck, and the other two go to the two motor drives.

The fourth port is available for custom peripherals/devices. You can use any standard ethernet patch cable (NOT crossover). If you're driving your own motors, etc, we recommend using STP (shielded twisted pair) ethernet cables to reduce electrical noise.

We recommend lengths of < 10ft for signal integrity.

On the other end, the ethernet pins break out as follows


  • Pin 1:
    • 3.3V Device TX (Host RX) - Open Drain
  • Pin 2:
    • 3.3V Device RX (Host TX)
  • Pin 3, 5, 7
    • V+ (15-17.5V)
    • These pins are paralleled for more current capacity. You should tie them all together.
  • Pin 4, 6, 8
    • GND
    • These pins are paralleled for more current capacity. You should tie them all together.

Breakout board

We also make a small breakout board that exposes the pins as terminal blocks and Dupoint 0.1" pins. Note that this version was intended for safe programming of some of our MCUs so we intentionally made the power traces thin. We'll make another version for higher power peripherals - please contact us if you are interested in it.


You can also buy an ethernet jack off Digikey/Mouser and solder wires straight to the pins if necessary. Just be careful of the pin ordering as some jacks are made with the clip facing up or down and the pins may be reversed as a result.

Connecting an arduino

First off, you must get an arduino that operates on 3.3V. Connecting a 5V arduino to Ohmni's multi-drop serial bus will permanently damage Ohmni.

Next, you have two options to interface the Arduino:

Option A. First, you can connect ONLY the Device RX line to the Arduino's serial RX pin and leave the Device TX line disconnected. In this mode, you won't be able to read back data from the arduino, but you can still send it commands. For devices like light strips, etc. this is an easy way to get started.

Option B. You can connect both the Device RX line to Arduino's serial RX pin and the Device TX line to Arduino's serial TX pin. HOWEVER, you must find a way (in software or other), to make the device TX open drain. I.e. on logic high, the pin is left high-impedance, and on logic low, the pin is driven to ground. Commonly, you can use a small-signal diode facing towards the arduino i.e. blocking current out but allow the arduino to pull down the line when it goes low.

If you use Option B without configuring/wiring the Arduino for open drain on the TX line, then the serial bus will likely short and be permanently damaged.

Software and packet protocol

The packet protocol is designed to be as simple as possible with an easy-to-compute checksum used to prevent corrupted packets from executing invalid commands or affecting data from the devices.

The design is based on the Herkulex servo packet protocol which is easy to use and extensible. For additional detail, you can refer to the protocol/command sections of the manual:


Each packet is just a sequence of bytes sent over the serial port at 115200 bps. The general form is:

  • 2 byte preamble - mandatory:
    • 0xff 0xff
  • 1 byte packet size (size includes entire header):
    • Minimum value 7, maximum value 223
    • Example: A packet with no additional data has size 7. A packet with 10 bytes of data has size 17.
  • 1 byte device id:
    • Minimum value 0, maximum value 250
    • Used to identify the destination device being addressed
    • Note: Ohmni already uses IDs in the range of 0-50. We recommend using id 100 and up for your devices to avoid overlap.
  • 1 byte cmd:
    • A command identifier. Servos have a well defined protocol with commands to read and write various registers, etc.
    • You can make up your own command identifiers if you're making your own device
  • 1 byte checksum A
    • Used to check for packet errors. Compute this by byte-by-byte XORing together all bytes in the packet EXCEPT the preamble and the checksums, then doing a bitwise AND with 0xFE:
    • packet_size ^ device_id ^ cmd ^ data[0] ^ data[1] ^ ... ^ data[n] & 0xFE
  • 1 byte checksum B
    • Used to check for packet errors.
    • Bitwise NOT of checksum A, then bitwise ANDed with 0xFE:
    • (~checksum_a) & 0xFE
  • n bytes data:
    • Can be 0-n bytes, whatever your command needs

Getting started with Arduino

We don't currently have an Arduino library, but the logic above should be easy to implement.

We suggest starting by using Option A above, with the nice side effect that you can connect the arduino's serial TX to your laptop to debug as normal while working on the routine to receive packets on the serial RX.

Core schematics for reference

Here are the connectors as pinned out on the board side