Xiao ESP32 C3 PWM Servo Control

I have big problems controlling a servo on the Xiao esp32c3.

The ServoESP32-Library from RoboticsBrno I use normaly does not work with this board.

The library ESP32_C3_ISR_Servo also doesn´t work and creates error-messages that the board would be no ESP32-C3-Board.

The ESP32_New_ISR_Servo from khoih-prog initially gaves the error message TIMER_INTR_T1 not defined.

So I deleted all lines with TIMER_INTR_T1 in the file ESP32_New_FastTimerInterrupt.h"

After this change the sketch upload was successful and the servo moves with values range from -30 to +60.

Can someone explain me this strange behaviour of the xiao esp32-board ? Is there a better way to control servos?

Hier is my sketch, simplified for one servo only:

#define TIMER_INTERRUPT_DEBUG 0
#define ISR_SERVO_DEBUG 1

// For ESP32_C3, select ESP32 timer number (0-1) for ESP32 and ESP32_S2, select ESP32 timer number (0-3)
#if defined(ARDUINO_ESP32C3_DEV)
#define USE_ESP32_TIMER_NO 0
#else
#define USE_ESP32_TIMER_NO 1
#endif

// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "ESP32_New_ISR_Servo.h"

// Published values for SG90 servos; adjust if needed
#define MIN_MICROS 544
#define MAX_MICROS 2450
#define NUM_SERVOS 1

typedef struct
{
  int servoIndex;
  uint8_t servoPin;
} ISR_servo_t;

ISR_servo_t ISR_servo[NUM_SERVOS] = {
  { -1, 9 }
};


void setup() {
  Serial.begin(115200);
  while (!Serial);
  delay(200);

  //Select ESP32 timer USE_ESP32_TIMER_NO
  ESP32_ISR_Servos.useTimer(USE_ESP32_TIMER_NO);

  ISR_servo[0].servoIndex = ESP32_ISR_Servos.setupServo(ISR_servo[0].servoPin, MIN_MICROS, MAX_MICROS);
}

void loop() {
  int position;  // position in degrees
  for (position = 0; position <= 360; position += 30) {
    ESP32_ISR_Servos.setPosition(ISR_servo[0].servoIndex, position);
    delay(1000);
  }
  ESP32_ISR_Servos.setPosition(ISR_servo[0].servoIndex, 180);
  delay(1000);
}

Try the ESP32 ESP32S2 AnalogWrite library. Simple to use, simple code. This example should work with the pins on your Xiao esp32c3 ...

1 Like

I am so happy! Excellent and uncomplicated.
This servo control library works perfectly.
I am very grateful to you. I had spent many hours unsuccessful without find a solution.

There seems to be an issue when this code is ran on the Xiao ESP32C3. Does the code need to be modified any further than commenting out the 2nd servo if only 1 is used? I also made the servo sweep back and forth so I added the code to go from 180 to 0 degrees. The ESP32C3 is getting the info but the pin does not send a signal to the servo.

Here is the code that I used:

/*
Additional board manager URL:
https://github.com/Dlloydev/ESP32-ESP32S2-AnalogWrite
*/

#include <pwmWrite.h>

Pwm pwm = Pwm();

const int servoPin4 = 4;
//const int servoPin5 = 5;

void setup() {
    Serial.begin(115200);
}

void loop() {
  int position;  // position in degrees
  for (position = 0; position <= 180; position++) {
    pwm.writeServo(servoPin4, position);
    Serial.println(position);
    // pwm.writeServo(servoPin5, 180-position);
    delay(500);
  }

  for (position = 180; position >= 0; position--) {
    pwm.writeServo(servoPin4, position);
    Serial.println(position); 
    // pwm.writeServo(servoPin5, 180-position);
    delay(500);
  }
}

Make sure you're using the latest version (4.3.3).

Your code on Wokwi with ESP32-C3.

If you need to specify a specific channel or print the status of all channels, take a look at this example.

Now you can easily smooth the motion of your servo(s) with just 2 extra parameters (speed and ke). Example.

In Wokwi, you can specify the version of any library like this (libraries.txt):

# Wokwi Library List
# See https://docs.wokwi.com/guides/libraries
ESP32 ESP32S2 AnalogWrite@4.3.3
1 Like

@dlloyd appreciate the prompt response.

I remade the code based on the new code that you presented. The information looks great on Wokwi. Not sure why but my Xiao ESP32C3 is making the servo jump around a little bit. Have you had this issue with the Xiao ESP32C3? Also, the values that I am getting for "ye" ranges from 0.00 - 1.00, does that look right? And do you have any experience using the wifi to control the servo?

Here is the code that I am using:

/*
  Controls three servos with different easing settings
  https://wokwi.com/projects/360276061783595009
  by dlloydev, March 2023.

  ⚪ The orange servo moves from 0 to 180 deg at 140 deg/s with sigmoid motion.
*/

#include <pwmWrite.h>

const int servoPin = 10;

// units in degrees per second
float speed = 140.0;

