MBC EXP6 6-Port Motor Expander (for LEGO® SPIKE™) × Pybricks

Control EXP6 with MicroPython in three steps

A Pybricks program always goes in this order: first import the library, then create an object and give EXP6 a name, and only then use that name to call functions (turn motors, read sensors). Work through the steps on the left and the program on the right grows with you. On the simulated EXP6, choose what is plugged into each expansion port to see how the motors turn and what the program prints.

Your hub: SPIKE Prime (EXP6 only works with SPIKE Prime, not EV3). EXP6 needs its own battery, or the motors will not move.
1

Import the library

The first thing your program does is tell Pybricks "I want to use the EXP6 library". Without this line, the hub does not know what MBC_EXP6 means and you get a NameError. EXP6 has only this one library, so there is nothing to choose.

  1. Download the library (save the file and keep its name): MBC_exp6_obj_Lib.py. The file name is MBC_exp6_obj_Lib.py.
  2. In Pybricks Code, click "Import a file" in the file panel on the left and import it into the same project.
  3. Do not rename the file (the program uses the name MBC_exp6_obj_Lib), and do not put it in a subfolder.
  4. Go back to your main program and type the lines below at the very top (marked ① in the program on the right).
2

Create an object: give EXP6 a name

Now you "create an object": you tell the program which hub port the EXP6 Host cable is plugged into, and you give it a name. From then on, you use this name every time you turn a motor or read a sensor.

Which hub port is the EXP6 Host cable plugged into?

Do you want multitasking?

The 3 in the program is the hub port: 1 to 6 = A to F (write a number only, not Port.C). The first number inside the brackets of the functions later is the expansion port on EXP6, 1 to 6. Do not mix them up.

3

Use the functions

You always write "exp6.function_name(expansion port, …)": the name, a dot, then the function. The first number in the brackets is usually the expansion port on EXP6, 1 to 6. Click a function and the program and output switch to its example. The motors on the simulated EXP6 move to match.

Simulated EXP6 (seen from above): choose what is plugged into each expansion port, then watch the motors turn and see what is printed on the right

At the start, expansion ports 1 and 2 have motors, port 4 a colour sensor, port 5 an ultrasonic sensor and port 6 a force sensor, the same ports the examples use. The simulation is only an illustration: 1% motor speed counts as 10 degrees per second (a real motor depends on its model), and the sensor values are simplified. The red pointer shows which way the motor shaft points; ↻ = clockwise. When you unplug the expander, the motors stop and every reading becomes 0 (not the last value). The library prints its connection warnings in both Chinese and English, so you see them here exactly as on the hub. About 0.5 seconds after the program ends, the EXP6 watchdog stops the motors (a turn that has not finished yet finishes first). With multitasking and the library's run_task, the motors stop the moment the program ends.

An example for every function

35 functions (everything in the library), each with a short example. Actions such as turning or stopping a motor usually go outside the loop and run once. Readings go inside the loop and are read again and again. Click "Try it" to jump to step 3; the program and output on the right switch to that example.

Complete examples

Two-wheel car drives a square (left wheel port 1, right wheel port 2)

        

!Only change the "Things to change" part at the top. Turns less than 90 degrees: make TURN a bit bigger; turns too far: make it smaller. Sides too long or too short: change SIDE (720 degrees is about 35 cm). Car drives backwards: change the 1 in set_motor_inverted(1, True) to 2.

Multitasking (advanced): light switch

        

!Write True as the 2nd value when you create the object, and create the object outside run_task(). Put await in front of every function. Always import run_task from MBC_exp6_obj_Lib: it keeps sending a heartbeat in the background and stops the motors when the program ends.

With an MBC sensor: line-following car

        

!This example plugs the Pathfinder 8-Way straight into the hub; import the Pathfinder library MBC_line8.py into the same project too. MBC sensors can also go into the EXP6 expansion ports; see "MBC sensors on the EXP6" below.

MBC sensors on the EXP6 (Line Follower, Laser Ranger, Controller, SharpShot)

When you run out of hub ports, plug MBC sensors straight into EXP6 ports 1–6, just as you would into the hub; there is nothing to set up. The EXP6 needs firmware dated 2026-11-03 or later. Same three steps as above: pick the sensor, pick the EXP6 port it is in, pick a function, and the program on the right follows along; both the normal style and multitask work.

1

Import: the core library + this sensor's product file

Which sensor is on the EXP6?

