GOcontroll Multibus module service and utilities for Moduline controllers (L4, M1, HMI1) running on i.MX8MM hardware.
Brings a Multibus module up after a controller boot, configures which physical
interface each connector pin pair carries, and publishes the battery cell
voltages it reads over isoSPI to /dev/shm for the rest of the system.
Two faces over one state machine: a background service that runs at boot, and a
terminal UI in the style of go-can for commissioning a machine. What the UI
shows is what the service does, because both drive the same code.
Pure standard-library Python 3 - no pyserial, no pyusb, nothing to install on the controller beyond the interpreter it already has.
gocontroll-usb-hostmode # report the OTG port's mode
gocontroll-usb-hostmode --apply # put it in host mode (needs a reboot)
go-multibus # interactive UI (no arguments)
go-multibus daemon # background service, systemd entry
go-multibus status # one-shot summary, scriptable
go-multibus config # show the stored configuration
go-multibus set 3 can # change one interface, store it
gocontroll-cellmon list # which Multibus devices are present
gocontroll-cellmon info # identity, role, capabilities
gocontroll-cellmon status # isoSPI link counters
gocontroll-cellmon cells # one cell snapshot
gocontroll-cellmon watch # keep printing snapshots
gocontroll-cellmon interfaces --set can,rs485,can,rs485,isospi
gocontroll-cellmon raw 03 60 f4 6c # raw isoSPI transfer
gocontroll-cellmon serve # publish to /dev/shm
gocontroll-modulebus probe 8 # module bus handshake, over SPI
gocontroll-modulebus start 8 # bootloader -> application
gocontroll-modulebus interfaces 8 # read/write config over SPIOnly one of go-multibus daemon and the UI can run at a time: both open the
module's protocol port, and two processes on one CDC port read each other's
answers. The UI refuses to start while the service is active and says so.
reset ──► start ──► configure ──► enumerate ──► running
(SPI) (SPI) (USB) (USB)
▲ ▲
│ └─ the controller now knows which
│ CAN and serial ports are real
└─ has to happen before USB comes up
| State | What happens |
|---|---|
reset |
Pulse the slot reset line; the STM32 restarts into the bootloader |
start |
Tell the bootloader its firmware is current so it starts the application |
configure |
Push the five interface modes over the module bus |
enumerate |
Wait for USB, open the protocol port, apply the cell configuration |
running |
Poll the cell snapshot and publish it |
Every step is idempotent and the sequence restarts from the top when the module disappears, so pulling a module and putting it back recovers on its own.
The configuration goes over SPI rather than USB because the controller has to
know the interface layout before it initialises the interfaces, and USB is not
up at that point. When the module bus does not answer - firmware older than
ModuleBus.c - the service falls back to configuring over USB.
The connector carries five interfaces. Pin numbers are for the 26 position uneven slot.
| Interface | Pins | Modes | Linux |
|---|---|---|---|
| 1 | 6/5 | CAN FD, fixed | mb_can1 |
| 2 | 12/13 | RS485 or RS232 | /dev/mb_serial1 |
| 3 | 18/19 | RS485 or CAN FD | mb_can2 / /dev/mb_serial2 |
| 4 | 24/25 | RS485 or CAN FD | mb_can3 / /dev/mb_serial3 |
| 5 | - | isoSPI, fixed | /dev/mb_protocol |
At most three CAN channels and at most three serial ports can be live at once.
mb_can2 and /dev/mb_serial2 are the same connector pins in different modes;
only one of each pair carries traffic.
Bus parameters - bitrate, sample point, FD - belong to go-can, the same as for
the baseboard buses. go-multibus owns the mode, go-can owns the parameters.
The service raises each interface with go-can apply, so the two never disagree
about what a bus is running at.
Names are pinned by debian/udev/81-gocontroll-multibus.rules, because kernel
names depend on the controller: a Moduline IV has four onboard mcp251x
controllers so the module lands on can4..can6, while an M1 has two and the
same module lands on can2..can4. The numbering follows the knowledge base
(CAN 1..3, RS485 1..3), not the connector interface numbers.
The tooling falls back to sysfs discovery when the rule is not installed, but anything else on the controller that refers to a name needs it.
/etc/gocontroll/multibus.json. A missing file is a working configuration.
{
"slot": 8,
"interfaces": ["can", "rs485", "can", "rs485", "isospi"],
"cell_count": 12,
"module_poll_ms": 100,
"publish_interval_s": 0.5,
"bring_up_can": true,
"can_bitrate": 500000,
"output": "/dev/shm/gocontroll/multibus-cells.json"
}interfaces is indexed by connector interface, so interfaces[2] is
interface 3 and drives mb_can2.
/dev/shm/gocontroll/multibus-cells.json holds the measurements and
multibus-state.json where the bring-up sequence stands. Both are written to a
temporary file and moved into place with os.replace, which is atomic on Linux:
a reader sees either the previous document or the new one, never half of either,
and needs no locking.
Branch on valid - it is link_ok && !stale. Failures are published too, rather
than leaving the last good document in place, so a reader can tell a dead service
from a dead battery link.
Voltages appear twice on purpose: cells_v rounded for display, cells_100uv
as the raw device values in the 100 uV steps the LTC681x family uses.
systemctl enable --now go-multibus # service + cell publishergocontroll-cellmon.service is an alternative for a controller that only wants
the cell data and configures the module some other way. Do not run both.
apt-get install go-multibusThat is everything a fresh controller needs: the tools, the service, the udev
naming rules, and gocontroll-usb-hostmode for the OTG port. Both services are
enabled but not started - at install time there may be no controller under the
filesystem, and on real hardware starting the service resets the module and
raises CAN interfaces, which an install should not do underneath a running
machine.
systemctl start go-multibusA controller whose OTG port is still in device mode needs one reboot.
gocontroll-usb-hostmode.service runs before go-multibus, notices the port is
not in host mode, and applies the change - but that lands in the u-boot
environment and the Falcon boot blob, so it only takes effect on the next boot.
Until then no module enumerates. On a controller that is already in host mode
the service is a no-op that costs one file read.
From a checkout, ./install.sh puts the same files in the same places. A
checkout also runs without installing at all: the entry points in bin/ add
../lib to their import path.
debian/build-deb.sh 0.1.0 distThe same script the release workflow runs, so what CI publishes is what you can
build and inspect locally. Tagging v* builds it, attaches it to a GitHub
release, and tells the GOcontroll apt index to pick it up.
The i.MX8 OTG port comes up as a USB device by default. A module plugged into
it never enumerates until the port is a host, and dr_mode = "otg" with a
runtime role switch is not enough - the chipidea driver switches the controller
but does not fully initialise the PHY, so the high-speed chirp fails and
descriptor transfers die with -71. Only dr_mode = "host" gives a full host
init.
gocontroll-usb-hostmode sets that, in both places a Moduline can boot from:
the otg_mode u-boot variable and the Falcon boot args blob. It backs up
everything it touches and verifies what it wrote. Run it without arguments to
see the current state; --apply changes it, --revert puts the backups back.
The module needs firmware with MbProto and ModuleBus - version 0.1.0 or
later. Older images serve the protocol port as a plain RS485 bridge, and the
tooling says so rather than reporting a vague timeout. Firmware lives in the
Module-Multibus repository.
examples/node-red-multibus-cells.json reads the published cell voltages every 500 ms and emits them as JSON, with one signal parsed out as an example. See examples/README.md.
- docs/gocontroll-cellmon.md - how the cell data reaches Linux and how to read it. This is the source for the knowledge base article.
- docs/go-multibus.md - the service, the UI, and the go-can integration.
- docs/multibus-usb-protocol.md - frame format and command reference.
- docs/multibus-interfaces.md - which interface combinations exist and why.
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | The module answered with an error, or is unreachable |
| 2 | Usage error |
MIT - see LICENSE.