Controller 2.4G (PS2) × SPIKE × Pybricks
Interactive tutorialSPIKE
First time? Try the interactive tutorial in your browser first, then follow the steps below with your hub.
MicroPython (Pybricks)Pybricks blocksPybricks built-in blocks (no library)
This page covers Pybricks (Python) only: how to set it up for the Controller 2.4G (PS2) on SPIKE Prime / Robot Inventor, how to read every value, the commands it supports and examples. Use the switcher above to change product, host or software.
Setup
What you need
- Sensor: Controller 2.4G (PS2) (SPIKE edition).
- Hub: SPIKE Prime or Robot Inventor.
- Computer: Chrome. Open code.pybricks.com and start writing; nothing to install.
- Hub firmware: must be switched to Pybricks firmware v4.1 first (the stable code.pybricks.com is v4.1.1; v4.1.0 from the MBC SPIKE Blocks website works too). Every example on this site is written for v4.1.
- Library:
MBC_gamepad_Lib.py(only needed for the "import the library" method). For a new program of your own, import justMBC_sensors.py: one file for every MBC sensor.
Note
The Pybricks 3.6.1 libraries and examples are no longer updated (the 2026 SPIKE Prime hubs cannot run 3.6.1 either). For hubs that have not been upgraded, the old files are under "Legacy (no longer updated)" on the Downloads page.
Install the software
- Open code.pybricks.com in Chrome.
- Switch the hub to Pybricks firmware. The easiest way is the MBC SPIKE Blocks website: top menu "SPIKE Hub → Install/Update Pybricks firmware", about 3 minutes.
- To go back to the official LEGO App later, switch back to the original LEGO firmware with the SPIKE App.
Make the software see the sensor
Pybricks can read the sensor two ways; pick one:
- Built-in commands (no library): read the sensor with Pybricks' own sensor commands. No extra file, quickest to start.
- Import the library: download MBC_gamepad_Lib.py to get more data, such as the high-resolution position and every channel.
To import the library:
- In code.pybricks.com, click "Import a file" in the file panel on the left.
- Import both the example file and
MBC_gamepad_Lib.py, so they are in the same project list. - Open the example and press ▶ to run.
Important
New projects: use
MBC_sensors.py. Old projects: just replace the one file they already use.MBC_sensors.pyis one file for every MBC sensor (the EXP6 and Servo6 expander libraries are separate downloads); writefrom MBC_sensors import MBC_LINE8, with the same class names as in the old files. The old file names (such asMBC_gamepad_Lib.pyused by the examples on this page) are kept for existing programs: swap in the new file with the same name and the program needs no changes. But every old-name file is the whole shared library, so importing 3 or more of them in one program goes over the SPIKE program slot limit of about 51 KB; to mix several sensors, import onlyMBC_sensors.py.
Tip
The file name must match the name in the program's
importexactly, or you will get anImportError. The library files on this site have no version suffix, so you do not need to rename them.
Tip
Most libraries take a
multitaskargument when you create the object:Falseis normal mode, one line after another;Trueis multitask mode, where every call needsawaitin front so the robot can do several things at once.
Wiring
- Plug the sensor into hub port C with the cable (any of A~F works).
- Use the same port in the program: built-in commands use
Port.APort.F; a library'sportargument is usually16for A~F. - Latch up, push the plug in gently.
Connect the gamepad to the receiver
The PS2 2.4G gamepad and its receiver are paired at the factory; no pairing button needed.
- Connect the receiver and turn the controller on.
- Switch the gamepad on; it connects by itself.
- Watch the green LED on the receiver: on = no gamepad yet; off = gamepad connected.
- The ANALOG (or MODE) light on the gamepad must be on for the sticks to give values. The receiver switches to this mode on connect; if the light is off, press the ANALOG button in the middle of the gamepad.
Caution
2.4 GHz is a shared band (Wi-Fi, microwave ovens). With 12 or more sets in use at one venue they may interfere; in that case use the Bluetooth version (PS4 / PS5).
Library and example downloads (Pybricks 4.1)
- Download all (.zip):
MBC_sensors.py,MBC_gamepad_Lib.pyand the 6 beginner examples - Library only: MBC_sensors.py (use this for new projects: one file for every MBC sensor,
from MBC_sensors import MBC_GAMEPAD) · MBC_gamepad_Lib.py (for the examples on this page and older programs)
Note
The Pybricks 3.6.1 examples are no longer updated; the old files are under "Legacy (no longer updated)" on the Downloads page. In those old examples a key comes out as a decimal (for example
1.0); it still works for checking whether the key is pressed, and you can wrap it inint()to print a whole number. For Pybricks 4.x use the SPIKE beginner examples or the MBC library.
All buttons as one number: buttons() and buttons_psx()
The library has two ways to pack the 16 buttons into one number. The buttons are the same; each button just has a different value:
| Button | pad.buttons() (MBC order) |
pad.buttons_psx() (PSX order) |
|---|---|---|
| Up / Right / Down / Left | 1 / 2 / 4 / 8 | 16 / 32 / 64 / 128 |
| ▲ / ● / ✕ / ■ | 16 / 32 / 64 / 128 | 4096 / 8192 / 16384 / 32768 |
| L2 / R2 / L1 / R1 | 256 / 512 / 1024 / 2048 | 256 / 512 / 1024 / 2048 |
| SELECT / L3 / R3 / START | 4096 / 8192 / 16384 / 32768 | 1 / 2 / 4 / 8 |
| Check one button | pad.pressed('CROSS') |
pad.pressed_psx('CROSS') or pad.buttons_psx() & PSX_CROSS |
- Beginners: use
buttons()/pressed(). The D-pad and shape-button values match the official App's "reflected light"; every example on this page uses them. buttons_psx()uses the PS2 gamepad's native button order. PS2 and PS4 receivers send the same number, and Arduino (I2C) reads the 16 buttons in this order too, so use it when your program shares button values with Arduino code. SPIKE edition only (the 7th value of mode 9).- Both are 0 while no gamepad is connected.
from pybricks.parameters import Port
from pybricks.tools import wait
from MBC_gamepad_Lib import MBC_GAMEPAD, PSX_CROSS
pad = MBC_GAMEPAD(Port.C)
while True:
pad.update() # update once per loop
n = pad.buttons_psx() # 16 buttons, PSX order
if n & PSX_CROSS: # ✕ pressed
print('CROSS')
if pad.pressed_psx('L1'): # or check by name
print('L1')
wait(50)Tip
How to use the Controller PS2 with EV3, the SPIKE App and Pybricks blocks is also summarised in the command guide (PDF).
Important
Pybricks 4.1 or later without the library (built-in ColorSensor blocks): the buttons (
reflection(), make it whole first) and LX, LY, RX (ambient()) still work, but RY is lost (on 4.1ambient()keeps only one decimal). For RY install the MBC library (mode 9, works on old and new firmware); a new-firmware alternative will be added after hardware testing.
First test
- Turn on the hub and connect it in code.pybricks.com.
- Import and open example 1 on this page, then press ▶.
- If the numbers printed in the console change with what is in front of the sensor, everything is connected.
FAQ
ImportError? The library was not imported into the same project, or its file name differs from theimportname. Rename it toMBC_gamepad_Lib.pyand import it again.- The example does not run, or a command is unknown? The hub firmware is too old: the examples on this site are written for Pybricks v4.1, so update the hub to v4.1 first. 3.6.1 is no longer updated; the old files are on the Downloads page.
- What happens if the sensor cable comes out? The program keeps running: the library returns the last value it read and prints one notice in the console (in Chinese and English:
MBC: Port C 讀不到感應器,先回傳上一次的值 / no sensor, using the last value); if the sensor was never connected it returnsNone. To check in your program, call the object'ssensor_ok():Trueif the last read worked,Falseif not (addawaitin multitask mode). Once you plug it back in the library keeps reading; if the sensor was missing from the start and you use multitask mode, run the program again. - An example marked "Pybricks only" shows strange numbers? It uses a new mode in sensor firmware from 2026-10 on; older firmware returns different numbers.
- The official App cannot connect any more? The hub now runs Pybricks firmware: switch back to the original LEGO firmware with the SPIKE App.
Reading values
Code assumes the sensor is on port A. For the MBC library form, import the library .py into Pybricks too.
16 buttons buttons16
| Range | Meaning |
|---|---|
0~65535 bit |
all 16 buttons in one word (PSX order, 1 = pressed): bit0 SELECT, 1 L3, 2 R3, 3 START, 4 Up, 5 Right, 6 Down, 7 Left, 8 L2, 9 R2, 10 L1, 11 R1, 12 Triangle, 13 Circle, 14 Cross, 15 Square; 0 when not connected |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[6]
buttons16 = w & 0xFFFF # 16 buttons (int16 back to 0~65535; negative while ■ is held)MBC library
from pybricks.parameters import Port
from MBC_gamepad_Lib import MBC_GAMEPAD
pad = MBC_GAMEPAD(Port.A)
pad.update() # refresh once per loop
buttons16 = pad.buttons_psx()mode 0 (edu) byte 6 🔒; mode 5 v[0] 🔒; mode 6 v[2]; mode 9 v[6] 🔒. Note: App 原始藍 = button16(.ino:201-211); Pybricks reflection() 編碼:11 鍵 complexXY = SELECT<<10 | R1<<9 | L1<<8 | ■<<7 | ✕<<6 | ●<<5 | ▲<<4 | 左<<3 | 下<<2 | 右<<1 | 上,a = /25、b = (餘+1)×10−1,3 個 int16 = a×256+b → reflection() = complexXY;L2、R2、L3、R3、START 讀不到(.ino:334-358)。2026-10-30 起(方案 A,使用者定案):模式 5 總和 S = ceil((10×complexXY + RY)×3.072),R = G = S/3、B = S−2R → Pybricks 4.1.x int(reflection()) = complexXY(不變)、round(reflection()×10) % 10 = RY(1~9);3.6.1/4.0.0 的 reflection() 照舊 = complexXY; v2 = button16(PSX); v6 = button16(PSX)
d-pad dpad
| Range | Meaning |
|---|---|
0~15 bit |
Up 1, Right 2, Down 4, Left 8 (added when pressed together), 0~15; 0 when not connected |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[0]
dpad = w & 0x0F # d-pad (bits 0~3)MBC library
from pybricks.parameters import Port
from MBC_gamepad_Lib import MBC_GAMEPAD
pad = MBC_GAMEPAD(Port.A)
pad.update() # refresh once per loop
dpad = pad.pressed('UP')mode 0 (edu) byte 1 🔒; mode 0 (toy) byte 0 🔒; mode 1 byte 0; mode 6 v[0] lo; mode 9 v[0] 🔒. Note: byte1 = 方向 | 形狀<<4(App 反射光):低 4 bit; byte0 = 方向 | 形狀<<4; 低 4 bit(高 4 bit = 形狀); v0 = 方向 | 形狀<<4(bit0-3)
shape buttons shape
| Range | Meaning |
|---|---|
0~15 bit |
Triangle 1, Circle 2, Cross 4, Square 8 (added when pressed together), 0~15; 0 when not connected; high nibble of 0x22 'R' |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[0]
shape = (w >> 4) & 0x0F # shape buttons (bits 4~7)MBC library
from pybricks.parameters import Port
from MBC_gamepad_Lib import MBC_GAMEPAD
pad = MBC_GAMEPAD(Port.A)
pad.update() # refresh once per loop
shape = pad.pressed('TRIANGLE')mode 0 (edu) byte 1 🔒; mode 0 (toy) byte 0 🔒; mode 1 byte 0; mode 6 v[0] hi; mode 9 v[0] 🔒. Note: 高 4 bit; 高 4 bit; 高 4 bit; v0 bit4-7
shoulder buttons shoulder
| Range | Meaning |
|---|---|
0~15 bit |
L2 1, R2 2, L1 4, R1 8 (added when pressed together), 0~15; 0 when not connected |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[1]
shoulder = w & 0x0F # shoulder buttons (bits 0~3)MBC library
from pybricks.parameters import Port
from MBC_gamepad_Lib import MBC_GAMEPAD
pad = MBC_GAMEPAD(Port.A)
pad.update() # refresh once per loop
shoulder = pad.pressed('L1')mode 0 (edu) byte 0 🔒; mode 2 byte 0; mode 6 v[1] lo; mode 9 v[1] 🔒; mode 0 (toy) byte 6 🔒; mode 0 (toy) byte 1 🔒. Note: byte0 = 肩 | 功能<<4(App 顏色,START = −128):低 4 bit; 低 4 bit(高 4 bit = 功能); v1 = 肩 | 功能<<4(bit0-3); 原始藍 = 肩 | 功能<<4(0~255); 單鍵編號(L2=1 R2=2 L1=3 R1=4 SELECT=5 L3=6 R3=7 START=8,沒按 0;同時按只回編號最小的那個)
function buttons func
| Range | Meaning |
|---|---|
0~15 bit |
SELECT 1, L3 2, R3 4, START 8 (added when pressed together), 0~15; 0 when not connected; high nibble of 0x22 'G' |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
w = dev.read(9)[1]
func = (w >> 4) & 0x0F # function buttons (bits 4~7)MBC library
from pybricks.parameters import Port
from MBC_gamepad_Lib import MBC_GAMEPAD
pad = MBC_GAMEPAD(Port.A)
pad.update() # refresh once per loop
func = pad.pressed('START')mode 0 (edu) byte 0 🔒; mode 2 byte 0; mode 6 v[1] hi; mode 9 v[1] 🔒; mode 0 (toy) byte 6 🔒; mode 0 (toy) byte 1 🔒. Note: 高 4 bit; 高 4 bit; v1 bit4-7; 原始藍 = 肩 | 功能<<4(0~255); 單鍵編號(L2=1 R2=2 L1=3 R1=4 SELECT=5 L3=6 R3=7 START=8,沒按 0;同時按只回編號最小的那個)
connected connected
| Range | Meaning |
|---|---|
0~1 |
1 = gamepad connected (psx.read() succeeds), 0 = not; while disconnected all buttons read 0 and sticks are centred |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
connected = dev.read(3)[0] # connectedMBC library
from pybricks.parameters import Port
from MBC_gamepad_Lib import MBC_GAMEPAD
pad = MBC_GAMEPAD(Port.A)
pad.update() # refresh once per loop
connected = pad.connected()mode 3 byte 0. Note: #67 改:LIGHT 3×int8 = [已連線, 電量, 0](原本 = 4 組按鍵)
battery battery
| Range | Meaning |
|---|---|
0 % |
0~100; PS2 pads report no battery → always 0 (PS4 only) |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
battery = dev.read(3)[1] # batteryMBC library
from pybricks.parameters import Port
from MBC_gamepad_Lib import MBC_GAMEPAD
pad = MBC_GAMEPAD(Port.A)
pad.update() # refresh once per loop
battery = pad.battery()mode 3 byte 1. Note: #67 改:PS2 送 0
stick LX / LY / RX / RY stick
| Range | Meaning |
|---|---|
1~9 (5 = centre) |
SPIKE gives a scaled value, 1 |
from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
dev = PUPDevice(Port.A) # sensor on port A
stick = list(dev.read(9)[2:6]) # [LX, LY, RX, RY]MBC library
from pybricks.parameters import Port
from MBC_gamepad_Lib import MBC_GAMEPAD
pad = MBC_GAMEPAD(Port.A)
pad.update() # refresh once per loop
stick = pad.stick('LX')mode 0 (edu) byte 2 🔒; mode 0 (toy) byte 2 🔒; mode 4 v[0]; mode 7 v[2] 🔒; mode 9 v[2] 🔒. Note: byte2~3 = LX×10+LY、byte4~5 = RX×10+RY(11~99,中心 55;App 原始紅/綠); 原始紅 = LX×10+LY、原始綠 = RX×10+RY(各 1~9,5 = 中間); v0 = LX | LY<<8、v1 = RX | RY<<8(各 1~9,5 = 中間); Pybricks ambient() 用:v2 = LX×1000 + LY×100 + RX×10 + RY(1111~9999,中心 5555);v0、v1 同模式 6;個位數 RY 在 4.1.x 的 ambient() 讀不到,4.1.x 改從模式 5 reflection() 的小數位讀(見按鍵 buttons16); v2~v5 = LX、LY、RX、RY(0~99 ÷10,0 補 1)
Not readable here:L2 analog, R2 analog, system keys, gamepad type. Use Arduino / mBlock / micro:bit for these.
Commands
The SPIKE port is read-only, so this software cannot send commands: calibrate with the button on the sensor and change settings over Arduino I2C, mBlock or micro:bit.
Examples
Example 1: Button test
What this example does: the hub display keeps showing the "button number". Press different buttons and the number changes, so you can check that every button works.
What you need
- 1 SPIKE hub
- Controller PS2 receiver + 2.4G gamepad (already connected, see above)
How to wire it: plug the receiver into port C.
Download the program
| Pybricks |
|---|
| ex01_button_number.py + library |
Pybricks code
# 掌控者PS2 範例 1:按鍵測試(螢幕顯示按鍵數字)
# 接線:掌控者PS2 接收器 → C 埠
from pybricks.hubs import PrimeHub
from pybricks.parameters import Port
from pybricks.tools import wait
from MBC_gamepad_Lib import MBC_GAMEPAD
hub = PrimeHub()
pad = MBC_GAMEPAD(Port.C) # 接收器插在 C 埠
while True:
pad.update() # 先讀一次手把
n = pad.buttons() # 全部按鍵合成一個數字:沒按 = 0,上 = 1、右 = 2、下 = 4、左 = 8…
hub.display.text(str(n)) # 主機螢幕捲動顯示這個數字
print(n) # 電腦畫面也印出來
wait(100) # 等 0.1 秒再讀下一次Step by step, what the program does
- Repeat forever: the blocks below keep running until you press stop.
- Read the buttons: the official App uses "reflected light", which reads the D-pad and the shape buttons; the MBC Blocks website and Pybricks use "all buttons", which reads all 16.
- Show it on the hub display: the number scrolls across the display (hold the button and wait for it to finish scrolling).
- Wait 0.1 second before reading again.
What you should see when it runs
| Holding | All three versions show | MBC Blocks website and Pybricks only |
|---|---|---|
| Nothing | 0 | |
| Up | 1 | |
| Right / Down / Left | 2 / 4 / 8 | |
| ▲ / ● / ✕ / ■ | 16 / 32 / 64 / 128 | |
| Up + ▲ together | 17 (1 + 16, pressed together = added up) | |
| L2 / R2 / L1 / R1 | Official App still shows 0 (shoulder buttons need the "color" block, used in Example 3) | 256 / 512 / 1024 / 2048 |
| SELECT / L3 / R3 / START | Official App still shows 0 | 4096 / 8192 / 16384 / 32768 |
Common questions
- Always 0, nothing happens no matter what I press? → The gamepad is not connected yet: check whether the green light on the receiver is still on, turn on the gamepad or change its batteries.
- The hub says it cannot find the sensor, or the numbers look strange? → The receiver may have the EV3 firmware. You need the SPIKE version.
Example 2: Joystick value
What this example does: the hub display shows the up-down position of the left stick (1 to 9). Released is 5; pushing up makes it bigger, pulling down makes it smaller.
What you need: same as Example 1.
How to wire it: plug the receiver into port C.
Download the program
| Pybricks |
|---|
| ex02_stick_value.py + library |
Pybricks code
# 掌控者PS2 範例 2:搖桿數值(螢幕顯示左搖桿上下的位置)
# 接線:掌控者PS2 接收器 → C 埠
from pybricks.hubs import PrimeHub
from pybricks.parameters import Port
from pybricks.tools import wait
from MBC_gamepad_Lib import MBC_GAMEPAD
hub = PrimeHub()
pad = MBC_GAMEPAD(Port.C) # 接收器插在 C 埠
while True:
pad.update() # 先讀一次手把
y = pad.stick('LY') # 左搖桿上下:1(拉到最下)~ 5(中間)~ 9(推到最上)
hub.display.number(y) # 主機螢幕顯示這個數字
print('LX', pad.stick('LX'), 'LY', y, 'RX', pad.stick('RX'), 'RY', pad.stick('RY')) # 電腦畫面印出 4 個方向
wait(100)Step by step, what the program does
- Read the left stick up-down:
- Official App: "raw red" reads X×10+Y (for example 59 = left-right centered at 5, pushed all the way up to 9), so use "remainder of dividing by 10" to pull out the Y (59 → 9).
- MBC Blocks website: just pick "left stick Y (1~9)".
- Pybricks:
pad.stick('LY').
- Show it on the hub display. The MBC Blocks website and Pybricks also print the number on the computer screen (Pybricks prints all 4 directions).
- Wait 0.1 second before reading again.
What you should see when it runs
- Stick released: 5.
- Slowly push up: 6, 7, 8, 9.
- Slowly pull down: 4, 3, 2, 1.
Common questions
- Always 5, even when I push the stick? → The gamepad is in "digital mode": press the ANALOG button on the gamepad once so the ANALOG light comes on.
- Shows 1 before I even turn on the gamepad? → On older receiver firmware, the sticks read 1 (same as pulled all the way down) while no gamepad is connected. Turn on the gamepad and it goes back to 5. Example 5 has a safety check so the car does not reverse right at startup because of this.
- Want the right stick, or the left-right direction? → MBC Blocks website: change the dropdown to "right stick Y" or "left stick X"; official App: use "raw green" for the right stick, and "whole part of dividing by 10" for left-right; Pybricks: change
'LY'to'RY','LX'or'RX'.
Example 3: Buttons control the light and sound
What this example does: press ▲ and the hub light turns green, ● red, ✕ blue, ■ pink (the same colors as on the gamepad); with nothing pressed the light turns off. Press L1 for a low beep, R1 for a high beep.
What you need: same as Example 1.
How to wire it: plug the receiver into port C.
Download the program
| Pybricks |
|---|
| ex03_light_sound.py + library |
Pybricks code
# 掌控者PS2 範例 3:按鍵控制燈和聲音
# 接線:掌控者PS2 接收器 → C 埠
# ▲ 綠燈、● 紅燈、✕ 藍燈、■ 粉紅燈(跟手把上的顏色一樣),都沒按就關燈;L1 低音嗶、R1 高音嗶
from pybricks.hubs import PrimeHub
from pybricks.parameters import Color, Port
from pybricks.tools import wait
from MBC_gamepad_Lib import MBC_GAMEPAD
hub = PrimeHub()
pad = MBC_GAMEPAD(Port.C) # 接收器插在 C 埠
while True:
pad.update() # 先讀一次手把
if pad.pressed('TRIANGLE'): # ▲
hub.light.on(Color.GREEN)
elif pad.pressed('CIRCLE'): # ●
hub.light.on(Color.RED)
elif pad.pressed('CROSS'): # ✕
hub.light.on(Color.BLUE)
elif pad.pressed('SQUARE'): # ■
hub.light.on(Color.MAGENTA)
else: # 4 個都沒按
hub.light.off()
if pad.pressed('L1'):
hub.speaker.beep(262, 100) # 低音 Do(262 Hz),響 0.1 秒
if pad.pressed('R1'):
hub.speaker.beep(523, 100) # 高音 Do(523 Hz)
wait(50)Step by step, what the program does
- First read the buttons into a "variable" (a variable = a box with a name that can hold one number): the official App uses two boxes, "buttons" holds reflected light (D-pad + shape buttons) and "shoulder" holds color (L1, R1, ...). The MBC Blocks website and Pybricks do not need to store anything; they just ask "is this button pressed?".
- If ▲ is pressed → green light.
- Else, if ● is pressed → red light; else if ✕ → blue light; else if ■ → pink light.
- None of them → light off.
- Two more "if"s: L1 pressed → low beep (note 60 in the official App, 262 Hz in the other two; both are a "Do"); R1 pressed → high beep (note 72 / 523 Hz, the Do one octave up).
- How the official App checks "▲ is pressed": whole part of (buttons ÷ 16), then remainder of dividing by 2 = 1. For ● use 32, ✕ 64, ■ 128; L1 is (shoulder ÷ 4), R1 is (shoulder ÷ 8).
What you should see when it runs
- Hold ▲: green light; release: light off.
- Press ▲ and ● together: green light (the first "if" that is true wins, the ones below are not checked).
- Hold L1: it keeps beeping the low note.
Common questions
- No sound for L1, R1 in the official App? → The shoulder buttons are read with the "color" block (this page was put together from the firmware and the official examples; it has not yet been checked one by one on real hardware). First run Example 1 to confirm the D-pad and shape buttons work; then change "reflected light" in Example 1 to "color", and pressing L1 should show 4, R1 should show 8.
- How do I change the light color? → Official App: click the color dot on the "set Power Button light to" block; MBC Blocks website: click the dropdown on the "set hub light to" block; Pybricks: change
Color.GREENto something likeColor.YELLOW.
Example 4: Drive with the D-pad
What this example does: hold a D-pad button and the car drives that way; release and it stops. Up = forward, Down = backward, Left = turn left in place, Right = turn right in place.
What you need
- Everything from Example 1, plus
- A two-wheel car (two motors as the wheels)
How to wire it
- Plug the receiver into port C
- Left wheel motor on E, right wheel motor on F
Download the program
| Pybricks |
|---|
| ex04_dpad_drive.py + library |
Pybricks code
# 掌控者PS2 範例 4:方向鍵開車(按住才走,放開就停)
# 接線:掌控者PS2 接收器 → C 埠;左輪馬達 → E 埠;右輪馬達 → F 埠
from pybricks.parameters import Direction, Port
from pybricks.pupdevices import Motor
from pybricks.tools import wait
from MBC_gamepad_Lib import MBC_GAMEPAD
pad = MBC_GAMEPAD(Port.C) # 接收器插在 C 埠
left = Motor(Port.E, Direction.COUNTERCLOCKWISE) # 左輪:反過來裝,所以設成逆時針才是往前
right = Motor(Port.F) # 右輪
while True:
pad.update() # 先讀一次手把
if pad.pressed('UP'): # 上 → 前進(功率 50%)
left.dc(50)
right.dc(50)
elif pad.pressed('DOWN'): # 下 → 後退
left.dc(-50)
right.dc(-50)
elif pad.pressed('LEFT'): # 左 → 原地左轉:左輪往後、右輪往前
left.dc(-30)
right.dc(30)
elif pad.pressed('RIGHT'): # 右 → 原地右轉
left.dc(30)
right.dc(-30)
else: # 都沒按 → 停
left.dc(0)
right.dc(0)
wait(50)Step by step, what the program does
- Tell the hub how the wheels are mounted: the two motors face each other, so the left wheel has to "spin the other way" to go forward.
- MBC Blocks website: "E is a LEGO motor, forward = counterclockwise" (F is clockwise); Pybricks:
Direction.COUNTERCLOCKWISE. Once this is set, giving both wheels a positive number means forward. - The official App's motor blocks have no such setting, so the left wheel E gets a minus sign: forward is E -50, F 50.
- MBC Blocks website: "E is a LEGO motor, forward = counterclockwise" (F is clockwise); Pybricks:
- Repeat forever: read the buttons.
- If Up is pressed → both wheels forward (50% power).
- Else if Down → both wheels backward.
- Else if Left → left wheel backward, right wheel forward → turn left in place (30% power; turning slower is easier to control).
- Else if Right → turn right in place.
- Nothing pressed → both wheels power 0, stop.
| Holding | MBC Blocks website, Pybricks (E, F) |
|---|---|
| Up | 50, 50 |
| Down | -50, -50 |
| Left | -30, 30 |
| Right | 30, -30 |
| Nothing | 0, 0 |
What you should see when it runs: hold Up and the car drives forward; release and the car stops.
Common questions
- Pressing Up drives backward? → The motors are mounted the opposite way from the standard LEGO car: MBC Blocks website: swap the "forward" directions of E and F; Pybricks: change the left wheel to
Direction.CLOCKWISEand the right wheel toMotor(Port.F, Direction.COUNTERCLOCKWISE); official App: flip the sign of every number. - Pressing Up spins in place? → Only one side is reversed: flip the sign (or forward direction) of that one motor only.
- The gamepad was turned off or ran out of battery, and the car keeps driving? → Press the center button on the hub first to stop the program, then check the gamepad batteries.
Example 5: Tank drive with two joysticks
What this example does: drive it like a tank: the left stick controls the left wheel, the right stick controls the right wheel. Push both forward = forward, pull both back = backward, one forward and one back = turn in place. The further you push, the faster it goes; release and it stops.
What you need: same as Example 4.
How to wire it: plug the receiver into port C; left wheel motor on E, right wheel motor on F.
Download the program
| Pybricks |
|---|
| ex05_tank_drive.py + library |
Pybricks code
# 掌控者PS2 範例 5:雙搖桿坦克開車(左搖桿管左輪、右搖桿管右輪)
# 接線:掌控者PS2 接收器 → C 埠;左輪馬達 → E 埠;右輪馬達 → F 埠
from pybricks.hubs import PrimeHub
from pybricks.parameters import Color, Direction, Port
from pybricks.pupdevices import Motor
from pybricks.tools import wait
from MBC_gamepad_Lib import MBC_GAMEPAD
hub = PrimeHub()
pad = MBC_GAMEPAD(Port.C) # 接收器插在 C 埠
left = Motor(Port.E, Direction.COUNTERCLOCKWISE) # 左輪:反過來裝,所以設成逆時針才是往前
right = Motor(Port.F) # 右輪
# 開始前的保護:等兩支搖桿都在中間(4~6)才開始,車子才不會一開機就衝出去
while True:
pad.update()
if 4 <= pad.stick('LY') <= 6 and 4 <= pad.stick('RY') <= 6:
break
wait(20)
hub.light.on(Color.GREEN) # 綠燈 = 可以開了
while True:
pad.update() # 先讀一次手把
ly = pad.stick('LY') # 左搖桿上下 1~9
ry = pad.stick('RY') # 右搖桿上下 1~9
# 死區:搖桿在中間附近(4~6)就當成 0,放開手車子就停
if 4 <= ly <= 6:
left_power = 0
else:
left_power = (ly - 5) * 25 # 7 → 50%、9 → 100%、3 → -50%、1 → -100%
if 4 <= ry <= 6:
right_power = 0
else:
right_power = (ry - 5) * 25
left.dc(left_power)
right.dc(right_power)
wait(20)Step by step, what the program does
- Safety check before starting: first "wait until" both sticks are centered (4 to 6), then turn the light green. This way the car will not suddenly shoot off if the gamepad is not connected yet, or a stick gets bumped right at startup.
- Repeat forever: read the left stick up-down (Y) and the right stick up-down (Y). In the official App that is "raw red" and "raw green", remainder of dividing by 10.
- Dead zone: if the stick is between 4 and 6, set the power to 0. When you let go, the stick does not always land exactly on 5; the dead zone is what makes the car stop properly.
- Else: power = (stick − 5) × 25. Pushed to 7 is 50%, all the way to 9 is 100%; pulled to 3 is -50%, all the way to 1 is -100%.
- Send the power to the motors: left wheel E uses the left stick's power, right wheel F uses the right stick's power. In the official App the left wheel is × -1 (same reason as Example 4).
What you should see when it runs
- Right after the program starts: release the sticks and the light turns green; the car does not move.
- Push both sticks forward together: it drives forward, faster the further you push.
- Left stick forward, right stick back: it turns right in place.
- Release: the car stops.
Common questions
- I pressed run but the green light never comes on? → The sticks are not centered, or the gamepad is not connected yet (on older receiver firmware the sticks read 1 while no gamepad is connected). Release the sticks and turn on the gamepad; the green light comes on and it starts.
- A small push does not move the car? → That is the dead zone; it only moves at 7 or more (or 3 or less). For more sensitivity you can change "4 to 6" to "5 to 5", but the car may creep slowly when you let go.
- Too fast to control? → Change "× 25" to "× 15" (full push becomes 60%).
- Forward and backward are swapped, or it spins in place? → See the common questions in Example 4.
Example 6 (advanced): Connection status and battery level
Note
This example has a Pybricks version only. The "connection status" and "battery level" it uses live in the receiver's mode 3, which the official LEGO App and the MBC Blocks website cannot read. The receiver firmware must be a version from 2026-10 or later; older firmware returns different numbers there. The code is the example itself, so there is no screenshot.
What this example does: the light turns red when the program starts, then it waits until the gamepad connects before turning green, beeping once, and printing the receiver type and firmware version; after that the display keeps showing the gamepad battery level (the PS2 2.4G gamepad has no battery information, so it is always 0). Turn the gamepad off and the light goes back to red and the display shows "--".
What you need: same as Example 1 (hub + receiver + gamepad).
How to wire it: plug the receiver into port C.
Download the program
| Pybricks |
|---|
| ex06_connected_battery.py + library |
Pybricks code
# 掌控者PS2 範例 6:連線狀態與電量(等手把連上才開始,螢幕顯示電量)
# 接線:掌控者PS2 接收器 → C 埠
# 只有 Pybricks 版:連線狀態和電量放在接收器的「模式 3」,官方 App 和積木網站讀不到;接收器韌體要 2026-10 以後
from pybricks.hubs import PrimeHub
from pybricks.parameters import Color, Port
from pybricks.tools import wait
from MBC_gamepad_Lib import MBC_GAMEPAD
hub = PrimeHub()
pad = MBC_GAMEPAD(Port.C) # 接收器插在 C 埠
hub.light.on(Color.RED) # 紅燈 = 手把還沒連上
while not pad.connected(): # 等到手把連上(True)才往下
wait(200)
hub.light.on(Color.GREEN) # 綠燈 = 連上了
hub.speaker.beep()
print('接收器種類', pad.kind(), ' 韌體', pad.firmware_version()) # 例如 PS4 (1, 0)
while True:
if pad.connected():
battery = pad.battery() # PS2 手把沒有電量資訊,一直是 0
hub.display.number(min(battery, 99)) # 螢幕最多只能顯示兩位數
hub.light.on(Color.GREEN)
print('已連線 電量', battery, '%')
else: # 手把關機或離太遠 → 斷線
hub.display.text('--')
hub.light.on(Color.RED)
print('斷線了')
wait(500)Step by step, what the program does
pad.connected(): whether a gamepad is connected to the receiver (True / False). At the start, "wait until connected" replaces Example 5's "wait until the sticks are centered", which is more reliable.pad.kind(),pad.firmware_version(): whether the receiver is the PS2 or the PS4 model, and which firmware version it has (read from the sensor's mode 8).- Repeat forever: while connected, show
pad.battery()(always 0 for PS2) and a green light; when disconnected, red light and "--". - This example never calls
pad.update(), because it does not read any buttons. For a program that reads buttons, follow Examples 1 to 5, and callconnected()outside the loop or only now and then (each call makes the receiver switch modes, which costs a few tens of ms).
What you should see when it runs: gamepad still off: red light. Turn on the gamepad, and the moment it connects: green light + beep, display shows 0 (PS2 has no battery level). Turn the gamepad off: red light, "--".
Common questions
- Why is the battery level always 0? → The PS2 2.4G gamepad simply does not report its battery level; to see a battery level, use the Controller PS4/PS5 Bluetooth Receiver.
- Red light all the time? → The gamepad is not turned on, or the receiver firmware is too old (before 2026-10) and has no connection status. Please update the firmware.
- Can I combine this with Example 5? → Yes: replace "wait until the sticks are centered" at the start of Example 5 with "wait until
pad.connected()".
More questions
- The hub does not detect the sensor? Unplug and replug, wait 2 s, try another port. The official app shows it as a colour sensor — that is expected.
- Can I use the official app and Pybricks on the same hub? One firmware at a time: Pybricks and the MBC Blocks website need Pybricks firmware, the official app needs LEGO firmware; you can switch back any time with the SPIKE app.
Still stuck? Contact us with your software and a screenshot of the program.
Downloads
- Pybricks library MBC_gamepad_Lib.py
- Shared library (for new projects, one file for every MBC sensor) MBC_sensors.py
- ps2_pybricks_spike_v4.x.zip
- Download centre (libraries, firmware, catalogue for every product)
Sources
- Host and software list:
_build/guides.json - Values and commands:
_build/protocol/ps2.json(single source of truth) - Setup:
_build/guides/software/spike-pybricks.en.md、_build/guides/product/ps2/spike-pybricks.en.md - Examples:
sensors/ps2/spike-examples.md