No Output from PWM on ESP32 - Correct usage of it? As not working

Hi, I have used a project from randomtutorials.com on my ESP32CAM module for a web browser controlled robot and I had this working fine with just turning IO pins off / on, but as one motor is a little quicker that the other when it goes forward / back it curves round in an arc. So all the hardware is working ok. I have a H Bridge controller that works fine for digital, but to get it going in a straight line I was going to set one output to 85% or may use it for accelerating / decelerating or use PWM for servos later etc.

But when I changed the code to use PWM there is no movement, so I suspect I have not configured it correctly or was somewhere that you need to de configure a pin before re using. I set 4 PWN channels 2,3,4,5 as I heard the camera uses PWM1? but I guess it shoudnt matter which channels I use.

Also I am turning the digital IOs on / off but to be honest surely all could be left on, and speed is controlled just by setting the PWM value of each channel.

But here are some snippets let me know if its set / used correctly thanks!

Motor and max speed definitions:

#define MOTOR_1_PIN_1    14
#define MOTOR_1_PIN_2    15
#define MOTOR_2_PIN_1    13
#define MOTOR_2_PIN_2    12

int Motor1_PWM_MAX = 255;
int Motor2_PWM_MAX = 255;

And void{setup} is at the end of the program and has this at the start:

  pinMode(MOTOR_1_PIN_1, OUTPUT);
  pinMode(MOTOR_1_PIN_2, OUTPUT);
  pinMode(MOTOR_2_PIN_1, OUTPUT);
  pinMode(MOTOR_2_PIN_2, OUTPUT);
  
  digitalWrite(MOTOR_1_PIN_1, LOW);
  digitalWrite(MOTOR_1_PIN_2, LOW);
  digitalWrite(MOTOR_2_PIN_1, LOW);
  digitalWrite(MOTOR_2_PIN_2, LOW);
//PWM channels = 2,3,4,5
  ledcSetup(MOTOR_1_PIN_1, 5000, 8);
  ledcAttachPin(MOTOR_1_PIN_1, 2);
  ledcSetup(MOTOR_1_PIN_2, 5000, 8);
  ledcAttachPin(MOTOR_1_PIN_2, 3);
  ledcSetup(MOTOR_2_PIN_1, 5000, 8);
  ledcAttachPin(MOTOR_2_PIN_1, 4);  
  ledcSetup(MOTOR_2_PIN_2, 5000, 8);
  ledcAttachPin(MOTOR_2_PIN_2, 5);

