The Basicmicro Python library reads, sets, and resets RoboClaw encoder counts from a Raspberry Pi or any computer: ReadEncM1 and ReadEncM2 return the current count, SetEncM1 and SetEncM2 write a value, ResetEncoders zeroes both channels, and the SpeedAccelDeccelPosition commands move a motor to a target position. Install it with pip install basicmicro.
The RoboClaw normally uses encoders in an automatic closed loop control scheme to maintain speed and position. However, there are situations where more custom control from your own code is called for. This application note covers using the Basicmicro Python library, formerly the RoboClaw Python library, to read, set, and reset encoders and to send position commands. The examples run on a Raspberry Pi 3, Raspberry Pi 4, Raspberry Pi 5, or Raspberry Pi Zero, as well as any other Linux, Windows, or macOS computer with Python 3.6 or newer, and work with any RoboClaw motor controller and any motor with a quadrature encoder. The library’s source code and additional examples are in the basicmicro_python repository on GitHub.
What You Need
- (1) RoboClaw motor controller
- (1) Raspberry Pi 3, 4, 5, or Zero
- (1) Pololu gear motor with quadrature encoder (any motor with a quadrature encoder can be used)
- (1) Micro USB cable for a USB connection to the RoboClaw
- (3) Female to female 0.1″ jumper cables for a GPIO serial connection instead of USB
- (1) Computer with BasicMicro Motion Studio installed
- (1) Small screwdriver
How Do You Install the Library with pip?
The library is published on PyPI under the name basicmicro and requires Python 3.6 or newer, so it installs the same way on every Raspberry Pi model and on desktop computers.
- Open a terminal on the Raspberry Pi and install the library with pip:
pip install basicmicro - On current versions of Raspberry Pi OS, pip refuses to install into the system Python and reports an “externally-managed-environment” error. If that happens, create and activate a virtual environment first, then install the library inside it:
python3 -m venv ~/basicmicro-env source ~/basicmicro-env/bin/activate pip install basicmicroActivate the environment again with the
sourcecommand in any new terminal session before running your scripts. - Add your user to the dialout group so Python can open the serial port, then log out and back in for the change to take effect:
sudo usermod -a -G dialout $USER
How Do You Set Up the Hardware?
Connect the motor to the RoboClaw’s M1 channel and hook up main power as shown in the Dual Channel RoboClaw Quick Start Guide, and wire the motor’s encoder to the RoboClaw’s EN1 header as shown in Pololu Encoder Wiring.
There are two ways to connect the Raspberry Pi to the RoboClaw. The simplest is USB: plug the micro USB cable into the RoboClaw and into one of the Pi’s USB ports, and the RoboClaw appears as the serial port /dev/ttyACM0. This works on every Pi model; the Raspberry Pi Zero needs a micro USB OTG adapter for its USB data port. The alternative is the Pi’s GPIO serial port: connect the Pi’s TXD pin (GPIO 14) to the RoboClaw’s S1 signal pin, the Pi’s RXD pin (GPIO 15) to the S2 signal pin, and a ground pin on the Pi to a ground pin on the RoboClaw. The GPIO serial port appears as /dev/serial0, and each Pi model needs its serial port enabled and configured first: see Configuring the Raspberry Pi 3 Serial Port, Configuring the Raspberry Pi 4 Serial Port, Configuring the Raspberry Pi 5 Serial Port, or Configuring the Raspberry Pi Zero Serial Port for your model.
The functions in this article communicate with the RoboClaw using packet serial. Before running your Python code, connect the RoboClaw to Motion Studio and set it to Packet Serial mode, and note the address and baud rate configured there. The values used in your script must match the controller’s settings.
How Do You Connect to the RoboClaw in Python?
All of the functions in this article are called on a Basicmicro object. Create the object with the serial port name and baud rate, then call Open() to start communication:
# Create the controller object and open the serial port
from basicmicro import Basicmicro
controller = Basicmicro("/dev/ttyACM0", 38400)
controller.Open()
Use /dev/ttyACM0 for a USB connection or /dev/serial0 for the GPIO serial port. The library also works as a context manager, which opens the port automatically and closes it when the block ends. The complete example at the end of this article uses this form:
# The port is opened on entry and closed automatically on exit
with Basicmicro("/dev/ttyACM0", 38400) as controller:
result = controller.ReadEncM1(0x80)
Encoder Functions in the Python Library
Below are the encoder functions available in the Python library. Every function takes the controller’s address as its first parameter. The read functions return a tuple whose first element reports whether the read succeeded, so check it before using the values that follow.
Reading Encoders
There are two functions used to read the current encoder values, one for each channel. Each returns a tuple of (success, count, status): success is True when the read succeeded, count is the current encoder count, and status holds the encoder’s direction and underflow and overflow flags.
ReadEncM1(address)
ReadEncM2(address)
In this example ReadEncM1 is called to read the current encoder count of channel 1, and the count is printed when the read succeeds:
# Read the current value of the encoder on channel 1
result = controller.ReadEncM1(0x80)
if result[0]:
print(f"Encoder 1 count: {result[1]}")
Reading Speed
These two functions read the speed of each motor in encoder counts per second. Each returns a tuple of (success, speed, status), where status holds the direction of motion.
ReadSpeedM1(address)
ReadSpeedM2(address)
In this example ReadSpeedM1 is called to read the speed of motor 1 in encoder counts per second:
# Read the speed of motor 1 in encoder counts per second
result = controller.ReadSpeedM1(0x80)
if result[0]:
print(f"Motor 1 speed: {result[1]} counts per second")
Reading Both Channels at Once
GetEncoders reads both encoder counts in a single command and returns (success, enc1, enc2). GetISpeeds does the same for the instantaneous speeds of both motors. When a script needs full telemetry, GetStatus returns the controller’s entire status in one command: both encoder counts, both speeds, temperatures, battery voltages, PWM values, motor currents, and speed and position error values. The same values are described in Using Encoders with the Arduino Library.
GetEncoders(address)
GetISpeeds(address)
GetStatus(address)
In this example both encoder counts are read with one command, and the channel 1 count and channel 2 count are printed:
# Read both encoder counts in one command
result = controller.GetEncoders(0x80)
if result[0]:
print(f"Encoder 1: {result[1]}, Encoder 2: {result[2]}")
Setting Encoders
These functions set their respective channel’s encoder count to a given value and return True when the command succeeds. The address of the RoboClaw and the cnt value to be set must be passed to them.
SetEncM1(address, cnt)
SetEncM2(address, cnt)
In this example the count of the channel 1 encoder is set to 10,000 counts:
# Set the channel 1 encoder count to 10000
controller.SetEncM1(0x80, 10000)
Resetting Encoders
Calling this function sets both encoder channels to zero. It returns True when the RoboClaw acknowledges the command.
ResetEncoders(address)
In this example the encoder counts for both channels are set to zero:
# Reset both encoder channels to zero
controller.ResetEncoders(0x80)
Position Commands
These functions move the motor on a given channel to a specific position in encoder counts. Accel and deccel set the acceleration and deceleration of the motor, speed sets the speed in encoder counts per second, and position is the target in encoder counts. Buffer controls whether the command is executed immediately or stored until any previous commands are finished; a value of 0 stores the command and a value of 1 executes it immediately. The M1M2 version positions both motors independently at the same time. Position PID parameters must be set and tuned, in Motion Studio or with SetM1PositionPID and SetM2PositionPID, before these commands will work correctly.
SpeedAccelDeccelPositionM1(address, accel, speed, deccel, position, buffer)
SpeedAccelDeccelPositionM2(address, accel, speed, deccel, position, buffer)
SpeedAccelDeccelPositionM1M2(address, accel1, speed1, deccel1, position1, accel2, speed2, deccel2, position2, buffer)
In this example motor 1 is moved to a position of 15,000 counts with acceleration and deceleration values of 10,000, a speed of 2,000 counts per second, and the buffer set to 1 so the command executes immediately:
# Move motor 1 to position 15000
controller.SpeedAccelDeccelPositionM1(0x80, 10000, 2000, 10000, 15000, 1)
Complete Example
The script below demonstrates each function from this article in order. It reads and prints the channel 1 encoder count, sets the count to 10,000 and reads it back, runs motor 1 and prints its speed, resets both encoders, and finally moves motor 1 to a position of 15,000 counts. Save it to a file, edit the port, baud rate, and address values at the top to match your setup, and run it with python3 roboclaw_encoders.py. More example scripts are in the library’s examples folder on GitHub.
#!/usr/bin/env python3
# Demonstrates the encoder functions of the Basicmicro Python library
# on a RoboClaw motor controller.
import time
from basicmicro import Basicmicro
# Serial port the RoboClaw is connected to
# USB: /dev/ttyACM0 on the Raspberry Pi
# GPIO serial port: /dev/serial0 on the Raspberry Pi
PORT = "/dev/ttyACM0"
# Baud rate and address as set in Motion Studio
BAUD = 38400
ADDRESS = 0x80
def main():
with Basicmicro(PORT, BAUD) as controller:
# Read and print the current encoder count for channel 1
result = controller.ReadEncM1(ADDRESS)
if result[0]:
print(f"Encoder 1 count: {result[1]}")
# Set the channel 1 encoder to 10000 counts and read it back
controller.SetEncM1(ADDRESS, 10000)
result = controller.ReadEncM1(ADDRESS)
if result[0]:
print(f"Encoder 1 count after SetEncM1: {result[1]}")
# Run motor 1 at about 50% duty cycle and read its speed
controller.DutyM1(ADDRESS, 16384)
time.sleep(2)
result = controller.ReadSpeedM1(ADDRESS)
if result[0]:
print(f"Motor 1 speed (counts per second): {result[1]}")
# Stop motor 1
controller.DutyM1(ADDRESS, 0)
# Reset both encoders to zero and read channel 1 back
controller.ResetEncoders(ADDRESS)
result = controller.ReadEncM1(ADDRESS)
if result[0]:
print(f"Encoder 1 count after reset: {result[1]}")
# Move motor 1 to position 15000
# Position PID parameters must be set and tuned first
controller.SpeedAccelDeccelPositionM1(ADDRESS, 10000, 2000, 10000, 15000, 1)
if __name__ == "__main__":
main()
Troubleshooting
If Python reports a permission error when opening the serial port, make sure your user is in the dialout group and that you logged out and back in after adding it. If the script reports that the basicmicro module is not found, the virtual environment it was installed in is not active; run the source command from the install steps first. If there is no response from the RoboClaw, check that the port name at the top of the script matches your connection, and confirm the address and baud rate in the script match the values set in Motion Studio. For a GPIO serial connection, also ensure there is a ground connection between the Pi and the RoboClaw, check that the TXD and RXD connections are not reversed (the Pi’s TXD pin attaches to the RoboClaw’s S1 signal pin and the Pi’s RXD pin to the S2 signal pin), and confirm the Pi’s serial port is enabled as described in the configuration article for your model.
Next Steps
For the general pattern of using the library and a reference of its most commonly used functions, see Using the RoboClaw Python Library. The position commands depend on a tuned position PID; Auto Tuning with Motion Studio covers setting those values automatically. To use the same encoder functions from an Arduino, see Using Encoders with the Arduino Library.