// When easing constant (ke) < 1.0, return value is normalized, when 1.0, returns pulse width (μs)
// ke = 0.0 is linear, between 0.0 and 1.0 is tunable sigmoid, 1.0 is normal response
// Normalized Tunable Sigmoid: https://www.desmos.com/calculator/ejkcwglzd1
float ke = 0.6;

// go to position (degrees)
uint8_t pos = 180;

// duration of travel in ms is degrees to move / speed * 1000
float dur = 180.0 / speed * 1000.0;

//uint32_t prevMs1, prevMs2, prevMs3;
uint32_t prevMs;

Pwm pwm = Pwm();

void setup() {
  Serial.begin(115200);
}

void loop() {
  uint32_t ms = millis();
  
  float ye;

  if (ms - prevMs > dur) {
    prevMs = ms;
    pos = (pos == 0) ? 180 : 0;
  }
  
  ye = pwm.writeServo(servoPin, pos, speed, ke);
  
  Serial.println(ye);
  delay(6);
}

I haven't used wifi to control anything yet, but the servo would jump if a new position is written to it before it finished moving to its previous position command. Perhaps this is the issue.

Yes, ye is the normalized y-axis servo easing value that the pwm.writeServo() function returns. Being normalized makes it easy to work with in calculations or to compare with other normalized signals when plotting as everything uses the same scale. Also nice to work with in your code for conditional checks, etc.

Maybe checking "ye" to see if the servo has completed its movement rather than using time (millis() and duration) would be a better approach when using wifi to send the servo settings.

When the servo position value is increased, ye goes from 0.0-1.0 at a rate determined by speed and motion determined by ke. When the servo position value is decreased, ye goes from 1.0-0.0 at a rate determined by speed and motion determined by ke.

Therefore, the servo has completed its travel when (ye <= 0.0 ) or (ye >= 1.0).

Here's your latest code modified to check ye rather than millis timing. The servo position won't update while its still in motion ...

1 Like

@dlloyd I appreciate the explanation.

I am working on a project that would require the servo to start at 0 degrees then at another time rotate to 180 degrees. The plan is to use the servo to rotate a diametric magnet while a second diametric magnet stays in place and in contact with the first. This will allow for the magnetism to be turned "on" when at 0 degrees and turned "off" when rotated 180 degrees. Not sure if easing is the way to go or if just going straight from 0 to 180 and 180 to 0 is the way to go. If so, then I may need to look at one if your previous examples or set the ke value to 1.0. I'm wondering if the latter will work with the example you just showed.

Interesting project!

ke defaults to 1.0 (which is normal servo response), so in this case you can also use just

pwm.writeServo(servoPin, pos);

Note that when ke is 0.0, the servo motion will be linear and time it takes to complete the movement is determined by speed. Say you want the servo to move from 0 to180 degrees in a straight line (linear) for 1.5 seconds ... then you would use:

//pwm.writeServo(servoPin, pos, speed, ke);
pwm.writeServo(servoPin, 180, (180/1.5), 0.0);
1 Like

I will test this out tomorrow. I'm using some code by Rui Santos to control the servo through wifi (ESP32 Servo Motor Web Server with Arduino IDE | Random Nerd Tutorials) but ran into some issues using the servo header file. I get feedback that wifi is working to rotate servo but servo doesn't move. I'm hoping your header file does the trick.

1 Like

No luck getting the servo to move by combining the codes. I ended up having to change the way the code was written based on what you mentioned. I believe the issue is that the arduino code is waiting for a value to be passed from a different source. Then based on that value, the servo would move.

Here is the code that I was using that had both your code and the code from Rui:

if(header.indexOf("GET /?value=")>=0) {
  pos1 = header.indexOf('=');
  pos2 = header.indexOf('&');
  valueString = header.substring(pos1+1, pos2);
  
  //Rotate the servo
  if (valueString.toInt() == 0) pos = 0;
  else if (valueString.toInt() == 180) pos = 180;;

  ye = pwm.writeServo(servoPin, pos, speed, ke);
  Serial.println(ye);
  delay(6);
}        

It's crazy that I actually have a servo (MG90S) and an ESP32-C3 and have never used them! It's time to give some real hardware a test run. I looked through the link you've provided and its well documented and quite interesting, so I'll try a wireless servo test. Just need to find (or make) some spare time ... probably take a few days, but I'll post my results.

EDIT:
All 15 pwm pins that are available on an ESP32-C3 Dev Module (0- 10, 18-21) were tested using an MG90S servo and worked as expected, therefore I don't expect any issues with the Xiao ESP32-C3 board.

1 Like

I did some more testing today and came to a realization that the code that I posted on post #11 actually did work. The problem that I was having was that I was trying to have Python code run and send info to get the servo to move. Python code is not working correctly to have the servo rotate but when I go to the web server and use the slider it works perfectly. So I may need to do some more trials with my Python code to get it to work the way I want. I appreciate you assistance and recommend using the servo and ESP32C3 if you have a need for it.

1 Like

Here's a little servo library I wrote for the ESP32C3 a while back. Let me know if it's any help

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.