UART Connection Issue with ODrive v3.6 (Firmware 0.5.4) on Raspberry Pi 5

Hi everyone,

I’m currently facing an issue when trying to connect my ODrive v3.6 (firmware version 0.5.4) via UART to a Raspberry Pi 5. I’m using the ODrive to control two BLDC hoverboard motors, either in velocity or torque mode, with an RC remote providing PWM signals.

What I’ve tried so far:

Firmware versions:
I initially tried with firmware 0.5.4 but UART communication doesn’t seem to work.
I read on the forum that 0.5.1 supports UART, but when I downgraded it doesn’t worked and I encountered other issues: specifically, the lack of Hall sensor calibration support in 0.5.1, which causes one of the motors to stop intermittently.
UART wiring:
Raspberry Pi TX→ ODrive RX
Raspberry Pi RX→ ODrive TX
Common ground

I’ve double-checked the wiring multiple times, so I’m confident it’s correct.
Baud rate:115200
Serial port /dev/serial0 (also tried /dev/ttyAMA0)

The core issue:

I just want to establish a stable UART connection with the 0.5.4 firmware since this version handles the dual motor control correctly (thanks to Hall sensor calibration). However, I can’t get the Raspberry Pi to recognize the ODrive over UART in this version.

Full code used for testing UART connection:

python

CopierModifier

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import serial
import time
import gpiod
import odrive

# =============================================================================
# 1. Connexion à l'ODrive via UART
# =============================================================================
# Serial connection parameters
SERIAL_PORT = '/dev/serial0'  # or '/dev/ttyAMA0'
BAUD_RATE = 115200

print("Connecting to ODrive via UART...")
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
time.sleep(2)  # Small delay to ensure connection
odrv0 = odrive.find_any("serial:\\dev\\ttyAMA0")

def send_command(cmd):
    """
    Send a command to the ODrive and return the response.
    """
    full_cmd = cmd + "\r\n"
    ser.write(full_cmd.encode())
    time.sleep(0.05)
    response = ser.readline().decode().strip()
    return response

# Activate CLOSED_LOOP_CONTROL for both axes (state = 8)
print("Activating CLOSED_LOOP_CONTROL mode...")
send_command("w axis0.requested_state 8")
send_command("w axis1.requested_state 8")

# =============================================================================
# 2. PWM reading from RC receiver via libgpiod
# =============================================================================
chip = gpiod.Chip("gpiochip0")
line_pwm_steering = chip.get_line(22)
line_pwm_speed = chip.get_line(27)

line_pwm_steering.request(consumer="pwm_steering", type=gpiod.LINE_REQ_EV_BOTH_EDGES)
line_pwm_speed.request(consumer="pwm_speed", type=gpiod.LINE_REQ_EV_BOTH_EDGES)

def measure_pulse_width(line):
    """
    Measure PWM pulse width in microseconds.
    """
    if not line.event_wait(sec=1):
        return None
    evt_rising = line.event_read()
    if evt_rising.type != gpiod.LineEvent.RISING_EDGE:
        return None
    start = evt_rising.sec * 1000000000 + evt_rising.nsec

    if not line.event_wait(sec=1):
        return None
    evt_falling = line.event_read()
    if evt_falling.type != gpiod.LineEvent.FALLING_EDGE:
        return None
    end = evt_falling.sec * 1000000000 + evt_falling.nsec

    pulse_width = (end - start) / 1000.0  # microseconds
    return pulse_width

# =============================================================================
# 3. Main loop: Read PWM and control ODrive via UART
# =============================================================================
MAX_SPEED = 2
DEADZONE_STEERING = 0.02
DEADZONE_SPEED = 0.05

print("Reading PWM values and controlling motors via UART...")
try:
    while True:
        pulse_steering = measure_pulse_width(line_pwm_steering)
        pulse_speed = measure_pulse_width(line_pwm_speed)
       
        if pulse_steering is None or pulse_speed is None:
            continue
        if pulse_steering > 2500:
            continue

        raw_steering = pulse_steering - 1500.0
        raw_speed = pulse_speed - 1500.0

        steering = raw_steering if abs(raw_steering) >= DEADZONE_STEERING * 500 else 0.0
        speed = raw_speed if abs(raw_speed) >= DEADZONE_SPEED * 500 else 0.0

        steering_norm = steering / 500.0
        speed_norm = speed / 500.0

        motor_speed = - (speed_norm * MAX_SPEED)
       
        motor_left = motor_speed + (steering_norm * MAX_SPEED / 4)
        motor_right = motor_speed - (steering_norm * MAX_SPEED / 4)

        motor_left = max(-MAX_SPEED, min(MAX_SPEED, motor_left))
        motor_right = max(-MAX_SPEED, min(MAX_SPEED, motor_right))

        cmd_left = f"w axis0.controller.input_torque {motor_left/2}"
        cmd_right = f"w axis1.controller.input_torque {-motor_right/2}"
        send_command(cmd_left)
        send_command(cmd_right)

        print(f"Motor Velocities: Left = {motor_left:.2f}, Right = {motor_right:.2f}")
        print(f"PWM pulses: Speed = {pulse_speed:.1f} µs, Steering = {pulse_steering:.1f} µs\n")
       
        time.sleep(0.1)

except KeyboardInterrupt:
    print("Stopping motors...")
    send_command("w axis0.controller.input_vel 0")
    send_command("w axis1.controller.input_vel 0")
    print("Exited.")
    ser.close()

What happens:

  • The odrive.find_any() call never detects the ODrive.
  • No responses from the ODrive when sending ASCII commands via ser.write().

My questions:

  1. Is UART supported in firmware 0.5.4? if yes, is there something specific I need to configure on the ODrive before it accepts UART communication?
  2. Are there known workarounds for UART communication issues in 0.5.4?
  3. Should I consider another firmware version that supports both UART and Hall sensor calibration for dual motor control?
  4. Do I need to disable GPIO serial console or enable certain UART parameters on the Pi side for ODrive compatibility?

Hi! At least last I checked, the ODrive library doesn’t support serial connections – e.g. you can’t use odrive.find_any(serial_port). But the rest of your ODrive interface code seems sane, and I think using pyserial makes sense, like you’re doing. iirc it’s /dev/ttyAMA0 for pi, but It’s been a while.

Yes, UART is supported in 0.5.4. No communication issues I’m aware of - check the docs here for instructions to set up UART. UART Interface — ODrive Documentation 0.5.6 documentation

I’m not quite sure what’s needed to do on the Pi side – worth checking their docs. If you set up UART on the ODrive as per the above docs link and it’s still not working, I’d get a USB-Serial adapter or oscilloscope to verify the Pi is sending anything at all.

Note you need to be very careful about ground loops to avoid damaging the ODrive or the Pi – I’d strongly recommend a digital isolator.