ESP32-C3 servo
Sweeps an SG90 end to end with a 50 Hz LEDC channel on GPIO 4: the pulse width is the angle, and the hardware keeps the frame while the core sleeps. The whole build, an ESP32-C3 and 1 more part, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Runs in your browser. Free, and no account needed.
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Servo: an SG90 swept from end to end by the LEDC hardware.
//
// A servo does not want PWM in the dimming sense. It wants a pulse *width*:
// about 1 ms for one end of the travel, 2 ms for the other, repeated fifty
// times a second, and between pulses it holds whatever it was last told.
//
// That is still a PWM waveform, so LEDC can make it, and making it in hardware
// is better than making it by hand: the pulse is exact, the core is asleep
// between frames, and nothing an interrupt does can stretch it.
//
// ledcAttach(pin, 50, 14) fifty frames a second, 16384 counts each
// ledcWrite(pin, duty) duty counts of high out of 16384
//
// A frame is 20000 us, so one count is 20000 / 16384 = 1.22 us and a pulse of
// `us` microseconds is `us * 16384 / 20000` counts. The multiplication is done
// in 32 bits with the division last, so 2000 us is 1638 counts and not 1637.
//
// Wire the servo's brown wire to GND, red to 5V and orange to GPIO 4.
const uint8_t SERVO_PIN = 4;
const uint32_t FRAME_HZ = 50;
const uint8_t FRAME_BITS = 14;
const uint32_t FRAME_US = 1000000UL / FRAME_HZ; // 20000
const uint32_t FRAME_COUNTS = 1UL << FRAME_BITS; // 16384
// An SG90's window, and how far it turns across it.
const uint32_t MIN_US = 1000;
const uint32_t MAX_US = 2000;
const int SWEEP_DEG = 180;
uint32_t countsForMicros(uint32_t us) {
return (us * FRAME_COUNTS) / FRAME_US;
}
void writeAngle(int degrees) {
if (degrees < 0) {
degrees = 0;
}
if (degrees > SWEEP_DEG) {
degrees = SWEEP_DEG;
}
uint32_t us = MIN_US + ((MAX_US - MIN_US) * (uint32_t)degrees) / (uint32_t)SWEEP_DEG;
ledcWrite(SERVO_PIN, countsForMicros(us));
}
void setup() {
Serial.begin(115200);
if (!ledcAttach(SERVO_PIN, FRAME_HZ, FRAME_BITS)) {
Serial.println("no LEDC channel free for the servo");
return;
}
writeAngle(90);
delay(500);
}
void loop() {
for (int angle = 0; angle <= SWEEP_DEG; angle += 5) {
writeAngle(angle);
delay(40);
}
Serial.println("at 180");
for (int angle = SWEEP_DEG; angle >= 0; angle -= 5) {
writeAngle(angle);
delay(40);
}
Serial.println("back at 0");
}
Parts list
3 parts, plus the jumper wires. Every one is in the editor's parts bin.
How it is wired
3 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.
- ESP32-C3-DevKitM-1 pin 4; Hobby servo pin PWM
- ESP32-C3-DevKitM-1 pin 5V; Hobby servo pin VCC
- Ground: ESP32-C3-DevKitM-1 pin GND; Hobby servo pin GND
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.