DC Motor

Description

DC motor drivers are available on the following module:

Main characteristics

  • Bidirectional control: Supports both forward and reverse rotation.

  • Variable speed control: PWM-based speed regulation with duty cycle from 0% to 100%.

  • Current monitoring: Real-time current measurement for each motor.

  • Fault detection: Hardware fault pin monitoring for driver protection.

  • High current capability: Designed to drive DC motors with appropriate current ratings.

The OI-Dc module uses H-bridge motor drivers to control DC motors. Each motor driver has two control inputs (IN1 and IN2) that are controlled via PWM to regulate motor speed and direction, and a disable pin for enabling/disabling the motor.

Note

The motor drivers operate at a fixed PWM frequency of 5 kHz, which provides a good balance between motor performance and switching losses.

Characteristics

DC motor driver specifications

Parameter

Value

Remark

Number of motors

4

Independent control

Power supply voltage

9-30V DC

Same as module supply

PWM frequency

5 kHz

Fixed

PWM resolution

13 bits

8192 steps

Direction control

Bidirectional

Forward/Reverse

Current monitoring

Yes

Per motor channel

Code examples

The example code below demonstrates how to control a DC motor with the OI-Dc module:

/**
 * @file DcMotorSimple.cpp 
 * @author Georges de Massol
 * @brief Simple example for OI-DC (DC Motor Controller) module
 * 
 * This example demonstrates basic motor control with the OpenIndus DC module:
 * - Run motor forward at full speed
 * - Measure current
 * - Run motor backward at full speed
 * - Measure current
 * - Stop motor
 * - Measure current (should be near 0A)
 * Hardware Wiring Diagram:
 * 
 *     OI-DC Module Pinout & Test Configuration
 *     =========================================
 * 
 *     Power Supply (24V)   
 *          +                                                         +----------------
 *          |                                                         |               |
 *          |                                                         |               |
 *     +----|-------------------------+-------+--------------+--------+----+          |
 *     |    |        |       |        |       |      |       |        |    |          |
 *     |  9V-30V   9V-30V   /B2      /A2      |     /B1     /A1       |    |        _____
 *     |   VIN      VIN     DIN4    HB4_2   HB3_2   DIN2   HB2_2   HB1_2   |       /     \
 *     |                                                                   |      |   M   |
 *     |                                                                   |       \_____/       
 *     |   GND      GND     DIN3    HB4_1   HB3_1   DIN1   HB2_1   HB1_1   |          |
 *     |    |        |       B2       A2      |      B1      A1       |    |          |
 *     |    |        |       |        |       |      |       |        |    |          |
 *     +----+-------------------------+-------+--------------+--------+----+          |
 *          |                                                         |               |
 *          |                                                         |               |
 *          -                                                         +----------------
 *         GND
 * 
 * @date November 2025
 */

#include "OpenIndus.h"
#include "Arduino.h"

Dc dc;  // Create DC motor controller object

static const char TAG[] = "Main";

void setup(void)
{
}

void loop(void)
{
    float current;
    
    // 1. Run motor forward at 100% speed
    dc.run(MOTOR_1, FORWARD, 100);
    current = dc.getCurrent(MOTOR_1);
    ESP_LOGI(TAG, "MOTOR_1 FORWARD: Current = %.3f A", current);
    
    delay(2000);  // Run for 2 seconds
    
    // 2. Run motor backward at 100% speed
    dc.run(MOTOR_1, REVERSE, 100);
    current = dc.getCurrent(MOTOR_1);
    ESP_LOGI(TAG, "MOTOR_1 REVERSE: Current = %.3f A", current);
    
    delay(2000);  // Run for 2 seconds
    
    // 3. Stop motor
    dc.stop(MOTOR_1);
    current = dc.getCurrent(MOTOR_1);
    ESP_LOGI(TAG, "MOTOR_1 STOPPED: Current = %.3f A", current);
    // Nothing to do in loop
    delay(2000);
}

This example demonstrates basic motor control operations:

  1. Starting a motor: Use run() with motor number, direction (FORWARD/REVERSE), and duty cycle (0-100%).

  2. Reading current: Use getCurrent() to monitor motor load.

  3. Stopping a motor: Use stop() to disable the motor driver.

Note

The duty cycle parameter in the run() function represents the percentage of maximum speed. A value of 100% corresponds to full speed, while 0% effectively stops the motor (though using stop() is preferred for this purpose).

PID Position Control

The OI-Dc module supports closed-loop position control using a quadrature encoder and a PID controller. This feature allows you to command a motor to move to an absolute target position (in encoder pulses) and hold it.

How it works:

  1. Connect a quadrature encoder to two digital inputs (e.g., DIN_1 and DIN_2).

  2. The encoder is automatically attached during module initialization.

  3. Use moveTo() to command the motor to a target position.

  4. The PID controller continuously adjusts the motor speed to minimize the position error.

  5. Use stop() to immediately brake the motor and disable position control.

Note

PID parameters (Kp, Ki, Kd, output limits, integral limits) can be tuned at runtime using setPidParams().

Wiring example:

OI-DC Module — PID Position Control
=====================================

Power Supply (24V)
     +
     |
+----|-------------------------+
|  9V-30V                     |
|   VIN        OI-DC          |
|                             |
|   HB1_1 ─── Motor A         |
|   HB1_2 ─── Motor B         |
|                             |
|   DIN_1 ─── Encoder A       |
|   DIN_2 ─── Encoder B       |
|                             |
|   GND                       |
+----|-------------------------+
     |
     -
    GND

The example code below demonstrates closed-loop PID position control:

/**
 * @file DcMotorPidCtrl.cpp
 * @brief PID position control example for OI-DC (DC Motor Controller) module
 * @author Kévin Lefeuvre (kevin.lefeuvre@openindus.com)
 * @copyright (c) [2026] OpenIndus, Inc. All rights reserved.
 * @see https://openindus.com
 */

#include "OpenIndus.h"
#include "Arduino.h"

#define POSITION_TOLERANCE  10.0f // Tolerance in pulses to consider the target reached
#define MOVE_TIMEOUT_MS     5000 // Maximum time to reach a target before giving up (ms)

Dc dc;

/* PID parameters — adjust to your motor/load */
static const pid_ctrl_parameter_f_t pidParams = {
    .kp          = 2.5f,
    .ki          = 0.1f,
    .kd          = 0.05f,
    .max_output  =  100.0f,
    .min_output  = -100.0f,
    .max_integral =  20.0f,
    .min_integral = -20.0f,
    .cal_type    = PID_CAL_TYPE_POSITIONAL,
};

/* Sequence of absolute target positions (encoder pulses) */
static const float targets[] = { 500.0f, -500.0f, 1000.0f, 0.0f };
static const size_t targetCount = sizeof(targets) / sizeof(targets[0]);

void setup(void)
{
    dc.setPidParams(MOTOR_1, &pidParams); // Apply custom PID gains to motor 1 

    printf("PID position control example started\n");
}

void loop(void)
{
    for (size_t i = 0; i < targetCount; i++) {
        float target = targets[i];

        printf("Moving MOTOR_1 to %.0f pulses\n", target);
        dc.moveTo(MOTOR_1, target);

        /* Poll position until target reached or timeout */
        uint32_t startMs = millis();
        while (true) {
            float pos = dc.getPosition(MOTOR_1);
            float error = target - pos;

            printf("Position: %.1f  Error: %.1f\n", pos, error);

            if (fabsf(error) <= POSITION_TOLERANCE) {
                printf("Target %.0f reached (position: %.1f)\n", target, pos);
                break;
            }

            if ((millis() - startMs) >= MOVE_TIMEOUT_MS) {
                printf("Timeout reaching target %.0f (position: %.1f)\n", target, pos);
                dc.stop(MOTOR_1);
                break;
            }

            delay(50);
        }

        delay(1000);  // Hold position for 1 second before next target
    }
}

Software API

class MotorDc : public Motor

DC Motor control class.

Subclassed by MotorDcPidCtrl

Public Static Functions

static void run(MotorNum_t motor, MotorDirection_t direction, float dutyCycle)

Run a DC motor with specified direction and duty cycle.

Parameters:
  • motor – Motor number

  • direction – Motor direction (FORWARD/REVERSE)

  • dutyCycle – Duty cycle percentage (0..100)

static void stop(MotorNum_t motor)

Stop a DC motor.

Parameters:

motor – Motor number

static void brake(MotorNum_t motor)

Brake a DC motor by calling run with speed 0.

Parameters:

motor – Motor number

static float getCurrent(MotorNum_t motor)

Get current consumption of a DC motor.

Parameters:

motor – Motor number

Returns:

float current in A

static uint8_t getFault(MotorNum_t motor)

Get the current fault status from DRV8873 for a specific motor.

Parameters:

motor – Motor number (0-3)

Returns:

uint8_t Fault status register value (0 if no fault)

static esp_err_t clearFault(MotorNum_t motor)

Clear all faults in DRV8873 for a specific motor.

Parameters:

motor – Motor number (0-3)

Returns:

esp_err_t ESP_OK on success, error code otherwise

static esp_err_t setMode(drv8873_mode_t mode, MotorNum_t motor)

Set the control mode of DRV8873 for a specific motor.

Parameters:
  • mode – Control mode to set (PH/EN, PWM, Independent, Disabled)

  • motor – Motor number (0-3)

Returns:

esp_err_t ESP_OK on success, error code otherwise

class MotorDcPidCtrl : public MotorDc

DC Motor control class with closed-loop PID position control.

Public Static Functions

static void attachEncoder(MotorNum_t motor, Encoder *encoder)

Attach an encoder to a motor for position feedback.

Parameters:
  • motor – Motor number

  • encoder – Pointer to an initialized Encoder instance

static void detachEncoder(MotorNum_t motor)

Detach the encoder from a motor and stop position control.

Parameters:

motor – Motor number

static void moveTo(MotorNum_t motor, float position)

Start closed-loop position control toward an absolute target.

Parameters:
  • motor – Motor number

  • position – Target position in encoder pulses (from last encoder reset)

static void stop(MotorNum_t motor)

Stop position control and brake the motor immediately.

Parameters:

motor – Motor number

static float getPosition(MotorNum_t motor)

Get the current encoder position.

Parameters:

motor – Motor number

Returns:

float Current position in encoder pulses

static void setPidParams(MotorNum_t motor, const pid_ctrl_parameter_f_t *params)

Update PID parameters for a motor at runtime.

Parameters:
  • motor – Motor number

  • params – Pointer to the new PID parameter structure

static void homing(HomingType_e type, DinNum_t dinNum, MotorNum_t motor, float dutyCycle, bool invertLogic = false, uint32_t timeoutMs = 30000)

Perform a homing sequence: run the motor until a sensor triggers, then brake and reset the encoder position to zero. The encoder must be attached beforehand via attachEncoder().

Parameters:
  • type – Homing strategy (only SENSOR_STOP supported)

  • dinNum – DIN connected to the homing sensor

  • motor – Motor number to drive during homing

  • dutyCycle – Motor duty cycle during homing (0–100 %)

  • invertLogic – When false (default): HIGH sensor → FORWARD, LOW → REVERSE. When true: HIGH sensor → REVERSE, LOW → FORWARD.

  • timeoutMs – Maximum time allowed for homing before timeout (ms, default 30000)