And this is an the move "forward" section (goes here is forward button pressed in web page - and of cause there is backwards left right that are all the same with different outputs set on.

  if(!strcmp(variable, "forward")) {
    Serial.println("Forward");
    digitalWrite(MOTOR_1_PIN_1, 1);
    digitalWrite(MOTOR_1_PIN_2, 0);
    digitalWrite(MOTOR_2_PIN_1, 1);
    digitalWrite(MOTOR_2_PIN_2, 0);
    ledcWrite(2, Motor1_PWM_MAX);
    ledcWrite(3, 0);
    ledcWrite(4, Motor2_PWM_MAX);
    ledcWrite(5, 0);
    
    
  }

Happy to post the whole code but that is all related to PWM as far as I know.

Thanks in advance!

Mickpic

What board model are you using? I've never needed to set pwm pins on an ESP32 to control motors. Can you provide the specifications of the ESP32, motor(s) and driver.

Hi, I am using the Ai Tinker ESP32CAM module. The motor driver / H Bridge is a L1119 and they are very small 6v DC motors that came with a tank chassis. I had it working fine when I just turned the IO Pins on, the motors would run in all directions, but I just wanted to try use PWM to them so I could adjust the speed.

So originally it worked fine as on / off only with the digitalWrite's but now with the ledcWrite it doesnt move at all, but connects to the web page with video fine. I was thinking I had missed some setup or another command was needed if something was not set right possibly.

Thanks

????????
Please, post a complete link....

Hi the complete link to the tutorial is below and it works fine. I modified it to use pwm to drive the motors and that is when I am having issues. The tutorial uses digital Io only and I was just trying to get pwm to work. The project works fine as it is both motors work, web interface all work fine Its that I modified it to use pwm and have probably missed or misused something thanks.

So my question is have I used the commands correctly above for pwm to work or have I done something wrong as the hardware works with on off digital IO so once I have pwm working it will drive the motors if the pins output it ok as it does work with digital Io.

Thanks

I cannot find documentation about this component, are you sure you have mentioned the correct model?

you do not need to use this method (ledcWrite), the code I inserted below would work fine if you had a sufficient driver.



  digitalWrite(pin1, LOW);
    digitalWrite(pin2, HIGH);
    digitalWrite(speedpin, 255);

If you are following the tutorial you mentioned in post#5, then why are you not using the driver in that tutorial?

Hi sorry I just checked and it's an L298N and the thing with those is they don't have enable inputs so you have to supply the pwm directly to the 4 inputs so that was why I was doing it like that.

When I used the code from the tutorial it drives around fine so I know the controllers good. It was just I don't think I was using it correctly. Interesting you used Digital write(pin,pwm value) so digital write will assign pwm to a pin if you write a decimal value to it? Every example I have seen used ledcWrite? But if that's not needed that's great.

So if I use it as you have shown with digitalWrite(pin,8 bit pwm value) it will set pwm on that pun and I can remove the ledcWrites/?

Thanks that sounds good.

You do not need to set pwm. ESP32 has pwm If you look at the datasheet for your board. If it was working with the example code then I don't see why you thought ledcWrite would improve it. If one motor was moving faster then the other then it could be for a number of reasons.

I'm not sure what enable inputs has to do with pwm. An enable pin just means that you enable/disable a motor.

Digital write is Digital write, it's not assigning pwm to a pin that already has pwm. Furthermore, you use an integer value between 0 - 255 not a decimal.

Can you upload the code you were using that you said worked?

One issue using the LEDC API to generate PWM on a ESP32CAM is that the ESP32CAM uses one of the LEDC channels.

Hi, the code that works is literally copied and pasted from the above link just with my Wifi SSID and PW set like this for forward:

if(!strcmp(variable, "forward")) {
    Serial.println("Forward");
    digitalWrite(MOTOR_1_PIN_1, 1);
    digitalWrite(MOTOR_1_PIN_2, 0);
    digitalWrite(MOTOR_2_PIN_1, 1);
    digitalWrite(MOTOR_2_PIN_2, 0);

And that worked fine using digitalWrite to turn the appropriate pins off / on. This robot works on a cheap rc tank chassis and I guess one motor is a little more strained that the other so when going in a straight line it curls round so I was going to get around that by driving one pin at say 85% PWM to then make it go straight. If I get more adventurous later I might add acceleration etc but its only a small robot and not like others I have made before on RPi that needed slowing down.

@arduinobotting re "I'm not sure what enable inputs has to do with pwm. An enable pin just means that you enable/disable a motor."

I think other modules for H Bridges have the 4 direction inputs, and also an enable inputs for each motor so I was presuming as your code had two digital writes as in logic level, then the "speedpin" PWM value that maybe people set the direction to the direction inputs and set the PWM against the enable input for each motor to set the PWM independently to the direction logic.

In my case though I only have 4 inputs so I need to send a PWM signal to the inputs in order to control their speed due to the lack of enable inputs rather that just turn them on. I hope that makes sense.

I am quite new to ESP's and Arduino IDE so it seemed from some researching that others used leccWrite for PWM but if that's not needed I could maybe do it like you mentioned. Set all outputs on, and then control them by setting the PWM duty cycle.

So instead of say: (original working code from tutorial)

    digitalWrite(MOTOR_1_PIN_1, 1);
    digitalWrite(MOTOR_1_PIN_2, 0);
    digitalWrite(MOTOR_2_PIN_1, 1);
    digitalWrite(MOTOR_2_PIN_2, 0);

I could instead use:(after turning all outputs on) and this is for forward:

digitalWrite(MOTOR_1_PIN_1, 255);
digitalWrite(MOTOR_1_PIN_2, 0);
digitalWrite(MOTOR_2_PIN_1, 200);  *set below 255 to try match the speed on the slower motor*
digitalWrite(MOTOR_2_PIN_2, 0);

But do you have to set up digitalWrite to set the PWM frequency like is done with ledcsetup()? Eg what frequency would it be at and does it default to 8 bit duty cycle or does that need setting up / configuring?

I will give it a go as above and let you know if it works thanks.

I use some NiMh AA for the motor power to the H Bridge (chassis had AA holders so made sense to use them) and use a lipo on a 5v boost module to power the ESP as otherwise it was freezing / restarting when ever the motors ran so separated the PSUs.

I will also do some more searching on using digitalWrite for PWM and how that's configured eg PWM frequency.....

Also would I be correct in thinking that using digitalWrite with a value is hardware PWM and ledcWrite is software? Good point about the data sheet I haven't even looked at it yet so will find that and have a look.

I have worked mainly with Microchip PIC's in the past and raspberry Pi..... But remember trawling the data sheet solves most errors if you get your head around them so that's a good mention.

Thanks

Please note I did the code as above just sending the PWM value and it actually worked fine. I had both set as 255 as in max, but I think the main issue is that I am using 4 x Amazon Basics NiMh batteries as a power source and as soon as it drives the voltage drops down considerably to 3 / 3,5v so I think I need to swap those u/s batteries for some of my 18650's and just sit them on top and Im sure it will then be fine. I think its just massively underpowered and barely able to run with no load on the tracks (sitting on a box). So thanks for the info when its powered properly I will update if my PWM can straighten it up ok. I have used these L298N's on larger geared motors on a RPi bot and they worked well so suspect bad batteries for the task!

Thanks will update later when the power issue is resolved. Also NiMh seem a nightmare to charge easily too so maybe will use AA li ion but they are a bit low capacity so will probably use 2 x 18650 Samsung F22's I have that can give 5A each....

Thanks!

Do not use that code, the motor pins control direction, it's either HIGH or LOW.

I looked back through my code when I used a L298N a while ago and I see that I used

digitalWrite(dir1, LOW);
digitalWrite(dir2,HIGH); 
analogWrite  (speedpin,mspeed );

speedpin was connected to EN1 on the driver and I used analogWrite as I was using an Arduino Mega but since you are using a ESP32 use digitalWrite for the speed pin.

Hi apologies these motor drivers were from ebay I believe and although they mis use L298N name in the title - a well known driver, they are not on a second look and seem to use a MAX1508 chip, but I have used those and they do work ok. They do now have an enable input hence the speed pin / direction pin is the same. This is what i did for the 4 directions. It drives ok but just doesn't have a lot of power as before.

  if(!strcmp(variable, "forward")) {
    Serial.println("Forward");
    digitalWrite(MOTOR_1_PIN_1, Motor1_PWM_MAX);
    digitalWrite(MOTOR_1_PIN_2, 0);
    digitalWrite(MOTOR_2_PIN_1, Motor2_PWM_MAX);
    digitalWrite(MOTOR_2_PIN_2, 0);
    
  }
  else if(!strcmp(variable, "left")) {
    Serial.println("Left");
    digitalWrite(MOTOR_1_PIN_1, 0);
    digitalWrite(MOTOR_1_PIN_2, Motor1_PWM_MAX);
    digitalWrite(MOTOR_2_PIN_1, Motor2_PWM_MAX);
    digitalWrite(MOTOR_2_PIN_2, 0);

  }

And that works it goes forward back left right but as its can barely move anyway I will upgrade the power to the motors (controller) and re test. I have both PWM max set to 255 at the mo but still barely moves think its the batteries cant give the juice out well.....

I will try attach a pic of the cheapo speed controllers....

image

You did not follow the example I provided, in my example I did NOT try to control the speed using the direction pins!

Besides, it is becoming difficult to try and help you when you don't even know what hardware you have. First you said L1119 , then L298N now you say MAX1508 chip? If you are not aware what components you have I suggest purchasing from a reliable source like amazon and purchase an actual L298N as since you have no idea what component you're using neither do I.

I was on holiday away from home at the weekend, so googled what I had and got the model numbers wrong as I found my image and it was it was an L298N in the title, but that was not what they were so thats my bad. But I had always said my H Bridge did not have any enable inputs so I needed to supply PWM straight to the direction inputs. So no I couldn't use your code directly as it required a H Bridge with enable inputs but you did effectively answer my questions which was only how to correctly use PWM on a ESP32:

digitalWrite(speedpin, 255);

So I used that instead on my direction pins....

But now it does functions correctly , but is just under powered which I think is due to the batteries. Also the chassis was supplied with the thinnest cables ever from the batteries and switch and that was my initial issue, so I had to replace them with thicker wires as the voltage was dropping too much. But now I just need to change the batteries for some 18650's and that will hopefully be all working then.

Sorry for the incorrect parts causing confusion but you did help with advising about how you set the PWM to the enable pins so I just used that to my input pins.

So the programming issue is resolved until I change it again :stuck_out_tongue: and I just need to sort out the power being low. I am going to use 3 x 18650s and a buck converter to 6V which is what the motors are to power the H Bridge and see how that goes.

Thanks for your help. Will update if my power source change fixes it. I will probably be able to power the ESP from the same batteries too and save space. These AA NiMh are a bit lacking in amps so it seems.

Hi Update on this project. It seemed to be working with digitalWrite(8 bit PWM Value) but when i dropped the value down they did not drive the motors at all. So as every single tutorial on using PWM with ESP32 (ESP32CAM AI Tinker Module in my case) seems to use ledcWrite I tried implementing that again but I have had no success no movement of the motors at all.

To be clear this was 100% working when driven by digitalWrite on /off and driving all motors at full speed ok. So its only when I have changed this to use ledcWrite instead that I suspect I have missed something.

So the basic for example in this basic dim an LED tutorial: ESP32 PWM with Arduino IDE (Analog Output) | Random Nerd Tutorials
Seems to be as per the tutorial and others i have looked at:

// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;

void setup(){
  // configure LED PWM functionalitites
  ledcSetup(ledChannel, freq, resolution);
  
  // attach the channel to the GPIO to be controlled
  ledcAttachPin(ledPin, ledChannel);
}
 
void loop(){
  // increase the LED brightness
  for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){   
    // changing the LED brightness with PWM
    ledcWrite(ledChannel, dutyCycle);
    delay(15);
  }

  // decrease the LED brightness
  for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
    // changing the LED brightness with PWM
    ledcWrite(ledChannel, dutyCycle);   
    delay(15);
  }
}

