2017-05-20 13:03:34 +01:00
|
|
|
/*
|
2019-10-27 10:13:24 +00:00
|
|
|
xsns_01_counter.ino - Counter sensors (water meters, electricity meters etc.) sensor support for Tasmota
|
2017-05-20 13:03:34 +01:00
|
|
|
|
2021-01-01 12:44:04 +00:00
|
|
|
Copyright (C) 2021 Maarten Damen and Theo Arends
|
2022-02-25 19:25:16 +00:00
|
|
|
Stefan Bode (Zero-Cross Dimmer)
|
2017-05-20 13:03:34 +01:00
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2019-06-16 15:43:23 +01:00
|
|
|
#ifdef USE_COUNTER
|
2017-05-20 13:03:34 +01:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* Counter sensors (water meters, electricity meters etc.)
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2018-11-06 16:33:51 +00:00
|
|
|
#define XSNS_01 1
|
|
|
|
|
2019-08-11 17:12:18 +01:00
|
|
|
#define D_PRFX_COUNTER "Counter"
|
|
|
|
#define D_CMND_COUNTERTYPE "Type"
|
|
|
|
#define D_CMND_COUNTERDEBOUNCE "Debounce"
|
2020-03-29 16:05:52 +01:00
|
|
|
#define D_CMND_COUNTERDEBOUNCELOW "DebounceLow"
|
|
|
|
#define D_CMND_COUNTERDEBOUNCEHIGH "DebounceHigh"
|
2019-08-11 14:18:11 +01:00
|
|
|
|
2019-08-11 17:12:18 +01:00
|
|
|
const char kCounterCommands[] PROGMEM = D_PRFX_COUNTER "|" // Prefix
|
2020-03-29 16:05:52 +01:00
|
|
|
"|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE "|" D_CMND_COUNTERDEBOUNCELOW "|" D_CMND_COUNTERDEBOUNCEHIGH ;
|
2019-08-11 17:12:18 +01:00
|
|
|
|
2019-11-24 11:24:35 +00:00
|
|
|
void (* const CounterCommand[])(void) PROGMEM = {
|
2020-03-29 16:05:52 +01:00
|
|
|
&CmndCounter, &CmndCounterType, &CmndCounterDebounce, &CmndCounterDebounceLow, &CmndCounterDebounceHigh };
|
2019-08-11 14:18:11 +01:00
|
|
|
|
2020-07-17 11:37:21 +01:00
|
|
|
uint8_t ctr_index[MAX_COUNTERS] = { 0, 1, 2, 3 };
|
|
|
|
|
2019-10-22 13:56:06 +01:00
|
|
|
struct COUNTER {
|
|
|
|
uint32_t timer[MAX_COUNTERS]; // Last counter time in micro seconds
|
2020-03-29 16:05:52 +01:00
|
|
|
uint32_t timer_low_high[MAX_COUNTERS]; // Last low/high counter time in micro seconds
|
2019-10-22 13:56:06 +01:00
|
|
|
uint8_t no_pullup = 0; // Counter input pullup flag (1 = No pullup)
|
2020-03-29 16:05:52 +01:00
|
|
|
uint8_t pin_state = 0; // LSB0..3 Last state of counter pin; LSB7==0 IRQ is FALLING, LSB7==1 IRQ is CHANGE
|
2019-10-22 13:56:06 +01:00
|
|
|
bool any_counter = false;
|
2020-08-12 08:18:05 +01:00
|
|
|
|
2019-10-22 13:56:06 +01:00
|
|
|
} Counter;
|
2017-05-20 13:03:34 +01:00
|
|
|
|
2020-08-12 08:18:05 +01:00
|
|
|
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
|
|
|
struct AC_ZERO_CROSS_DIMMER {
|
2022-02-28 15:06:49 +00:00
|
|
|
bool startReSync = false; // set to TRUE if zero-cross event occurs
|
|
|
|
bool startMeasurePhase[MAX_COUNTERS] ; // set to TRUE if channel is ON and zero-cross occurs to initiate phase measure on channel
|
2022-03-13 10:30:05 +00:00
|
|
|
bool pwm_defined[MAX_COUNTERS]; // check if all GPIO are set and zerocross enabled. Then ADD dimmer.
|
2022-02-28 15:06:49 +00:00
|
|
|
bool PWM_ON[MAX_COUNTERS] ; // internal ON/OFF of the channel
|
|
|
|
uint32_t current_cycle_ClockCycles = 0; // amount of clock cycles between two zero-cross events.
|
|
|
|
uint32_t currentPWMCycleCount[MAX_COUNTERS] ; // clock cycle time of PWM channel, required to measure actual phase. [3] is phase of zero-cross
|
|
|
|
int16_t currentShiftClockCycle[MAX_COUNTERS]; // dynamic phase correction per channel in clock cycles
|
|
|
|
uint32_t tobe_cycle_timeClockCycles = 0; // clock cycles between zero-cross events. Depend on main frequency and CPU speed
|
2022-03-13 10:30:05 +00:00
|
|
|
uint32_t lastCycleCount = 0; // Last value of GetCycleCount during zero-cross sychronisation
|
|
|
|
uint32_t currentSteps = 100; // dynamic value of zero-crosses between two sychronisation intervalls (default=20 == 200ms at 100Hz)
|
|
|
|
uint32_t high; // cycle counts for PWM high vaule. needs long enough (4µs) to secure fire TRIAC
|
2020-08-12 08:18:05 +01:00
|
|
|
} ac_zero_cross_dimmer;
|
2021-08-28 21:17:52 +01:00
|
|
|
#endif //USE_AC_ZERO_CROSS_DIMMER
|
2020-07-01 16:46:13 +01:00
|
|
|
|
2021-04-02 16:14:08 +01:00
|
|
|
void IRAM_ATTR CounterIsrArg(void *arg) {
|
2020-07-17 11:37:21 +01:00
|
|
|
uint32_t index = *static_cast<uint8_t*>(arg);
|
|
|
|
|
2019-10-22 13:56:06 +01:00
|
|
|
uint32_t time = micros();
|
2020-03-29 16:05:52 +01:00
|
|
|
uint32_t debounce_time;
|
|
|
|
|
|
|
|
if (Counter.pin_state) {
|
|
|
|
// handle low and high debounce times when configured
|
2020-04-27 10:54:23 +01:00
|
|
|
if (digitalRead(Pin(GPIO_CNTR1, index)) == bitRead(Counter.pin_state, index)) {
|
2020-03-29 16:05:52 +01:00
|
|
|
// new pin state to be ignored because debounce time was not met during last IRQ
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
debounce_time = time - Counter.timer_low_high[index];
|
|
|
|
if bitRead(Counter.pin_state, index) {
|
|
|
|
// last valid pin state was high, current pin state is low
|
2021-06-11 17:14:12 +01:00
|
|
|
if (debounce_time <= Settings->pulse_counter_debounce_high * 1000) return;
|
2020-03-29 16:05:52 +01:00
|
|
|
} else {
|
|
|
|
// last valid pin state was low, current pin state is high
|
2021-06-11 17:14:12 +01:00
|
|
|
if (debounce_time <= Settings->pulse_counter_debounce_low * 1000) return;
|
2020-03-29 16:05:52 +01:00
|
|
|
}
|
|
|
|
// passed debounce check, save pin state and timing
|
|
|
|
Counter.timer_low_high[index] = time;
|
|
|
|
Counter.pin_state ^= (1<<index);
|
|
|
|
// do not count on rising edge
|
2020-07-01 16:46:13 +01:00
|
|
|
if bitRead(Counter.pin_state, index) {
|
|
|
|
// PWMfrequency 100
|
|
|
|
// restart PWM each second (german 50Hz has to up to 0.01% deviation)
|
2020-08-12 08:18:05 +01:00
|
|
|
// restart initiated by setting Counter.startReSync = true;
|
|
|
|
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
2022-02-28 15:06:49 +00:00
|
|
|
// if zero-cross events occur ond channel is on. phase on PWM must be measured
|
2022-03-12 16:43:53 +00:00
|
|
|
if ( ac_zero_cross_dimmer.startMeasurePhase[index] == true ) {
|
2022-02-25 18:49:06 +00:00
|
|
|
ac_zero_cross_dimmer.currentPWMCycleCount[index] = ESP.getCycleCount();
|
|
|
|
ac_zero_cross_dimmer.startMeasurePhase[index] = false;
|
|
|
|
}
|
2022-03-13 10:30:05 +00:00
|
|
|
// if zero-cross event occurs (200ms window, 5-times a second) and device is online for >10sec
|
2022-03-12 16:43:53 +00:00
|
|
|
if (index == 3 && RtcSettings.pulse_counter[index]%(Settings->pwm_frequency / 5) == 0 && ac_zero_cross_dimmer.pwm_defined[index] && millis() > 10000) {
|
2022-02-25 18:49:06 +00:00
|
|
|
ac_zero_cross_dimmer.currentPWMCycleCount[index] = ESP.getCycleCount();
|
2022-03-13 10:30:05 +00:00
|
|
|
|
2022-02-25 18:49:06 +00:00
|
|
|
if (ac_zero_cross_dimmer.lastCycleCount > 0) {
|
2022-02-28 15:06:49 +00:00
|
|
|
// start phase measure on PWM channels and initiate phase sync with zero-cross.
|
2020-08-12 08:18:05 +01:00
|
|
|
ac_zero_cross_dimmer.startReSync = true;
|
2022-02-25 18:49:06 +00:00
|
|
|
for (uint8_t k=0; k < MAX_COUNTERS-1; k++ ) {
|
2022-02-28 15:06:49 +00:00
|
|
|
if (ac_zero_cross_dimmer.PWM_ON[k] == true) ac_zero_cross_dimmer.startMeasurePhase[k] = true;
|
2022-02-25 18:49:06 +00:00
|
|
|
}
|
|
|
|
ac_zero_cross_dimmer.currentSteps = (ac_zero_cross_dimmer.currentPWMCycleCount[index]-ac_zero_cross_dimmer.lastCycleCount+(ac_zero_cross_dimmer.tobe_cycle_timeClockCycles/2))/(ac_zero_cross_dimmer.tobe_cycle_timeClockCycles);
|
2022-02-28 15:06:49 +00:00
|
|
|
ac_zero_cross_dimmer.current_cycle_ClockCycles = (ac_zero_cross_dimmer.currentPWMCycleCount[index]-ac_zero_cross_dimmer.lastCycleCount)/ac_zero_cross_dimmer.currentSteps;
|
2020-07-01 16:46:13 +01:00
|
|
|
}
|
2022-02-25 18:49:06 +00:00
|
|
|
ac_zero_cross_dimmer.lastCycleCount = ac_zero_cross_dimmer.currentPWMCycleCount[index];
|
2020-07-01 16:46:13 +01:00
|
|
|
}
|
2022-02-25 18:49:06 +00:00
|
|
|
|
2021-08-28 21:17:52 +01:00
|
|
|
#endif //USE_AC_ZERO_CROSS_DIMMER
|
2020-07-01 16:46:13 +01:00
|
|
|
return;
|
|
|
|
}
|
2020-03-29 16:05:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
debounce_time = time - Counter.timer[index];
|
2021-06-11 17:14:12 +01:00
|
|
|
if (debounce_time > Settings->pulse_counter_debounce * 1000) {
|
2019-10-22 13:56:06 +01:00
|
|
|
Counter.timer[index] = time;
|
2021-06-11 17:14:12 +01:00
|
|
|
if (bitRead(Settings->pulse_counter_type, index)) {
|
2019-10-22 13:56:06 +01:00
|
|
|
RtcSettings.pulse_counter[index] = debounce_time;
|
2017-05-20 13:03:34 +01:00
|
|
|
} else {
|
2019-10-22 13:56:06 +01:00
|
|
|
RtcSettings.pulse_counter[index]++;
|
2017-05-20 13:03:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-16 19:11:12 +00:00
|
|
|
/********************************************************************************************/
|
|
|
|
|
2022-02-25 18:49:06 +00:00
|
|
|
void CounterInterruptDisable(bool state)
|
|
|
|
{
|
2020-07-17 15:18:07 +01:00
|
|
|
if (state) { // Disable interrupts
|
|
|
|
if (Counter.any_counter) {
|
|
|
|
for (uint32_t i = 0; i < MAX_COUNTERS; i++) {
|
|
|
|
if (PinUsed(GPIO_CNTR1, i)) {
|
|
|
|
detachInterrupt(Pin(GPIO_CNTR1, i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Counter.any_counter = false;
|
|
|
|
}
|
|
|
|
} else { // Enable interrupts
|
|
|
|
if (!Counter.any_counter) {
|
|
|
|
CounterInit();
|
|
|
|
}
|
|
|
|
}
|
2020-07-17 11:49:42 +01:00
|
|
|
}
|
|
|
|
|
2019-08-13 16:33:35 +01:00
|
|
|
bool CounterPinState(void)
|
2017-05-20 13:03:34 +01:00
|
|
|
{
|
2020-06-24 14:50:14 +01:00
|
|
|
if ((XdrvMailbox.index >= AGPIO(GPIO_CNTR1_NP)) && (XdrvMailbox.index < (AGPIO(GPIO_CNTR1_NP) + MAX_COUNTERS))) {
|
|
|
|
bitSet(Counter.no_pullup, XdrvMailbox.index - AGPIO(GPIO_CNTR1_NP));
|
|
|
|
XdrvMailbox.index -= (AGPIO(GPIO_CNTR1_NP) - AGPIO(GPIO_CNTR1));
|
2019-08-13 13:52:46 +01:00
|
|
|
return true;
|
2017-05-20 13:03:34 +01:00
|
|
|
}
|
2019-08-13 13:52:46 +01:00
|
|
|
return false;
|
2017-05-20 13:03:34 +01:00
|
|
|
}
|
|
|
|
|
2018-11-14 13:32:09 +00:00
|
|
|
void CounterInit(void)
|
2017-05-20 13:03:34 +01:00
|
|
|
{
|
2019-06-30 15:44:36 +01:00
|
|
|
for (uint32_t i = 0; i < MAX_COUNTERS; i++) {
|
2020-04-27 11:54:07 +01:00
|
|
|
if (PinUsed(GPIO_CNTR1, i)) {
|
2020-08-12 08:18:05 +01:00
|
|
|
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
2022-03-12 16:43:53 +00:00
|
|
|
if (Settings->flag4.zerocross_dimmer) {
|
|
|
|
ac_zero_cross_dimmer.current_cycle_ClockCycles = ac_zero_cross_dimmer.tobe_cycle_timeClockCycles = microsecondsToClockCycles(1000000 / Settings->pwm_frequency);
|
|
|
|
// short fire on PWM to ensure not to hit next sinus curve but trigger the TRIAC. 0.78% of duty cycle (10ms) ~4µs
|
|
|
|
ac_zero_cross_dimmer.high = ac_zero_cross_dimmer.current_cycle_ClockCycles / 256;
|
2022-03-13 10:30:05 +00:00
|
|
|
|
|
|
|
// Support for dimmer 1-3. Counter4 reseverd for zero-cross signal
|
2022-03-12 16:43:53 +00:00
|
|
|
if ((i < MAX_COUNTERS-1 && PinUsed(GPIO_PWM1, i)) || ( i == MAX_COUNTERS-1) ) {
|
|
|
|
ac_zero_cross_dimmer.pwm_defined[i] = true;
|
2022-03-13 10:14:40 +00:00
|
|
|
if (i == 3) {
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("ZeroCross initialized"));
|
|
|
|
} else {
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("Dimmer: [%d] initialized. READY. Dimmer %d"), i+1, Light.fade_running ? Light.fade_cur_10[i] : Light.fade_start_10[i]);
|
|
|
|
}
|
|
|
|
|
2022-03-12 16:43:53 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-28 21:17:52 +01:00
|
|
|
#endif //USE_AC_ZERO_CROSS_DIMMER
|
2019-10-22 13:56:06 +01:00
|
|
|
Counter.any_counter = true;
|
2020-04-27 10:54:23 +01:00
|
|
|
pinMode(Pin(GPIO_CNTR1, i), bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP);
|
2021-06-11 17:14:12 +01:00
|
|
|
if ((0 == Settings->pulse_counter_debounce_low) && (0 == Settings->pulse_counter_debounce_high) && !Settings->flag4.zerocross_dimmer) {
|
2020-03-29 16:05:52 +01:00
|
|
|
Counter.pin_state = 0;
|
2020-07-17 11:37:21 +01:00
|
|
|
attachInterruptArg(Pin(GPIO_CNTR1, i), CounterIsrArg, &ctr_index[i], FALLING);
|
2020-03-29 16:05:52 +01:00
|
|
|
} else {
|
|
|
|
Counter.pin_state = 0x8f;
|
2020-07-17 11:37:21 +01:00
|
|
|
attachInterruptArg(Pin(GPIO_CNTR1, i), CounterIsrArg, &ctr_index[i], CHANGE);
|
2020-03-29 16:05:52 +01:00
|
|
|
}
|
2017-05-20 13:03:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-22 13:56:06 +01:00
|
|
|
void CounterEverySecond(void)
|
|
|
|
{
|
|
|
|
for (uint32_t i = 0; i < MAX_COUNTERS; i++) {
|
2020-04-27 11:54:07 +01:00
|
|
|
if (PinUsed(GPIO_CNTR1, i)) {
|
2021-06-11 17:14:12 +01:00
|
|
|
if (bitRead(Settings->pulse_counter_type, i)) {
|
2019-10-22 13:56:06 +01:00
|
|
|
uint32_t time = micros() - Counter.timer[i];
|
|
|
|
if (time > 4200000000) { // 70 minutes
|
|
|
|
RtcSettings.pulse_counter[i] = 4200000000; // Set Timer to max in case of no more interrupts due to stall of measured device
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-13 13:52:46 +01:00
|
|
|
void CounterSaveState(void)
|
|
|
|
{
|
|
|
|
for (uint32_t i = 0; i < MAX_COUNTERS; i++) {
|
2020-04-27 11:54:07 +01:00
|
|
|
if (PinUsed(GPIO_CNTR1, i)) {
|
2021-06-11 17:14:12 +01:00
|
|
|
Settings->pulse_counter[i] = RtcSettings.pulse_counter[i];
|
2019-08-13 13:52:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-28 13:08:33 +00:00
|
|
|
void CounterShow(bool json)
|
2017-05-20 13:03:34 +01:00
|
|
|
{
|
2019-08-11 14:18:11 +01:00
|
|
|
bool header = false;
|
2019-01-28 13:08:33 +00:00
|
|
|
uint8_t dsxflg = 0;
|
2019-06-30 15:44:36 +01:00
|
|
|
for (uint32_t i = 0; i < MAX_COUNTERS; i++) {
|
2020-04-27 11:54:07 +01:00
|
|
|
if (PinUsed(GPIO_CNTR1, i)) {
|
2018-12-21 15:17:06 +00:00
|
|
|
char counter[33];
|
2021-06-11 17:14:12 +01:00
|
|
|
if (bitRead(Settings->pulse_counter_type, i)) {
|
2018-08-09 12:24:13 +01:00
|
|
|
dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter);
|
2017-05-20 13:03:34 +01:00
|
|
|
} else {
|
|
|
|
dsxflg++;
|
2019-02-18 10:06:16 +00:00
|
|
|
snprintf_P(counter, sizeof(counter), PSTR("%lu"), RtcSettings.pulse_counter[i]);
|
2017-05-20 13:03:34 +01:00
|
|
|
}
|
2017-11-04 15:36:51 +00:00
|
|
|
|
|
|
|
if (json) {
|
2018-04-28 14:55:38 +01:00
|
|
|
if (!header) {
|
2019-03-23 16:57:31 +00:00
|
|
|
ResponseAppend_P(PSTR(",\"COUNTER\":{"));
|
2018-04-28 14:55:38 +01:00
|
|
|
}
|
2019-08-11 14:18:11 +01:00
|
|
|
ResponseAppend_P(PSTR("%s\"C%d\":%s"), (header)?",":"", i +1, counter);
|
|
|
|
header = true;
|
2017-05-20 13:03:34 +01:00
|
|
|
#ifdef USE_DOMOTICZ
|
2020-10-29 12:37:09 +00:00
|
|
|
if ((0 == TasmotaGlobal.tele_period) && (1 == dsxflg)) {
|
2017-11-04 15:36:51 +00:00
|
|
|
DomoticzSensor(DZ_COUNT, RtcSettings.pulse_counter[i]);
|
|
|
|
dsxflg++;
|
|
|
|
}
|
2017-05-20 13:03:34 +01:00
|
|
|
#endif // USE_DOMOTICZ
|
2021-06-11 17:14:12 +01:00
|
|
|
if ((0 == TasmotaGlobal.tele_period ) && (Settings->flag3.counter_reset_on_tele)) {
|
2019-12-28 15:26:15 +00:00
|
|
|
RtcSettings.pulse_counter[i] = 0;
|
|
|
|
}
|
2017-05-20 13:03:34 +01:00
|
|
|
#ifdef USE_WEBSERVER
|
|
|
|
} else {
|
2019-08-11 14:18:11 +01:00
|
|
|
WSContentSend_PD(PSTR("{s}" D_COUNTER "%d{m}%s%s{e}"),
|
2021-06-11 17:14:12 +01:00
|
|
|
i +1, counter, (bitRead(Settings->pulse_counter_type, i)) ? " " D_UNIT_SECOND : "");
|
2017-11-04 15:36:51 +00:00
|
|
|
#endif // USE_WEBSERVER
|
2017-05-20 13:03:34 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-11 14:18:11 +01:00
|
|
|
if (header) {
|
|
|
|
ResponseJsonEnd();
|
2018-04-28 14:55:38 +01:00
|
|
|
}
|
2017-05-20 13:03:34 +01:00
|
|
|
}
|
|
|
|
|
2020-08-12 08:18:05 +01:00
|
|
|
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
|
|
|
void SyncACDimmer(void)
|
|
|
|
{
|
2022-02-25 18:49:06 +00:00
|
|
|
if (ac_zero_cross_dimmer.startReSync ) {
|
2020-08-12 08:18:05 +01:00
|
|
|
// currently only support one AC Dimmer PWM. Plan to support up to 4 Dimmer on same Phase.
|
2022-02-25 18:49:06 +00:00
|
|
|
for (uint32_t i = 0; i < MAX_COUNTERS-1; i++) {
|
2022-02-28 15:06:49 +00:00
|
|
|
if (Light.fade_start_10[i] == 0 && Light.fade_cur_10[i] == 0 && ac_zero_cross_dimmer.PWM_ON[i]==false ) continue;
|
2022-03-12 16:43:53 +00:00
|
|
|
if (ac_zero_cross_dimmer.pwm_defined[i] && (ac_zero_cross_dimmer.startMeasurePhase[i] == 0 || ac_zero_cross_dimmer.PWM_ON[i] == false ) )
|
2020-08-12 08:18:05 +01:00
|
|
|
{
|
2022-02-25 18:49:06 +00:00
|
|
|
uint32_t phaseStart_ActualClockCycles; // As-Is positon of PWM after Zero Cross
|
|
|
|
uint32_t phaseStart_ToBeClockCycles; // To be position after zero-cross to fire PWM start
|
2022-02-28 15:06:49 +00:00
|
|
|
int16_t phaseShift_ClockCycles; //
|
2022-02-25 18:49:06 +00:00
|
|
|
|
|
|
|
|
2020-08-12 08:18:05 +01:00
|
|
|
// reset trigger for PWM sync
|
|
|
|
ac_zero_cross_dimmer.startReSync = false;
|
2022-02-25 18:49:06 +00:00
|
|
|
// calculate timeoffset to fire PWM based on Dimmer
|
2022-11-27 16:13:23 +00:00
|
|
|
phaseStart_ToBeClockCycles = (ac_zero_cross_dimmer.tobe_cycle_timeClockCycles * (1024 - ac_zero_cross_power(Light.fade_running ? Light.fade_cur_10[i] : Light.fade_start_10[i]))) / 1024;
|
2022-02-25 18:49:06 +00:00
|
|
|
|
|
|
|
// Limit range to avoid overshoot and undershoot
|
|
|
|
phaseStart_ToBeClockCycles = tmin(tmax(phaseStart_ToBeClockCycles, 160000), 0.95* ac_zero_cross_dimmer.tobe_cycle_timeClockCycles);
|
|
|
|
|
|
|
|
// Switch OFF dimmer
|
|
|
|
if (Light.fade_start_10[i] == 0 && !Light.fade_running) {
|
|
|
|
ac_zero_cross_dimmer.PWM_ON[i]=false;
|
2022-02-28 15:06:49 +00:00
|
|
|
Light.fade_cur_10[i] = 0;
|
2022-02-25 18:49:06 +00:00
|
|
|
digitalWrite(Pin(GPIO_PWM1, i), LOW);
|
2022-02-28 15:06:49 +00:00
|
|
|
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CNT2: [%d], curr: %d, final: %d, fading: %d, phase-shift: %d, ON/OFF: %d"),i, Light.fade_cur_10[i], Light.fade_start_10[i], Light.fade_running, phaseStart_ToBeClockCycles,ac_zero_cross_dimmer.PWM_ON[i]);
|
|
|
|
continue;
|
2022-02-25 18:49:06 +00:00
|
|
|
}
|
|
|
|
// Calculyte clockcycles between zero-cross [3] and start of the current PWM signal [i]
|
|
|
|
phaseStart_ActualClockCycles = ac_zero_cross_dimmer.currentPWMCycleCount[i]-ac_zero_cross_dimmer.currentPWMCycleCount[3];
|
|
|
|
|
|
|
|
// Calulate additional or less clockcycles to move current phase position to should be position
|
|
|
|
phaseShift_ClockCycles = (int32_t)((int32_t)phaseStart_ToBeClockCycles-(int32_t)phaseStart_ActualClockCycles)/100;
|
2022-02-28 15:47:21 +00:00
|
|
|
|
|
|
|
if ( ac_zero_cross_dimmer.PWM_ON[i] == 0 ) {
|
|
|
|
// because in LOOP calculate the timelag to fire PWM correctly with zero-cross
|
|
|
|
uint32_t timelag_ClockCycles = (ESP.getCycleCount() - ac_zero_cross_dimmer.currentPWMCycleCount[3])%ac_zero_cross_dimmer.tobe_cycle_timeClockCycles;
|
|
|
|
timelag_ClockCycles = ((phaseStart_ToBeClockCycles + ac_zero_cross_dimmer.tobe_cycle_timeClockCycles) - timelag_ClockCycles)%ac_zero_cross_dimmer.tobe_cycle_timeClockCycles;
|
|
|
|
|
|
|
|
delayMicroseconds(clockCyclesToMicroseconds(timelag_ClockCycles));
|
|
|
|
ac_zero_cross_dimmer.PWM_ON[i]=true;
|
|
|
|
pinMode(Pin(GPIO_PWM1, i), OUTPUT);
|
|
|
|
} else {
|
2022-03-13 10:30:05 +00:00
|
|
|
// currentShiftClockCycle is an I-Controller (not PID) to realign the phase. grace time are 5 clock cycles
|
2022-02-28 15:47:21 +00:00
|
|
|
ac_zero_cross_dimmer.currentShiftClockCycle[i] += phaseShift_ClockCycles > 5 ? 1 : (phaseShift_ClockCycles < -5 ? -1 : 0);
|
|
|
|
ac_zero_cross_dimmer.current_cycle_ClockCycles += ac_zero_cross_dimmer.currentShiftClockCycle[i]+phaseShift_ClockCycles;
|
|
|
|
}
|
|
|
|
#ifdef ESP8266
|
|
|
|
// Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t
|
|
|
|
startWaveformClockCycles(Pin(GPIO_PWM1, i), ac_zero_cross_dimmer.high, ac_zero_cross_dimmer.current_cycle_ClockCycles - ac_zero_cross_dimmer.high, 0, -1, 0, true);
|
|
|
|
#endif // ESP8266
|
|
|
|
#ifdef ESP32
|
2022-03-13 10:30:05 +00:00
|
|
|
// Under investigation. Still not working
|
2022-02-28 15:47:21 +00:00
|
|
|
double esp32freq = 1000000.0 / clockCyclesToMicroseconds(ac_zero_cross_dimmer.current_cycle_ClockCycles);
|
|
|
|
ledcSetup(i, esp32freq, 10);
|
|
|
|
ledcAttachPin(Pin(GPIO_PWM1, i), i);
|
|
|
|
ledcWrite(i, 5);
|
|
|
|
|
|
|
|
#endif // ESP32
|
2022-02-25 18:49:06 +00:00
|
|
|
|
2022-11-27 16:13:23 +00:00
|
|
|
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CNT: [%d], shift: %d, dimm_time_CCs %d, phaseShift_CCs %d, currentPWMcylce: %lu, current_cycle_CC: %lu, lastcc %lu, currentSteps %lu, currDIM %lu, last delta:%lu"),
|
|
|
|
i, ac_zero_cross_dimmer.currentShiftClockCycle[i], phaseStart_ToBeClockCycles,phaseShift_ClockCycles,ac_zero_cross_dimmer.currentPWMCycleCount[i],ac_zero_cross_dimmer.current_cycle_ClockCycles , ac_zero_cross_dimmer.lastCycleCount, ac_zero_cross_dimmer.currentSteps, Light.fade_cur_10[i],phaseStart_ActualClockCycles); // Light fading
|
2022-02-28 15:06:49 +00:00
|
|
|
//AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CNT: [%d], curr: %d, final: %d, fading: %d, phase-shift: %d, ON/OFF: %d"),i, Light.fade_cur_10[i], Light.fade_start_10[i], Light.fade_running, phaseStart_ToBeClockCycles,ac_zero_cross_dimmer.PWM_ON[i]);
|
2022-02-25 18:49:06 +00:00
|
|
|
|
2022-02-28 15:47:21 +00:00
|
|
|
} // do sync onchannel
|
|
|
|
} // loop on counter
|
|
|
|
} // zero cross detected
|
|
|
|
} // end SyncACDimmer
|
2021-08-28 21:17:52 +01:00
|
|
|
#endif //USE_AC_ZERO_CROSS_DIMMER
|
2020-08-12 08:18:05 +01:00
|
|
|
|
2019-07-26 09:21:36 +01:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* Commands
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2019-08-11 14:18:11 +01:00
|
|
|
void CmndCounter(void)
|
2019-07-26 09:21:36 +01:00
|
|
|
{
|
2019-08-11 14:18:11 +01:00
|
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) {
|
2020-04-27 11:54:07 +01:00
|
|
|
if ((XdrvMailbox.data_len > 0) && PinUsed(GPIO_CNTR1, XdrvMailbox.index -1)) {
|
2019-08-11 14:18:11 +01:00
|
|
|
if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) {
|
|
|
|
RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload;
|
2021-06-11 17:14:12 +01:00
|
|
|
Settings->pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload;
|
2019-08-11 14:18:11 +01:00
|
|
|
} else {
|
|
|
|
RtcSettings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload;
|
2021-06-11 17:14:12 +01:00
|
|
|
Settings->pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload;
|
2019-07-26 09:21:36 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-11 17:12:18 +01:00
|
|
|
ResponseCmndIdxNumber(RtcSettings.pulse_counter[XdrvMailbox.index -1]);
|
2019-07-26 09:21:36 +01:00
|
|
|
}
|
2019-08-11 14:18:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CmndCounterType(void)
|
|
|
|
{
|
|
|
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) {
|
2020-04-27 11:54:07 +01:00
|
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && PinUsed(GPIO_CNTR1, XdrvMailbox.index -1)) {
|
2021-06-11 17:14:12 +01:00
|
|
|
bitWrite(Settings->pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1);
|
2019-08-11 14:18:11 +01:00
|
|
|
RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0;
|
2021-06-11 17:14:12 +01:00
|
|
|
Settings->pulse_counter[XdrvMailbox.index -1] = 0;
|
2019-07-26 09:21:36 +01:00
|
|
|
}
|
2021-06-11 17:14:12 +01:00
|
|
|
ResponseCmndIdxNumber(bitRead(Settings->pulse_counter_type, XdrvMailbox.index -1));
|
2019-07-26 09:21:36 +01:00
|
|
|
}
|
2019-08-11 14:18:11 +01:00
|
|
|
}
|
2019-07-26 09:21:36 +01:00
|
|
|
|
2019-08-11 14:18:11 +01:00
|
|
|
void CmndCounterDebounce(void)
|
|
|
|
{
|
|
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) {
|
2021-06-11 17:14:12 +01:00
|
|
|
Settings->pulse_counter_debounce = XdrvMailbox.payload;
|
2019-08-11 14:18:11 +01:00
|
|
|
}
|
2021-06-11 17:14:12 +01:00
|
|
|
ResponseCmndNumber(Settings->pulse_counter_debounce);
|
2019-07-26 09:21:36 +01:00
|
|
|
}
|
|
|
|
|
2020-03-29 16:05:52 +01:00
|
|
|
void CmndCounterDebounceLow(void)
|
|
|
|
{
|
|
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) {
|
2021-06-11 17:14:12 +01:00
|
|
|
Settings->pulse_counter_debounce_low = XdrvMailbox.payload;
|
2020-03-29 16:05:52 +01:00
|
|
|
CounterInit();
|
|
|
|
}
|
2021-06-11 17:14:12 +01:00
|
|
|
ResponseCmndNumber(Settings->pulse_counter_debounce_low);
|
2020-03-29 16:05:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CmndCounterDebounceHigh(void)
|
|
|
|
{
|
|
|
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) {
|
2021-06-11 17:14:12 +01:00
|
|
|
Settings->pulse_counter_debounce_high = XdrvMailbox.payload;
|
2020-03-29 16:05:52 +01:00
|
|
|
CounterInit();
|
|
|
|
}
|
2021-06-11 17:14:12 +01:00
|
|
|
ResponseCmndNumber(Settings->pulse_counter_debounce_high);
|
2020-03-29 16:05:52 +01:00
|
|
|
}
|
|
|
|
|
2017-11-03 17:07:25 +00:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* Interface
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2022-11-11 09:44:56 +00:00
|
|
|
bool Xsns01(uint32_t function)
|
2017-11-03 17:07:25 +00:00
|
|
|
{
|
2019-01-28 13:08:33 +00:00
|
|
|
bool result = false;
|
2017-11-03 17:07:25 +00:00
|
|
|
|
2019-10-22 13:56:06 +01:00
|
|
|
if (Counter.any_counter) {
|
|
|
|
switch (function) {
|
|
|
|
case FUNC_EVERY_SECOND:
|
|
|
|
CounterEverySecond();
|
|
|
|
break;
|
|
|
|
case FUNC_JSON_APPEND:
|
|
|
|
CounterShow(1);
|
|
|
|
break;
|
2020-08-12 08:18:05 +01:00
|
|
|
#ifdef USE_AC_ZERO_CROSS_DIMMER
|
2022-02-28 15:06:49 +00:00
|
|
|
case FUNC_EVERY_50_MSECOND:
|
2020-08-12 08:18:05 +01:00
|
|
|
SyncACDimmer();
|
|
|
|
break;
|
2021-08-28 21:17:52 +01:00
|
|
|
#endif //USE_AC_ZERO_CROSS_DIMMER
|
2017-11-03 17:07:25 +00:00
|
|
|
#ifdef USE_WEBSERVER
|
2019-10-22 13:56:06 +01:00
|
|
|
case FUNC_WEB_SENSOR:
|
|
|
|
CounterShow(0);
|
|
|
|
break;
|
2017-11-03 17:07:25 +00:00
|
|
|
#endif // USE_WEBSERVER
|
2019-10-22 13:56:06 +01:00
|
|
|
case FUNC_SAVE_BEFORE_RESTART:
|
|
|
|
case FUNC_SAVE_AT_MIDNIGHT:
|
|
|
|
CounterSaveState();
|
|
|
|
break;
|
|
|
|
case FUNC_COMMAND:
|
|
|
|
result = DecodeCommand(kCounterCommands, CounterCommand);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (function) {
|
|
|
|
case FUNC_INIT:
|
|
|
|
CounterInit();
|
|
|
|
break;
|
|
|
|
case FUNC_PIN_STATE:
|
|
|
|
result = CounterPinState();
|
|
|
|
break;
|
|
|
|
}
|
2017-11-03 17:07:25 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2019-06-16 15:43:23 +01:00
|
|
|
|
|
|
|
#endif // USE_COUNTER
|