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

Control EXP6 with Pybricks blocks in three steps

Blocks follow the same order as MicroPython: first import the library under "set up", then create an object and give EXP6 a name, and finally, under "program", use that name to call functions (turn motors, read sensors). Work through the steps on the left and the blocks on the right grow with you. These block pictures were built in Pybricks Code and exported as they are, so they look exactly like what you see on screen.

Check first: Pybricks block coding needs a licence code or a Patreon membership. Without one, you can write MicroPython instead; it works exactly the same way (switch the right side to "Matching MicroPython" to see it). EXP6 only works with the SPIKE Prime hub, and it needs its own battery, or the motors will not move.

1

Import the library

The first thing in a block program is a red "from … import …" block under the yellow "set up" block. It tells Pybricks to use the EXP6 library. With LEGO motors and sensors only, this one is all you need; with MBC sensors you also import a product file (see "MBC sensors on the EXP6" below).

  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. Do not rename the file.
  3. Click "+" to add a new file and choose to write it with blocks.
  4. Drag the red "from … import …" block out of the block list and attach it under "set up". Choose MBC_exp6_obj_Lib in the first box and MBC_EXP6 in the second.

Download block files (complete examples): ex01 one turn, ex02 three motors, ex03 light switch, ex04 button control, ex05 square. In Pybricks Code click "Import a file" and keep it in the same project as MBC_exp6_obj_Lib.py; it opens as blocks. The examples wait, so they use multitask (true) and add one more block, "from MBC_exp6_obj_Lib import run_task", which sends the heartbeat while waiting.

2

Create an object: give EXP6 a name

Drag a red "… is a …" block and attach it under the import block. On the far left is the name you give EXP6; after it come the hub port and whether to use multitasking. From then on, you choose this name every time you turn a motor or read a sensor.

Which hub port is the EXP6 Host cable plugged into?

Give it a name (in Pybricks you can use any English name; here are three to try):

Do you want multitasking? (the green true / false on the block)

The port box takes the hub port: on SPIKE 1 to 6 = A to F, so 3 is port C. The first number in a function block is the expansion port on EXP6, 1 to 6. Do not mix them up.

3

Use the functions

Put a red function block under "program": choose the name exp6 in the first box, await in the middle and the function in the last box. Then click the ">" on the right to add boxes for the values (the first is usually the expansion port, 1 to 6). Click a function and the blocks and output switch to its example.

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).

Block examples for every function

33 functions, each with a set of blocks you can copy (name exp6, multitasking true). Actions such as turning or stopping a motor usually go under "program" and run once. Readings go inside a repeat loop and are read again and again. Click "Try it" to jump to step 3; the blocks and output on the right switch to that example.

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. It is the same three steps as above: pick the sensor, pick the EXP6 port it is in, pick a function, and the blocks and code on the right follow along. All blocks were built and exported in Pybricks Code; the block version always uses multitask (true).

1

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

Which sensor is on the EXP6?

Under "set up" attach three red "from … import …" blocks: the first two import MBC_EXP6 and run_task from the core MBC_exp6_obj_Lib (this run_task is the EXP6 library's one and sends the heartbeat while you wait); the third imports only the product file of the sensor you plug in. 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?

    Do not mix up the two numbers: the 3 in the MBC_EXP6 block is the hub port (the EXP6 Host cable is in port C, as in the tutorial above); the number in the sensor block is the EXP6 port 1–6.

    3

    Use the functions: same names as on the hub

    Full example: PD line following with a Line Follower 8-ch in EXP6 port 5

    PD line following: blocks and MicroPython

    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.

    exp61truetrueline8awaitwidth5>exp610exp620awaitmotor_powerawaitmotor_powererrorline8awaitpos100PDerrorKP×errorlastError−KD×+exp61SPEEDPD+exp62SPEEDPD−lastErrorerrorset toawaitmotor_powerawaitmotor_powerset toset to10mswaitifelserepeatwhileawaitset_motor_invertedImport only the product file you use: a hub program is limited to about...Wiring: EXP6 Host cable to hub port C; Line Follower 8-ch (SPIKE mode) ...program
    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
        # 接線:EXP6 的 Host 線插主機 C 孔;循行者 8 路插 EXP6 第 5 孔;左馬達接 EXP6 擴充孔 1(反裝)、右馬達接擴充孔 2
        # 只匯入用到的產品檔:主機程式上限約 52 KB,循行者只要 MBC_exp6_line(EXP6_LINE8)。EXP6_LINE8(exp6, 5) = 插在 exp6 的 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. Import the library file into the project first. If MBC_exp6_obj_Lib.py is not imported, you cannot choose MBC_EXP6 in the red blocks.
    2. await and call must match multitasking: with multitasking true choose await; with multitasking false choose call. If you mix them, the program fails on the hub.
    3. Wait with the keep_alive_wait function, not a long wait block. If EXP6 hears nothing from the hub for more than 0.5 seconds, it stops the motors by itself (a watchdog). For the multitasking version, add one more block in set up: "from MBC_exp6_obj_Lib import run_task" (every downloadable block example has it). Then the library's run_task sends the heartbeat in the background, plain wait blocks are fine, and the motors stop as soon as the program ends. Without that block, Pybricks' own run_task is used, which sends no heartbeat, so a wait longer than 0.5 seconds needs the keep_alive_wait function block.
    4. Do not mix up the two kinds of port number: the "is a" block takes the hub port (1 to 6 = A to F); the first number in a function block is the expansion port on EXP6. The EXP6 ports take LEGO motors, the LEGO colour / ultrasonic / force sensors, and MBC sensors too (see "MBC sensors on the EXP6" above).
    5. Unplugging the expander does not stop the program: readings become 0 (not the last value), the computer prints a warning at most once a second, and it reconnects by itself when you plug it back in. A turn (motor_run_degrees, motor_track_target) moves on right away and does not wait for the turn to finish, so add a keep_alive_wait after it. Blocks cannot read the connected attribute or call the object's run_task; to use them, write MicroPython instead.