Skip to content

Common mistakes

If your program is not doing what you expect, read this page before anything else. Each entry is a mistake that is easy to make while learning MARLIN, shown as the wrong code next to the right code.

These are mistakes your program makes silently. MARLIN reports other problems directly, with an error message, a toast, or a console line. For those, see Troubleshooting.

Passing objects instead of classes to configure

Wrong:

robot.configure(
    sensors={"S1": IMU("S1")},
    effectors={"M1": Thruster("M1")},
)

Right:

robot.configure(
    sensors={"S1": IMU},
    effectors={"M1": Thruster},
)

configure() expects hardware classes, not constructed hardware objects. See Classes if that distinction is new to you.

Forgetting while robot.running

This mistake applies especially in the driver() function.

Wrong:

def driver(robot):
    power = robot.controller.joystick("ONE").value
    robot.thruster("M1").set_duty(power)

This runs once and then returns. When a callback returns, MARLIN idles outputs. See while loops.

Right:

def driver(robot):
    while robot.running:
        power = robot.controller.joystick("ONE").value
        robot.thruster("M1").set_duty(power)

Adding your own time.sleep() to the loop

Wrong:

def driver(robot):
    while robot.running:
        robot.thruster("M1").set_duty(30)
        time.sleep(0.02)

Right:

def driver(robot):
    while robot.running:
        robot.thruster("M1").set_duty(30)

robot.running already waits for the next 20 ms tick, so your loop runs fifty times a second on its own. A time.sleep(0.02) on top of that sleeps twice per pass and halves the rate to 25 Hz.

Not checking sensor freshness

Wrong:

gyro_z = robot.imu("S1").read().gyro_z

Right:

reading = robot.imu("S1").read()
if reading.ok:
    gyro_z = reading.gyro_z
else:
    robot.thruster("M1").set_duty(0)

Sensor values can be missing or stale. Check ok before using sensor data to make motion decisions.

Expecting button("A").is_down to mean “just pressed”

is_down tells you whether the button is currently held. It is not a press event. To detect a new press, compare the current value to the previous value. See Controller state for the worked example.

Calling too many sensors

A robot may configure at most 2 IMUs and 1 RadioBeacon

Wrong:

robot.configure(
    sensors={"S1": IMU, "S2": IMU, "S3": IMU, "S4": IMU, "S5": RadioBeacon, "S6": RadioBeacon},
    effectors={},
)

Right:

robot.configure(
    sensors={"S1": IMU, "S2": IMU, "S6": RadioBeacon},
    effectors={},
)

Pass the port explicitly whenever more than one sensor of a type might be configured:

front = robot.imu("S2").read()
rear = robot.imu("S3").read()

Opening serial from Python

Do not open the Controller serial port in your Python program.

The MARLIN extension owns the serial connection. Your code talks to the extension through the marlin package.

Ignoring configuration errors

If robot.configure() raises an error, stop and fix the configuration.

Configuration errors usually mean your declared ports or types do not match what MARLIN can use, or that you have exceeded a sensor limit (at most 2 IMUs, at most 1 RadioBeacon).

The extension is pointed at the wrong Python

You do not install the marlin package yourself. The extension ships with it and adds it to the Python it is using. So if the extension reports that Python cannot import marlin, it is almost always pointed at a different Python than you expect.

To check, open a terminal in VS Code (Terminal → New Terminal) and run:

python -c "import sys; print(sys.executable)"

On macOS and Linux, use python3 instead of python. On Windows, python3 often opens the Microsoft Store instead of running anything. Use python or py -3.

Compare that path against the interpreter shown in the VS Code status bar, and against the Marlin-controller: Python Path setting. Point the setting at the same interpreter the Python extension has selected.

Controller input can still exist even when the robot link is unhealthy.

Use robot status to choose safe behaviour. continue skips the rest of this pass through the loop:

def driver(robot):
    while robot.running:
        if not robot.link_ok or robot.estopped:
            robot.thruster("M1").set_duty(0)
            continue

        robot.thruster("M1").set_duty(robot.controller.joystick("ONE").value)