Skip to content

Commit

Permalink
Merge pull request #316 from brentru/add-pwm-patch
Browse files Browse the repository at this point in the history
Add PWM Output
  • Loading branch information
brentru authored Nov 15, 2022
2 parents 0f7d5fb + 99f18a7 commit 583bd8a
Show file tree
Hide file tree
Showing 9 changed files with 728 additions and 186 deletions.
568 changes: 419 additions & 149 deletions src/Wippersnapper.cpp

Large diffs are not rendered by default.

55 changes: 43 additions & 12 deletions src/Wippersnapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,32 @@
#endif

#include "components/ds18x20/ws_ds18x20.h"
#include "components/pwm/ws_pwm.h"
#include "components/servo/ws_servo.h"

// External libraries
#include "Adafruit_MQTT.h" // MQTT Client
#include "Arduino.h" // Wiring

// ESP32-IDF components
#ifdef ARDUINO_ARCH_ESP32
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32C3
#include "esp32c3/rom/rtc.h"
#elif CONFIG_IDF_TARGET_ESP32S3
#include "esp32s3/rom/rtc.h"
#else
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "rom/rtc.h"
#endif // ESP_IDF_VERSION_MAJOR
#endif // ARDUINO_ARCH_ESP32

// Note: These might be better off in their respective wrappers
#include <SPI.h>

Expand All @@ -67,7 +87,7 @@
#endif

#define WS_VERSION \
"1.0.0-beta.54" ///< WipperSnapper app. version (semver-formatted)
"1.0.0-beta.55" ///< WipperSnapper app. version (semver-formatted)

// Reserved Adafruit IO MQTT topics
#define TOPIC_IO_THROTTLE "/throttle" ///< Adafruit IO Throttle MQTT Topic
Expand Down Expand Up @@ -172,6 +192,7 @@ class WipperSnapper_Component_I2C;
class ws_ledc;
#endif
class ws_servo;
class ws_pwm;
class ws_ds18x20;