So my code is a slightly modified version of this tutorial I will paste full code at end:
ESP32-CAM Remote Controlled Car Robot Web Server | Random Nerd Tutorials

Except I am using a RC tank chassis with 2 small 6V motors, I have 3 18650s powering the motors at 6V via a buck converter and for now the ESP32 is powered by a boost converter from a lipo but I will be swapping that for a buck to lose the lipo now my buck converter has arrived.

Also the motor driver is a MAX1508 module - no enable inputs, so I drive the 4 input pins with PWM instead of digitalWrite a on / 1 which worked fine. But I get no movement.

I added an LED off on button which are the only ones that work now too.

So here is my code and these are the only parts I have changed:

line 135 to 144 is variable definitions
I added 194 and 195 to add LED on off buttons as the LED on the CAM is on GPIO 4.
and you will see my digitalWrites that were working "//" out from 308 on being replaced by the ledcWrites.

This is just the forward part:

  if(!strcmp(variable, "forward")) {
    Serial.println("Forward");
    // digitalWrite(MOTOR_1_PIN_1, Motor1_PWM_MAX);
    // digitalWrite(MOTOR_1_PIN_2, Motor_PWM_Zero);
    // digitalWrite(MOTOR_2_PIN_1, Motor2_PWM_MAX);
    // digitalWrite(MOTOR_2_PIN_2, Motor_PWM_Zero);
   
    ledcWrite(PWMChannelM1P1,Motor1_PWM_MAX);
    ledcWrite(PWMChannelM1P2,Motor_PWM_Zero);
    ledcWrite(PWMChannelM2P1,Motor1_PWM_MAX);
    ledcWrite(PWMChannelM2P2,Motor_PWM_Zero);
    
  }

