Skip to content

Commit 583bd8a

Browse files
authored
Merge pull request #316 from brentru/add-pwm-patch
Add PWM Output
2 parents 0f7d5fb + 99f18a7 commit 583bd8a

File tree

9 files changed

+728
-186
lines changed

9 files changed

+728
-186
lines changed

src/Wippersnapper.cpp

Lines changed: 419 additions & 149 deletions
Large diffs are not rendered by default.

src/Wippersnapper.h

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,32 @@
4545
#endif
4646

4747
#include "components/ds18x20/ws_ds18x20.h"
48+
#include "components/pwm/ws_pwm.h"
4849
#include "components/servo/ws_servo.h"
4950

5051
// External libraries
5152
#include "Adafruit_MQTT.h" // MQTT Client
5253
#include "Arduino.h" // Wiring
5354

55+
// ESP32-IDF components
56+
#ifdef ARDUINO_ARCH_ESP32
57+
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
58+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
59+
#include "esp32/rom/rtc.h"
60+
#elif CONFIG_IDF_TARGET_ESP32S2
61+
#include "esp32s2/rom/rtc.h"
62+
#elif CONFIG_IDF_TARGET_ESP32C3
63+
#include "esp32c3/rom/rtc.h"
64+
#elif CONFIG_IDF_TARGET_ESP32S3
65+
#include "esp32s3/rom/rtc.h"
66+
#else
67+
#error Target CONFIG_IDF_TARGET is not supported
68+
#endif
69+
#else // ESP32 Before IDF 4.0
70+
#include "rom/rtc.h"
71+
#endif // ESP_IDF_VERSION_MAJOR
72+
#endif // ARDUINO_ARCH_ESP32
73+
5474
// Note: These might be better off in their respective wrappers
5575
#include <SPI.h>
5676

@@ -67,7 +87,7 @@
6787
#endif
6888

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

7292
// Reserved Adafruit IO MQTT topics
7393
#define TOPIC_IO_THROTTLE "/throttle" ///< Adafruit IO Throttle MQTT Topic
@@ -172,6 +192,7 @@ class WipperSnapper_Component_I2C;
172192
class ws_ledc;
173193
#endif
174194
class ws_servo;
195+
class ws_pwm;
175196
class ws_ds18x20;
176197

