EncoderMotor Driver Unit

The EncoderMotorDriverUnit class controls Unit EncoderMotor Driver over I2C. It supports signed open-loop PWM, position and speed control, encoder feedback, motor sleep, soft start and stop, RGB LED control, and input power monitoring.

Support the following products:

Unit EncoderMotor Driver

UiFlow2 Example

Basic Motor Control

Open the m5cores3_unit_encoder_motor_driver_basic_example.m5f2 project in UiFlow2.

Select CoreS3 and connect the Unit to Port A (SDA=G2, SCL=G1, I2C 100 kHz). The default Unit address is 0x2F.

The m5ui page provides three touch buttons:

  • + adds 20 percentage points to the signed PWM target. Pressing it while the target is 80 sets the target to 0.

  • - subtracts 20 percentage points from the signed PWM target. Pressing it while the target is -80 sets the target to 0.

  • STOP sets PWM to 0.

Positive and negative PWM select opposite directions. The center label displays the commanded PWM percentage in the range -80~80; it does not display RPM or encoder counts. The target starts at 0.

Note

This example uses the Unit’s default open-loop mode and enabled motor driver. If a previous program selected a closed-loop mode or disabled the driver, restore open-loop mode with set_motor_mode(MODE_OPEN_LOOP) and enable it with enable_motor() before running this example. Restarting the Unit also restores these runtime defaults. Motor power must be present. Press STOP before ending the example.

UiFlow2 Code Block:

m5cores3_unit_encoder_motor_driver_basic_example.png

Example output:

The CoreS3 screen shows the signed PWM target. For example, two + taps from startup display 40; STOP displays 0.

MicroPython Example

Basic Motor Control

Run the following example on CoreS3 with the same connection and Unit runtime settings described above. The +, - and STOP controls have the same behavior as the UiFlow2 project.

MicroPython Code Block:

  1# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
  2#
  3# SPDX-License-Identifier: MIT
  4
  5import os, sys, io
  6import M5
  7from M5 import *
  8import m5ui
  9import lvgl as lv
 10from unit import EncoderMotorDriverUnit
 11from hardware import Pin
 12from hardware import I2C
 13
 14
 15page0 = None
 16label_title = None
 17button_speed_up = None
 18button_speed_down = None
 19label_speed = None
 20button_stop = None
 21i2c0 = None
 22encodermotordriver_0 = None
 23speed = None
 24
 25
 26def button_stop_short_clicked_event(event_struct):
 27    global \
 28        page0, \
 29        label_title, \
 30        button_speed_up, \
 31        button_speed_down, \
 32        label_speed, \
 33        button_stop, \
 34        i2c0, \
 35        encodermotordriver_0, \
 36        speed
 37    speed = 0
 38    encodermotordriver_0.set_motor_pwm_duty(speed)
 39    label_speed.set_text(str(speed))
 40
 41
 42def button_speed_up_short_clicked_event(event_struct):
 43    global \
 44        page0, \
 45        label_title, \
 46        button_speed_up, \
 47        button_speed_down, \
 48        label_speed, \
 49        button_stop, \
 50        i2c0, \
 51        encodermotordriver_0, \
 52        speed
 53    if speed < 80:
 54        speed = speed + 20
 55    else:
 56        speed = 0
 57    encodermotordriver_0.set_motor_pwm_duty(speed)
 58    label_speed.set_text(str(speed))
 59
 60
 61def button_speed_down_short_clicked_event(event_struct):
 62    global \
 63        page0, \
 64        label_title, \
 65        button_speed_up, \
 66        button_speed_down, \
 67        label_speed, \
 68        button_stop, \
 69        i2c0, \
 70        encodermotordriver_0, \
 71        speed
 72    if speed > -80:
 73        speed = speed - 20
 74    else:
 75        speed = 0
 76    encodermotordriver_0.set_motor_pwm_duty(speed)
 77    label_speed.set_text(str(speed))
 78
 79
 80def button_stop_event_handler(event_struct):
 81    global \
 82        page0, \
 83        label_title, \
 84        button_speed_up, \
 85        button_speed_down, \
 86        label_speed, \
 87        button_stop, \
 88        i2c0, \
 89        encodermotordriver_0, \
 90        speed
 91    event = event_struct.code
 92    if event == lv.EVENT.SHORT_CLICKED and True:
 93        button_stop_short_clicked_event(event_struct)
 94    return
 95
 96
 97def button_speed_up_event_handler(event_struct):
 98    global \
 99        page0, \
100        label_title, \
101        button_speed_up, \
102        button_speed_down, \
103        label_speed, \
104        button_stop, \
105        i2c0, \
106        encodermotordriver_0, \
107        speed
108    event = event_struct.code
109    if event == lv.EVENT.SHORT_CLICKED and True:
110        button_speed_up_short_clicked_event(event_struct)
111    return
112
113
114def button_speed_down_event_handler(event_struct):
115    global \
116        page0, \
117        label_title, \
118        button_speed_up, \
119        button_speed_down, \
120        label_speed, \
121        button_stop, \
122        i2c0, \
123        encodermotordriver_0, \
124        speed
125    event = event_struct.code
126    if event == lv.EVENT.SHORT_CLICKED and True:
127        button_speed_down_short_clicked_event(event_struct)
128    return
129
130
131def setup():
132    global \
133        page0, \
134        label_title, \
135        button_speed_up, \
136        button_speed_down, \
137        label_speed, \
138        button_stop, \
139        i2c0, \
140        encodermotordriver_0, \
141        speed
142
143    M5.begin()
144    Widgets.setRotation(0)
145    m5ui.init()
146    page0 = m5ui.M5Page(bg_c=0x000000)
147    label_title = m5ui.M5Label(
148        "Encoder Motor Control",
149        x=20,
150        y=5,
151        text_c=0x079EE1,
152        bg_c=0xFFFFFF,
153        bg_opa=0,
154        font=lv.font_montserrat_24,
155        parent=page0,
156    )
157    button_speed_up = m5ui.M5Button(
158        text="+",
159        x=50,
160        y=165,
161        bg_c=0x2196F3,
162        text_c=0xFFFFFF,
163        font=lv.font_montserrat_24,
164        parent=page0,
165    )
166    button_speed_down = m5ui.M5Button(
167        text="-",
168        x=230,
169        y=165,
170        bg_c=0x2196F3,
171        text_c=0xFFFFFF,
172        font=lv.font_montserrat_24,
173        parent=page0,
174    )
175    label_speed = m5ui.M5Label(
176        "00",
177        x=132,
178        y=75,
179        text_c=0x079EE1,
180        bg_c=0xFFFFFF,
181        bg_opa=0,
182        font=lv.font_montserrat_40,
183        parent=page0,
184    )
185    button_stop = m5ui.M5Button(
186        text="STOP",
187        x=112,
188        y=165,
189        bg_c=0xF32121,
190        text_c=0xFFFFFF,
191        font=lv.font_montserrat_24,
192        parent=page0,
193    )
194
195    button_stop.add_event_cb(button_stop_event_handler, lv.EVENT.ALL, None)
196    button_speed_up.add_event_cb(button_speed_up_event_handler, lv.EVENT.ALL, None)
197    button_speed_down.add_event_cb(button_speed_down_event_handler, lv.EVENT.ALL, None)
198
199    i2c0 = I2C(0, scl=Pin(1), sda=Pin(2), freq=100000)
200    encodermotordriver_0 = EncoderMotorDriverUnit(i2c0, 0x2F)
201    speed = 0
202    encodermotordriver_0.set_motor_pwm_duty(speed)
203    page0.screen_load()
204
205
206def loop():
207    global \
208        page0, \
209        label_title, \
210        button_speed_up, \
211        button_speed_down, \
212        label_speed, \
213        button_stop, \
214        i2c0, \
215        encodermotordriver_0, \
216        speed
217    M5.update()
218
219
220if __name__ == "__main__":
221    try:
222        setup()
223        while True:
224            loop()
225    except (Exception, KeyboardInterrupt) as e:
226        try:
227            m5ui.deinit()
228            from utility import print_error_msg
229
230            print_error_msg(e)
231        except ImportError:
232            print("please update to latest firmware")

Example output:

The center label shows the PWM target. If the Unit is not detected, the screen shows a connection-check prompt.

API

EncoderMotorDriverUnit

class unit.encoder_motor_driver.EncoderMotorDriverUnit(i2c, address=micropython.const)

Bases: object

Control the Unit EncoderMotor Driver over I2C.

The Unit supports signed open-loop PWM, position and speed control, encoder feedback, motor sleep, soft start and stop, RGB LED control, and input power monitoring. The object borrows the supplied I2C bus and does not deinitialize it. If the configured address is not found, check the Unit’s Grove connection, power supply, and I2C address.

Parameters:
  • i2c (I2C | PAHUBUnit) – Initialized I2C bus or compatible PAHUB port.

  • address (int) – 7-bit I2C address in the range 0x08~0x77. Defaults to 0x2F.

UiFlow2 Code Block:

init.png

MicroPython Code Block:

from hardware import I2C, Pin
from unit import EncoderMotorDriverUnit

i2c0 = I2C(0, scl=Pin(1), sda=Pin(2), freq=100000)
encodermotordriver_0 = EncoderMotorDriverUnit(i2c0, 0x2F)

Control mode constants:

  • MODE_OPEN_LOOP = 0: open-loop PWM control.

  • MODE_POSITION = 1: position control.

  • MODE_SPEED = 2: speed control.

