Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/compile-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ jobs:
platforms: |
- name: arduino:mbed_nano
artifact-name-suffix: arduino-mbed_nano-nanorp2040connect
- fqbn: arduino:esp32:nano_nora
platforms: |
- name: arduino:esp32
artifact-name-suffix: arduino-esp32-nano_nora

steps:
- name: Checkout repository
Expand Down
4 changes: 3 additions & 1 deletion src/Servo.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@
#include "renesas/ServoTimers.h"
#elif defined(ARDUINO_ARCH_XMC)
#include "xmc/ServoTimers.h"
#elif defined(ARDUINO_ARCH_ESP32)
#include "esp32/ServoTimers.h"
#else
#error "This library only supports boards with an AVR, SAM, SAMD, NRF52, STM32F4, Renesas or XMC processor."
#error "This library only supports boards with an AVR, SAM, SAMD, NRF52, STM32F4, Renesas, XMC or ESP32 processor."
#endif

#define Servo_VERSION 2 // software version of this library
Expand Down
146 changes: 146 additions & 0 deletions src/esp32/Servo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#if defined(ARDUINO_ARCH_ESP32)

#include <Arduino.h>
#include <Servo.h>

#if defined __has_include
# if __has_include ("pinDefinitions.h")
# include "pinDefinitions.h"
# endif
#endif

/*
* This group/channel/timmer mapping is for information only;
* the details are handled by lower-level code
*
* LEDC Chan to Group/Channel/Timer Mapping
** ledc: 0 => Group: 0, Channel: 0, Timer: 0
** ledc: 1 => Group: 0, Channel: 1, Timer: 0
** ledc: 2 => Group: 0, Channel: 2, Timer: 1
** ledc: 3 => Group: 0, Channel: 3, Timer: 1
** ledc: 4 => Group: 0, Channel: 4, Timer: 2
** ledc: 5 => Group: 0, Channel: 5, Timer: 2
** ledc: 6 => Group: 0, Channel: 6, Timer: 3
** ledc: 7 => Group: 0, Channel: 7, Timer: 3
** ledc: 8 => Group: 1, Channel: 0, Timer: 0
** ledc: 9 => Group: 1, Channel: 1, Timer: 0
** ledc: 10 => Group: 1, Channel: 2, Timer: 1
** ledc: 11 => Group: 1, Channel: 3, Timer: 1
** ledc: 12 => Group: 1, Channel: 4, Timer: 2
** ledc: 13 => Group: 1, Channel: 5, Timer: 2
** ledc: 14 => Group: 1, Channel: 6, Timer: 3
** ledc: 15 => Group: 1, Channel: 7, Timer: 3
*/

class ServoImpl {
uint8_t pin;

public:
ServoImpl(const uint8_t _pin, const uint8_t _channel) : pin(_pin) {
// Setup timer
ledcSetup(_channel, (1000000 / REFRESH_INTERVAL), LEDC_MAX_BIT_WIDTH);

// Attach timer to a LED pin
ledcAttachPin(pin, _channel);
}

~ServoImpl() {
ledcDetachPin(pin);
}

void set(const uint8_t _channel, const uint32_t duration_us) {
ledcWrite(_channel, LEDC_US_TO_TICKS(duration_us));
}

uint32_t get(const uint8_t _channel) const {
return LEDC_TICKS_TO_US(ledcRead(_channel));
}
};

static ServoImpl* servos[MAX_PWM_SERVOS] = {nullptr}; // static array of servo structures
uint8_t ServoCount = 0; // the total number of attached servos

#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min) // minimum value in us for this servo
#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max) // maximum value in us for this servo

Servo::Servo()
{
if (ServoCount < MAX_PWM_SERVOS) {
this->servoIndex = ServoCount++;
} else {
this->servoIndex = INVALID_SERVO; // too many servos
}
}

uint8_t Servo::attach(int pin)
{
return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
}

uint8_t Servo::attach(int pin, int min, int max)
{
servos[this->servoIndex] = new ServoImpl(pin, this->servoIndex);

this->min = (MIN_PULSE_WIDTH - min);
this->max = (MAX_PULSE_WIDTH - max);
return this->servoIndex;
}

void Servo::detach()
{
delete servos[this->servoIndex];
servos[this->servoIndex] = NULL;
}

void Servo::write(int value)
{
// treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
if (value < MIN_PULSE_WIDTH)
{
if (value < 0)
value = 0;
else if (value > 180)
value = 180;

value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX());
}
writeMicroseconds(value);
}

void Servo::writeMicroseconds(int value)
{
if (!servos[this->servoIndex]) {
return;
}
// calculate and store the values for the given channel
byte channel = this->servoIndex;
if( (channel < MAX_PWM_SERVOS) ) // ensure channel is valid
{
if (value < SERVO_MIN()) // ensure pulse width is valid
value = SERVO_MIN();
else if (value > SERVO_MAX())
value = SERVO_MAX();

servos[this->servoIndex]->set(this->servoIndex, value);
}
}

int Servo::read() // return the value as degrees
{
return map(readMicroseconds(), SERVO_MIN(), SERVO_MAX(), 0, 180);
}

int Servo::readMicroseconds()
{
if (!servos[this->servoIndex]) {
return 0;
}
return servos[this->servoIndex]->get(this->servoIndex);
}

bool Servo::attached()
{
return servos[this->servoIndex] != NULL;
}

#endif
8 changes: 8 additions & 0 deletions src/esp32/ServoTimers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#define MAX_PWM_SERVOS 16

#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM

constexpr uint32_t BIT_RESOLUTION = (1 << LEDC_MAX_BIT_WIDTH) - 1;

#define LEDC_US_TO_TICKS(us) static_cast<uint32_t>((us * BIT_RESOLUTION) / REFRESH_INTERVAL)
#define LEDC_TICKS_TO_US(ticks) static_cast<uint32_t>((ticks * REFRESH_INTERVAL) / BIT_RESOLUTION)
Loading