mirror of https://github.com/arendst/Tasmota.git
Analog GPIO changes
- Analog GPIO ``ADC Input`` with ``AdcParam<x> 1,<start_range>,<end_range>,<margin>,1`` provide direct light control - Analog GPIO ``ADC Voltage`` with ``AdcParam<x> 11,<start_range>,<end_range>,<lowest_voltage>,<highest_voltage>`` provide energy monitoring with dc voltage - Analog GPIO ``ADC Current`` with ``AdcParam<x> 12,<start_range>,<end_range>,<lowest_current>,<highest_current>`` provide energy monitoring with dc voltage
This commit is contained in:
parent
87ec0cce21
commit
88293d7414
|
@ -10,12 +10,15 @@ All notable changes to this project will be documented in this file.
|
|||
- Matter support for split lights (`SetOption68 1` and `SetOption37 128`) (#21834)
|
||||
- Berry `webserver_async` (#21836)
|
||||
- NeoPool command `NPSetOption<x>` to enabled/disable data validation/connection statistics (#21850)
|
||||
- Analog GPIO ``ADC Input`` with ``AdcParam<x> 1,<start_range>,<end_range>,<margin>,1`` provide direct light control
|
||||
- Analog GPIO ``ADC Voltage`` with ``AdcParam<x> 11,<start_range>,<end_range>,<lowest_voltage>,<highest_voltage>`` provide energy monitoring with dc voltage
|
||||
- Analog GPIO ``ADC Current`` with ``AdcParam<x> 12,<start_range>,<end_range>,<lowest_current>,<highest_current>`` provide energy monitoring with dc voltage
|
||||
|
||||
### Breaking Changed
|
||||
|
||||
### Changed
|
||||
- Berry consolidated constants for solidified classes reduces Flash size (#2185)
|
||||
- Berry updated precompiled Windows binary
|
||||
- Berry updated precompiled Windows binary (#21858)
|
||||
|
||||
### Fixed
|
||||
- Berry `light.get` for separate RGB/CT (#21818)
|
||||
|
|
|
@ -126,6 +126,9 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
|
|||
- Support for Sonoff iFan04-H using template [#16402](https://github.com/arendst/Tasmota/issues/16402)
|
||||
- Support for Sonoff POWCT Ring [#21131](https://github.com/arendst/Tasmota/issues/21131)
|
||||
- Support for Wooliis Hall Effect Coulometer or Battery capacity monitor [#21732](https://github.com/arendst/Tasmota/issues/21732)
|
||||
- Analog GPIO ``ADC Input`` with ``AdcParam<x> 1,<start_range>,<end_range>,<margin>,1`` provide direct light control
|
||||
- Analog GPIO ``ADC Voltage`` with ``AdcParam<x> 11,<start_range>,<end_range>,<lowest_voltage>,<highest_voltage>`` provide energy monitoring with dc voltage
|
||||
- Analog GPIO ``ADC Current`` with ``AdcParam<x> 12,<start_range>,<end_range>,<lowest_current>,<highest_current>`` provide energy monitoring with dc voltage
|
||||
- Skip MQTT response if command is prefixed with underscore [#21740](https://github.com/arendst/Tasmota/issues/21740)
|
||||
- Skip MQTT response if commands are executed prefixed with ``Backlog2`` (no delay) or ``Backlog3`` [#21740](https://github.com/arendst/Tasmota/issues/21740)
|
||||
- Extend command ``SetOption147 1`` to disable publish of IRReceived MQTT messages [#21574](https://github.com/arendst/Tasmota/issues/21574)
|
||||
|
@ -170,6 +173,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
|
|||
- ESP32 MI32 refactoring, bugfixes, generic device scanning [#21603](https://github.com/arendst/Tasmota/issues/21603)
|
||||
- ESP32 MI32 improve parser [#21648](https://github.com/arendst/Tasmota/issues/21648)
|
||||
- ESP32 TM1621 number overflow from "9999" to "12E3" [#21131](https://github.com/arendst/Tasmota/issues/21131)
|
||||
- Berry updated precompiled Windows binary [#21858](https://github.com/arendst/Tasmota/issues/21858)
|
||||
- Berry simplified `module persist` [#21812](https://github.com/arendst/Tasmota/issues/21812)
|
||||
- Berry consolidated constants for solidified classes reduces Flash size [#2185](https://github.com/arendst/Tasmota/issues/2185)
|
||||
- Matter refactoring of bridged devices [#21575](https://github.com/arendst/Tasmota/issues/21575)
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#ifdef ESP8266
|
||||
#define ANALOG_RESOLUTION 10 // 12 = 4095, 11 = 2047, 10 = 1023
|
||||
#define ANALOG_RANGE 1023 // 4095 = 12, 2047 = 11, 1023 = 10
|
||||
#define ANALOG_PERCENT 10 // backward compatible div10 range
|
||||
#endif // ESP8266
|
||||
|
||||
#ifdef ESP32
|
||||
|
@ -42,8 +41,6 @@
|
|||
#define ANALOG_RESOLUTION 12 // 12 = 4095, 11 = 2047, 10 = 1023
|
||||
#undef ANALOG_RANGE
|
||||
#define ANALOG_RANGE 4095 // 4095 = 12, 2047 = 11, 1023 = 10
|
||||
#undef ANALOG_PERCENT
|
||||
#define ANALOG_PERCENT ((ANALOG_RANGE + 50) / 100) // approximation to 1% ADC range
|
||||
#endif // ESP32
|
||||
|
||||
#define TO_CELSIUS(x) ((x) - 273.15f)
|
||||
|
@ -212,52 +209,54 @@ void AdcGetSettings(uint32_t idx) {
|
|||
|
||||
void AdcInitParams(uint8_t idx) {
|
||||
if ((Adcs.type != Adc[idx].type) || (Adc[idx].param1 > 1000000)) {
|
||||
if (ADC_TEMP == Adc[idx].type) {
|
||||
// Default Shelly 2.5 and 1PM parameters
|
||||
Adc[idx].param1 = ANALOG_NTC_BRIDGE_RESISTANCE;
|
||||
Adc[idx].param2 = ANALOG_NTC_RESISTANCE;
|
||||
Adc[idx].param3 = ANALOG_NTC_B_COEFFICIENT * 10000;
|
||||
Adc[idx].param4 = 0; // Default to Shelly mode with NTC towards GND
|
||||
}
|
||||
else if (ADC_LIGHT == Adc[idx].type) {
|
||||
Adc[idx].param1 = ANALOG_LDR_BRIDGE_RESISTANCE;
|
||||
Adc[idx].param2 = ANALOG_LDR_LUX_CALC_SCALAR;
|
||||
Adc[idx].param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000;
|
||||
}
|
||||
else if (ADC_RANGE == Adc[idx].type) {
|
||||
Adc[idx].param1 = 0;
|
||||
Adc[idx].param2 = ANALOG_RANGE;
|
||||
Adc[idx].param3 = 0;
|
||||
Adc[idx].param4 = 100;
|
||||
}
|
||||
else if (ADC_CT_POWER == Adc[idx].type) {
|
||||
Adc[idx].param1 = ANALOG_CT_FLAGS; // (uint32_t) 0
|
||||
Adc[idx].param2 = ANALOG_CT_MULTIPLIER; // (uint32_t) 100000
|
||||
Adc[idx].param3 = ANALOG_CT_VOLTAGE; // (int) 10
|
||||
}
|
||||
else if (ADC_PH == Adc[idx].type) {
|
||||
Adc[idx].param1 = ANALOG_PH_CALSOLUTION_LOW_PH * ANALOG_PH_DECIMAL_MULTIPLIER; // PH of the calibration solution 1, which is the one with the lower PH
|
||||
Adc[idx].param2 = ANALOG_PH_CALSOLUTION_LOW_ANALOG_VALUE; // Reading of AnalogInput while probe is in solution 1
|
||||
Adc[idx].param3 = ANALOG_PH_CALSOLUTION_HIGH_PH * ANALOG_PH_DECIMAL_MULTIPLIER; // PH of the calibration solution 2, which is the one with the higher PH
|
||||
Adc[idx].param4 = ANALOG_PH_CALSOLUTION_HIGH_ANALOG_VALUE; // Reading of AnalogInput while probe is in solution 2
|
||||
}
|
||||
else if (ADC_MQ == Adc[idx].type) {
|
||||
Adc[idx].param1 = ANALOG_MQ_TYPE; // Could be MQ-002, MQ-004, MQ-131 ....
|
||||
Adc[idx].param2 = (int)(ANALOG_MQ_A * ANALOG_MQ_DECIMAL_MULTIPLIER); // Exponential regression
|
||||
Adc[idx].param3 = (int)(ANALOG_MQ_B * ANALOG_MQ_DECIMAL_MULTIPLIER); // Exponential regression
|
||||
Adc[idx].param4 = (int)(ANALOG_MQ_RatioMQCleanAir * ANALOG_MQ_DECIMAL_MULTIPLIER); // Exponential regression
|
||||
}
|
||||
else if (ADC_VOLTAGE == Adc[idx].type) {
|
||||
Adc[idx].param1 = 0;
|
||||
Adc[idx].param2 = ANALOG_RANGE;
|
||||
Adc[idx].param3 = 0;
|
||||
Adc[idx].param4 = ANALOG_V33 * 10000;
|
||||
}
|
||||
else if (ADC_CURRENT == Adc[idx].type) {
|
||||
Adc[idx].param1 = 0;
|
||||
Adc[idx].param2 = ANALOG_RANGE;
|
||||
Adc[idx].param3 = 0;
|
||||
Adc[idx].param4 = ANALOG_V33 * 10000;
|
||||
switch (Adc[idx].type) {
|
||||
case ADC_INPUT:
|
||||
Adc[idx].param1 = 0;
|
||||
Adc[idx].param2 = ANALOG_RANGE;
|
||||
Adc[idx].param3 = 3; // Margin
|
||||
Adc[idx].param4 = 0; // Default mode (0) or Direct mode (1) using Dimmer or Channel command
|
||||
case ADC_TEMP:
|
||||
// Default Shelly 2.5 and 1PM parameters
|
||||
Adc[idx].param1 = ANALOG_NTC_BRIDGE_RESISTANCE;
|
||||
Adc[idx].param2 = ANALOG_NTC_RESISTANCE;
|
||||
Adc[idx].param3 = ANALOG_NTC_B_COEFFICIENT * 10000;
|
||||
Adc[idx].param4 = 0; // Default to Shelly mode with NTC towards GND
|
||||
break;
|
||||
case ADC_LIGHT:
|
||||
Adc[idx].param1 = ANALOG_LDR_BRIDGE_RESISTANCE;
|
||||
Adc[idx].param2 = ANALOG_LDR_LUX_CALC_SCALAR;
|
||||
Adc[idx].param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000;
|
||||
break;
|
||||
case ADC_RANGE:
|
||||
Adc[idx].param1 = 0;
|
||||
Adc[idx].param2 = ANALOG_RANGE;
|
||||
Adc[idx].param3 = 0;
|
||||
Adc[idx].param4 = 100;
|
||||
break;
|
||||
case ADC_CT_POWER:
|
||||
Adc[idx].param1 = ANALOG_CT_FLAGS; // (uint32_t) 0
|
||||
Adc[idx].param2 = ANALOG_CT_MULTIPLIER; // (uint32_t) 100000
|
||||
Adc[idx].param3 = ANALOG_CT_VOLTAGE; // (int) 10
|
||||
break;
|
||||
case ADC_PH:
|
||||
Adc[idx].param1 = ANALOG_PH_CALSOLUTION_LOW_PH * ANALOG_PH_DECIMAL_MULTIPLIER; // PH of the calibration solution 1, which is the one with the lower PH
|
||||
Adc[idx].param2 = ANALOG_PH_CALSOLUTION_LOW_ANALOG_VALUE; // Reading of AnalogInput while probe is in solution 1
|
||||
Adc[idx].param3 = ANALOG_PH_CALSOLUTION_HIGH_PH * ANALOG_PH_DECIMAL_MULTIPLIER; // PH of the calibration solution 2, which is the one with the higher PH
|
||||
Adc[idx].param4 = ANALOG_PH_CALSOLUTION_HIGH_ANALOG_VALUE; // Reading of AnalogInput while probe is in solution 2
|
||||
break;
|
||||
case ADC_MQ:
|
||||
Adc[idx].param1 = ANALOG_MQ_TYPE; // Could be MQ-002, MQ-004, MQ-131 ....
|
||||
Adc[idx].param2 = (int)(ANALOG_MQ_A * ANALOG_MQ_DECIMAL_MULTIPLIER); // Exponential regression
|
||||
Adc[idx].param3 = (int)(ANALOG_MQ_B * ANALOG_MQ_DECIMAL_MULTIPLIER); // Exponential regression
|
||||
Adc[idx].param4 = (int)(ANALOG_MQ_RatioMQCleanAir * ANALOG_MQ_DECIMAL_MULTIPLIER); // Exponential regression
|
||||
break;
|
||||
case ADC_VOLTAGE:
|
||||
case ADC_CURRENT:
|
||||
Adc[idx].param1 = 0;
|
||||
Adc[idx].param2 = ANALOG_RANGE;
|
||||
Adc[idx].param3 = 0;
|
||||
Adc[idx].param4 = ANALOG_V33 * 10000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((Adcs.type != Adc[idx].type) || (0 == Adc[idx].param1) || (Adc[idx].param1 > ANALOG_RANGE)) {
|
||||
|
@ -385,7 +384,6 @@ uint16_t AdcRead(uint32_t pin, uint32_t factor) {
|
|||
return analog;
|
||||
}
|
||||
|
||||
#ifdef USE_RULES
|
||||
void AdcEvery250ms(void) {
|
||||
char adc_idx[3] = { 0 };
|
||||
uint32_t offset = 0;
|
||||
|
@ -395,12 +393,35 @@ void AdcEvery250ms(void) {
|
|||
offset = 1;
|
||||
#endif
|
||||
if (ADC_INPUT == Adc[idx].type) {
|
||||
uint16_t new_value = AdcRead(Adc[idx].pin, 5);
|
||||
if ((new_value < Adc[idx].last_value -ANALOG_PERCENT) || (new_value > Adc[idx].last_value +ANALOG_PERCENT)) {
|
||||
int adc = AdcRead(Adc[idx].pin, 4); // 4 = 16 mS
|
||||
int new_value = changeUIntScale(adc, Adc[idx].param1, Adc[idx].param2, 0, 100);
|
||||
if ((new_value < Adc[idx].last_value -Adc[idx].param3) ||
|
||||
(new_value > Adc[idx].last_value +Adc[idx].param3) ||
|
||||
((0 == new_value) && (Adc[idx].last_value != 0)) || // Lowest end
|
||||
((100 == new_value) && (Adc[idx].last_value != 100))) { // Highest end
|
||||
Adc[idx].last_value = new_value;
|
||||
uint16_t value = Adc[idx].last_value / ANALOG_PERCENT;
|
||||
Response_P(PSTR("{\"ANALOG\":{\"A%ddiv10\":%d}}"), idx + offset, (value > 99) ? 100 : value);
|
||||
XdrvRulesProcess(0);
|
||||
if (-1 == Adc[idx].indexOfPointer) {
|
||||
Adc[idx].indexOfPointer = 0;
|
||||
continue; // Skip action on restart
|
||||
}
|
||||
#ifdef USE_LIGHT
|
||||
if (0 == Adc[idx].param4) { // Default (0) or Direct mode (1)
|
||||
#endif // USE_LIGHT
|
||||
Response_P(PSTR("{\"ANALOG\":{\"A%ddiv10\":%d}}"), idx + offset, new_value);
|
||||
XdrvRulesProcess(0);
|
||||
#ifdef USE_LIGHT
|
||||
} else {
|
||||
char command[33];
|
||||
if (Settings->flag3.pwm_multi_channels) { // SetOption68 - Enable multi-channels PWM instead of Color PWM
|
||||
// snprintf_P(command, sizeof(command), PSTR("_" D_CMND_CHANNEL "%d %d"), idx +1, new_value);
|
||||
snprintf_P(command, sizeof(command), PSTR(D_CMND_CHANNEL "%d %d"), idx +1, new_value);
|
||||
} else {
|
||||
// snprintf_P(command, sizeof(command), PSTR("_" D_CMND_DIMMER "3 %d"), new_value);
|
||||
snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER "3 %d"), new_value); // Change both RGB and W(W) Dimmers with no fading
|
||||
}
|
||||
ExecuteCommand(command, SRC_SWITCH);
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
}
|
||||
}
|
||||
else if (ADC_JOY == Adc[idx].type) {
|
||||
|
@ -416,7 +437,6 @@ void AdcEvery250ms(void) {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif // USE_RULES
|
||||
|
||||
uint8_t AdcGetButton(uint32_t pin) {
|
||||
for (uint32_t idx = 0; idx < Adcs.present; idx++) {
|
||||
|
@ -641,16 +661,21 @@ void AdcShow(bool json) {
|
|||
|
||||
switch (Adc[idx].type) {
|
||||
case ADC_INPUT: {
|
||||
uint16_t analog = AdcRead(Adc[idx].pin, 5);
|
||||
|
||||
if (json) {
|
||||
AdcShowContinuation(&jsonflg);
|
||||
ResponseAppend_P(PSTR("\"A%d\":%d"), idx + offset, analog);
|
||||
#ifdef USE_LIGHT
|
||||
if (0 == Adc[idx].param4) { // Default (0) or Direct mode (1)
|
||||
#endif // USE_LIGHT
|
||||
uint16_t analog = AdcRead(Adc[idx].pin, 5);
|
||||
if (json) {
|
||||
AdcShowContinuation(&jsonflg);
|
||||
ResponseAppend_P(PSTR("\"A%d\":%d"), idx + offset, analog);
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_ANALOG, "", idx + offset, analog);
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_ANALOG, "", idx + offset, analog);
|
||||
#endif // USE_WEBSERVER
|
||||
}
|
||||
#ifdef USE_LIGHT
|
||||
}
|
||||
#endif // USE_LIGHT
|
||||
break;
|
||||
}
|
||||
case ADC_TEMP: {
|
||||
|
@ -815,10 +840,11 @@ void CmndAdcParam(void) {
|
|||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_ADCS)) {
|
||||
uint8_t idx = XdrvMailbox.index -1;
|
||||
if (XdrvMailbox.data_len) {
|
||||
if (XdrvMailbox.payload > ADC_INPUT) {
|
||||
if (XdrvMailbox.payload > 0) {
|
||||
AdcGetSettings(idx);
|
||||
if (ArgC() > 3) { // Process parameter entry
|
||||
char argument[XdrvMailbox.data_len];
|
||||
// AdcParam 1, 0, ANALOG_RANGE, 0 ADC_INPUT rule | dimmer
|
||||
// AdcParam 2, 32000, 10000, 3350 ADC_TEMP Shelly mode
|
||||
// AdcParam 2, 32000, 10000, 3350, 1 ADC_TEMP Alternate mode
|
||||
// AdcParam 3, 10000, 12518931, -1.405
|
||||
|
@ -834,7 +860,8 @@ void CmndAdcParam(void) {
|
|||
Adc[idx].type = XdrvMailbox.payload;
|
||||
Adc[idx].param1 = strtol(ArgV(argument, 2), nullptr, 10); // param1 = int
|
||||
Adc[idx].param2 = strtol(ArgV(argument, 3), nullptr, 10); // param2 = int
|
||||
if (ADC_RANGE == XdrvMailbox.payload) {
|
||||
if ((ADC_INPUT == XdrvMailbox.payload) ||
|
||||
(ADC_RANGE == XdrvMailbox.payload)) {
|
||||
Adc[idx].param3 = abs(strtol(ArgV(argument, 4), nullptr, 10)); // param3 = abs(int)
|
||||
Adc[idx].param4 = abs(strtol(ArgV(argument, 5), nullptr, 10)); // param4 = abs(int)
|
||||
} else {
|
||||
|
@ -897,13 +924,7 @@ void CmndAdcParam(void) {
|
|||
// AddLog(LOG_LEVEL_INFO, PSTR("ADC: MQ reset mq%d, a = %2_f, b = %2_f, ratioMQCleanAir = %2_f"), Adc[idx].param1, &a, &b, &ratioMQCleanAir);
|
||||
}
|
||||
} else { // Set default values based on current adc type
|
||||
// AdcParam 2
|
||||
// AdcParam 3
|
||||
// AdcParam 4
|
||||
// AdcParam 5
|
||||
// AdcParam 6
|
||||
// AdcParam 7
|
||||
// AdcParam 8
|
||||
// AdcParam 1
|
||||
Adcs.type = 0;
|
||||
AdcInitParams(idx);
|
||||
}
|
||||
|
@ -914,7 +935,9 @@ void CmndAdcParam(void) {
|
|||
// AdcParam
|
||||
AdcGetSettings(idx);
|
||||
Response_P(PSTR("{\"" D_CMND_ADCPARAM "%d\":[%d,%d,%d"), idx +1, Adcs.type, Adc[idx].param1, Adc[idx].param2);
|
||||
if ((ADC_RANGE == Adc[idx].type) || (ADC_MQ == Adc[idx].type)) {
|
||||
if ((ADC_INPUT == Adc[idx].type) ||
|
||||
(ADC_RANGE == Adc[idx].type) ||
|
||||
(ADC_MQ == Adc[idx].type)) {
|
||||
ResponseAppend_P(PSTR(",%d,%d"), Adc[idx].param3, Adc[idx].param4); // param3 = int, param4 = int
|
||||
}
|
||||
else {
|
||||
|
@ -980,11 +1003,9 @@ bool Xsns02(uint32_t function) {
|
|||
default:
|
||||
if (Adcs.present) {
|
||||
switch (function) {
|
||||
#ifdef USE_RULES
|
||||
case FUNC_EVERY_250_MSECOND:
|
||||
AdcEvery250ms();
|
||||
break;
|
||||
#endif // USE_RULES
|
||||
case FUNC_EVERY_SECOND:
|
||||
AdcEverySecond();
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue