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 Compressor Frequency for MiElHVAC (#22347)
- 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
@ -24,6 +26,7 @@ All notable changes to this project will be documented in this file.
### Fixed
### Removed
- DALI inverted signal configuration using compile time defines
## [14.3.0.1] 20241022
### 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 `DaliGroup` to add gear to groups
- 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 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)

View File

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

View File

@ -19,6 +19,9 @@
--------------------------------------------------------------------------------------------
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
- Add receive collision detection
0.1.0.8 20241019 update - Rename command `DaliCommission` to `DaliScan`
@ -79,19 +82,16 @@
* 16 group address 100AAAAS
* Special command 101CCCC1 to 110CCCC1
* 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
#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
#define DALI_INIT_STATE 50 // DALI init dimmer state 50/254
#endif
@ -280,7 +280,7 @@ void (* const DALICommand[])(void) PROGMEM = {
&CmndDaliSend, &CmndDaliQuery, &CmndDaliScan, &CmndDaliGroup, &CmndDaliGear };
struct DALI {
uint32_t bit_time;
uint32_t bit_cycles;
uint32_t last_activity;
uint32_t received_dali_data; // Data received from DALI bus
uint8_t pin_rx;
@ -295,6 +295,8 @@ struct DALI {
bool response;
bool light_sync;
bool probe;
bool invert_rx;
bool invert_tx;
} *Dali = nullptr;
/*********************************************************************************************\
@ -334,7 +336,7 @@ uint32_t DaliAddress2Target(uint32_t adr) {
void DaliEnableRxInterrupt(void) {
Dali->available = false;
attachInterrupt(Dali->pin_rx, DaliReceiveData, FALLING);
attachInterrupt(Dali->pin_rx, DaliReceiveData, (Dali->invert_rx) ? RISING : FALLING);
}
void DaliDisableRxInterrupt(void) {
@ -365,16 +367,15 @@ void DaliReceiveData(void) {
*/
if (Dali->available) { return; } // Skip if last input is not yet handled
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;
bool dali_read;
uint32_t received_dali_data = 0;
uint32_t bit_number = 0;
while (bit_number < 38) {
while (ESP.getCycleCount() < wait);
wait += Dali->bit_time; // Auto roll-over
dali_read = digitalRead(Dali->pin_rx);
if (DALI_IN_INVERT) { dali_read != dali_read; }
wait += Dali->bit_cycles; // Auto roll-over
dali_read = (digitalRead(Dali->pin_rx) != Dali->invert_rx);
#ifdef DALI_DEBUG
digitalWrite(DALI_DEBUG_PIN, bit_number&1); // Add LogicAnalyzer poll indication
#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);
wait += Dali->bit_time; // Auto roll-over
digitalWrite(Dali->pin_tx, (Dali->invert_tx) ? !pin_value : pin_value);
wait += Dali->bit_cycles; // Auto roll-over
while (ESP.getCycleCount() < wait);
if (!collision) {
dali_read = digitalRead(Dali->pin_rx);
if (DALI_IN_INVERT) { dali_read != dali_read; }
dali_read = (digitalRead(Dali->pin_rx) != Dali->invert_rx);
if ((HIGH == pin_value) && (LOW == dali_read)) { // Collision if write is 1 and bus is 0
collision = true;
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.
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
DaliInitLight();
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
DaliSendData(DALI_BROADCAST_DP, Dali->dimmer); // Restore lights
DaliSendData(DALI_BROADCAST_DP, Dali->power); // Restore lights
#endif // USE_LIGHT
return cnt;
}
@ -763,6 +763,9 @@ void ResponseDali(void) {
void DaliLoop(void) {
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) {
Dali->available = false;
return; // Rx collision
@ -842,28 +845,45 @@ bool DaliSetChannels(void) {
/*-------------------------------------------------------------------------------------------*/
bool DaliInit(void) {
if (!PinUsed(GPIO_DALI_TX) || !PinUsed(GPIO_DALI_RX)) { return false; }
Dali = (DALI*)calloc(sizeof(DALI), 1);
if (!Dali) { return false; }
Dali->pin_rx = Pin(GPIO_DALI_RX);
Dali->pin_tx = Pin(GPIO_DALI_TX);
Dali->pin_tx = -1;
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);
digitalWrite(Dali->pin_tx, HIGH);
digitalWrite(Dali->pin_tx, (Dali->invert_tx) ? LOW : HIGH); // Idle
pinMode(Dali->pin_rx, INPUT);
#ifdef DALI_DEBUG
pinMode(DALI_DEBUG_PIN, OUTPUT);
digitalWrite(DALI_DEBUG_PIN, HIGH);
#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;
// 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();