177198
/**************************************************************************/
@@ -208,6 +229,7 @@ class Wippersnapper {
208229
virtual ws_status_t networkStatus();
209230
ws_board_status_t getBoardStatus();
210231

232+
bool generateDeviceUID();
211233
bool buildWSTopics();
212234
void subscribeWSTopics();
213235
bool buildErrorTopics();
@@ -272,13 +294,15 @@ class Wippersnapper {
272294
outgoing payload data */
273295
uint16_t bufSize; /*!< Length of data inside buffer */
274296

275-
ws_board_status_t _boardStatus; ///< Hardware's registration status
297+
ws_board_status_t _boardStatus =
298+
WS_BOARD_DEF_IDLE; ///< Hardware's registration status
276299

277300
Wippersnapper_DigitalGPIO *_digitalGPIO; ///< Instance of digital gpio class
278301
Wippersnapper_AnalogIO *_analogIO; ///< Instance of analog io class
279302
Wippersnapper_FS *_fileSystem; ///< Instance of Filesystem (native USB)
280303
WipperSnapper_LittleFS
281-
*_littleFS; ///< Instance of LittleFS Filesystem (non-native USB)
304+
*_littleFS; ///< Instance of LittleFS Filesystem (non-native USB)
305+
ws_pwm *_pwmComponent; ///< Instance of pwm class
282306
ws_servo *_servoComponent; ///< Instance of servo class
283307
ws_ds18x20 *_ds18x20Component; ///< Instance of DS18x20 class
284308

@@ -311,15 +335,17 @@ class Wippersnapper {
311335
device to a broker. */
312336
char *_topic_signal_servo_device = NULL; /*!< Topic carries messages from a
313337
broker to a device. */
338+
char *_topic_signal_pwm_brkr =
339+
NULL; /*!< Topic carries PWM messages from a device to a broker. */
340+
char *_topic_signal_pwm_device =
341+
NULL; /*!< Topic carries PWM messages from a broker to a device. */
314342
char *_topic_signal_ds18_brkr = NULL; /*!< Topic carries ds18x20 messages from
315343
a device to a broker. */
316344
char *_topic_signal_ds18_device = NULL; /*!< Topic carries ds18x20 messages
317345
from a broker to a device. */
318346

319347
wippersnapper_signal_v1_CreateSignalRequest
320348
_incomingSignalMsg; /*!< Incoming signal message from broker */
321-
322-
// i2c signal msg
323349
wippersnapper_signal_v1_I2CRequest msgSignalI2C =
324350
wippersnapper_signal_v1_I2CRequest_init_zero; ///< I2C request wrapper
325351
///< message
@@ -332,6 +358,9 @@ class Wippersnapper {
332358
// servo message
333359
wippersnapper_signal_v1_ServoRequest
334360
msgServo; ///< ServoRequest wrapper message
361+
wippersnapper_signal_v1_PWMRequest msgPWM =
362+
wippersnapper_signal_v1_PWMRequest_init_zero; ///< PWM request wrapper
363+
///< message.
335364

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

407+
Adafruit_MQTT_Subscribe *_topic_description_sub; /*!< Subscription callback
408+
for registration topic. */
409+
Adafruit_MQTT_Publish *_topic_signal_device_pub; /*!< Subscription callback
410+
for D2C signal topic. */
411+
Adafruit_MQTT_Subscribe *_topic_signal_brkr_sub; /*!< Subscription callback
412+
for C2D signal topic. */
378413
Adafruit_MQTT_Subscribe
379-
*_topic_description_sub; /*!< Subscription for registration topic. */
380-
Adafruit_MQTT_Publish
381-
*_topic_signal_device_pub; /*!< Subscription for D2C signal topic. */
414+
*_topic_signal_i2c_sub; /*!< Subscription callback for I2C topic. */
382415
Adafruit_MQTT_Subscribe
383-
*_topic_signal_brkr_sub; /*!< Subscription for C2D signal topic. */
416+
*_topic_signal_servo_sub; /*!< Subscription callback for servo topic. */
384417
Adafruit_MQTT_Subscribe
385-
*_topic_signal_i2c_sub; /*!< Subscribes to signal's I2C topic. */
418+
*_topic_signal_pwm_sub; /*!< Subscription callback for pwm topic. */
386419
Adafruit_MQTT_Subscribe
387420
*_topic_signal_ds18_sub; /*!< Subscribes to signal's ds18x20 topic. */
388-
Adafruit_MQTT_Subscribe
389-
*_topic_signal_servo_sub; /*!< Subscribes to device's servo topic. */
390421

391422
Adafruit_MQTT_Subscribe
392423
*_err_sub; /*!< Subscription to Adafruit IO Error topic. */

src/components/ledc/drivers/ws_ledc_servo.cpp renamed to src/components/ledc/drivers/servo/ws_ledc_servo.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,8 @@ void ws_ledc_servo::setLEDCDriver(ws_ledc *ledcManager) {
6262
uint8_t ws_ledc_servo::attach(int pin, int minPulseWidth, int maxPulseWidth,
6363
int servoFreq) {
6464
// Attempt to attach a pin to ledc channel
65-
uint8_t chan =
66-
_ledcMgr->attachPin((uint8_t)pin, (double)servoFreq, LEDC_TIMER_WIDTH);
67-
if (chan == 255) // error!
65+
uint8_t chan = _ledcMgr->attachPin((uint8_t)pin, (double)servoFreq, 16);
66+
if (chan == LEDC_CH_ERR) // error!
6867
return chan;
6968
// configure the servo object and assign it to a pin
7069
_servo.Pin.nbr = pin;

src/components/ledc/ws_ledc.cpp

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,33 +42,51 @@ ws_ledc::~ws_ledc() {
4242
}
4343
}
4444

45+
/**************************************************************************/
46+
/*!
47+
@brief Checks if a channel has already been allocated for a pin.
48+
@param pin Desired GPIO pin number.
49+
@return The channel number if the pin was successfully or already
50+
attached, otherwise LEDC_CH_ERR.
51+
*/
52+
/**************************************************************************/
53+
uint8_t ws_ledc::_getChannel(uint8_t pin) {
54+
// have we already attached this pin?
55+
for (int i = 0; i < MAX_LEDC_PWMS; i++) {
56+
if (_ledcPins[i].pin == pin)
57+
return _ledcPins[i].chan;
58+
}
59+
return LEDC_CH_ERR;
60+
}
61+
4562
/**************************************************************************/
4663
/*!
4764
@brief Allocates a timer + channel for a pin and attaches it.
4865
@param pin Desired GPIO pin number.
49-
@param freq Desired LEDC timer frequency, in Hz.
50-
@param resolution Desired LEDC timer resolution, in bits.
51-
@return The channel number if the pin was successfully attached,
52-
otherwise 255.
66+
@param freq Desired timer frequency, in Hz.
67+
@param resolution Desired timer resolution, in bits.
68+
@return The channel number if the pin was successfully or already
69+
attached, otherwise 255.
5370
*/
5471
/**************************************************************************/
5572
uint8_t ws_ledc::attachPin(uint8_t pin, double freq, uint8_t resolution) {
5673
// have we already attached this pin?
5774
for (int i = 0; i < MAX_LEDC_PWMS; i++) {
5875
if (_ledcPins[i].pin == pin)
59-
return 255;
76+
return _ledcPins[i].chan;
6077
}
6178

6279
// allocate chanel
6380
uint8_t chanNum = _allocateChannel(freq, resolution);
64-
if (chanNum == 255)
81+
if (chanNum == LEDC_CH_ERR)
6582
return chanNum;
6683

6784
// attach pin to channel
6885
ledcAttachPin(pin, chanNum);
6986

7087
// allocate pin in pool
7188
_ledcPins[chanNum].pin = pin;
89+
_ledcPins[chanNum].chan = chanNum;
7290

7391
return chanNum;
7492
}
@@ -97,25 +115,17 @@ void ws_ledc::detachPin(uint8_t pin) {
97115
/**************************************************************************/
98116
/*!
99117
@brief Allocates a channel and timer.
100-
@param freq Desired LEDC timer frequency, in Hz.
101-
@param resolution Desired LEDC timer resolution, in bits.
118+
@param freq Timer frequency, in Hz.
119+
@param resolution Timer resolution, in bits.
102120
@return The channel number if the timer was successfully initialized,
103121
otherwise 255.
104122
*/
105123
/**************************************************************************/
106-
uint8_t ws_ledc::_allocateChannel(double freq, uint8_t resolution) {
107-
// attempt to allocate an inactive channel
108-
uint8_t chanNum = 255;
109-
for (int i = 0; i < MAX_LEDC_PWMS; i++) {
110-
if (_ledcPins[i].isActive == false) {
111-
chanNum = i;
112-
break;
113-
}
114-
}
115-
116-
// did we fail to allocate?
117-
if (chanNum == 255)
118-
return 255;
124+
uint8_t ws_ledc::_allocateChannel(double freq, uint8_t resolution = 16) {
125+
// obtain an inactive channel number
126+
uint8_t chanNum = _getInactiveChannel();
127+
if (chanNum == LEDC_CH_ERR)
128+
return LEDC_CH_ERR; // failed to obtain inactive channel #
119129

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

142+
/**************************************************************************/
143+
/*!
144+
@brief Returns an inactive LEDC channel number.
145+
@returns Inactive channel number if free, otherwise LEDC_CH_ERR.
146+
*/
147+
/**************************************************************************/
148+
uint8_t ws_ledc::_getInactiveChannel() {
149+
for (int ch = 0; ch < sizeof(_ledcPins); ch++) {
150+
if (_ledcPins[ch].isActive == false) {
151+
return ch;
152+
}
153+
}
154+
return LEDC_CH_ERR;
155+
}
156+
157+
/**************************************************************************/
158+
/*!
159+
@brief Arduino AnalogWrite function, but for ESP32's LEDC.
160+
@param pin The desired pin to write to.
161+
@param value The duty cycle.
162+
*/
163+
/**************************************************************************/
164+
void ws_ledc::analogWrite(uint8_t pin, int value) {
165+
if (value > 255 || value < 0)
166+
return;
167+
168+
uint8_t ch;
169+
ch = _getChannel(pin);
170+
if (ch == LEDC_CH_ERR) {
171+
Serial.println("ERROR: Pin not attached to channel");
172+
return;
173+
}
174+
175+
// perform duty cycle calculation provided value
176+
// (assumes 12-bit resolution, 2^12)
177+
uint32_t dutyCycle = (4095 / 255) * min(value, 255);
178+
179+
// set the duty cycle of the pin
180+
setDuty(pin, dutyCycle);
181+
}
182+
132183
/**************************************************************************/
133184
/*!
134185
@brief Sets the duty cycle of a pin
@@ -150,4 +201,21 @@ void ws_ledc::setDuty(uint8_t pin, uint32_t duty) {
150201
ledcWrite(chan, duty);
151202
}
152203

204+
/**************************************************************************/
205+
/*!
206+
@brief Writes a square wave with a fixed duty cycle and variable
207+
frequency to a pin. Used by piezo buzzers and speakers.
208+
@param pin The desired pin to write to.
209+
@param freq The frequency of the tone, in Hz.
210+
*/
211+
/**************************************************************************/
212+
void ws_ledc::tone(uint8_t pin, uint32_t freq) {
213+
uint8_t ch;
214+
ch = _getChannel(pin);
215+
if (ch == LEDC_CH_ERR) {
216+
// Serial.println("ERROR: Pin not previously attached!");
217+
}
218+
ledcWriteTone(ch, freq);
219+
}
220+
153221
#endif // ARDUINO_ARCH_ESP32

src/components/ledc/ws_ledc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "esp32-hal-ledc.h"
2222
#include "esp_err.h"
2323

24+
#define LEDC_CH_ERR 255 ///< LEDC channel error
2425
#define MAX_LEDC_PWMS \
2526
16 ///< maximum # of LEDC channels (see: LEDC Chan to Group/Channel/Timer
2627
///< Mapping)
@@ -51,10 +52,15 @@ class ws_ledc {
5152
~ws_ledc();
5253
uint8_t attachPin(uint8_t pin, double freq, uint8_t resolution);
5354
void detachPin(uint8_t pin);
55+
5456
void setDuty(uint8_t pin, uint32_t duty);
57+
void analogWrite(uint8_t pin, int value);
58+
void tone(uint8_t pin, uint32_t freq);
5559

5660
private:
5761
uint8_t _allocateChannel(double freq, uint8_t resolution);
62+
uint8_t _getChannel(uint8_t pin);
63+
uint8_t _getInactiveChannel();
5864
ledcPin_t _ledcPins[MAX_LEDC_PWMS]; ///< Pool of usable LEDC pins
5965
};
6066
extern Wippersnapper WS;

0 commit comments

Comments
 (0)