Add experimental support for Shelly DALI Dimmer Gen3 (See template in file xdrv_75_dali.ino)

This commit is contained in:
Theo Arends 2024-10-24 23:03:14 +02:00
parent 80686a8c52
commit 886221a1d6
4 changed files with 64 additions and 35 deletions

View File

@ -12,6 +12,8 @@ All notable changes to this project will be documented in this file.
- Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC (#22345) - Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC (#22345)
- Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC (#22347) - Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC (#22347)
- SolaxX1 Meter mode (#22330) - SolaxX1 Meter mode (#22330)
- DALI inverted signal configuration using GPIO DALI RX_i/TX_i
- Experimental support for Shelly DALI Dimmer Gen3 (See template in file xdrv_75_dali.ino)
### Breaking Changed ### Breaking Changed
@ -24,6 +26,7 @@ All notable changes to this project will be documented in this file.
### Fixed ### Fixed
### Removed ### Removed
- DALI inverted signal configuration using compile time defines
## [14.3.0.1] 20241022 ## [14.3.0.1] 20241022
### Added ### Added

View File

@ -120,6 +120,8 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
- DALI command `DaliGear` to set max found gear to speed up scan response - DALI command `DaliGear` to set max found gear to speed up scan response
- DALI command `DaliGroup` to add gear to groups - DALI command `DaliGroup` to add gear to groups
- DALI command `DaliTarget` to set light control broadcast, group number or gear number - DALI command `DaliTarget` to set light control broadcast, group number or gear number
- DALI inverted signal configuration using GPIO DALI RX_i/TX_i
- Experimental support for Shelly DALI Dimmer Gen3 (See template in file xdrv_75_dali.ino)
- Mitsubishi Electric HVAC Operation time for MiElHVAC [#22334](https://github.com/arendst/Tasmota/issues/22334) - Mitsubishi Electric HVAC Operation time for MiElHVAC [#22334](https://github.com/arendst/Tasmota/issues/22334)
- Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC [#22345](https://github.com/arendst/Tasmota/issues/22345) - Mitsubishi Electric HVAC Outdoor Temperature for MiElHVAC [#22345](https://github.com/arendst/Tasmota/issues/22345)
- Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC [#22347](https://github.com/arendst/Tasmota/issues/22347) - Mitsubishi Electric HVAC Compressor Frequency for MiElHVAC [#22347](https://github.com/arendst/Tasmota/issues/22347)

View File

@ -224,6 +224,7 @@ enum UserSelectablePins {
GPIO_WOOLIIS_RX, // Wooliis Battery capacity monitor Serial RX GPIO_WOOLIIS_RX, // Wooliis Battery capacity monitor Serial RX
GPIO_ADC_VOLTAGE, GPIO_ADC_CURRENT, // Analog Voltage and Current GPIO_ADC_VOLTAGE, GPIO_ADC_CURRENT, // Analog Voltage and Current
GPIO_BL0906_RX, // BL0906 Serial interface GPIO_BL0906_RX, // BL0906 Serial interface
GPIO_DALI_RX_INV, GPIO_DALI_TX_INV, // DALI
GPIO_SENSOR_END }; GPIO_SENSOR_END };
// Error as warning to rethink GPIO usage with max 2045 // Error as warning to rethink GPIO usage with max 2045
@ -495,6 +496,7 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_WOOLIIS_RX "|" D_SENSOR_WOOLIIS_RX "|"
D_SENSOR_ADC_VOLTAGE "|" D_SENSOR_ADC_CURRENT "|" D_SENSOR_ADC_VOLTAGE "|" D_SENSOR_ADC_CURRENT "|"
D_SENSOR_BL0906_RX "|" D_SENSOR_BL0906_RX "|"
D_SENSOR_DALI_RX "_i|" D_SENSOR_DALI_TX "_i|"
; ;
const char kSensorNamesFixed[] PROGMEM = const char kSensorNamesFixed[] PROGMEM =
@ -592,11 +594,6 @@ const uint16_t kGpioNiceList[] PROGMEM = {
* Protocol specifics * Protocol specifics
\*-------------------------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------------------------*/
#ifdef USE_DALI
AGPIO(GPIO_DALI_RX), // DALI RX
AGPIO(GPIO_DALI_TX), // DALI TX
#endif // USE_DALI
#ifdef USE_I2C #ifdef USE_I2C
AGPIO(GPIO_I2C_SCL) + MAX_I2C, // I2C SCL AGPIO(GPIO_I2C_SCL) + MAX_I2C, // I2C SCL
AGPIO(GPIO_I2C_SDA) + MAX_I2C, // I2C SDA AGPIO(GPIO_I2C_SDA) + MAX_I2C, // I2C SDA
@ -834,6 +831,13 @@ const uint16_t kGpioNiceList[] PROGMEM = {
#endif #endif
#endif // USE_LIGHT #endif // USE_LIGHT
#ifdef USE_DALI
AGPIO(GPIO_DALI_TX), // DALI TX
AGPIO(GPIO_DALI_TX_INV), // DALI TX inverted
AGPIO(GPIO_DALI_RX), // DALI RX
AGPIO(GPIO_DALI_RX_INV), // DALI RX inverted
#endif // USE_DALI
/*-------------------------------------------------------------------------------------------*\ /*-------------------------------------------------------------------------------------------*\
* Transmission sensors * Transmission sensors
\*-------------------------------------------------------------------------------------------*/ \*-------------------------------------------------------------------------------------------*/

View File

@ -19,6 +19,9 @@
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
Version yyyymmdd Action Description Version yyyymmdd Action Description
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
1.0.0.1 20241024 update - Change from signal invert defines to GPIO config DALI RX_i/DALI TX_i
- Fix inverted DALI signal support
- Experimental support for Shelly DALI Dimmer Gen3
1.0.0.0 20241022 update - Refactor commission 1.0.0.0 20241022 update - Refactor commission
- Add receive collision detection - Add receive collision detection
0.1.0.8 20241019 update - Rename command `DaliCommission` to `DaliScan` 0.1.0.8 20241019 update - Rename command `DaliCommission` to `DaliScan`
@ -79,19 +82,16 @@
* 16 group address 100AAAAS * 16 group address 100AAAAS
* Special command 101CCCC1 to 110CCCC1 * Special command 101CCCC1 to 110CCCC1
* A = Address bit, S = 0 Direct Arc Power control, S = 1 Command, C = Special command * A = Address bit, S = 0 Direct Arc Power control, S = 1 Command, C = Special command
*
* Shelly DALI Dimmer Gen3 (ESP32C3-8M)
* Template {"NAME":"Shelly DALI Dimmer Gen3","GPIO":[34,4736,1,3840,11360,11392,128,129,1,1,576,0,0,0,0,0,0,0,0,1,1,1],"FLAG":0,"BASE":1}
* AdcGpio1 10000,10000,4000 <- Temperature parameters
* Backlog ButtonTopic 0; SetOption1 1; SetOption11 0; SetOption32 20
* rule1 on button1#state=2 do dimmer + endon on button2#state=2 do dimmer - endon on button1#state=3 do power 2 endon on button2#state=3 do power 2 endon
\*********************************************************************************************/ \*********************************************************************************************/
#define XDRV_75 75 #define XDRV_75 75
#ifndef DALI_IN_INVERT
#define DALI_IN_INVERT 0 // DALI RX inverted (1)
#endif
#ifndef DALI_OUT_INVERT
#define DALI_OUT_INVERT 0 // DALI TX inverted (1)
#endif
#ifndef DALI_MAX_SHORT_ADDRESS
#define DALI_MAX_SHORT_ADDRESS 64 // DALI default max short addresses
#endif
#ifndef DALI_INIT_STATE #ifndef DALI_INIT_STATE
#define DALI_INIT_STATE 50 // DALI init dimmer state 50/254 #define DALI_INIT_STATE 50 // DALI init dimmer state 50/254
#endif #endif
@ -280,7 +280,7 @@ void (* const DALICommand[])(void) PROGMEM = {
&CmndDaliSend, &CmndDaliQuery, &CmndDaliScan, &CmndDaliGroup, &CmndDaliGear }; &CmndDaliSend, &CmndDaliQuery, &CmndDaliScan, &CmndDaliGroup, &CmndDaliGear };
struct DALI { struct DALI {
uint32_t bit_time; uint32_t bit_cycles;
uint32_t last_activity; uint32_t last_activity;
uint32_t received_dali_data; // Data received from DALI bus uint32_t received_dali_data; // Data received from DALI bus
uint8_t pin_rx; uint8_t pin_rx;
@ -295,6 +295,8 @@ struct DALI {
bool response; bool response;
bool light_sync; bool light_sync;
bool probe; bool probe;
bool invert_rx;
bool invert_tx;
} *Dali = nullptr; } *Dali = nullptr;
/*********************************************************************************************\ /*********************************************************************************************\
@ -334,7 +336,7 @@ uint32_t DaliAddress2Target(uint32_t adr) {
void DaliEnableRxInterrupt(void) { void DaliEnableRxInterrupt(void) {
Dali->available = false; Dali->available = false;
attachInterrupt(Dali->pin_rx, DaliReceiveData, FALLING); attachInterrupt(Dali->pin_rx, DaliReceiveData, (Dali->invert_rx) ? RISING : FALLING);
} }
void DaliDisableRxInterrupt(void) { void DaliDisableRxInterrupt(void) {
@ -365,16 +367,15 @@ void DaliReceiveData(void) {
*/ */
if (Dali->available) { return; } // Skip if last input is not yet handled if (Dali->available) { return; } // Skip if last input is not yet handled
uint32_t gap_time = millis() - Dali->last_activity; uint32_t gap_time = millis() - Dali->last_activity;
uint32_t wait = ESP.getCycleCount() + (Dali->bit_time / 2); uint32_t wait = ESP.getCycleCount() + (Dali->bit_cycles / 2);
int bit_state = 0; int bit_state = 0;
bool dali_read; bool dali_read;
uint32_t received_dali_data = 0; uint32_t received_dali_data = 0;
uint32_t bit_number = 0; uint32_t bit_number = 0;
while (bit_number < 38) { while (bit_number < 38) {
while (ESP.getCycleCount() < wait); while (ESP.getCycleCount() < wait);
wait += Dali->bit_time; // Auto roll-over wait += Dali->bit_cycles; // Auto roll-over
dali_read = digitalRead(Dali->pin_rx); dali_read = (digitalRead(Dali->pin_rx) != Dali->invert_rx);
if (DALI_IN_INVERT) { dali_read != dali_read; }
#ifdef DALI_DEBUG #ifdef DALI_DEBUG
digitalWrite(DALI_DEBUG_PIN, bit_number&1); // Add LogicAnalyzer poll indication digitalWrite(DALI_DEBUG_PIN, bit_number&1); // Add LogicAnalyzer poll indication
#endif // DALI_DEBUG #endif // DALI_DEBUG
@ -465,13 +466,12 @@ void DaliSendDataOnce(uint16_t send_dali_data) {
} }
} }
digitalWrite(Dali->pin_tx, (pin_value == DALI_OUT_INVERT) ? LOW : HIGH); digitalWrite(Dali->pin_tx, (Dali->invert_tx) ? !pin_value : pin_value);
wait += Dali->bit_time; // Auto roll-over wait += Dali->bit_cycles; // Auto roll-over
while (ESP.getCycleCount() < wait); while (ESP.getCycleCount() < wait);
if (!collision) { if (!collision) {
dali_read = digitalRead(Dali->pin_rx); dali_read = (digitalRead(Dali->pin_rx) != Dali->invert_rx);
if (DALI_IN_INVERT) { dali_read != dali_read; }
if ((HIGH == pin_value) && (LOW == dali_read)) { // Collision if write is 1 and bus is 0 if ((HIGH == pin_value) && (LOW == dali_read)) { // Collision if write is 1 and bus is 0
collision = true; collision = true;
pin_value = LOW; pin_value = LOW;
@ -686,7 +686,7 @@ void DaliProgramShortAddress(uint8_t shortadr) {
// The slave shall store the received 6-bit address (AAAAAA) as a short address if it is selected. // The slave shall store the received 6-bit address (AAAAAA) as a short address if it is selected.
DaliSendData(DALI_PROGRAM_SHORT_ADDRESS, (shortadr << 1) | 0x01); DaliSendData(DALI_PROGRAM_SHORT_ADDRESS, (shortadr << 1) | 0x01);
AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: Set short address %d"), shortadr +1); AddLog(LOG_LEVEL_INFO, PSTR("DLI: Set short address %d"), shortadr +1);
} }
/*-------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------*/
@ -738,9 +738,9 @@ uint32_t DaliCommission(uint8_t init_arg) {
#ifdef USE_LIGHT #ifdef USE_LIGHT
DaliInitLight(); DaliInitLight();
uint32_t address = (Settings->sbflag1.dali_light) ? DaliTarget2Address(Dali->target) : DALI_BROADCAST_DP; uint32_t address = (Settings->sbflag1.dali_light) ? DaliTarget2Address(Dali->target) : DALI_BROADCAST_DP;
DaliSendData(address, Dali->dimmer); // Restore lights DaliSendData(address, Dali->power); // Restore lights
#else #else
DaliSendData(DALI_BROADCAST_DP, Dali->dimmer); // Restore lights DaliSendData(DALI_BROADCAST_DP, Dali->power); // Restore lights
#endif // USE_LIGHT #endif // USE_LIGHT
return cnt; return cnt;
} }
@ -763,6 +763,9 @@ void ResponseDali(void) {
void DaliLoop(void) { void DaliLoop(void) {
if (!Dali->available || Dali->response) { return; } if (!Dali->available || Dali->response) { return; }
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Rx 0x%05X"), Dali->received_dali_data);
if (Dali->received_dali_data &0x00010000) { if (Dali->received_dali_data &0x00010000) {
Dali->available = false; Dali->available = false;
return; // Rx collision return; // Rx collision
@ -842,28 +845,45 @@ bool DaliSetChannels(void) {
/*-------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------*/
bool DaliInit(void) { bool DaliInit(void) {
if (!PinUsed(GPIO_DALI_TX) || !PinUsed(GPIO_DALI_RX)) { return false; }
Dali = (DALI*)calloc(sizeof(DALI), 1); Dali = (DALI*)calloc(sizeof(DALI), 1);
if (!Dali) { return false; } if (!Dali) { return false; }
Dali->pin_rx = Pin(GPIO_DALI_RX); Dali->pin_tx = -1;
Dali->pin_tx = Pin(GPIO_DALI_TX); if (PinUsed(GPIO_DALI_TX)) {
Dali->pin_tx = Pin(GPIO_DALI_TX);
}
else if (PinUsed(GPIO_DALI_TX_INV)) {
Dali->pin_tx = Pin(GPIO_DALI_TX_INV);
Dali->invert_tx = true;
}
Dali->pin_rx = -1;
if (PinUsed(GPIO_DALI_RX)) {
Dali->pin_rx = Pin(GPIO_DALI_RX);
}
else if (PinUsed(GPIO_DALI_RX_INV)) {
Dali->pin_rx = Pin(GPIO_DALI_RX_INV);
Dali->invert_rx = true;
}
if ((-1 == Dali->pin_tx) || (-1 == Dali->pin_rx)) {
free(Dali);
return false;
}
AddLog(LOG_LEVEL_INFO, PSTR("DLI: GPIO%d(RX) and GPIO%d(TX)"), Dali->pin_rx, Dali->pin_tx); AddLog(LOG_LEVEL_INFO, PSTR("DLI: GPIO%d(RX%s) and GPIO%d(TX%s)"),
Dali->pin_rx, (Dali->invert_rx)?"i":"", Dali->pin_tx, (Dali->invert_tx)?"i":"");
pinMode(Dali->pin_tx, OUTPUT); pinMode(Dali->pin_tx, OUTPUT);
digitalWrite(Dali->pin_tx, HIGH); digitalWrite(Dali->pin_tx, (Dali->invert_tx) ? LOW : HIGH); // Idle
pinMode(Dali->pin_rx, INPUT); pinMode(Dali->pin_rx, INPUT);
#ifdef DALI_DEBUG #ifdef DALI_DEBUG
pinMode(DALI_DEBUG_PIN, OUTPUT); pinMode(DALI_DEBUG_PIN, OUTPUT);
digitalWrite(DALI_DEBUG_PIN, HIGH); digitalWrite(DALI_DEBUG_PIN, HIGH);
#endif // DALI_DEBUG #endif // DALI_DEBUG
Dali->max_short_address = (DALI_MAX_SHORT_ADDRESS <= 64) ? DALI_MAX_SHORT_ADDRESS : 64; Dali->max_short_address = 64;
Dali->dimmer = DALI_INIT_STATE; Dali->dimmer = DALI_INIT_STATE;
// Manchester twice 1200 bps = 2400 bps = 417 (protocol 416.76 +/- 10%) us // Manchester twice 1200 bps = 2400 bps = 417 (protocol 416.76 +/- 10%) us
Dali->bit_time = ESP.getCpuFreqMHz() * 1000000 / 2400; Dali->bit_cycles = ESP.getCpuFreqMHz() * 1000000 / 2400;
DaliEnableRxInterrupt(); DaliEnableRxInterrupt();