Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PWM auto-phasing for lights by default (new behavior) unless SetOption134 1 #14590

Merged
merged 3 commits into from
Jan 25, 2022
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file.
- Command ``SspmLog<relay> [x]`` to retrieve relay power state change and cause logging
- Command ``SspmScan`` to rescan Sonoff SPM modbus
- Support for MQ analog sensor for air quality by Francesco Adriani (#14581)
- PWM auto-phasing for lights by default (new behavior) unless ``SetOption134 1``

### Changed
- BME68x-Sensor-API library from v3.5.9 to v4.4.7
Expand Down
136 changes: 136 additions & 0 deletions lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,139 @@
#include "esp8266toEsp32.h"

// ESP Stuff

/*********************************************************************************************\
* ESP32 analogWrite emulation support
\*********************************************************************************************/

#if CONFIG_IDF_TARGET_ESP32C3
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#else // other ESP32
uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#endif // CONFIG_IDF_TARGET_ESP32C3 vs ESP32

uint32_t _analog_pin2chan(uint32_t pin) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if ((_pwm_channel[channel] < 99) && (_pwm_channel[channel] == pin)) {
return channel;
}
}
return 0;
}

void _analogWriteFreqRange(void) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (_pwm_channel[channel] < 99) {
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num);
}
}
}

// input range is in full range, ledc needs bits
uint32_t _analogGetResolution(uint32_t x) {
uint32_t bits = 0;
while (x) {
bits++;
x >>= 1;
}
return bits;
}

void analogWriteRange(uint32_t range) {
_pwm_bit_num = _analogGetResolution(range);
_analogWriteFreqRange();
}

void analogWriteFreq(uint32_t freq) {
_pwm_frequency = freq;
_analogWriteFreqRange();
}

bool analogAttach(uint32_t pin) {
// Find if pin is already attached
uint32_t channel;
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (_pwm_channel[channel] == pin) {
// Already attached
// Serial.printf("PWM: Already attached pin %d to channel %d\n", pin, channel);
return true;
}
}
// Find an empty channel
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (99 == _pwm_channel[channel]) {
_pwm_channel[channel] = pin;
ledcAttachPin(pin, channel + PWM_CHANNEL_OFFSET);
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num);
// Serial.printf("PWM: New attach pin %d to channel %d\n", pin, channel);
return true;
}
}
// No more channels available
return false;
}

void analogWrite(uint8_t pin, int val)
{
uint32_t channel = _analog_pin2chan(pin);
if ( val >> (_pwm_bit_num-1) ) ++val;
ledcWrite(channel + PWM_CHANNEL_OFFSET, val);
// Serial.printf("write %d - %d\n",channel,val);
}

/*
The primary goal of this library is to add phase control to PWM ledc
functions.

Phase control allows to stress less the power supply of LED lights.
By default all phases are starting at the same moment. This means
the the power supply always takes a power hit at the start of each
new cycle, even if the average power is low.

Phase control is also of major importance for H-bridge where
both PWM lines should NEVER be active at the same time.

Unfortunately Arduino Core does not allow any customization nor
extendibility for the ledc/analogWrite functions. We have therefore
no other choice than duplicating part of Arduino code.

WARNING: this means it can easily break if ever Arduino internal
implementation changes.
*/

#include "driver/ledc.h"

#ifdef SOC_LEDC_SUPPORT_HS_MODE
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1)
#else
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
#endif

// exported from Arduno Core
extern uint8_t channels_resolution[LEDC_CHANNELS];

void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase)
{
uint32_t chan = _analog_pin2chan(pin) + PWM_CHANNEL_OFFSET;
if (duty >> (_pwm_bit_num-1) ) ++duty;

if(chan >= LEDC_CHANNELS){
return;
}
uint8_t group=(chan/8), channel=(chan%8);

//Fixing if all bits in resolution is set = LEDC FULL ON
uint32_t max_duty = (1 << channels_resolution[chan]) - 1;
phase = phase % max_duty;

if(duty == max_duty){ // no sure whether this is needed anymore TODO
duty = max_duty + 1;
}

ledc_set_duty_with_hpoint((ledc_mode_t)group, (ledc_channel_t)channel, duty, phase);
ledc_update_duty((ledc_mode_t)group, (ledc_channel_t)channel);
}
100 changes: 12 additions & 88 deletions lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,105 +28,29 @@

#include <Esp.h>