Besides the core MBC_exp6_obj_Lib, import the product file of the sensor you plug in (for example from MBC_exp6_line import *, which gives the names LINE8 and LINE16). Why several files: a Pybricks hub program (your program plus every library) is limited to about 52 KB, and all of them together are too big, so you load only what you use.

    2

    Create the objects: EXP6 first, then the sensor

    Which EXP6 port is the sensor in?

    Multitask?

    3

    Use the functions: same names as on the hub

    Full example: PD line following with a Line Follower 8-ch on the EXP6

    PD line following (Line Follower 8-ch in EXP6 port 5, left motor in port 1, right motor in port 2)

    Function names and return values are the same as with the sensor plugged straight into the hub (for example pos100() and width() on the Line Follower). In multitask mode put await in front of every function and just use wait: the imported run_task comes from the EXP6 library and sends the heartbeat in the background, so the watchdog does not stop the motors.

    from pybricks.hubs import PrimeHub
    from pybricks.tools import multitask, run_task, wait
    
    from MBC_exp6_line import EXP6_LINE8
    from MBC_exp6_obj_Lib import MBC_EXP6, run_task
    
    # Set up.
    hub = PrimeHub()
    exp6 = MBC_EXP6(3, True)
    line8 = EXP6_LINE8(exp6, 5)
    SPEED = 50
    KP = 0.4
    KD = 1.5
    error = 0
    lastError = 0
    PD = 0
    
    async def main():
        global error, PD, lastError
        # Wiring: EXP6 Host cable in hub port C; Line Follower 8-ch in EXP6 port 5; left motor in EXP6 port 1 (mounted reversed), right motor in port 2
        # Import only the product files you use (hub program limit about 52 KB); the Line Follower needs just MBC_exp6_line (EXP6_LINE8). EXP6_LINE8(exp6, 5) = the sensor in EXP6 port 5
        await exp6.set_motor_inverted(1, True)
        while True:
            await wait(0)
            if await line8.width() > 5:
                await exp6.motor_power(1, 0)
                await exp6.motor_power(2, 0)
            else:
                error = await line8.pos100()
                PD = error * KP + (error - lastError) * KD
                await exp6.motor_power(1, SPEED + PD)
                await exp6.motor_power(2, SPEED - PD)
                lastError = error
            await wait(10)
    
    
    run_task(main())

    Download this example: block file exp6_line8_pd_block.py, Python file exp6_line8_pd_python.py (PD line following: left motor in EXP6 port 1, right motor in port 2; in Pybricks Code click "Import a file" and keep it in the same project as MBC_exp6_obj_Lib.py, MBC_exp6_mbc_common.py and MBC_exp6_line.py).

    Things to know

    1. Always connect the EXP6 battery. Every motor on the expansion ports runs on EXP6's own battery (a 2S LiPo is recommended). With only the Host cable and no battery, nothing moves. The EXP6 ports take LEGO motors, the LEGO SPIKE colour, ultrasonic and force sensors, and MBC sensors too (see "MBC sensors on the EXP6" above).
    2. Do not mix up the two kinds of port number. The 3 in MBC_EXP6(3) is the hub port (1 to 6 = A to F). The 1 in exp6.motor_run(1, 50) is the expansion port on EXP6 (the 1 to 6 printed on the board).
    3. Wait with keep_alive_wait(), not wait(). If EXP6 hears nothing from the hub for more than 0.5 seconds, it stops the motors by itself (a watchdog for safety; you cannot turn it off). In the normal style, always write waits as exp6.keep_alive_wait(milliseconds). If a calculation takes a long time, call exp6.keep_alive() once in the middle. With multitasking, use the library's run_task(): it sends the heartbeat in the background, so plain await wait() is fine.
    4. Unplugging the expander does not stop the program. Readings become 0 (not the last value), actions cannot be sent, and the computer prints a warning at most once a second. Plug it back in and it reconnects by itself. To check whether it is connected, read exp6.connected. Tick "Unplug the expander" on the simulated EXP6 to see this.
    5. A turn command moves on right away and does not wait for the turn to finish. After motor_run_degrees() or motor_track_target(), wait a little, or keep reading the angle until it gets there. If you send a new turn before the last one is done, the new one is dropped. Right after power-on, the motors need about 1 second to get ready, so start your program by waiting until get_device_id(port) == 5. End your program with stop_all(1) so every motor stops.