ESP32 with a L298N with PWM Control not Functioning Properly

Hey all,

Looking for some help with an issue I am running into. I want to note that I have found a couple of workarounds to my issue, but I would really like to get this PWM motor control working properly without using them.

I am currently working on an IoT Cloud project that requires a PWM controlled Servo and a PWM controlled motor which I am controlling with an ESP32. I have a L298N that I am trying to use, which is connected to a 12V power source. I would like to be able to drive the connected motor at the full 12V.

I have successfully done this with esp32 Espressif Systems version 3.3.0 by using ledcAttachChannel(), but I would like to use the Arduino IoT Cloud editor to maintain and deploy my code. It is stuck on esp32 Espressif Systems version 2.0.17 with no timetable for an update.

With the requirement of using 2.0.17 in mind, I also need to be able to manually set the channel that the motor controller uses to channel 1. This is due to my servo always getting automatically assigned to channel 0 for some reason (might have to do with the servo library I am using) and then the motor controller also getting assigned the same channel. This leads me to have to use ledcSetup() and ledcAttachPin() to be able to manually set the channel used, I believe.

Implementing this as seen below though, for some reason the max output voltage I get coming out of the motor controller with no load is ~5V. I have played around with the frequency variable, but it seems I can't get any different output.

I did do a continuity test between the GND pin on the L298N and the GND pin on the ESP32 and it did pass. The fact that I have seen 12V in version 3.3.0 makes me believe that it isn't a wiring issue, but an issue in my code somewhere. Any help would be greatly appreciated!

#include <Wire.h> // For IC2 Communication
#include <ESP32Servo.h> // ESP32Servo - Version: Latest

const int elevatorMotorPWM = 16;
const int elevatorMotorChannel = 1;
const int elevatorMotorFrequency = 5000;
const int elevatorMotorResolution = 8;
const int elevatorMotorPin1 = 4;
const int elevatorMotorPin2 = 2;

Servo servo;
const int servoPin = 17;

/// @brief Function called once on boot of program
void setup() {
  pinMode(elevatorMotorPin1, OUTPUT);
  pinMode(elevatorMotorPin2, OUTPUT);

  // Works in 3.3.0
  // ledcAttachChannel(elevatorMotorPWM, elevatorMotorFrequency, elevatorMotorResolution, elevatorMotorChannel);
  // ledcWrite(elevatorMotorPWM, 0);

  ledcSetup(elevatorMotorChannel, elevatorMotorFrequency, elevatorMotorResolution);
  ledcAttachPin(elevatorMotorPWM, elevatorMotorChannel);
  ledcWrite(elevatorMotorChannel, 0);
  digitalWrite(elevatorMotorPin1, HIGH);
  digitalWrite(elevatorMotorPin2, HIGH);

  servo.attach(servoPin);
  servoSetPosition(0.0);

  // Maxes out at 5V, not the expected and desired 12V
  motorSetSpeed(elevatorMotorChannel, elevatorMotorPin1, elevatorMotorPin2, 1.0);
}

void loop() {
  // put your main code here, to run repeatedly:
}

/// @brief Function that sets a motor's speed and direction
/// @param channel The PWM channel that the motor controller is using
/// @param pin1 One of the pins that the motor controller is plugged into
/// @param pin2 One of the pins that the motor controller is plugged into
/// @param percentage A value between -1.0 and 1.0 that represents the percentage and direction for the motor to run 
void motorSetSpeed(int channel, int pin1, int pin2, double percentage) {
  if (percentage < -1.0) {
    percentage = -1.0;
  } else if (percentage > 1.0) {
    percentage = 1.0;
  }

  int value = round(abs(255 * percentage));
  if (percentage > 0.0) {
    ledcWrite(channel, value);
    digitalWrite(pin1, HIGH);
    digitalWrite(pin2, LOW);
  } else if (percentage < 0.0) {
    ledcWrite(channel, value);
    digitalWrite(pin1, LOW);
    digitalWrite(pin2, HIGH);
  } else {
    ledcWrite(channel, value);
    digitalWrite(pin1, HIGH);
    digitalWrite(pin2, HIGH);
  }
}

/// @brief Function that sets the servo's position given a percent of rotation
/// @param percent A value between 0.0 and 1.0 with 0.0 being stored position and 1.0 being pouring position
void servoSetPosition(double percent) {
  if (percent < 0.0) {
    percent = 0.0;
  } else if (percent > 1.0) {
    percent = 1.0;
  }

  // Map the values 0.0-1.0 to 5-105
  int value = 100 * percent + 5;
  servo.write(value);
}

Measuring a PWM signal using a DMM would give that result. Do the measurement using an oscilloscope.

Using a DMM I measured out1 on the motor controller to be 5V with the 2.0.17 library and the code above, not the PWM signal. With the 3.3.0 library and modified code I was getting 12V from out1. I don’t have an oscilloscope, but maybe this is the excuse to buy one.

Does your DMM have Hz and Duty% measurement capability?

Tom.... :smiley: :+1: :coffee: :australia:

It looks like it does. I will get some measurements a bit later today and update this thread.

I think you were lucky it worked with the other processor. That board is 5V logic and it appears you are using 3V3.

Here is my reference: L298N Motor Driver Module Pinout, Datasheet, Features & Specs

Not sure if that is completely true though. I found this data sheet which has the following on page 3:

L298.pdf (242.8 KB)

Edit: Actually looking at this, maybe these are the wrong values. I’ll have to dig through this data sheet more.

Hey all, just want to update everyone on this issue. While measuring the Hz and Duty% I figured out the root cause of my issue.

The code below is incorrect:

    ledcWrite(channel, value);
    digitalWrite(pin1, LOW);
    digitalWrite(pin2, HIGH);

Here is the corrected code. Order seems to matter. Make sure to set the PWR and GND pins first, then the speed control pin:

    digitalWrite(pin1, LOW);
    digitalWrite(pin2, HIGH);
    ledcWrite(channel, value);

Definitely lost too much sleep over this, but thanks to everyone for their help!