If not already done so already, install Arduino software and make basic connections, as described in page for MRMS ESP32: Arduino, IMU, eFuse, BT, WiFi, CAN Bus (mrm-esp32).


PWM control is a native method for almost any motor driver. It is implemented by using a (full) H bridge, which accepts PWM signal. Some local logic is needed in order to avoid problems, such as shoot through, when low- and high-side FETs are open. So, the simplest motor controller boards accept PWM signals. Either 2 PWM (PWM/PWM) or a PWM and one digital (PWM/DIR). ML-R offers controllers of the both types. PWM/PWM is a little more flexible but it demands 2 times more PWM pins which may be scarce. In most aspects they are similar. Here we will make a program which uses such a simple PWM/DIR controller.


Starting from basic connection, enhance the setup with ML-R Motor Driver 1x125A (mrm-mot1x125): In the picture there is no motor, but you may want to attach it to the green output connector of the motor board.


The program will drive a motor, first gradually to maximum rotation, then gradually to maximum opposite rotation, and in the end towards stop.

#define GPIO_SLP 32
#define GPIO_PWM 33
#define GPIO_PH 25

#define CHANNEL 0
#define FREQUENCY_HZ 500

#define RESOLUTION_BITS 7 // 1 - 16

void setSpeed(int8_t speed){
    ledcWrite(CHANNEL, abs(speed));
    digitalWrite(GPIO_PH, speed < 0 ? LOW : HIGH);

void setup() {
    ledcAttachPin(GPIO_PWM, CHANNEL);
    pinMode(GPIO_SLP, OUTPUT);
    pinMode(GPIO_PH, OUTPUT);
    digitalWrite(GPIO_SLP, HIGH);

void loop() {  
  int8_t step = 1;
  for (int8_t speed = 1; !(speed == 0 && step == 1); speed += step){
    if (abs(speed) == MAXIMUM_DUTY_CYCLE)
      step = -step;

First section defines GPIO pins as in the picture. GPIO_SLP is a sleep-mode pin, GPIO_PWM a PWM input signal, and GPIO_PH (phase) selects rotation direction. CHANNEL can be any whole number between 0 and 9 and chooses ESP32 PWM channel. Frequency, FREQUENCY_HZ, will be 500 Hz. Original Arduino boards use frequencies from around 500 till 1000 Hz. 500 is more than enough because the motor has a high rotational inertia and it is not possible to change speed more than 500 times per second. RESOLUTION_BITS 7 defines speed range between -127 (=27-1) and 127. It defines MAXIMUM_DUTY_CYCLE as 127. Bit shifting 1 will do the math for us.

Function setSpeed(speed) sets speed speed. speed must be in range -127 to 127. No input values validation is done anywhere in our examples to keep them verbatim. Doing no input parameter checking is a big no-no for a production code. PWM/EN motor drivers usually accept digital 0 or 1 on one pin (selects spin direction) and a variable PWM signal width on the other. setSpeed() implements this behaviour.

setup() sets PWM configuration, according to our selected constant, in the first command. It uses ledcSetup() function, which accepts channel number as first argument. The channels will have to be different as every pin will use its own PWM signal, if we use more than one motor controller. The second instruction uses ledcAttachPin() function to attach PWM signal to selected pin.

2 instructions follow to set digital mode as output and the last one wakes up the controller from sleep mode.

loop() is a standard C code so there are no new things to learn about the hardware here. A loop that sets motor's speed.


Some pins are not available for PWM output. Check this list.