martypy.Marty
Python library for communicating with Marty the Robot V1 and V2 by Robotical.
Reference version: MartyPy 3.8.0, checked against the published package on 7 October 2026. Signatures below match this version; where an annotation differs from the actual return value, the entry explains that difference.
MartyPy on PyPI · Source for this release
Install or update in the Python environment that will run your program:
python -m pip install --upgrade martypy
Getting started on a computer (replace the example IP address with Marty's actual address):
from martypy import Marty
my_marty = Marty("wifi", "192.168.0.53")
try:
my_marty.get_ready()
my_marty.dance()
finally:
my_marty.close()
The tags 1️⃣ and 2️⃣ indicate Marty V1 and Marty V2 support. Features also depend on the connected hardware, add-ons and firmware. Methods without a robot-version tag, such as translation and colour helpers, may operate on the computer rather than send a robot command.
Desktop Python and Blocks
This is the reference for the installed MartyPy package. Native Marty V2 connections use WiFi, USB or the expansion port; 'socket' is the legacy Marty V1 connection. A native MartyPy 'bluetooth' connection method is not provided by this release.
In Blocks, Python runs in the browser and supported Marty calls use the app's existing connection to the robot, including Bluetooth. The app supplies my_marty; do not copy desktop connection/installation code into its editor. Browser Python supports a selected set of Marty-style APIs, not every native MartyPy function. Browser speech uses browser audio support and does not require installing FFmpeg on your computer.
Speech and translation requirements
The speech and translation APIs use Robotical's online service, so internet access is needed. Text passed to these functions is sent to that service. Desktop speech needs FFmpeg; speak_on_computer also needs an audio playback backend such as ffplay. Translation and get_language do not require FFmpeg. Default speech settings are ALTO/FEMALE, English and speed 1, and persist on the Marty object until changed.
Waiting for sensor conditions
The foot_on_ground and foot_obstacle_sensed methods return Python Booleans: use True/False, not the strings 'true'/'false'. Sensor getters normally read the latest received telemetry rather than performing a fresh blocking measurement. Missing or stale telemetry can produce empty/zero readings; a missing foot sensor can raise MartyCommandException.
Put subsequent movement after the wait loop, not inside it. Include a short sleep and a timeout so the loop does not spin indefinitely:
import time
from martypy import Marty
my_marty = Marty("wifi", "192.168.0.53")
try:
my_marty.get_ready()
deadline = time.monotonic() + 15
while not my_marty.foot_obstacle_sensed("right"):
if time.monotonic() >= deadline:
raise TimeoutError("No obstacle detected within 15 seconds")
time.sleep(0.05)
my_marty.kick("right")
finally:
my_marty.close()
Support Marty safely with clear space around it when testing movements. Check Boolean command results and catch/report exceptions where appropriate: a generated command or accepted request is not by itself proof that the robot completed the movement.
Marty Objects
Call methods on your connected Marty instance, for example my_marty.walk(num_steps=10). In signatures, Optional means a value may be None, and Union means more than one input type is accepted. Marty.Disco group constants are written as Disco in the signatures.
Connections and blocking
__init__
__init__(method: str, locator: str = "", extra_client_types: dict = dict(), blocking: Union[bool, None] = None, *args, **kwargs) -> None
Start a connection to Marty 1️⃣ 2️⃣
For example:
- Marty("wifi", "192.168.0.53") to connect to Marty via WiFi on IP Address 192.168.0.53
- Marty("usb", "COM2") on a Windows computer with Marty connected by USB cable to COM2
- Marty("usb", "/dev/tty.SLAB_USBtoUART") on a Mac computer with Marty connected by USB cable to /dev/tty.SLAB_USBtoUART
- Marty("exp", "/dev/ttyAMA0") on a Raspberry Pi computer with Marty connected by expansion cable to /dev/ttyAMA0
Blocking Mode
Movement commands that expose a blocking argument (e.g. walk(), dance(), move_joint(), but also hold_position()) comes with two modes - blocking and non-blocking.
Issuing a command in blocking mode will make your program pause until Marty physically stops moving. Only then the next line of your code will be executed.
In non-blocking mode, each movement command simply tells Marty what to do and returns immediately, meaning that your code will continue to execute while Marty is moving.
Movement methods whose signatures include blocking take an optional blocking argument that can be used to choose the mode for that call. If you plan to use the same mode all or most of the time, you can use the Marty.set_blocking() method or use the blocking constructor argument. The latter defaults to True (blocking) if not provided.
Arguments:
- method - method of connecting to Marty - it may be: “usb”, “wifi”, “socket” (Marty V1) or “exp” (expansion port used to connect to a Raspberry Pi, etc)
- locator - location to connect to, depending on the method of connection this is the serial port name, network (IP) Address or network name (hostname) of Marty that the computer should use to communicate with Marty. If method is "usb" and there is only a single Marty connected, locator can be left out and the Marty will be found automatically. If multiple Martys are detected, one of them will be chosen arbitrarily and connected to.
- blocking - Default movement command mode for this Marty instance.
- True (default): blocking mode
- False: non-blocking mode
Raises:
- MartyConfigException if the parameters are invalid
- MartyConnectException if Marty couldn't be contacted
is_conn_ready
is_conn_ready() -> bool
Check if the robot is connected and the connection is ready to accept commands 2️⃣
Arguments:
None
Returns:
True if the robot is connected and ready
is_blocking
is_blocking() -> bool
Check the default movement command behaviour of this Marty. 1️⃣ 2️⃣
Returns:
True if movement commands block by default
set_blocking
set_blocking(blocking: bool)
Change whether movement commands default to blocking or non-blocking behaviour for this Marty. 1️⃣ 2️⃣
The blocking behaviour can also be specified on a per-command basis using the blocking= argument which takes precedence over Marty's overall setting.
Arguments:
- blocking - whether or not to block by default
close
close() -> None
Close the connection to Marty. Call this explicitly in a finally block when a desktop program ends. Closing the connection is not a replacement for intentionally stopping movement with stop.
Movement
get_ready
get_ready(blocking: Optional[bool] = None) -> bool
Move Marty to the normal standing position and wiggle eyebrows 1️⃣ 2️⃣ Will also enable motors for Marty v1 1️⃣
Arguments:
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning.
Returns:
True if Marty accepted the request
stand_straight
stand_straight(move_time: int = 2000, blocking: Optional[bool] = None) -> bool
Move Marty to the normal standing position 1️⃣ 2️⃣
Arguments:
- move_time - How long (in milliseconds) Marty will take to reach the normal standing position. (Higher number means slower movement.)
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
walk
walk(num_steps: int = 2, start_foot: str = 'auto', turn: int = 0, step_length: int = 25, move_time: int = 1500, blocking: Optional[bool] = None) -> bool
Make Marty walk 1️⃣ 2️⃣
Arguments:
- num_steps - how many steps to take
- start_foot - 'left', 'right' or 'auto', start walking with this foot Note: 2️⃣ Unless you specify 'auto', all steps are taken with the same foot so it only makes sense to use the start_foot argument with num_steps=1.
- turn - How much to turn (-100 to 100 in degrees), 0 is straight.
- step_length - How far to step (approximately in mm)
- move_time - duration of each step in milliseconds (not the total duration for all steps)
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
sidestep
sidestep(side: str, steps: int = 1, step_length: int = 35, move_time: int = 1000, blocking: Optional[bool] = None) -> bool
Take sidesteps 1️⃣ 2️⃣
Arguments:
- side - 'left' or 'right', direction to step in
- steps - number of steps to take
- step_length - how broad the steps are (up to 127)
- move_time - duration of each sidestep in milliseconds
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
lift_foot
lift_foot(side: str) -> bool
Lift a foot. 2️⃣
- side - 'left' or 'right'.
Returns True if Marty accepts the request. This method has no blocking argument and does not wait for the movement to finish.
lower_foot
lower_foot(side: str) -> bool
Lower a foot. 2️⃣
- side - 'left' or 'right'.
Returns True if Marty accepts the request. This method has no blocking argument and does not wait for the movement to finish.
wave
wave(side: str) -> bool
Wave an arm. 2️⃣
- side - 'left' or 'right'. The argument is required on Marty (for example, my_marty.wave(“right”)).
Returns True if Marty accepts the request. This method has no blocking argument and does not wait for the movement to finish.
dance
dance(side: str = 'right', move_time: int = 3000, blocking: Optional[bool] = None) -> bool
Boogie, Marty! 1️⃣ 2️⃣
Arguments:
- side - 'left' or 'right', which side to start on
- move_time - how long this movement should last, in milliseconds
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
celebrate
celebrate(move_time: int = 4000, blocking: Optional[bool] = None) -> bool
Celebrate. 1️⃣ 2️⃣ On Marty V2 this currently uses the wiggle trajectory.
Arguments:
- move_time - how long this movement should last, in milliseconds
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
wiggle
wiggle(move_time: int = 4000, blocking: Optional[bool] = None) -> bool
Wiggle. 1️⃣ 2️⃣
Arguments:
- move_time - how long this movement should last, in milliseconds
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
circle_dance
circle_dance(side: str = 'right', move_time: int = 2500, blocking: Optional[bool] = None) -> bool
Circle dance. 1️⃣ 2️⃣
Arguments:
- side - 'left' or 'right', which side to start on
- move_time - how long this movement should last, in milliseconds
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
eyes
eyes(pose_or_angle: Union[str, int], move_time: int = 1000, blocking: Optional[bool] = None) -> bool
Move the eyes to a pose or an angle 1️⃣ 2️⃣
Arguments:
- pose_or_angle - 'angry', 'excited', 'normal', 'wide', or 'wiggle' 2️⃣ - alternatively this can be an angle in degrees (which can be a negative number)
- move_time - how long this movement should last, in milliseconds
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
kick
kick(side: str = 'right', twist: int = 0, move_time: int = 2500, blocking: Optional[bool] = None) -> bool
Kick one of Marty's feet 1️⃣ 2️⃣
Arguments:
- side - 'left' or 'right', which foot to use
- twist - the amount of twisting do do while kicking (in degrees)
- move_time - how long this movement should last, in milliseconds
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
arms
arms(left_angle: int, right_angle: int, move_time: int, blocking: Optional[bool] = None) -> bool
Move both of Marty's arms to angles you specify 1️⃣ 2️⃣
Arguments:
- left_angle - Angle of the left arm (degrees -100 to 100)
- right_angle - Position of the right arm (degrees -100 to 100)
- move_time - how long this movement should last, in milliseconds
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
lean
lean(direction: str, amount: Optional[int] = None, move_time: int = 1000, blocking: Optional[bool] = None) -> bool
Lean over in a direction 1️⃣ 2️⃣
Arguments:
- direction - 'left', 'right', 'forward', or 'back'
- amount - How much to lean. The defaults and the exact meaning is different between Marty V1 and V2:
- 1️⃣ If not specified or None, amount defaults to 50 (no specific unit). Limit is -60/60 in either direction.
- 2️⃣ If not specified or None, amount defaults to 29 degrees. Limit for foward and back is 45 and limit for left and right is 60.
- move_time - How long this movement should last, in milliseconds.
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
move_joint
move_joint(joint_name_or_num: Union[int, str], position: int, move_time: int, blocking: Optional[bool] = None) -> bool
Move a specific joint to a position 1️⃣ 2️⃣
Arguments:
- joint_name_or_num - joint to move, see the Marty.JOINT_IDS dictionary (can be name or number)
- position - angle in degrees
- move_time - how long this movement should last, in milliseconds
- blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking().
Returns:
True if Marty accepted the request
Raises:
MartyCommandException if the joint_name_or_num is unknown
hold_position
hold_position(hold_time: int, blocking: Optional[bool] = None) -> bool
Hold Marty at its current position 2️⃣
Arguments:
hold_time, time to hold position in milli-seconds - blocking - Blocking mode override; whether to wait for physical movement to finish before returning. Defaults to the value returned by self.is_blocking(). Holding position counts as movement because Marty is using its motors to actively resist any attempts to move its joints.
Returns:
True if Marty accepted the request
is_moving
is_moving() -> bool
Check if Marty is moving 2️⃣
Arguments:
none
Returns:
True if Marty is moving
is_paused
is_paused() -> bool
Check if Marty is paused 2️⃣
Returns:
True if Marty is paused
stop
stop(stop_type: Optional[str] = None) -> bool
Stop Marty's movement 1️⃣ 2️⃣
You can also control what way to “stop” you want with the parameter stop_type. For instance:
- 'clear queue' to finish the current movement before stopping (clear any queued movements)
- 'clear and stop' stop immediately (and clear queues)
- 'clear and disable' 1️⃣ stop and disable the robot
- 'clear and zero' stop and move back to get_ready pose
- 'pause' pause motion
- 'pause and disable' 1️⃣ pause motion and disable the robot
Arguments:
- stop_type - the way to stop - see the options above
Raises:
MartyCommandException if the stop_type is unknown
With stop_type omitted, this uses 'clear and stop'. Pass the string name, not a numeric stop code. Returns True if the stop request succeeds.
resume
resume() -> bool
Resume Marty's movement after a pause 2️⃣
Returns:
True if Marty accepted the request
Sound, speech and translation
set_volume
set_volume(volume: int) -> bool
Set Marty's speaker volume. 2️⃣
- volume - from 0 (silent) to 100 (maximum).
Returns True if Marty accepts the request. This does not change the computer speaker volume.
get_volume
get_volume() -> int
Read Marty's speaker volume (0 to 100). 2️⃣
play_sound
play_sound(name_or_freq_start: Union[str,int], freq_end: Optional[int] = None, duration: Optional[int] = None) -> bool
Play a named sound (Marty V2 2️⃣) or make a tone (Marty V1 1️⃣)
Arguments:
- name_or_freq_start - name of the sound, e.g. 'excited' or 'no_way' 2️⃣
- name_or_freq_start - starting frequency, min 20, max 20000, Hz 1️⃣
- freq_end - ending frequency, min 20, max 20000, Hz 1️⃣
- duration - milliseconds, maximum 5000 1️⃣
Returns:
True if Marty accepted the request
play_mp3
play_mp3(filename: str, progress_callback: Callable[[int, int], bool] = None) -> bool
Stream an MP3 file from the computer to Marty's speaker. 2️⃣
- filename - local path to the MP3 file on the computer.
- progress_callback - optional function called with (bytes_sent, total_bytes). Return True to continue streaming or False to abort.
Returns True if streaming succeeds. This method has no blocking argument; it waits for the streaming operation, not a separate movement-completion wait. File, connection and streaming failures may return False or raise exceptions.
set_voice
set_voice(voice: str)
Set the default voice for subsequent calls to speak and speak_on_computer. Available from MartyPy 3.8.0.
Arguments:
- voice - a voice name (case-insensitive). Common choices are FEMALE, MALE, KITTEN, ALIEN, GIANT, BOLT, STARLIGHT and WHIRLWIND. FEMALE is an alias for ALTO; MALE is an alias for TENOR; SQUEAK and CHIPMUNK are aliases for KITTEN.
Other supported names are ALTO, TENOR, THUNDER, STONE, RUMBLE, ECHO, DRIFT, BREEZE, WAVE, BLAZE, MIST, DAWN, CRYSTAL, LULLABY, AURORA, RADIANCE and FLASH. KITTEN produces meows rather than spoken words.
The initial voice is ALTO (FEMALE). Returns None. An unknown voice raises ValueError.
set_voice_speed
set_voice_speed(speed: float)
Set the default speed for subsequent speech. Available from MartyPy 3.8.0.
Arguments:
- speed - a number from 0.1 to 2 inclusive. 1 is normal speed; a smaller number is slower and a larger number is faster.
Returns None. Invalid or non-finite speeds raise ValueError. Desktop MartyPy changes playback rate, which can also change pitch.
set_speech_language
set_speech_language(language: str)
Set the language/accent used for subsequent speech synthesis. Available from MartyPy 3.8.0. This does not translate the words.
Arguments:
- language - a supported code such as en or fr, or a recognised language name such as French.
The initial language is English (en). Supported speech codes are ar, zh-cn, da, nl, en, fr, de, hi, is, it, ja, ko, nb, pl, pt-br, pt, ro, ru, es, es-419, sv, tr and cy.
Returns None. An unsupported speech language raises ValueError. Translation supports additional languages; a language accepted by translate is not necessarily supported for speech.
speak
speak(words: str = 'hello', voice: Optional[str] = None, blocking: Optional[bool] = False, language: Optional[str] = None, speed: Optional[float] = None) -> bool
Speak through Marty's speaker. 2️⃣
Arguments:
- words - text to say. The online synthesis request uses at most the first 128 characters (after any KITTEN voice substitution).
- voice - voice name, or None to use the setting from set_voice.
- blocking - True requests waiting for speech to finish. The default is False: unlike movement commands, this does not inherit set_blocking. Passing None also does not request waiting in MartyPy 3.8.0.
- language - supported speech language/accent, or None to use set_speech_language.
- speed - speech speed from 0.1 to 2, or None to use set_voice_speed.
Returns:
True if the audio stream was accepted; False if streaming failed. Explicit voice, language and speed values apply to this call without changing the saved defaults. Language and speed overrides and saved speech settings are available from MartyPy 3.8.0.
Desktop MartyPy requires internet access and FFmpeg. Audio is generated using Robotical's online service, processed on the computer and streamed to Marty. Network, synthesis, audio-processing and connection errors may raise exceptions.
my_marty.set_voice("FEMALE")
my_marty.set_voice_speed(1.2)
my_marty.speak("Hello from Marty", blocking=True)
speak_on_computer
speak_on_computer(words: str = 'hello', voice: Optional[str] = None, language: Optional[str] = None, speed: Optional[float] = None) -> bool
Speak through the computer's speakers, not Marty's. Available from MartyPy 3.8.0.
Arguments:
- words - text to say (the same synthesis limit as speak).
- voice, language, speed - optional overrides with the same meanings as speak. None uses the saved speech defaults.
Returns:
True after computer playback finishes. This call waits and has no blocking argument. It does not send an audio command to Marty. Desktop use requires internet, FFmpeg and a supported playback backend such as ffplay. Invalid settings or network/audio failures can raise exceptions. In Blocks, browser audio is used instead and FFmpeg is not required.
translate
translate(words: str, language: str) -> str
Translate text using Robotical's online service. Available from MartyPy 3.8.0. Internet access is required; no robot command is sent.
Arguments:
- words - text to translate.
- language - target language code, such as fr (French), es (Spanish) or el (Greek). Recognised language names can also be used. Translation supports more languages than speech synthesis.
Returns:
The translated text as a string. Empty text and text consisting only of digits are returned unchanged. This function does not speak, change the speech accent or change the selected speech language.
Malformed service responses raise ValueError; network and HTTP failures raise exceptions rather than being reported as successful translations.
my_marty.set_speech_language("fr")
translated = my_marty.translate("Hello", "fr")
my_marty.speak(translated, blocking=True)
get_language
get_language() -> str
Return the computer's configured locale language. Available from MartyPy 3.8.0.
For a recognised locale, the result is a language name such as English or French; otherwise it is the normalised locale code. If the locale is unavailable, English is used. This does not read a language setting from Marty and does not report the current speech language.
In Blocks, the corresponding function reports the editor language instead of the desktop Python locale.
Sensors and joint readings
get_accelerometer
get_accelerometer(axis: Optional[str] = None) -> float
Read the latest accelerometer telemetry. 1️⃣ 2️⃣
- axis - 'x', 'y' or 'z'. On Marty V2, omit it to read all three axes. On Marty V1, an axis is required.
Actual return value:
With an axis, returns that acceleration reading. On V2 without an axis, returns a three-value sequence [x, y, z] (a list for normal telemetry, or (0, 0, 0) before any telemetry arrives), despite the float annotation. Individual-axis reads return 0 when data is unavailable or stale. An unknown axis raises MartyCommandException.
get_distance_sensor
get_distance_sensor() -> Union[int, float]
Get the latest value from the distance sensor 1️⃣ 2️⃣
Returns:
The distance sensor reading. The meaning of the returned value is different between Marty V1 and V2:
- 1️⃣ Returns a raw distance sensor reading as a float.
- 2️⃣ Returns the distance in millimeters as int. Both will return 0 if no distance sensor is found.
foot_on_ground
foot_on_ground(add_on_or_side: str) -> bool
Checks whether the foot is on a surface 2️⃣
Arguments:
- add_on_or_side - Takes in the name of a color sensor, name of an IR sensor, 'left' for the add on connected to the left foot, or 'right' for the add on connected to the right foot.
Returns:
A boolean for whether the addon detects the ground. True for ground detected, False otherwise.
Returns a Python Boolean, not a 'true'/'false' string.
foot_obstacle_sensed
foot_obstacle_sensed(add_on_or_side: str) -> bool
Checks whether there is an obstacle in front of the foot 2️⃣
Arguments:
- add_on_or_side - Takes in the name of a color sensor, name of an IR sensor, 'left' for the add on connected to the left foot, or 'right' for the add on connected to the right foot.
Returns:
A boolean for whether the addon detects an obstacle. True for obstacle detected, False otherwise.
This returns a Python Boolean, not the text 'true'/'false'. Use the value directly in conditions.
get_obstacle_sensor_reading
get_obstacle_sensor_reading(add_on_or_side: str) -> int
Gets a raw obstacle sensor reading from an IR or color sensor 2️⃣
Arguments:
- add_on_or_side - Takes in the name of a color sensor, name of an IR sensor, 'left' for the add on connected to the left foot, or 'right' for the add on connected to the right foot.
Returns:
Raw reading of obstacle sensor data from the add on.
get_ground_sensor_reading
get_ground_sensor_reading(add_on_or_side: str) -> int
Gets a raw ground sensor reading from an IR or color sensor 2️⃣
Arguments:
- add_on_or_side - Takes in the name of a color sensor, name of an IR sensor, 'left' for the add on connected to the left foot, or 'right' for the add on connected to the right foot.
Returns:
Raw reading of ground sensor data from the add on.
get_color_sensor_color
get_color_sensor_color(add_on_or_side: str) -> int
Read the colour classification from a colour sensor. 2️⃣
- add_on_or_side - a colour sensor's name, or 'left'/'right' for the corresponding foot sensor.
Actual return value:
A string: yellow, green, blue, purple, red, air or unclear. 'air' means the sensor does not detect the ground; 'unclear' means the colour cannot be classified. If there is no current sensor data, 0 can be returned. A missing or invalid sensor can raise MartyCommandException.
The published 3.8.0 annotation says int, but a successful colour classification is a string, not an integer.
get_color_sensor_hex
get_color_sensor_hex(add_on_or_side: str) -> str
Read a colour sensor's RGB channels as a hexadecimal colour string, without a leading #. 2️⃣
- add_on_or_side - a colour sensor name, or 'left'/'right'.
If there is no current sensor data, 0 can be returned despite the str annotation. A missing or invalid sensor can raise MartyCommandException.
get_color_sensor_value_by_channel
get_color_sensor_value_by_channel(add_on_or_side: str, channel: str) -> int
Read a raw channel from a colour sensor. 2️⃣
- add_on_or_side - a colour sensor name, or 'left'/'right'.
- channel - 'clear', 'red', 'green' or 'blue' (case-insensitive).
Returns the channel reading, or 0 if no current data is available. Invalid channels or missing/invalid sensors can raise MartyCommandException.
get_joint_position
get_joint_position(joint_name_or_num: Union[int, str]) -> float
Get the position (angle in degrees) of a joint 2️⃣
Arguments:
- joint_name_or_num - see the Marty.JOINT_IDS dictionary (can be name or number)
Returns:
Angle of the joint in degrees
Raises:
MartyCommandException if the joint_name_or_num is unknown
get_joint_current
get_joint_current(joint_name_or_num: Union[int, str]) -> float
Get the current (in milli-Amps) of a joint 1️⃣ 2️⃣ This can be useful in detecting when the joint is working hard and is related to the force which the joint's motor is exerting to stay where it is
Arguments:
- joint_name_or_num - see the Marty.JOINT_IDS dictionary (can be name or number)
Returns:
the current of the joint in milli-Amps (this will be 0 if the joint current is unknown)
Raises:
MartyCommandException if the joint_name_or_num is unknown
get_joint_status
get_joint_status(joint_name_or_num: Union[int, str]) -> int
Get information about a joint 2️⃣ This can be helpful to find out if the joint is working correctly and if it is moving at the moment, etc
Arguments:
- joint_name_or_num - see the Marty.JOINT_IDS dictionary (can be name or number)
Returns:
a code number which is the sum of codes in the Marty.JOINT_STATUS dictionary will be 0 if the joint status is unknown
Raises:
MartyCommandException if the joint_name_or_num is unknown
get_motor_current
get_motor_current(motor_id: int) -> float
Get current flowing through a joint motor 1️⃣ 2️⃣
Arguments:
motor_id, integer >= 0 (non-negative) selects which motor to query
Returns:
Instantaneous current sense reading from motor motor_id
get_battery_remaining
get_battery_remaining() -> float
Get the battery remaining percentage 2️⃣
Returns:
The battery remaining capacity in percent
Status, naming and calibration
get_robot_status
get_robot_status() -> Dict
Read the latest robot-status telemetry. 2️⃣ The dictionary can be empty when telemetry is unavailable or stale.
Typical keys are:
- workQCount - queued work items/movements.
- isMoving - whether Marty is moving.
- isPaused - whether movement is paused.
- isFwUpdating - whether a firmware update is in progress.
- flags - raw status flags.
- updateTime - time at which this telemetry was received.
Additional fields such as heapFree, heapMin, pixRGBT, loopMsAvg and loopMsMax depend on the status packet/firmware.
get_joints
get_joints() -> Dict
Read the latest telemetry for Marty's joints. 2️⃣ Returns a dictionary keyed by joint ID, or an empty dictionary if telemetry is unavailable/stale.
Each joint entry can contain:
- IDNo - joint ID (see Marty.JOINT_IDS).
- name - joint name, when known.
- pos - joint angle in degrees.
- current - motor current in milliamps.
- enabled - whether the servo is enabled.
- commsOK - whether the servo is communicating.
- flags - joint status bitmask (see Marty.JOINT_STATUS).
- updateTime - last update time, when available.
get_power_status
get_power_status() -> Dict
Read the latest battery/power telemetry. 2️⃣ Returns an empty dictionary if telemetry is unavailable/stale.
Possible keys include:
- battRemainCapacityPercent - remaining charge percentage.
- battTempDegC - battery temperature in degrees Celsius.
- battRemainCapacityMAH - remaining capacity in milliamp-hours.
- battFullCapacityMAH - full capacity in milliamp-hours.
- battCurrentMA - battery current in milliamps (including charging).
- power5VOnTimeSecs - time the joint/add-on supply has been on.
- powerUSBIsConnected - whether USB power is connected.
- power5VIsOn - whether the joint/add-on supply is on.
- battInfoValid, powerUSBIsValid, powerFlags - validity/raw status information.
- updateTime - last telemetry update time.
Fields may be omitted when the corresponding information is not valid or not reported.
get_add_ons_status
get_add_ons_status() -> Dict
Read the latest add-on telemetry. 2️⃣ Returns a dictionary keyed by add-on ID, with an additional top-level updateTime entry. It can be empty if telemetry is unavailable/stale.
Add-on entries can include IDNo, name, type, whoAmI, whoAmITypeCode, valid and data (10 raw bytes whose meaning depends on the add-on).
When iterating over values, filter out non-dictionary entries such as updateTime:
for add_on_id, status in my_marty.get_add_ons_status().items():
if isinstance(status, dict):
print(add_on_id, status.get("name"), status.get("whoAmI"))
get_add_on_status
get_add_on_status(add_on_name_or_id: Union[int, str]) -> Dict
Read the latest status for one add-on. 2️⃣
- add_on_name_or_id - numeric add-on ID or configured name. Use the numeric ID returned by get_add_ons_status when a name does not resolve.
Returns a dictionary with IDNo, valid and raw data, plus available hardware metadata such as name, type, whoAmI and whoAmITypeCode. Returns an empty dictionary when no matching current status is available.
get_system_info
get_system_info() -> Dict
Read cached firmware/hardware identification information. 2️⃣
The returned dictionary can include:
- HardwareVersion - hardware version string.
- SystemName - firmware/system identifier (for example RIC); the exact value depends on the firmware and must not be assumed to be RicFirmwareESP32.
- SystemVersion - firmware version, for example '1.3.21'.
- SerialNo - serial number.
- MAC - base MAC address.
- RicHwRevNo - RIC hardware revision.
Available fields depend on the firmware. The Marty V1 client returns legacy placeholder identification values; use get_firmware_version/get_chatter for V1 firmware reporting.
get_hw_elems_list
get_hw_elems_list() -> List
Get a list of all of the hardware elements on Marty 2️⃣
Arguments:
none
Returns:
List containing a dictionary for each hardware element, each element is in the form:
- "name" - name of the hardware element
- "type" - type of element, see Marty.HW_ELEM_TYPES, other types may appear as add-ons
- "busName" - name of the bus the element is connected to
- "addr" - address of the element if it is connected to a bus
- "addrValid" - 1 if the address is valid, else 0
- "IDNo" - identification number of the element
- "whoAmI" - string from the hardware which may contain additional identification
- "whoAmITypeCode" - string indicating the type of hardware
- "SN" - serial number of the hardware element
- "versionStr" - version string of hardware element in semantic versioning (semver) format
- "commsOK" - 1 if the element is communicating ok, 0 if not
set_marty_name
set_marty_name(name: str) -> bool
Set Marty's name 2️⃣
Arguments:
name to call Marty
Returns:
True if successful in setting the name
get_marty_name
get_marty_name() -> str
Get Marty's name 2️⃣
Arguments:
none
Returns:
the name given to Marty
is_marty_name_set
is_marty_name_set() -> bool
Check if Marty's name is set 2️⃣
Arguments:
none
Returns:
True if Marty's name is set
is_calibrated
is_calibrated() -> bool
Check if Marty is calibrated 2️⃣
save_calibration
save_calibration() -> bool
Set the current motor positions as the zero positions 1️⃣ 2️⃣
BE CAREFUL, this can cause unexpected movement or self-interference
clear_calibration
clear_calibration() -> bool
Mark the current calibration as invalid 1️⃣ 2️⃣
This has no immediate physical effect. Marty will still remember the last calibration but will report that it needs to be calibrated again. (You may notice that a “Calibrate” button appears in the app for example.)
Disco and indicator lights
disco_off
disco_off(add_on: Union[Disco, str] = Disco.ALL, api='led') -> bool
Turn off a Disco add-on or group. 2️⃣
- add_on - configured add-on name, or Marty.Disco.ALL, Marty.Disco.EYES, Marty.Disco.ARMS or Marty.Disco.FEET.
- api - 'led' (default) for the firmware LED API, or 'raw_query' for the legacy raw add-on protocol. Most programs should omit this argument.
Returns True if the requests succeed.
disco_pattern
disco_pattern(pattern: int, add_on: Union[Disco, str] = Disco.ALL) -> bool
Deprecated: use disco_named_pattern for named firmware LED patterns. 2️⃣
- pattern - 1 or 2 (legacy raw add-on pattern).
- add_on - configured add-on name or a Marty.Disco group.
Returns True if the requests succeed. Other pattern numbers raise an exception.
disco_named_pattern
disco_named_pattern(add_on: str, pattern: str) -> bool
Run a named light pattern on a Disco add-on. 2️⃣ Preferred over the deprecated disco_pattern method.
- add_on - the configured add-on name, such as 'LEDeye'. A Marty.Disco group is also accepted by the implementation, despite the str annotation.
- pattern - 'show-off', 'pinwheel' or 'off'. Names are case-sensitive.
Returns True if the request is accepted. An unknown pattern raises MartyCommandException.
my_marty.disco_named_pattern("LEDeye", "pinwheel")
disco_color
disco_color(color: Union[str, Tuple[int, int, int]] = 'white', add_on: Union[Disco, str] = Disco.ALL, region: Union[int, str] = 'all', api='led') -> bool
Set the colour of a Disco add-on or group. 2️⃣
- color - six-digit hex string, RGB tuple of three integers from 0 to 255, or a built-in name: white, red, blue, yellow, green, teal, pink, purple or orange.
- add_on - configured add-on name (such as 'LEDeye'), or Marty.Disco.ALL, Marty.Disco.EYES, Marty.Disco.ARMS or Marty.Disco.FEET.
- region - 'all' (default) or a region number supported by that add-on/firmware. Region availability depends on the hardware; it is not universally limited to 0, 1 and 2. Use disco_color_specific_led to address an individual LED.
- api - 'led' (default) or 'raw_query' (legacy raw add-on protocol). This is an optional MartyPy argument, not a colour value; most programs should omit it.
Returns True if the requests succeed. Use one of the two supported api values; other values do not select a valid operation.
my_marty.disco_color((255, 0, 12), add_on="LEDeye")
my_marty.disco_color("blue", add_on=Marty.Disco.EYES)
disco_color_specific_led
disco_color_specific_led(color: Union[str, Tuple[int, int, int]], add_on: str, add_on_who_am_i: str, led_id: int) -> bool
Set one LED on a Disco add-on. 2️⃣
- color - a named colour, six-digit hex string or RGB tuple (0–255 per channel).
- add_on - the configured add-on name.
- add_on_who_am_i - the hardware type string, such as 'LEDeye', 'LEDarm' or 'LEDfoot'; this determines LED position mapping. This is not the add-on's configured name.
- led_id - zero-based LED position, in range for that hardware. LEDeye has 12 positions, 0–11; the library maps these to the hardware LED order.
Returns True if Marty accepts the request. Use get_hw_elems_list to inspect hardware metadata.
disco_color_eyepicker
disco_color_eyepicker(colours: Union[str, List[str]], add_on: str) -> bool
Set individual colours for Disco eyes. 2️⃣
- colours - one six-digit hex string, or a list of up to 12 six-digit hex strings. A leading # is optional. List positions are mapped to eye LED positions; do not use named colours here.
- add_on - the configured eye add-on name.
Returns True if Marty accepts the request.
rgb_operator
rgb_operator(r: int, g: int, b: int) -> list[int]
Create an RGB colour from red, green and blue channel values. Use values from 0 to 255 for LED commands.
Actual return value: the tuple (r, g, b), not a list. The list[int] annotation in MartyPy 3.8.0 is inaccurate. The function does not send a robot command or validate/clamp the input.
hsv_operator
hsv_operator(h: int, s: int, v: int) -> list[int]
Convert HSV (hue, saturation, value) to an RGB list. This is HSV, not HSL.
- h - hue in degrees; wraps around at 360.
- s - saturation: 0–1, or values greater than 1 interpreted as percentages (0–100).
- v - value/brightness: 0–1, or values greater than 1 interpreted as percentages (0–100).
Saturation and value are clamped to 0–1 after conversion. Returns [red, green, blue] with integer channels from 0 to 255. Convert to a tuple when passing to disco_color, whose RGB input is a tuple.
colour = tuple(my_marty.hsv_operator(120, 100, 100))
my_marty.disco_color(colour, add_on="LEDeye")
function_led
function_led(colour: Tuple[int, int, int], breathe: str = 'on', breath_ms: int = 1000) -> bool
Set Marty's function/indicator LED, not a Disco add-on. 2️⃣
- colour - RGB tuple, with each channel from 0 to 255.
- breathe - 'on' for steady light or 'breath' for the breathing effect.
- breath_ms - breathing period in milliseconds.
Returns True if Marty accepts the request. A colour with other than three channels raises MartyCommandException.
function_led_off
function_led_off() -> bool
Turn off Marty's function/indicator LED. 2️⃣ Returns True if Marty accepts the request.
Advanced APIs, files and callbacks
add_on_query
add_on_query(add_on_name: str, data_to_write: bytes, num_bytes_to_read: int) -> Dict
Write and read an add-on directly (raw-mode) 2️⃣
Arguments:
- add_on_name - name of the add-on (see get_add_ons_status() at the top level or response to addon/list REST API command)
- data_to_write - can be zero length if nothing is to be written, the first byte will generally be the register or opcode of the add-on
- num_bytes_to_read - number of bytes to read from the device - can be zero
Returns:
Dict with keys including: “rslt” - the result which will be “ok” if the query succeeded “dataRead” - the data read from the add-on
send_ric_rest_cmd
send_ric_rest_cmd(ricRestCmd: str) -> None
Send a command in RIC REST format to Marty 2️⃣
This is a special purpose command which you can use to do advanced control of Marty
Arguments:
- ricRestCmd - string containing the command to send to Marty
Returns:
None
send_ric_rest_cmd_sync
send_ric_rest_cmd_sync(ricRestCmd: str) -> Dict
Send a command in RIC REST format to Marty and wait for reply 2️⃣
This is a special purpose command which you can use to do advanced control of Marty
Arguments:
- ricRestCmd - string containing the command to send to Marty
Returns:
Dictionary containing the response received from Marty
send_file
send_file(filename: str, progress_callback: Callable[[int, int], bool] = None, file_dest: str = 'fs') -> bool
Upload a file from the computer to Marty. 2️⃣
- filename - local path to the file to upload.
- progress_callback - optional function taking exactly two arguments: (bytes_sent, total_bytes). Return True to continue or False to abort.
- file_dest - 'fs' for file storage, or 'ricfw' for a RIC firmware update. Use 'ricfw' only when intentionally updating firmware with the correct image.
Returns True if uploading succeeds. Operating-system or connection failures can raise exceptions.
def upload_progress(bytes_sent, total_bytes):
print(bytes_sent, "of", total_bytes)
return True
my_marty.send_file("example.mp3", progress_callback=upload_progress)
get_file_list
get_file_list() -> List[str]
Request the files stored on Marty. 2️⃣ Returns the list supplied by firmware, or an empty list if the file-list request fails.
Although the 3.8.0 annotation is List[str], entries can be file-information dictionaries rather than plain filename strings. Inspect the returned entries instead of assuming a string-only list.
delete_file
delete_file(filename: str) -> bool
Delete a stored file from Marty. 2️⃣
- filename - the filename on Marty, not a path on the computer.
Returns True if the delete request succeeds. Deletion is destructive; only delete files you intentionally want to remove.
register_logging_callback
register_logging_callback(loggingCallback: Callable[[str], None]) -> None
Register a function to receive each RIC log message. 2️⃣ Primarily useful for debugging.
- loggingCallback - a function taking one string (the log message).
Returns None. Keep callbacks short; do not run long movement/wait operations inside communication callbacks.
register_publish_callback
register_publish_callback(messageCallback: Callable[[int], None]) -> None
Register a function called after each published telemetry message has been decoded. 2️⃣
- messageCallback - a function taking one integer topic code.
Compare the topic with Marty.PUBLISH_TOPIC_ACCELEROMETER, Marty.PUBLISH_TOPIC_SERVOS, Marty.PUBLISH_TOPIC_POWER, Marty.PUBLISH_TOPIC_ADDONS or Marty.PUBLISH_TOPIC_ROBOT_STATUS, then read the updated data with the normal getter methods. Returns None. Keep callbacks short.
register_report_callback
register_report_callback(messageCallback: Callable[[str], None]) -> None
Register a function to receive RIC report messages, including alerts. 2️⃣
- messageCallback - a function taking one JSON string.
Reports may include msgType (such as 'warn'), msgBody (such as 'freeFallDet' or 'overCurrentDet') and IDNo for the hardware element. Returns None. Parse with json.loads when needed, and keep callbacks short.
get_interface_stats
get_interface_stats() -> Dict
Read communication statistics for debugging. 2️⃣
The dictionary may include roundTripAvgMS, msgRxRatePS, msgTxRatePS, unmatched, matched, unnumbered, timedOut, uploadBPS, rxCount and txCount. Published telemetry rates can appear as servosPS, imuPS, robotPS, powerPS or addonsPS. Available keys depend on the interface.
get_test_output
get_test_output() -> str
Read output captured by MartyPy's test interface. For automated library testing with method='test', not for reading a normal robot connection's program output.
Legacy MartyV1 commands
These are legacy interfaces. Use get_ready rather than hello for new programs.
enable_motors
enable_motors(enable: bool = True, clear_queue: bool = True) -> bool
Toggle power to motors 1️⃣
Arguments:
- enable - True/False toggle
- clear_queue - Default True, prevents unfinished but 'muted' motions from jumping as soon as motors are enabled
On V1, disabling motors returns False even when the disable command succeeds; do not treat that return value alone as proof of a communication failure.
enable_safeties
enable_safeties(enable: bool = True) -> bool
Tell the board to turn on 'normal' safeties 1️⃣
fall_protection
fall_protection(enable: bool = True) -> bool
Toggle fall protections 1️⃣
Arguments:
- enable - True/False toggle
motor_protection
motor_protection(enable: bool = True) -> bool
Toggle motor current protections 1️⃣
Arguments:
- enable - True/False toggle
battery_protection
battery_protection(enable: bool = True) -> bool
Toggle low battery protections 1️⃣
Arguments:
- enable - True/False toggle
buzz_prevention
buzz_prevention(enable: bool = True) -> bool
Toggle motor buzz prevention 1️⃣
Arguments:
- enable - True/False toggle
lifelike_behaviour
lifelike_behaviour(enable: bool = True) -> bool
Tell the robot whether it can or can't move now and then in a lifelike way when idle. 1️⃣
Arguments:
- enable - True/False toggle
ros_command
ros_command(*byte_array: int) -> bool
Low level proxied access to the ROS Serial API between the modem and main controller 1️⃣
keyframe
keyframe(time: float, num_of_msgs: int, msgs) -> List[bytes]
Takes in information about movements and generates keyframes returns a list of bytes 1️⃣
Arguments:
- time - time (in seconds) taken to complete movement
- num_of_msgs - number of commands sent
- msgs - commands sent in the following format [(ID CMD), (ID CMD), etc…]
get_chatter
get_chatter() -> bytes
Return chatter topic data (variable length) 1️⃣
get_firmware_version
get_firmware_version() -> bool
Ask the board to print the firmware version over chatter 1️⃣
ros_serial_formatter
ros_serial_formatter(topicID: int, send: bool = False, *message: int) -> List[int]
Formats message into ROS serial format and returns formatted message as a list 1️⃣
Calls ros_command with the processed message if send is True. More information about the ROS serial format can be found here: http://wiki.ros.org/rosserial/Overview/Protocol
pinmode_gpio
pinmode_gpio(gpio: int, mode: str) -> bool
Configure a GPIO pin 1️⃣
Arguments:
- gpio - pin number between 0 and 7
- mode - choose from: 'digital in','analog in' or 'digital out'
write_gpio
write_gpio(gpio: int, value: int) -> bool
Write a value to a GPIO port 1️⃣
digitalread_gpio
digitalread_gpio(gpio: int) -> bool
Read from GPIO 1️⃣
Arguments:
GPIO pin number, >= 0 (non-negative)
Returns:
Returns High/Low state of a GPIO pin
set_parameter
set_parameter(*byte_array: int) -> bool
Set board parameters 1️⃣
Arguments:
- byte_array - a list in the following format [paramID, params]
i2c_write
i2c_write(*byte_array: int) -> bool
Write a bytestream to the i2c port. 1️⃣
The first byte should be the address, following from that the datagram folows standard i2c spec
i2c_write_to_ric
i2c_write_to_ric(address: int, byte_array: bytes) -> bool
Write a formatted bytestream to the i2c port. 1️⃣
The bytestream is formatted in the ROS serial format.
address: the other device's address
i2c_write_to_rick
i2c_write_to_rick(address: int, byte_array: bytes) -> bool
Write a formatted bytestream to the i2c port. 1️⃣
The bytestream is formatted in the ROS serial format. address: the other device's address
get_battery_voltage
get_battery_voltage() -> float
Get the voltage of the battery 1️⃣
Returns:
The battery voltage reading as a float in Volts
hello
hello(blocking: Optional[bool] = None) -> bool
Zero joints and wiggle eyebrows. 1️⃣ 2️⃣ On V2 this calls the get-ready trajectory; use get_ready for new programs. The optional blocking override works like other movement methods that expose blocking.
discover
discover() -> List[str]
Try and find us some Martys! 1️⃣
Helper commands
__del__
__del__() -> None
Object cleanup hook, which calls close. Normally invoked by Python; do not rely on its timing to stop a robot or release a connection. Use close explicitly.