From 532afbaecd262622ee2c9379708be37c37467d3a Mon Sep 17 00:00:00 2001
From: Stephan Hadinger
Date: Mon, 24 Jan 2022 22:48:57 +0100
Subject: [PATCH 1/3] PWM auto-phasing for lights by default (new behavior)
unless ``SetOption134 1``
---
CHANGELOG.md | 1 +
.../src/esp8266toEsp32.cpp | 136 ++
.../src/esp8266toEsp32.h | 98 +-
lib/libesp32/berry/generate/be_const_strtab.h | 1 +
.../berry/generate/be_const_strtab_def.h | 1970 +++++++++--------
lib/libesp32/berry/generate/be_fixed_gpio.h | 13 +-
lib/libesp32/berry_tasmota/src/be_gpio_lib.c | 7 +
tasmota/settings.h | 2 +-
tasmota/xdrv_04_light.ino | 13 +-
tasmota/xdrv_52_3_berry_gpio.ino | 7 +-
10 files changed, 1167 insertions(+), 1081 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aac9ff6e4..c0de60b54 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file.
- Command ``SspmLog [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
diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp
index d11d09931..f9072ff7d 100644
--- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp
+++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp
@@ -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);
+}
diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h
index d5c747485..adda5f2eb 100644
--- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h
+++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h
@@ -28,105 +28,29 @@
#include
-/*********************************************************************************************\
- * 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;
-}
+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);
-inline void analogWriteRange(uint32_t range) {
- _pwm_bit_num = _analogGetResolution(range);
- _analogWriteFreqRange();
-}
+// Extended version that also allows to change phase
+extern void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase = 0);
-inline void analogWriteFreq(uint32_t freq) {
- _pwm_frequency = freq;
- _analogWriteFreqRange();
-}
-
-/*
-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);
-}
/*********************************************************************************************/
diff --git a/lib/libesp32/berry/generate/be_const_strtab.h b/lib/libesp32/berry/generate/be_const_strtab.h
index 0168eaeec..6bedac03a 100644
--- a/lib/libesp32/berry/generate/be_const_strtab.h
+++ b/lib/libesp32/berry/generate/be_const_strtab.h
@@ -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;
diff --git a/lib/libesp32/berry/generate/be_const_strtab_def.h b/lib/libesp32/berry/generate/be_const_strtab_def.h
index 82ada2935..a67a809c1 100644
--- a/lib/libesp32/berry/generate/be_const_strtab_def.h
+++ b/lib/libesp32/berry/generate/be_const_strtab_def.h
@@ -1,736 +1,737 @@
-be_define_const_str(, "", 2166136261u, 0, 0, &be_const_str_introspect);
-be_define_const_str(_X0A, "\n", 252472541u, 0, 1, &be_const_str__X2Ew);
-be_define_const_str(_X20, " ", 621580159u, 0, 1, &be_const_str_valuer_error);
-be_define_const_str(_X21_X3D, "!=", 2428715011u, 0, 2, &be_const_str_SERIAL_8E1);
-be_define_const_str(_X21_X3D_X3D, "!==", 559817114u, 0, 3, &be_const_str_json_fdump);
-be_define_const_str(_X23, "#", 638357778u, 0, 1, &be_const_str_get_string);
-be_define_const_str(_X23autoexec_X2Ebat, "#autoexec.bat", 3382890497u, 0, 13, &be_const_str__error);
-be_define_const_str(_X23autoexec_X2Ebe, "#autoexec.be", 1181757091u, 0, 12, NULL);
-be_define_const_str(_X23display_X2Eini, "#display.ini", 182218220u, 0, 12, &be_const_str_pixel_size);
-be_define_const_str(_X23init_X2Ebat, "#init.bat", 3297595077u, 0, 9, &be_const_str_get_percentage);
-be_define_const_str(_X23preinit_X2Ebe, "#preinit.be", 687035716u, 0, 11, &be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dzip_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20);
-be_define_const_str(_X2502d_X25s_X2502d, "%02d%s%02d", 1587999717u, 0, 10, NULL);
-be_define_const_str(_X2504d_X2D_X2502d_X2D_X2502dT_X2502d_X3A_X2502d_X3A_X2502d, "%04d-%02d-%02dT%02d:%02d:%02d", 3425528601u, 0, 29, NULL);
-be_define_const_str(_X25s_X2Eautoconf, "%s.autoconf", 3560383524u, 0, 11, &be_const_str_size);
-be_define_const_str(_X26lt_X3BError_X3A_X20apply_X20new_X20or_X20remove_X26gt_X3B, "<Error: apply new or remove>", 2855507949u, 0, 34, &be_const_str_Tele);
-be_define_const_str(_X26lt_X3BNone_X26gt_X3B, "<None>", 2602165498u, 0, 12, &be_const_str_light);
-be_define_const_str(_X28_X29, "()", 685372826u, 0, 2, &be_const_str_CFG_X3A_X20loading_X20_X27_X25s_X27);
-be_define_const_str(_X2B, "+", 772578730u, 0, 1, &be_const_str_percentage);
-be_define_const_str(_X2C, ",", 688690635u, 0, 1, &be_const_str_argument_X20must_X20be_X20a_X20function);
-be_define_const_str(_X2D_X2D_X3A_X2D_X2D, "--:--", 1370615441u, 0, 5, &be_const_str_check_not_method);
-be_define_const_str(_X2E, ".", 722245873u, 0, 1, &be_const_str_content_send);
-be_define_const_str(_X2E_X2E, "..", 2748622605u, 0, 2, &be_const_str_SERIAL_7O2);
-be_define_const_str(_X2Eautoconf, ".autoconf", 2524679088u, 0, 9, &be_const_str_CFG_X3A_X20loading_X20);
-be_define_const_str(_X2Ebe, ".be", 1325797348u, 0, 3, &be_const_str_call);
-be_define_const_str(_X2Ebec, ".bec", 3985273221u, 0, 4, &be_const_str_geti);
-be_define_const_str(_X2Elen, ".len", 850842136u, 0, 4, &be_const_str__X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E);
-be_define_const_str(_X2Ep, ".p", 1171526419u, 0, 2, &be_const_str_delete_all_configs);
-be_define_const_str(_X2Ep1, ".p1", 249175686u, 0, 3, &be_const_str_dac_voltage);
-be_define_const_str(_X2Ep2, ".p2", 232398067u, 0, 3, &be_const_str_tasmota);
-be_define_const_str(_X2Esize, ".size", 1965188224u, 0, 5, &be_const_str_line_dsc);
-be_define_const_str(_X2Etapp, ".tapp", 1363391594u, 0, 5, NULL);
-be_define_const_str(_X2Ew, ".w", 1255414514u, 0, 2, NULL);
-be_define_const_str(_X2F, "/", 705468254u, 0, 1, &be_const_str_reset_search);
-be_define_const_str(_X2F_X2Eautoconf, "/.autoconf", 2212074393u, 0, 10, &be_const_str_from_to);
-be_define_const_str(_X2F_X3Frst_X3D, "/?rst=", 580074707u, 0, 6, &be_const_str_name);
-be_define_const_str(_X2Fac, "/ac", 3904651978u, 0, 3, &be_const_str_CFG_X3A_X20could_X20not_X20run_X20_X25s_X20_X28_X25s_X20_X2D_X20_X25s_X29);
-be_define_const_str(_X3A, ":", 1057798253u, 0, 1, NULL);
-be_define_const_str(_X3C, "<", 957132539u, 0, 1, &be_const_str_BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29);
-be_define_const_str(_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "
", 3546571739u, 0, 11, &be_const_str_i2c_enabled);
-be_define_const_str(_X3C_X2Fselect_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "", 1863865923u, 0, 16, &be_const_str_pc);
-be_define_const_str(_X3C_X3D, "<=", 2499223986u, 0, 2, NULL);
-be_define_const_str(_X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, "", 3147934216u, 0, 82, &be_const_str_cb_obj);
-be_define_const_str(_X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, "", 1205771629u, 0, 72, &be_const_str_AudioFileSourceFS);
-be_define_const_str(_X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, "", 2052843416u, 0, 25, NULL);
+be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3E_X26_X23129668_X3B_X20Auto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "", 452285201u, 0, 120, &be_const_str_gc);
+be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, "