Motor1_PWM_MAX / motor 2 are just so I can set a PWM value in code as one motor is a little quicker it goes in a curve when going straight so I just want to set one at 85% or similar when I get it working. So that is all they are for - to straighten the movement up .

then in void(setup) at 416 you can see my "//" out pin modes and digital writes with the new ledcWrites under this.

This is the part I added at the start of void setup nothing else changed:

//  pinMode(MOTOR_1_PIN_1, OUTPUT);
//  pinMode(MOTOR_1_PIN_2, OUTPUT);
//  pinMode(MOTOR_2_PIN_2, OUTPUT);
//  pinMode(MOTOR_2_PIN_1, OUTPUT);
//  pinMode(ledPin, OUTPUT);
  
//  digitalWrite(MOTOR_1_PIN_1, HIGH);
//  digitalWrite(MOTOR_1_PIN_2, HIGH);
//  digitalWrite(MOTOR_2_PIN_1, HIGH);
//  digitalWrite(MOTOR_2_PIN_2, HIGH);
//PWM channels = 2,3,4,5
  ledcAttachPin(MOTOR_1_PIN_1, PWMChannelM1P1);
  ledcAttachPin(MOTOR_1_PIN_2, PWMChannelM1P2);
  ledcAttachPin(MOTOR_2_PIN_1, PWMChannelM2P1);  
  ledcAttachPin(MOTOR_2_PIN_2, PWMChannelM2P2);
  ledcSetup(MOTOR_1_PIN_1, PWMFreq, PWMResolution);
  ledcSetup(MOTOR_1_PIN_2, PWMFreq, PWMResolution);
  ledcSetup(MOTOR_2_PIN_1, PWMFreq, PWMResolution);
  ledcSetup(MOTOR_2_PIN_2, PWMFreq, PWMResolution); 

And the entire code is here:

/*********
  Rui Santos
  Complete instructions at https://RandomNerdTutorials.com/esp32-cam-projects-ebook/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/

#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h"             // disable brownout problems
#include "soc/rtc_cntl_reg.h"    // disable brownout problems
#include "esp_http_server.h"

// Replace with your network credentials
const char* ssid = "My SSID Name yes I changed this";
const char* password = "My SSID PW";

#define PART_BOUNDARY "123456789000000000000987654321"

#define CAMERA_MODEL_AI_THINKER
//#define CAMERA_MODEL_M5STACK_PSRAM
//#define CAMERA_MODEL_M5STACK_WITHOUT_PSRAM
//#define CAMERA_MODEL_M5STACK_PSRAM_B
//#define CAMERA_MODEL_WROVER_KIT

#if defined(CAMERA_MODEL_WROVER_KIT)
  #define PWDN_GPIO_NUM    -1
  #define RESET_GPIO_NUM   -1
  #define XCLK_GPIO_NUM    21
  #define SIOD_GPIO_NUM    26
  #define SIOC_GPIO_NUM    27
  
  #define Y9_GPIO_NUM      35
  #define Y8_GPIO_NUM      34
  #define Y7_GPIO_NUM      39
  #define Y6_GPIO_NUM      36
  #define Y5_GPIO_NUM      19
  #define Y4_GPIO_NUM      18
  #define Y3_GPIO_NUM       5
  #define Y2_GPIO_NUM       4
  #define VSYNC_GPIO_NUM   25
  #define HREF_GPIO_NUM    23
  #define PCLK_GPIO_NUM    22

#elif defined(CAMERA_MODEL_M5STACK_PSRAM)
  #define PWDN_GPIO_NUM     -1
  #define RESET_GPIO_NUM    15
  #define XCLK_GPIO_NUM     27
  #define SIOD_GPIO_NUM     25
  #define SIOC_GPIO_NUM     23
  
  #define Y9_GPIO_NUM       19
  #define Y8_GPIO_NUM       36
  #define Y7_GPIO_NUM       18
  #define Y6_GPIO_NUM       39
  #define Y5_GPIO_NUM        5
  #define Y4_GPIO_NUM       34
  #define Y3_GPIO_NUM       35
  #define Y2_GPIO_NUM       32
  #define VSYNC_GPIO_NUM    22
  #define HREF_GPIO_NUM     26
  #define PCLK_GPIO_NUM     21

#elif defined(CAMERA_MODEL_M5STACK_WITHOUT_PSRAM)
  #define PWDN_GPIO_NUM     -1
  #define RESET_GPIO_NUM    15
  #define XCLK_GPIO_NUM     27
  #define SIOD_GPIO_NUM     25
  #define SIOC_GPIO_NUM     23
  
  #define Y9_GPIO_NUM       19
  #define Y8_GPIO_NUM       36
  #define Y7_GPIO_NUM       18
  #define Y6_GPIO_NUM       39
  #define Y5_GPIO_NUM        5
  #define Y4_GPIO_NUM       34
  #define Y3_GPIO_NUM       35
  #define Y2_GPIO_NUM       17
  #define VSYNC_GPIO_NUM    22
  #define HREF_GPIO_NUM     26
  #define PCLK_GPIO_NUM     21

#elif defined(CAMERA_MODEL_AI_THINKER)
  #define PWDN_GPIO_NUM     32
  #define RESET_GPIO_NUM    -1
  #define XCLK_GPIO_NUM      0
  #define SIOD_GPIO_NUM     26
  #define SIOC_GPIO_NUM     27
  
  #define Y9_GPIO_NUM       35
  #define Y8_GPIO_NUM       34
  #define Y7_GPIO_NUM       39
  #define Y6_GPIO_NUM       36
  #define Y5_GPIO_NUM       21
  #define Y4_GPIO_NUM       19
  #define Y3_GPIO_NUM       18
  #define Y2_GPIO_NUM        5
  #define VSYNC_GPIO_NUM    25
  #define HREF_GPIO_NUM     23
  #define PCLK_GPIO_NUM     22

#elif defined(CAMERA_MODEL_M5STACK_PSRAM_B)
  #define PWDN_GPIO_NUM     -1
  #define RESET_GPIO_NUM    15
  #define XCLK_GPIO_NUM     27
  #define SIOD_GPIO_NUM     22
  #define SIOC_GPIO_NUM     23
  
  #define Y9_GPIO_NUM       19
  #define Y8_GPIO_NUM       36
  #define Y7_GPIO_NUM       18
  #define Y6_GPIO_NUM       39
  #define Y5_GPIO_NUM        5
  #define Y4_GPIO_NUM       34
  #define Y3_GPIO_NUM       35
  #define Y2_GPIO_NUM       32
  #define VSYNC_GPIO_NUM    25
  #define HREF_GPIO_NUM     26
  #define PCLK_GPIO_NUM     21

#else
  #error "Camera model not selected"
#endif

#define MOTOR_1_PIN_1    14
#define MOTOR_1_PIN_2    15
#define MOTOR_2_PIN_1    13
#define MOTOR_2_PIN_2    12

int Motor1_PWM_MAX = 100;
int Motor2_PWM_MAX = 100;
int Motor_PWM_Zero = 0;
const int ledPin = 4;
const int PWMFreq = 5000; /* 5 KHz */
const int PWMChannelM1P1 = 2;
const int PWMChannelM1P2 = 3;
const int PWMChannelM2P1 = 4;
const int PWMChannelM2P2 = 5;
const int PWMResolution = 8;