Encoder phase constants:

  • ENCODER_MODE_AB = 0: AB phase order.

  • ENCODER_MODE_BA = 1: BA phase order.

set_motor_mode(mode)

Set the motor control mode.

Parameters:

mode (int) – One of MODE_OPEN_LOOP, MODE_POSITION, or MODE_SPEED.

Returns:

None.

UiFlow2 Code Block:

set_motor_mode.png

motor_mode_option.png

get_motor_mode()

Get the current motor control mode.

Returns:

Current mode. Range: 0~2.

Return type:

int

UiFlow2 Code Block:

get_motor_mode.png

set_motor_pwm_duty(percent)

Set the signed open-loop motor PWM duty.

Parameters:

percent (int or float) – Signed duty. Unit: percent. Range: -100~100. The sign selects the direction.

Returns:

None.

UiFlow2 Code Block:

set_motor_pwm_duty.png

get_motor_pwm_duty()

Get the motor PWM value as a signed percentage.

In open-loop mode this is the commanded PWM duty. In a closed-loop mode the firmware may update the register with its live motor output.

Returns:

Signed PWM duty. Unit: percent. Range: -100~100.

Return type:

int

UiFlow2 Code Block:

get_motor_pwm_duty.png

read_motor_speed()

Read the live device-native motor speed sample.

Returns:

Signed native speed sample. Range: -127~127. The firmware updates this value every 20 ms. It is not RPM and has no verified physical-unit conversion.

Return type:

int

UiFlow2 Code Block:

read_motor_speed.png

stop_motor()

Stop the motor and leave it in open-loop mode.

The method clears PWM before and after changing the mode so a previous closed-loop output cannot remain active after the mode transition.

Returns:

None.

UiFlow2 Code Block:

stop_motor.png

read_encoder_count()

Read the current encoder count without clearing it.

Returns:

Signed encoder count. Range: -2147483648~2147483647.

Return type:

int

UiFlow2 Code Block:

read_encoder_count.png

set_encoder_count(count)

Set the current encoder count.

Parameters:

count (int) – Signed encoder count. Range: -2147483648~2147483647.

Returns:

None.

UiFlow2 Code Block:

set_encoder_count.png

clear_encoder_count()

Clear the encoder count to zero.

Returns:

None.

UiFlow2 Code Block:

clear_encoder_count.png

set_encoder_mode(mode)

Set the encoder AB or BA phase order.

A changed value is retained in the Unit’s flash and immediately restarts the Unit. The call waits for the current I2C address to become available again. Avoid unnecessary calls to limit flash writes.

Parameters:

mode (int) – ENCODER_MODE_AB or ENCODER_MODE_BA.

Returns:

None.

UiFlow2 Code Block:

set_encoder_mode.png

encoder_mode_option.png

get_encoder_mode()

Get the encoder phase order.

Returns:

ENCODER_MODE_AB or ENCODER_MODE_BA.

Return type:

int

UiFlow2 Code Block:

get_encoder_mode.png

set_position_pid(kp, ki, kd)

Set the position-loop PID coefficients.

Parameters:
  • kp (int) – Native proportional coefficient. Range: 0~255.

  • ki (int) – Native integral coefficient. Range: 0~255.

  • kd (int) – Native derivative coefficient. Range: 0~255.

Returns:

None.

UiFlow2 Code Block:

set_position_pid.png

get_position_pid()

Get the position-loop PID coefficients.

Returns:

(kp, ki, kd). Each native coefficient is 0~255.

Return type:

tuple

UiFlow2 Code Block:

get_position_pid.png

set_position_target_count(count)

Set the position-loop target in encoder counts.

Parameters:

count (int) – Signed target. Unit: encoder counts. Range: -2147483648~2147483647.

Returns:

None.

UiFlow2 Code Block:

set_position_target_count.png

get_position_target_count()

Get the configured position-loop target in encoder counts.

Returns:

Signed target. Unit: encoder counts. Range: -2147483648~2147483647.

Return type:

int

UiFlow2 Code Block:

get_position_target_count.png

set_position_speed_limit(speed_limit)

Set the native position-loop maximum speed.

The position controller uses the absolute value of this speed limit. The value is a device-native setting without a documented RPM conversion.

Parameters:

speed_limit (int) – Signed device-native speed limit. Range: -127~127.

Returns:

None.

UiFlow2 Code Block:

set_position_speed_limit.png

get_position_speed_limit()

Get the native position-loop maximum speed.

Returns:

Signed, dimensionless device value. Range: -127~127. This value is not RPM.

Return type:

int

UiFlow2 Code Block:

get_position_speed_limit.png

set_speed_pid(kp, ki, kd)

Set the speed-loop PID coefficients.

