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:
- 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?
- Are there known workarounds for UART communication issues in 0.5.4?
- Should I consider another firmware version that supports both UART and Hall sensor calibration for dual motor control?
- Do I need to disable GPIO serial console or enable certain UART parameters on the Pi side for ODrive compatibility?