static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";

httpd_handle_t camera_httpd = NULL;
httpd_handle_t stream_httpd = NULL;

static const char PROGMEM INDEX_HTML[] = R"rawliteral(
<html>
  <head>
    <title>ESP32-CAM Robot</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body { font-family: Arial; text-align: center; margin:0px auto; padding-top: 30px;}
      table { margin-left: auto; margin-right: auto; }
      td { padding: 8 px; }
      .button {
        background-color: #2f4468;
        border: none;
        color: white;
        padding: 10px 20px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 18px;
        margin: 6px 3px;
        cursor: pointer;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
        -khtml-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        -webkit-tap-highlight-color: rgba(0,0,0,0);
      }
      img {  width: auto ;
        max-width: 100% ;
        height: auto ; 
      }
    </style>
  </head>
  <body>
    <h1>ESP32-CAM Robot</h1>
    <img src="" id="photo" >
    <table>
      <tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('forward');" ontouchstart="toggleCheckbox('forward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Forward</button></td></tr>
      <tr><td align="center"><button class="button" onmousedown="toggleCheckbox('left');" ontouchstart="toggleCheckbox('left');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Left</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('stop');" ontouchstart="toggleCheckbox('stop');">Stop</button></td><td align="center"><button class="button" onmousedown="toggleCheckbox('right');" ontouchstart="toggleCheckbox('right');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Right</button></td></tr>
      <tr><td colspan="3" align="center"><button class="button" onmousedown="toggleCheckbox('backward');" ontouchstart="toggleCheckbox('backward');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Backward</button></td></tr>                   
      <tr><td colspan="3" align="left"><button class="button" onmousedown="toggleCheckbox('led-on');" ontouchstart="toggleCheckbox('led-on');">led-on</button></td></tr>
      <tr><td colspan="3" align="left"><button class="button" onmousedown="toggleCheckbox('led-off');" ontouchstart="toggleCheckbox('led-off');">led-off</button></td></tr>
    </table>
   <script>
   function toggleCheckbox(x) {
     var xhr = new XMLHttpRequest();
     xhr.open("GET", "/action?go=" + x, true);
     xhr.send();
   }
   window.onload = document.getElementById("photo").src = window.location.href.slice(0, -1) + ":81/stream";
  </script>
  </body>
</html>
)rawliteral";

static esp_err_t index_handler(httpd_req_t *req){
  httpd_resp_set_type(req, "text/html");
  return httpd_resp_send(req, (const char *)INDEX_HTML, strlen(INDEX_HTML));
}

static esp_err_t stream_handler(httpd_req_t *req){
  camera_fb_t * fb = NULL;
  esp_err_t res = ESP_OK;
  size_t _jpg_buf_len = 0;
  uint8_t * _jpg_buf = NULL;
  char * part_buf[64];

  res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
  if(res != ESP_OK){
    return res;
  }

  while(true){
    fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Camera capture failed");
      res = ESP_FAIL;
    } else {
      if(fb->width > 400){
        if(fb->format != PIXFORMAT_JPEG){
          bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
          esp_camera_fb_return(fb);
          fb = NULL;
          if(!jpeg_converted){
            Serial.println("JPEG compression failed");
            res = ESP_FAIL;
          }
        } else {
          _jpg_buf_len = fb->len;
          _jpg_buf = fb->buf;
        }
      }
    }
    if(res == ESP_OK){
      size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
      res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
    }
    if(res == ESP_OK){
      res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
    }
    if(res == ESP_OK){
      res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
    }
    if(fb){
      esp_camera_fb_return(fb);
      fb = NULL;
      _jpg_buf = NULL;
    } else if(_jpg_buf){
      free(_jpg_buf);
      _jpg_buf = NULL;
    }
    if(res != ESP_OK){
      break;
    }
    //Serial.printf("MJPG: %uB\n",(uint32_t)(_jpg_buf_len));
  }
  return res;
}

