Hamilton MPE Integration

Was wondering if anyone has started working on Hamilton MPE Integration? It would be awesome to use as a standalone device.

The only caveat is that it is a state machine, you need to call commands in a specific order. This makes it exceptionally hard to error handle for in Venus, we spent many months developing a Venus MPE error handling library that lets us step through these states. I wonder if this effects the implementation in pylabrobot?

It uses the same device toolkit as the Nimbus and can communicate over either ethernet or USB (with a FDTI driver installed).

If no one else is tackling this, what’s the best way to get started on it?

2 Likes

haven’t heard of anyone working on this. thanks for your interest!

I imagine you would want to implement it on the command level. that would mean you get the “lets us step through these states” before you even get higher level commands.

I am not sure what the states are and what the constraints are, but I am sure it will be easy to build it into python. if venus can do it, it is easy in python.

do you have firmware documentation? if so, consult that. otherwise you will probably need to use Wireshark or similar (or an ethernet switch with managed mode makes a cheap sniffer). decoding the packets will be relatively straightforward using @cody’s tcp interface.

as for contributing, just make a PR when you have something that works for your needs.

thanks! let me know if I can help

No firmware documentation.

So basically setup a middleman and then run the MPE through all its commands?

yes

if you know what goes on the wire you can then do everything yourself

2 Likes

If it’s the same as the Nimbus there is probably an introspection API that will be much easier to use for discovery than testing commands one by one

2 Likes

Can you provide a bit of info on how to run this?

1 Like

Take a look at tcp_backend, this was used to get the nimbus commands using that introspection layer. On mobile so don’t have an example handy, it should be fairly friendly to use.

2 Likes

Claude built me backend but having some error connecting IRL.

I was able to connect but initialize commands are throwing weird errors.

This is the IP I used:

# ---------------------------------------------------------------------------
# Configuration — edit before running on real hardware
# ---------------------------------------------------------------------------
HOST: str = "192.168.100.100"         # IP address of the Hamilton MPE
PORT: int = 2000                     # Default MPE TCP port

what are your network settings? which OS?

Windows

Pretty sure this is right, the MPE is throwing back error codes that claude can’t match to the real error codes, so its connecting but not sending good commands

good, ip settings look correct. based on this i would say it should communicate

can you use the socket you are using to see what it responds with?

claudes on that, it made some mistake in the commands to send, it cooking one second

1 Like

just connect it to the machine directly

“i am connected to the machine. start ipython in a tmux session and make it work”

Haha I pretty much did that with copilot, just allow for the session. It chewed and maybe made some head way. I had to leave tho, I’ll check tomorrow.

It seems like what I’m sending is causing an non documented error which it’s really tripping things up.

The thing also has a USB COM, maybe I should try to figure that out?

sweet

is there a way you can see how venus initializes the machine?

The thing also has a USB COM, maybe I should try to figure that out?

might be doable, but I don’t think we have worked with the tcp protocol over usb before so this could be a can of worms that you don’t want to open. (also ethernet is generally nicer than usb) if you have seen the mpe work over ethernet i’d recommend that

Yeah I can try the wire shark way, I may have made some assumptions that it’s the exact same protocol as Nimbus.

USB might be nice in the future, on my laptop I have to use a dongle rn. We normally use the usb route

1 Like

I don’t really have time today so I just asked claude to summarize the error:

# Hamilton MPE Error Analysis

## Error

```

RuntimeError: Hamilton error 5: Error response (action=0x5): 210002000a020f0028003078303030312e3078303030312e3078313030303a307830312c3078303031362c30783032304100

```

-–

## Decoding `Hamilton error 5`

The `5` is the **HOI2 action code** returned by the device. From `protocol.py`:

| Code | Name | Meaning |

|------|------|---------|

| `5` | `COMMAND_EXCEPTION` | The MPE received the command but rejected or failed to execute it at the HOI2 protocol level |

This is raised in `tcp_backend.py` when the response action code matches `Hoi2Action.COMMAND_EXCEPTION`.

-–

## Decoding the Hex Payload

Raw hex: `210002000a020f0028003078303030312e3078303030312e3078313030303a307830312c3078303031362c30783032304100`

Decoded ASCII payload: `0x0001.0x0001.0x1000:0x01,0x0016,0x020A`

| Field | Value | Interpretation |

|-------|-------|----------------|

| `0x0001.0x0001.0x1000` | HOI2 address | Node=1, Object=1, Interface=0x1000 — the SPE/MPE control object that reported the error |

| `0x01` | Error category | 1 = general exception |

| `0x0016` | 22 decimal | HOI2 sub-error — likely “method/object not found” during object resolution |

| `0x020A` | 522 decimal | Secondary error code — does **not** map to any `MPEReturnCode` in `mpe_errors.py`, indicating a protocol-level rejection rather than an application-level MPE error |

-–

## Root Cause Analysis

### Most Likely: Wrong TCP Port

The config in `mpe_initialize.py` uses:

```python

PORT: int = 2000

```

However, `mpe_backend.py` explicitly states:

> *“The MPE typically listens on TCP port 50007 (same as the Nimbus default).”*

The HOI2 handshake may partially succeed on port 2000 (if something else is listening), but the object/method discovery then resolves the wrong or nonexistent `ISPEControl` address. The subsequent `Initialize` command is sent to that wrong address, and the device returns `COMMAND_EXCEPTION`.

Sub-error `0x0016` (22) is consistent with an HOI2 “method not located on object” failure.

### Other Possible Causes

- **Device in a failed/mid-process state** — `Initialize` is not valid while the SPE state machine is not in `Uninitialized` state; the device throws `COMMAND_EXCEPTION` instead of an MPE return code.

- **HOI2 object discovery failure** — If the root-object registration (Protocol-3) does not correctly enumerate `ISPEControl`, all subsequent commands target the wrong address.

-–

## Recommended Fix

Change the port in `mpe_initialize.py`:

```python

# Before

PORT: int = 2000

# After

PORT: int = 50007

```

If the device is already mid-process and the port was correct, send an `abort()` first before retrying `initialize()`.

-–

## Relevant Source Files

| File | Relevance |

|------|-----------|

| `pylabrobot/liquid_handling/backends/hamilton/tcp_backend.py` | Where `RuntimeError` is raised (action code check) |

| `pylabrobot/liquid_handling/backends/hamilton/tcp/protocol.py` | `Hoi2Action` enum — defines `COMMAND_EXCEPTION = 5` |

| `pylabrobot/postive_pressure/backends/hamilton/mpe_errors.py` | `MPEReturnCode` enum and `MPEError` — not reached in this case |

| `pylabrobot/postive_pressure/backends/hamilton/mpe_backend.py` | Notes default port as 50007 |

| `pylab_robot_hack/mpe_initialize.py` | Config file — `PORT` set to 2000 |