DC Motor
Description
DC motor drivers are available on the following module:
OI-Dc (x4)
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
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:
Starting a motor: Use run() with motor number, direction (FORWARD/REVERSE), and duty cycle (0-100%).
Reading current: Use getCurrent() to monitor motor load.
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:
Connect a quadrature encoder to two digital inputs (e.g., DIN_1 and DIN_2).
The encoder is automatically attached during module initialization.
Use
moveTo()to command the motor to a target position.The PID controller continuously adjusts the motor speed to minimize the position error.
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
-
static void run(MotorNum_t motor, MotorDirection_t direction, float dutyCycle)
-
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)
-
static void attachEncoder(MotorNum_t motor, Encoder *encoder)