static esp_err_t cmd_handler(httpd_req_t *req){
  char*  buf;
  size_t buf_len;
  char variable[32] = {0,};
  
  buf_len = httpd_req_get_url_query_len(req) + 1;
  if (buf_len > 1) {
    buf = (char*)malloc(buf_len);
    if(!buf){
      httpd_resp_send_500(req);
      return ESP_FAIL;
    }
    if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
      if (httpd_query_key_value(buf, "go", variable, sizeof(variable)) == ESP_OK) {
      } else {
        free(buf);
        httpd_resp_send_404(req);
        return ESP_FAIL;
      }
    } else {
      free(buf);
      httpd_resp_send_404(req);
      return ESP_FAIL;
    }
    free(buf);
  } else {
    httpd_resp_send_404(req);
    return ESP_FAIL;
  }

  sensor_t * s = esp_camera_sensor_get();
  int res = 0;
  
  if(!strcmp(variable, "forward")) {
    Serial.println("Forward");
    // digitalWrite(MOTOR_1_PIN_1, Motor1_PWM_MAX);
    // digitalWrite(MOTOR_1_PIN_2, Motor_PWM_Zero);
    // digitalWrite(MOTOR_2_PIN_1, Motor2_PWM_MAX);
    // digitalWrite(MOTOR_2_PIN_2, Motor_PWM_Zero);
   
    ledcWrite(PWMChannelM1P1,Motor1_PWM_MAX);
    ledcWrite(PWMChannelM1P2,Motor_PWM_Zero);
    ledcWrite(PWMChannelM2P1,Motor1_PWM_MAX);
    ledcWrite(PWMChannelM2P2,Motor_PWM_Zero);
    
  }
  else if(!strcmp(variable, "left")) {
    Serial.println("Left");
  //  digitalWrite(MOTOR_1_PIN_1, Motor_PWM_Zero);
  //  digitalWrite(MOTOR_1_PIN_2, Motor1_PWM_MAX);
  //  digitalWrite(MOTOR_2_PIN_1, Motor2_PWM_MAX);
  //  digitalWrite(MOTOR_2_PIN_2, Motor_PWM_Zero);
    ledcWrite(PWMChannelM1P1,Motor_PWM_Zero);
    ledcWrite(PWMChannelM1P2,Motor1_PWM_MAX);
    ledcWrite(PWMChannelM2P1,Motor2_PWM_MAX);
    ledcWrite(PWMChannelM2P2,Motor_PWM_Zero);
  }
  else if(!strcmp(variable, "right")) {
    Serial.println("Right");
  //  digitalWrite(MOTOR_1_PIN_1, Motor1_PWM_MAX);
  //  digitalWrite(MOTOR_1_PIN_2, Motor_PWM_Zero);
  //  digitalWrite(MOTOR_2_PIN_1, Motor_PWM_Zero);
  //  digitalWrite(MOTOR_2_PIN_2, Motor2_PWM_MAX);
    ledcWrite(PWMChannelM1P1,Motor_PWM_Zero);
    ledcWrite(PWMChannelM1P2,Motor1_PWM_MAX);
    ledcWrite(PWMChannelM2P1,Motor2_PWM_MAX);
    ledcWrite(PWMChannelM2P2,Motor_PWM_Zero);
  }
  else if(!strcmp(variable, "backward")) {
    Serial.println("Backward");
  //  digitalWrite(MOTOR_1_PIN_1, Motor_PWM_Zero);
  //  digitalWrite(MOTOR_1_PIN_2, Motor1_PWM_MAX);
  //  digitalWrite(MOTOR_2_PIN_1, Motor_PWM_Zero);
  //  digitalWrite(MOTOR_2_PIN_2, Motor2_PWM_MAX);    
    ledcWrite(PWMChannelM1P1,Motor_PWM_Zero);
    ledcWrite(PWMChannelM1P2,Motor1_PWM_MAX);
    ledcWrite(PWMChannelM2P1,Motor_PWM_Zero);
    ledcWrite(PWMChannelM2P2,Motor2_PWM_MAX);
  }
  else if(!strcmp(variable, "stop")) {
    Serial.println("Stop");
  //  digitalWrite(MOTOR_1_PIN_1, Motor_PWM_Zero);
  //  digitalWrite(MOTOR_1_PIN_2, Motor_PWM_Zero);
  //  digitalWrite(MOTOR_2_PIN_1, Motor_PWM_Zero);
  //  digitalWrite(MOTOR_2_PIN_2, Motor_PWM_Zero);
    ledcWrite(PWMChannelM1P1,Motor_PWM_Zero);
    ledcWrite(PWMChannelM1P2,Motor_PWM_Zero);
    ledcWrite(PWMChannelM2P1,Motor_PWM_Zero);
    ledcWrite(PWMChannelM2P2,Motor_PWM_Zero);
  }
  else if(!strcmp(variable, "led-on")) {
    Serial.println("LED On");
    digitalWrite(ledPin, 1);
  }
  else if(!strcmp(variable, "led-off")) {
    Serial.println("LED Off");
    digitalWrite(ledPin, 0);
  }
  else {
    res = -1;
  }

  if(res){
    return httpd_resp_send_500(req);
  }

  httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  return httpd_resp_send(req, NULL, 0);
}

