7.4 KiB
MUM Adapter Internals (Melexis Universal Master)
This document describes how the MumLinInterface adapter wraps the Melexis
pymumclient and pylin packages, how frames flow across the LIN bus, and
which MUM-specific behaviors callers need to understand.
Overview
- Location:
ecu_framework/lin/mum.py - Vendor reference scripts:
vendor/automated_lin_test/(test_led_control.py,test_auto_addressing.py,power_cycle.py) - Default MUM endpoint:
192.168.7.2over USB-RNDIS - LIN device name on MUM:
lin0 - Power-control device on MUM:
power_out0 - Required Python packages:
pylin,pymumclient(Melexis-supplied; not on PyPI). Seevendor/automated_lin_test/install_packages.sh.
What the MUM gives you that BabyLIN doesn't
- Built-in power control on
power_out0— the adapter callspower_up()inconnect()andpower_down()indisconnect(). No external Owon PSU needed for the standard flow. - Network access: the MUM is IP-reachable, so the host machine (Windows, Linux, Pi) does not need vendor native libraries — only the two Python packages.
- Direct transport-layer access for sending raw frames with LIN 1.x Classic checksum (required for BSM-SNPD diagnostic frames).
What it doesn't give you
- No passive listen. The MUM is master-driven. To "receive" a slave-published frame, the master sends a header on that frame ID and the slave must respond.
MumLinInterface.receive(id=None)raisesNotImplementedErrorfor that reason. - No SDF / schedule manager. The adapter does not run a schedule; tests publish frames explicitly (or pull slave frames explicitly) on each call.
Mermaid: connect / receive / send
sequenceDiagram
autonumber
participant T as Test/Fixture
participant A as MumLinInterface
participant MM as pymumclient (MelexisUniversalMaster)
participant PL as pylin (LinDevice22 / TransportLayer)
participant E as ECU
T->>A: connect()
A->>MM: MelexisUniversalMaster()
A->>MM: open_all(host)
A->>MM: get_device(power_out0)
A->>MM: get_device(lin0)
A->>MM: linmaster.setup()
A->>PL: LinBusManager(linmaster)
A->>PL: LinDevice22(lin_bus)
A->>PL: set baudrate
A->>PL: get_device(bus/transport_layer)
A->>MM: power_control.power_up()
Note over A: sleep(boot_settle_seconds)
A-->>T: connected
T->>A: receive(id=0x11)
A->>PL: send_message(master_to_slave=False, frame_id=0x11, data_length=4)
PL->>E: header for 0x11
E-->>PL: response bytes
PL-->>A: bytes
A-->>T: LinFrame(id=0x11, data=...)
T->>A: send(LinFrame(0x0A, payload))
A->>PL: send_message(master_to_slave=True, frame_id=0x0A, data_length=8, data=payload)
PL->>E: header + payload (Enhanced checksum)
T->>A: send_raw(bytes)
A->>PL: transport_layer.ld_put_raw(data, baudrate)
Note over PL,E: LIN 1.x Classic checksum (required for BSM-SNPD)
T->>A: disconnect()
A->>MM: power_control.power_down()
A->>MM: linmaster.teardown()
Public API
MumLinInterface(host, lin_device='lin0', power_device='power_out0', baudrate=19200, frame_lengths=None, default_data_length=8, boot_settle_seconds=0.5)
LinInterface contract (matches Mock and BabyLIN adapters):
connect()— opens MUM, sets up LIN, and powers up the ECUdisconnect()— powers down and tears down (best-effort)send(frame: LinFrame)— publishes a master-to-slave frame using Enhanced checksumreceive(id: int, timeout: float = 1.0) -> LinFrame | None— triggers a slave read forid. Thetimeoutargument is informational; the underlyingpylincall is synchronous. Any pylin exception is treated as "no data" and returnsNone. Passingid=NoneraisesNotImplementedError.
MUM-only extras:
send_raw(bytes)— sends a raw LIN frame using Classic checksum via the transport layer'sld_put_raw. Use this for BSM-SNPD diagnostic frames; the firmware will reject them if Enhanced is used.power_up()/power_down()— direct control overpower_out0power_cycle(wait=2.0)— convenience:power_down(), sleep,power_up(), thenboot_settle_secondssleep
Frame-length resolution
Because the MUM is master-driven, every receive needs to know how many bytes
to ask for. The adapter resolves this from frame_lengths:
- Built-in defaults for the 4SEVEN library (ALM_Status=4, ALM_Req_A=8, ConfigFrame=3, PWM_Frame=8, VF_Frame=8, Tj_Frame=8, PWM_wo_Comp=8, NVM_Debug=8).
- Anything in the constructor's
frame_lengthsargument overrides the defaults. - If a frame ID isn't in the map,
default_data_length(default 8) is used.
In YAML, hex keys work:
interface:
type: mum
frame_lengths:
0x0A: 8
0x11: 4
The config loader coerces hex strings ("0x0A") and integers alike.
Diagnostic frames (BSM-SNPD)
The vendor's test_auto_addressing.py flow runs LIN 2.1 BSM-SNPD via raw
frames on 0x3C (MasterReq). The framework supports the same flow:
# inside a test that already has the MUM 'lin' fixture
data = bytearray([
0x7F, # NAD broadcast
0x06, # PCI: 6 data bytes
0xB5, # SID: BSM-SNPD
0xFF, # Supplier ID LSB
0x7F, # Supplier ID MSB
0x01, # subfunction (INIT)
0x02, # param 1
0xFF, # param 2
])
lin.send_raw(bytes(data))
send_raw() calls transport_layer.ld_put_raw(data=..., baudrate=...)
which uses LIN 1.x Classic checksum. Using lin.send() for these frames
would compute Enhanced checksum and the firmware would discard the frame.
Error surfaces
pymumclient is not installed/pylin is not installed— raised onconnect()if the Melexis packages aren't importable. The error message points atvendor/automated_lin_test/install_packages.sh.MUM not connected— callingsend/receive/send_rawbeforeconnect()(or afterdisconnect()).MUM transport layer not available— raised bysend_rawwhen the LIN device didn't exposebus/transport_layer. Practically always available on MUM firmware that supports diagnostic frames.- pylin exceptions during
receive— converted toNone(treated as a timeout / no-data). Use this to drive timeout-tolerant tests without try/except in the test body.
Unit testing without hardware
The adapter accepts mum_module= and pylin_module= constructor arguments
that bypass the real package imports. Tests in
tests/unit/test_mum_adapter_mocked.py use simple in-memory fakes to drive
the connect / send / receive / send_raw / power-cycle paths end to end. See
that file for a complete shim implementation.
from ecu_framework.lin.mum import MumLinInterface
iface = MumLinInterface(
host="10.0.0.1",
boot_settle_seconds=0.0,
mum_module=fake_mum,
pylin_module=fake_pylin,
)
iface.connect()
# ... assertions ...
iface.disconnect()
Notes and pitfalls
- Boot settling: After
power_up()the adapter sleepsboot_settle_seconds(default 0.5 s) so the ECU has time to come up before the first frame. Increase if your ECU boots slowly. - Owon PSU coexistence: the MUM provides power on
power_out0independently ofecu_framework/power/. Leavepower_supply.enabled: falsefor the standard MUM flow; enable it only for over/under-voltage scenarios that need a separate, programmable rail. - Networking: USB-RNDIS bring-up can take a few seconds after plugging in the MUM. If
connect()fails with a connection-refused,ping 192.168.7.2first. - Multiple MUMs: only one MUM is supported per
MumLinInterfaceinstance. Differenthostaddresses can run different fixture sessions side-by-side.