/**************************************************************************/
Expand Down Expand Up @@ -208,6 +229,7 @@ class Wippersnapper {
virtual ws_status_t networkStatus();
ws_board_status_t getBoardStatus();

bool generateDeviceUID();
bool buildWSTopics();
void subscribeWSTopics();
bool buildErrorTopics();
Expand Down Expand Up @@ -272,13 +294,15 @@ class Wippersnapper {
outgoing payload data */
uint16_t bufSize; /*!< Length of data inside buffer */

ws_board_status_t _boardStatus; ///< Hardware's registration status
ws_board_status_t _boardStatus =
WS_BOARD_DEF_IDLE; ///< Hardware's registration status

Wippersnapper_DigitalGPIO *_digitalGPIO; ///< Instance of digital gpio class
Wippersnapper_AnalogIO *_analogIO; ///< Instance of analog io class
Wippersnapper_FS *_fileSystem; ///< Instance of Filesystem (native USB)
WipperSnapper_LittleFS
*_littleFS; ///< Instance of LittleFS Filesystem (non-native USB)
*_littleFS; ///< Instance of LittleFS Filesystem (non-native USB)
ws_pwm *_pwmComponent; ///< Instance of pwm class
ws_servo *_servoComponent; ///< Instance of servo class
ws_ds18x20 *_ds18x20Component; ///< Instance of DS18x20 class

Expand Down Expand Up @@ -311,15 +335,17 @@ class Wippersnapper {
device to a broker. */
char *_topic_signal_servo_device = NULL; /*!< Topic carries messages from a
broker to a device. */
char *_topic_signal_pwm_brkr =
NULL; /*!< Topic carries PWM messages from a device to a broker. */
char *_topic_signal_pwm_device =
NULL; /*!< Topic carries PWM messages from a broker to a device. */
char *_topic_signal_ds18_brkr = NULL; /*!< Topic carries ds18x20 messages from
a device to a broker. */
char *_topic_signal_ds18_device = NULL; /*!< Topic carries ds18x20 messages
from a broker to a device. */

wippersnapper_signal_v1_CreateSignalRequest
_incomingSignalMsg; /*!< Incoming signal message from broker */

// i2c signal msg
wippersnapper_signal_v1_I2CRequest msgSignalI2C =
wippersnapper_signal_v1_I2CRequest_init_zero; ///< I2C request wrapper
///< message
Expand All @@ -332,6 +358,9 @@ class Wippersnapper {
// servo message
wippersnapper_signal_v1_ServoRequest
msgServo; ///< ServoRequest wrapper message
wippersnapper_signal_v1_PWMRequest msgPWM =
wippersnapper_signal_v1_PWMRequest_init_zero; ///< PWM request wrapper
///< message.

char *throttleMessage; /*!< Pointer to throttle message data. */
int throttleTime; /*!< Total amount of time to throttle the device, in
Expand Down Expand Up @@ -375,18 +404,20 @@ class Wippersnapper {
char *_err_topic = NULL; /*!< Adafruit IO MQTT error message topic. */
char *_throttle_topic = NULL; /*!< Adafruit IO MQTT throttle message topic. */

Adafruit_MQTT_Subscribe *_topic_description_sub; /*!< Subscription callback
for registration topic. */
Adafruit_MQTT_Publish *_topic_signal_device_pub; /*!< Subscription callback
for D2C signal topic. */
Adafruit_MQTT_Subscribe *_topic_signal_brkr_sub; /*!< Subscription callback
for C2D signal topic. */
Adafruit_MQTT_Subscribe
*_topic_description_sub; /*!< Subscription for registration topic. */
Adafruit_MQTT_Publish
*_topic_signal_device_pub; /*!< Subscription for D2C signal topic. */
*_topic_signal_i2c_sub; /*!< Subscription callback for I2C topic. */
Adafruit_MQTT_Subscribe
*_topic_signal_brkr_sub; /*!< Subscription for C2D signal topic. */
*_topic_signal_servo_sub; /*!< Subscription callback for servo topic. */
Adafruit_MQTT_Subscribe
*_topic_signal_i2c_sub; /*!< Subscribes to signal's I2C topic. */
*_topic_signal_pwm_sub; /*!< Subscription callback for pwm topic. */
Adafruit_MQTT_Subscribe
*_topic_signal_ds18_sub; /*!< Subscribes to signal's ds18x20 topic. */
Adafruit_MQTT_Subscribe
*_topic_signal_servo_sub; /*!< Subscribes to device's servo topic. */

Adafruit_MQTT_Subscribe
*_err_sub; /*!< Subscription to Adafruit IO Error topic. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,8 @@ void ws_ledc_servo::setLEDCDriver(ws_ledc *ledcManager) {
uint8_t ws_ledc_servo::attach(int pin, int minPulseWidth, int maxPulseWidth,
int servoFreq) {
// Attempt to attach a pin to ledc channel
uint8_t chan =
_ledcMgr->attachPin((uint8_t)pin, (double)servoFreq, LEDC_TIMER_WIDTH);
if (chan == 255) // error!
uint8_t chan = _ledcMgr->attachPin((uint8_t)pin, (double)servoFreq, 16);
if (chan == LEDC_CH_ERR) // error!
return chan;
// configure the servo object and assign it to a pin
_servo.Pin.nbr = pin;
Expand Down
File renamed without changes.
110 changes: 89 additions & 21 deletions src/components/ledc/ws_ledc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,33 +42,51 @@ ws_ledc::~ws_ledc() {
}
}

/**************************************************************************/
/*!
@brief Checks if a channel has already been allocated for a pin.
@param pin Desired GPIO pin number.
@return The channel number if the pin was successfully or already
attached, otherwise LEDC_CH_ERR.
*/
/**************************************************************************/
uint8_t ws_ledc::_getChannel(uint8_t pin) {
// have we already attached this pin?
for (int i = 0; i < MAX_LEDC_PWMS; i++) {
if (_ledcPins[i].pin == pin)
return _ledcPins[i].chan;
}
return LEDC_CH_ERR;
}

/**************************************************************************/
/*!
@brief Allocates a timer + channel for a pin and attaches it.
@param pin Desired GPIO pin number.
@param freq Desired LEDC timer frequency, in Hz.
@param resolution Desired LEDC timer resolution, in bits.
@return The channel number if the pin was successfully attached,
otherwise 255.
@param freq Desired timer frequency, in Hz.
@param resolution Desired timer resolution, in bits.
@return The channel number if the pin was successfully or already
attached, otherwise 255.
*/
/**************************************************************************/
uint8_t ws_ledc::attachPin(uint8_t pin, double freq, uint8_t resolution) {
// have we already attached this pin?
for (int i = 0; i < MAX_LEDC_PWMS; i++) {
if (_ledcPins[i].pin == pin)
return 255;
return _ledcPins[i].chan;
}

// allocate chanel
uint8_t chanNum = _allocateChannel(freq, resolution);
if (chanNum == 255)
if (chanNum == LEDC_CH_ERR)
return chanNum;

// attach pin to channel
ledcAttachPin(pin, chanNum);

// allocate pin in pool
_ledcPins[chanNum].pin = pin;
_ledcPins[chanNum].chan = chanNum;

return chanNum;
}
Expand Down Expand Up @@ -97,25 +115,17 @@ void ws_ledc::detachPin(uint8_t pin) {
/**************************************************************************/
/*!
@brief Allocates a channel and timer.
@param freq Desired LEDC timer frequency, in Hz.
@param resolution Desired LEDC timer resolution, in bits.
@param freq Timer frequency, in Hz.
@param resolution Timer resolution, in bits.
@return The channel number if the timer was successfully initialized,
otherwise 255.
*/
/**************************************************************************/
uint8_t ws_ledc::_allocateChannel(double freq, uint8_t resolution) {
// attempt to allocate an inactive channel
uint8_t chanNum = 255;
for (int i = 0; i < MAX_LEDC_PWMS; i++) {
if (_ledcPins[i].isActive == false) {
chanNum = i;
break;
}
}

// did we fail to allocate?
if (chanNum == 255)
return 255;
uint8_t ws_ledc::_allocateChannel(double freq, uint8_t resolution = 16) {
// obtain an inactive channel number
uint8_t chanNum = _getInactiveChannel();
if (chanNum == LEDC_CH_ERR)
return LEDC_CH_ERR; // failed to obtain inactive channel #

// attempt to set up a ledc_timer on the free channel
double rc = ledcSetup(uint8_t(chanNum), freq, resolution);
Expand All @@ -129,6 +139,47 @@ uint8_t ws_ledc::_allocateChannel(double freq, uint8_t resolution) {
return chanNum;
}

/**************************************************************************/
/*!
@brief Returns an inactive LEDC channel number.
@returns Inactive channel number if free, otherwise LEDC_CH_ERR.
*/
/**************************************************************************/
uint8_t ws_ledc::_getInactiveChannel() {
for (int ch = 0; ch < sizeof(_ledcPins); ch++) {
if (_ledcPins[ch].isActive == false) {
return ch;
}
}
return LEDC_CH_ERR;
}

/**************************************************************************/
/*!
@brief Arduino AnalogWrite function, but for ESP32's LEDC.
@param pin The desired pin to write to.
@param value The duty cycle.
*/
/**************************************************************************/
void ws_ledc::analogWrite(uint8_t pin, int value) {
if (value > 255 || value < 0)
return;

uint8_t ch;
ch = _getChannel(pin);
if (ch == LEDC_CH_ERR) {
Serial.println("ERROR: Pin not attached to channel");
return;
}

// perform duty cycle calculation provided value
// (assumes 12-bit resolution, 2^12)
uint32_t dutyCycle = (4095 / 255) * min(value, 255);

// set the duty cycle of the pin
setDuty(pin, dutyCycle);
}

/**************************************************************************/
/*!
@brief Sets the duty cycle of a pin
Expand All @@ -150,4 +201,21 @@ void ws_ledc::setDuty(uint8_t pin, uint32_t duty) {
ledcWrite(chan, duty);
}

/**************************************************************************/
/*!
@brief Writes a square wave with a fixed duty cycle and variable
frequency to a pin. Used by piezo buzzers and speakers.
@param pin The desired pin to write to.
@param freq The frequency of the tone, in Hz.
*/
/**************************************************************************/
void ws_ledc::tone(uint8_t pin, uint32_t freq) {
uint8_t ch;
ch = _getChannel(pin);
if (ch == LEDC_CH_ERR) {
// Serial.println("ERROR: Pin not previously attached!");
}
ledcWriteTone(ch, freq);
}

#endif // ARDUINO_ARCH_ESP32
6 changes: 6 additions & 0 deletions src/components/ledc/ws_ledc.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "esp32-hal-ledc.h"
#include "esp_err.h"

#define LEDC_CH_ERR 255 ///< LEDC channel error
#define MAX_LEDC_PWMS \
16 ///< maximum # of LEDC channels (see: LEDC Chan to Group/Channel/Timer
///< Mapping)
Expand Down Expand Up @@ -51,10 +52,15 @@ class ws_ledc {
~ws_ledc();
uint8_t attachPin(uint8_t pin, double freq, uint8_t resolution);
void detachPin(uint8_t pin);

void setDuty(uint8_t pin, uint32_t duty);
void analogWrite(uint8_t pin, int value);
void tone(uint8_t pin, uint32_t freq);

private:
uint8_t _allocateChannel(double freq, uint8_t resolution);
uint8_t _getChannel(uint8_t pin);
uint8_t _getInactiveChannel();
ledcPin_t _ledcPins[MAX_LEDC_PWMS]; ///< Pool of usable LEDC pins
};
extern Wippersnapper WS;
Expand Down
Loading

0 comments on commit 583bd8a

Please sign in to comment.