void startCameraServer(){
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  config.server_port = 80;
  httpd_uri_t index_uri = {
    .uri       = "/",
    .method    = HTTP_GET,
    .handler   = index_handler,
    .user_ctx  = NULL
  };

  httpd_uri_t cmd_uri = {
    .uri       = "/action",
    .method    = HTTP_GET,
    .handler   = cmd_handler,
    .user_ctx  = NULL
  };
  httpd_uri_t stream_uri = {
    .uri       = "/stream",
    .method    = HTTP_GET,
    .handler   = stream_handler,
    .user_ctx  = NULL
  };
  if (httpd_start(&camera_httpd, &config) == ESP_OK) {
    httpd_register_uri_handler(camera_httpd, &index_uri);
    httpd_register_uri_handler(camera_httpd, &cmd_uri);
  }
  config.server_port += 1;
  config.ctrl_port += 1;
  if (httpd_start(&stream_httpd, &config) == ESP_OK) {
    httpd_register_uri_handler(stream_httpd, &stream_uri);
  }
}

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
  
//  pinMode(MOTOR_1_PIN_1, OUTPUT);
//  pinMode(MOTOR_1_PIN_2, OUTPUT);
//  pinMode(MOTOR_2_PIN_2, OUTPUT);
//  pinMode(MOTOR_2_PIN_1, OUTPUT);
//  pinMode(ledPin, OUTPUT);
  
//  digitalWrite(MOTOR_1_PIN_1, HIGH);
//  digitalWrite(MOTOR_1_PIN_2, HIGH);
//  digitalWrite(MOTOR_2_PIN_1, HIGH);
//  digitalWrite(MOTOR_2_PIN_2, HIGH);
//PWM channels = 2,3,4,5
  ledcAttachPin(MOTOR_1_PIN_1, PWMChannelM1P1);
  ledcAttachPin(MOTOR_1_PIN_2, PWMChannelM1P2);
  ledcAttachPin(MOTOR_2_PIN_1, PWMChannelM2P1);  
  ledcAttachPin(MOTOR_2_PIN_2, PWMChannelM2P2);
  ledcSetup(MOTOR_1_PIN_1, PWMFreq, PWMResolution);
  ledcSetup(MOTOR_1_PIN_2, PWMFreq, PWMResolution);
  ledcSetup(MOTOR_2_PIN_1, PWMFreq, PWMResolution);
  ledcSetup(MOTOR_2_PIN_2, PWMFreq, PWMResolution); 




 
  Serial.begin(115200);
  Serial.setDebugOutput(false);
  
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG; 
  
  if(psramFound()){
    config.frame_size = FRAMESIZE_VGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }
  
  // Camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }
  // Wi-Fi connection
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  
  Serial.print("Camera Stream Ready! Go to: http://");
  Serial.println(WiFi.localIP());
  
  // Start streaming web server
  startCameraServer();
}

void loop() {
  
}

I hope that made sense as it seems to be as per the LED dimming tutorials way of using ledcWrite. I didnt see any use or set mode to set up digital outputs in any tutorials about using cWrite as that would stop the PWM working and set it digital from PWM apparently.

Also I saw comments about which order to have the attachpin and csetups I have my attach pin then setup but tried both ways.

If anyone has an idea of why its not outputting PWM that would be great as the hardware is sound as it works when a 1 is written to drive the motors so suspect something else.

And finally would a zero PWM output be detected as a logic 0 on the motor driver input as im not sure it would be earthed if that makes sense. I presume the outputs can only be 6V or earthed so if its below about 2.5v I should have a 0 and an earthed output and the other terminal of the motor would be driven by the PWM signal and move. But that could be it if the code is ok...

Also there are different libraries for ESP32 and firmware updates? I might have an older library or need new firmware maybe but let me know what you think....

Thanks

Hi yes thanks for that I had seen that, so I use channels 2,3,4,5 in my code.

Your using 8 bit pwm so Motor1_PWM_MAX =100 is not max, 255 is. But maybe your limiting your
speed for a reason.

Hi yes I set it to 100 so I could tell if it worked as it would go less than half speed of when it was just fully on - digital io 1 I mean.

I think I will have to try a basic demo ledcWrite program to see if that actually works and try dim an led or get my scope on it to rule out my code. If it doesn't maybe I have an old library or something for the esp not sure...

The other person said you can digitalWrite(255) as in 8 bit pwm value but that didn't work when lowered from 255 (no movement) so I suspect it's setting an analogue voltage. Everything I could find says use ledcWrite though so went with that. But no movement from motors at all like that for some reason.....

Thanks!