/*********************************************************************************************\
* ESP32 analogWrite emulation support
\*********************************************************************************************/

#if CONFIG_IDF_TARGET_ESP32C3
#define PWM_SUPPORTED_CHANNELS 6
#define PWM_CHANNEL_OFFSET 1 // Webcam uses channel 0, so we offset standard PWM

uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#else // other ESP32
#define PWM_SUPPORTED_CHANNELS 8
#define PWM_CHANNEL_OFFSET 2 // Webcam uses channel 0, so we offset standard PWM

uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS] = { 99, 99, 99, 99, 99, 99, 99, 99 };
uint32_t _pwm_frequency = 977; // Default 977Hz
uint8_t _pwm_bit_num = 10; // Default 1023
#endif // CONFIG_IDF_TARGET_ESP32C3 vs ESP32

inline uint32_t _analog_pin2chan(uint32_t pin) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if ((_pwm_channel[channel] < 99) && (_pwm_channel[channel] == pin)) {
return channel;
}
}
return 0;
}

inline void _analogWriteFreqRange(void) {
for (uint32_t channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (_pwm_channel[channel] < 99) {
// uint32_t duty = ledcRead(channel + PWM_CHANNEL_OFFSET);
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num);
// ledcWrite(channel + PWM_CHANNEL_OFFSET, duty);
}
}
// Serial.printf("freq - range %d - %d\n",freq,range);
}
extern uint8_t _pwm_channel[PWM_SUPPORTED_CHANNELS];
extern uint32_t _pwm_frequency;
extern uint8_t _pwm_bit_num;

void _analogWriteFreqRange(void);
// input range is in full range, ledc needs bits
inline uint32_t _analogGetResolution(uint32_t x) {
uint32_t bits = 0;
while (x) {
bits++;
x >>= 1;
}
return bits;
}

inline void analogWriteRange(uint32_t range) {
_pwm_bit_num = _analogGetResolution(range);
_analogWriteFreqRange();
}

inline void analogWriteFreq(uint32_t freq) {
_pwm_frequency = freq;
_analogWriteFreqRange();
}
uint32_t _analogGetResolution(uint32_t x);
void analogWriteRange(uint32_t range);
void analogWriteFreq(uint32_t freq);
bool analogAttach(uint32_t pin);
void analogWrite(uint8_t pin, int val);

// Extended version that also allows to change phase
extern void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase = 0);

/*
inline void analogAttach(uint32_t pin, uint32_t channel) {
_pwm_channel[channel &7] = pin;
ledcAttachPin(pin, channel + PWM_CHANNEL_OFFSET);
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num);
// Serial.printf("attach %d - %d\n", channel, pin);
}
*/
inline bool analogAttach(uint32_t pin) {
// Find if pin is already attached
uint32_t channel;
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (_pwm_channel[channel] == pin) {
// Already attached
// Serial.printf("PWM: Already attached pin %d to channel %d\n", pin, channel);
return true;
}
}
// Find an empty channel
for (channel = 0; channel < PWM_SUPPORTED_CHANNELS; channel++) {
if (99 == _pwm_channel[channel]) {
_pwm_channel[channel] = pin;
ledcAttachPin(pin, channel + PWM_CHANNEL_OFFSET);
ledcSetup(channel + PWM_CHANNEL_OFFSET, _pwm_frequency, _pwm_bit_num);
// Serial.printf("PWM: New attach pin %d to channel %d\n", pin, channel);
return true;
}
}
// No more channels available
return false;
}

inline void analogWrite(uint8_t pin, int val)
{
uint32_t channel = _analog_pin2chan(pin);
if ( val >> (_pwm_bit_num-1) ) ++val;
ledcWrite(channel + PWM_CHANNEL_OFFSET, val);
// Serial.printf("write %d - %d\n",channel,val);
}

/*********************************************************************************************/

Expand Down
1 change: 1 addition & 0 deletions lib/libesp32/berry/generate/be_const_strtab.h
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ extern const bcstring be_const_str_set_matrix_pixel_color;
extern const bcstring be_const_str_set_percentage;
extern const bcstring be_const_str_set_pixel_color;
extern const bcstring be_const_str_set_power;
extern const bcstring be_const_str_set_pwm;
extern const bcstring be_const_str_set_rate;
extern const bcstring be_const_str_set_style_bg_color;
extern const bcstring be_const_str_set_style_line_color;
Expand Down
Loading