Parameters:
  • kp (int) – Native proportional coefficient. Range: 0~255.

  • ki (int) – Native integral coefficient. Range: 0~255.

  • kd (int) – Native derivative coefficient. Range: 0~255.

Returns:

None.

UiFlow2 Code Block:

set_speed_pid.png

get_speed_pid()

Get the speed-loop PID coefficients.

Returns:

(kp, ki, kd). Each native coefficient is 0~255.

Return type:

tuple

UiFlow2 Code Block:

get_speed_pid.png

set_speed_target(speed)

Set the native closed-loop speed target.

Parameters:

speed (int) – Signed, dimensionless target. Range: -127~127. The value is not RPM.

Returns:

None.

UiFlow2 Code Block:

set_speed_target.png

get_speed_target()

Get the configured closed-loop speed target.

Returns:

Signed, dimensionless target. Range: -127~127. This value is not RPM.

Return type:

int

UiFlow2 Code Block:

get_speed_target.png

enable_soft_start()

Enable soft start and stop in open-loop mode.

The setting is retained in the Unit’s flash. Repeating the call while already enabled avoids another write.

Returns:

None.

UiFlow2 Code Block:

enable_soft_start.png

disable_soft_start()

Disable soft start and stop in open-loop mode.

The setting is retained in the Unit’s flash. Repeating the call while already disabled avoids another write.

Returns:

None.

UiFlow2 Code Block:

disable_soft_start.png

is_soft_start_enabled()

Return whether soft start and stop is enabled.

Returns:

True when soft start and stop is enabled.

Return type:

bool

UiFlow2 Code Block:

is_soft_start_enabled.png

enable_motor()

Set Motor nSleep to run and enable the motor driver.

Returns:

None.

UiFlow2 Code Block:

enable_motor.png

disable_motor()

Set Motor nSleep to sleep and disable the motor driver.

Returns:

None.

UiFlow2 Code Block:

disable_motor.png

is_motor_enabled()

Return whether Motor nSleep is in the run state.

Returns:

True when the motor driver is enabled.

Return type:

bool

UiFlow2 Code Block:

is_motor_enabled.png

set_rgb_color(color)

Set the onboard RGB LED color.

Parameters:

color (int) – RGB888 color in 0xRRGGBB format. Range: 0x000000~0xFFFFFF.

Returns:

None.

UiFlow2 Code Block:

set_rgb_color.png

get_rgb_color()

Get the onboard RGB LED color.

Returns:

RGB888 color in 0xRRGGBB format. Range: 0x000000~0xFFFFFF.

Return type:

int

UiFlow2 Code Block:

get_rgb_color.png

set_rgb_brightness(percent)

Set the onboard RGB LED brightness.

Parameters:

percent (int or float) – Brightness. Unit: percent. Range: 0~100.

Returns:

None.

UiFlow2 Code Block:

set_rgb_brightness.png

get_rgb_brightness()

Get the onboard RGB LED brightness.

Returns:

Brightness. Unit: percent. Range: 0~100.

Return type:

int

UiFlow2 Code Block:

get_rgb_brightness.png

read_input_voltage()

Read the motor-supply input voltage.

Returns:

Input voltage. Unit: V. Range depends on the supply.

Return type:

float

UiFlow2 Code Block:

read_input_voltage.png

read_input_voltage_adc()

Read the raw 12-bit motor-supply voltage ADC sample.

Returns:

Raw ADC sample. Range: 0~4095. Use read_input_voltage() for volts.

Return type:

int

UiFlow2 Code Block:

read_input_voltage_adc.png

read_input_current()

Read the motor-supply input current.

Returns:

Input current. Unit: A. Range depends on the load.

Return type:

float

UiFlow2 Code Block:

read_input_current.png

get_firmware_version()

Get the Unit firmware version register value.

Returns:

Firmware version value. Range: 0~255.

Return type:

int

UiFlow2 Code Block:

get_firmware_version.png

get_uid()

Get the device’s 12-byte STM32 unique identifier.

Returns:

Device UID with exactly 12 bytes in protocol order.

Return type:

bytes

UiFlow2 Code Block:

get_uid.png

get_i2c_address()

Get the Unit I2C address from the device.

Returns:

7-bit I2C address. Range: 0x08~0x77.

Return type:

int

UiFlow2 Code Block:

get_i2c_address.png

set_i2c_address(address)

Set the Unit I2C address and switch this object to it.

A changed address is retained in the Unit’s flash. The protocol states that the Unit restarts and requires about 300 ms before responding at the new address. This method waits and polls before updating the object’s address. Avoid unnecessary calls to limit flash writes.

Parameters:

address (int) – New 7-bit I2C address. Range: 0x08~0x77.

Returns:

None.

UiFlow2 Code Block:

set_i2c_address.png