DALI add send retry on collision detection
This commit is contained in:
parent
413bb3b6c6
commit
d7b1bb3574
@ -65,6 +65,8 @@
|
||||
--------------------------------------------------------------------------------------------
|
||||
Version yyyymmdd Action Description
|
||||
--------------------------------------------------------------------------------------------
|
||||
1.3.0.4 20251123 update - Add send retry on collision detection
|
||||
- Prep DALI-2 24-bit transceive
|
||||
1.3.0.3 20251122 update - Remove sleep dependency from frame handling
|
||||
- Change receive timeout from 50 ms to 20 ms (DALI protocol is 9.2 ms)
|
||||
- Add DALI DT8 RGBWAF Control Gear (receive) for Tasmota color light control
|
||||
@ -139,6 +141,14 @@
|
||||
#define DALI_DEBUG_PIN 4 // Debug GPIO
|
||||
#endif
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
#define DALI_COLLISION 0x10000000 // Collision data mask
|
||||
#define DALI_BACKWARD_FRAME 0x00000000 // Backward frame mask
|
||||
#define DALI_FORWARD_16BIT_FRAME 0x01000000 // DALI 16-bit forward frame mask
|
||||
#define DALI_FORWARD_24BIT_FRAME 0x02000000 // DALI-2 24-bit forward frame mask
|
||||
#define DALI_FORWARD_FRAME 0x03000000 // = DALI_FORWARD_16BIT_FRAME | DALI_FORWARD_24BIT_FRAME
|
||||
|
||||
#include "include/xdrv_75_dali.h"
|
||||
|
||||
#define DALI_MAX_STORED 17 // Store broadcast and group states
|
||||
@ -159,7 +169,6 @@ struct DALI {
|
||||
uint32_t bit_cycles;
|
||||
uint32_t last_activity;
|
||||
uint32_t received_dali_data; // Data received from DALI bus
|
||||
uint32_t color_sequence;
|
||||
uint8_t pin_rx;
|
||||
uint8_t pin_tx;
|
||||
uint8_t max_short_address;
|
||||
@ -364,37 +373,43 @@ void DaliDisableRxInterrupt(void) {
|
||||
void IRAM_ATTR DaliReceiveData(void); // Fix ESP8266 ISR not in IRAM! exception
|
||||
void DaliReceiveData(void) {
|
||||
/*
|
||||
DALI-2 Forward frame (1 Start bit + 24 data bits) * 2 bits/bit (manchester encoding) + 2 * 2 Stop bits = 54 bits
|
||||
DALI data 0xFE6432 1 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0 0 0 1 1 0 0 1 0 Forward frame - 23.2 ms
|
||||
Start and Stop bits 1 1 1
|
||||
Manchester data 01010101010101011010010110100110101010010110100110
|
||||
Stop bits 1111
|
||||
|
||||
Forward frame (1 Start bit + 16 data bits) * 2 bits/bit (manchester encoding) + 2 * 2 Stop bits = 38 bits
|
||||
DALI data 0xFE64 1 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0 Forward frame
|
||||
DALI data 0xFE64 1 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0 Forward frame - 16.2 ms
|
||||
Start and Stop bits 1 1 1
|
||||
Manchester data 0101010101010101101001011010011010
|
||||
Stop bits 1111
|
||||
|
||||
Backward frame (1 Start bit + 8 data bits) * 2 bits/bit (manchester encoding) + 2 * 2 Stop bits = 22 bits
|
||||
DALI data 0x64 0 1 1 0 0 1 0 0 Backward frame
|
||||
DALI data 0x64 0 1 1 0 0 1 0 0 Backward frame - 10 ms
|
||||
Start and Stop bits 1 1 1
|
||||
Manchester data 011001011010011010
|
||||
Stop bits 1111
|
||||
|
||||
Bit number 01234567890123456789012345678901234567
|
||||
1 2 3
|
||||
Bit number 0123456789012345678901234567890123456789012345678901234
|
||||
1 2 3 4 5
|
||||
*/
|
||||
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_cycles / 2);
|
||||
int bit_state = 0;
|
||||
bool dali_read;
|
||||
bool forward_frame = true;
|
||||
uint32_t frame_type = 2; // 0 = 8-bit backward, 1 = 16-bit forward, 2 = 24-bit forward
|
||||
uint32_t received_dali_data = 0;
|
||||
uint32_t bit_number = 0;
|
||||
while (bit_number < 38) {
|
||||
while (bit_number < 54) {
|
||||
while (ESP.getCycleCount() < wait);
|
||||
wait += Dali->bit_cycles; // Auto roll-over +1Te
|
||||
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
|
||||
if (bit_number < 34) { // 34 manchester encoded bits
|
||||
if (bit_number < 50) { // 50 manchester encoded bits
|
||||
bit_state += (dali_read) ? 1 : -1;
|
||||
if (0 == bit_state) { // Manchester encoding total 2 bits is always 0
|
||||
if (bit_number > 2) { // Skip start bit
|
||||
@ -402,11 +417,17 @@ void DaliReceiveData(void) {
|
||||
received_dali_data |= dali_read;
|
||||
}
|
||||
}
|
||||
else if ((2 == bit_state) &&
|
||||
else if ((2 == bit_state) && // Invalid manchester data (might be stop bit)
|
||||
(bit_number == 19)) { // Possible backward frame detected - Chk stop bits
|
||||
bit_state = 0;
|
||||
bit_number = 35;
|
||||
forward_frame = false;
|
||||
bit_number = 51; // Continue receiving stop bits
|
||||
frame_type = 0; // 0 = 8-bit backward, 1 = 16-bit forward, 2 = 24-bit forward
|
||||
}
|
||||
else if ((2 == bit_state) && // Invalid manchester data (might be stop bit)
|
||||
(bit_number == 35)) { // Possible 16-bit forward frame detected - Chk stop bits
|
||||
bit_state = 0;
|
||||
bit_number = 51; // Continue receiving stop bits
|
||||
frame_type = 1; // 0 = 8-bit backward, 1 = 16-bit forward, 2 = 24-bit forward
|
||||
}
|
||||
else if (abs(bit_state) > 1) { // Invalid manchester data (too many 0 or 1)
|
||||
break;
|
||||
@ -424,11 +445,9 @@ void DaliReceiveData(void) {
|
||||
}
|
||||
Dali->last_activity = millis(); // Start Forward Frame delay time (>22Te)
|
||||
|
||||
if (forward_frame) { // Forward frame received
|
||||
received_dali_data |= 0x00020000; // Forward frame received
|
||||
}
|
||||
received_dali_data |= (frame_type << 24); // 0 = 8-bit backward, 1 = 16-bit forward, 2 = 24-bit forward
|
||||
if (bit_state != 0) { // Invalid Manchester encoding including start and stop bits
|
||||
received_dali_data |= 0x00010000; // Possible collision or invalid reply of repeated frame due to handling of first frame
|
||||
received_dali_data |= DALI_COLLISION; // Possible collision or invalid reply of repeated frame due to handling of first frame
|
||||
}
|
||||
if (Dali->response || // Response from last message send
|
||||
(Dali->received_dali_data != received_dali_data)) { // Skip duplicates
|
||||
@ -441,84 +460,103 @@ void DaliReceiveData(void) {
|
||||
* DALI send
|
||||
\*-------------------------------------------------------------------------------------------*/
|
||||
|
||||
void DaliSendDataOnce(uint16_t send_dali_data) {
|
||||
void DaliSendDataOnce(uint32_t send_dali_data) {
|
||||
/*
|
||||
DALI-2 protocol forward frame
|
||||
DALI data 0xFE6432 1 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0 0 0 1 1 0 0 1 0
|
||||
Start and Stop bits 1 1 1
|
||||
Manchester data 01010101010101011010010110100110101010010110100110
|
||||
Stop bits 1111
|
||||
|
||||
DALI protocol forward frame
|
||||
DALI data 0xFE64 1 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0
|
||||
Start and Stop bits 1 1 1
|
||||
Manchester data 0101010101010101101001011010011010
|
||||
Stop bits 1111
|
||||
|
||||
Bit number 01234567890123456789012345678901234567
|
||||
1 2 3
|
||||
Bit number 012345678901234567890123456789012345678901234567890123
|
||||
1 2 3 4 5
|
||||
*/
|
||||
Dali->last_activity += 14; // As suggested by DALI protocol (>22Te = 9.17 ms) - We need to add 1.1 ms due to not waiting for stop bits
|
||||
while (!TimeReached(Dali->last_activity)) {
|
||||
delay(1); // Wait for bus to be free if needed
|
||||
}
|
||||
bool bit_value;
|
||||
bool pin_value;
|
||||
bool dali_read;
|
||||
bool collision = false;
|
||||
uint32_t bit_pos = 15;
|
||||
uint32_t bit_number = 0;
|
||||
uint32_t retry = 2;
|
||||
bool collision;
|
||||
do {
|
||||
collision = false;
|
||||
uint32_t send_data = send_dali_data;
|
||||
uint32_t bit_pos = 15; // 16-bit forward frame
|
||||
uint32_t max_bit_number = 34;
|
||||
if (send_data & DALI_FORWARD_24BIT_FRAME) {
|
||||
bit_pos = 23; // 24-bit forward frame
|
||||
max_bit_number = 50;
|
||||
}
|
||||
uint32_t bit_number = 0;
|
||||
|
||||
#ifdef ESP32
|
||||
{portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
portENTER_CRITICAL(&mux);
|
||||
#endif
|
||||
|
||||
uint32_t wait = ESP.getCycleCount();
|
||||
while (bit_number < 35) { // 417 * 35 = 35Te = 14.7 ms
|
||||
if (!collision) {
|
||||
if (0 == (bit_number &1)) { // Even bit
|
||||
// Start bit, Stop bit, Data bits
|
||||
bit_value = (0 == bit_number) ? 1 : (34 == bit_number) ? 0 : (bool)((send_dali_data >> bit_pos--) &1); // MSB first
|
||||
} else { // Odd bit
|
||||
bit_value = !bit_value; // Complement bit
|
||||
}
|
||||
pin_value = bit_value ? LOW : HIGH; // Invert bit
|
||||
} else {
|
||||
if (34 == bit_number) {
|
||||
pin_value = HIGH; // Set to idle
|
||||
}
|
||||
Dali->last_activity += 14; // As suggested by DALI protocol (>22Te = 9.17 ms) - We need to add 1.1 ms due to not waiting for stop bits
|
||||
while (!TimeReached(Dali->last_activity)) {
|
||||
delay(1); // Wait for bus to be free if needed
|
||||
}
|
||||
|
||||
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) != Dali->invert_rx);
|
||||
if ((HIGH == pin_value) && (LOW == dali_read)) { // Collision if write is 1 and bus is 0
|
||||
collision = true;
|
||||
pin_value = LOW;
|
||||
bit_number = 29; // Keep bus low for 4 bits
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Tx collision"));
|
||||
}
|
||||
}
|
||||
|
||||
bit_number++;
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
portEXIT_CRITICAL(&mux);}
|
||||
{portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
portENTER_CRITICAL(&mux);
|
||||
#endif
|
||||
|
||||
// delayMicroseconds(1100); // Wait 3Te as sending stop bits - adds to total 15.8 ms
|
||||
Dali->last_activity = millis(); // Start Forward Frame delay time (>22Te)
|
||||
uint32_t wait = ESP.getCycleCount();
|
||||
while (bit_number <= max_bit_number) { // 417 * 35 = 35Te = 14.7 ms
|
||||
if (!collision) {
|
||||
if (0 == (bit_number &1)) { // Even bit
|
||||
// Start bit, Stop bit, Data bits
|
||||
bit_value = (0 == bit_number) ? 1 : (max_bit_number == bit_number) ? 0 : (bool)((send_data >> bit_pos--) &1); // MSB first
|
||||
} else { // Odd bit
|
||||
bit_value = !bit_value; // Complement bit
|
||||
}
|
||||
pin_value = bit_value ? LOW : HIGH; // Invert bit
|
||||
} else {
|
||||
if (max_bit_number == bit_number) {
|
||||
pin_value = HIGH; // Set to idle
|
||||
}
|
||||
}
|
||||
|
||||
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) != Dali->invert_rx);
|
||||
if ((HIGH == pin_value) && (LOW == dali_read)) { // Collision if write is 1 and bus is 0
|
||||
collision = true;
|
||||
pin_value = LOW;
|
||||
bit_number = max_bit_number -5; // Keep bus low for 4 bits - break sequence
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Tx collision"));
|
||||
}
|
||||
}
|
||||
|
||||
bit_number++;
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
portEXIT_CRITICAL(&mux);}
|
||||
#endif
|
||||
|
||||
// delayMicroseconds(1100); // Wait 3Te as sending stop bits - adds to total 15.8 ms
|
||||
Dali->last_activity = millis(); // Start Forward Frame delay time (>22Te)
|
||||
} while (retry-- && collision);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*/
|
||||
|
||||
void DaliSendData(uint32_t adr, uint32_t cmd) {
|
||||
void DaliSendData(uint32_t adr, uint32_t cmd, uint32_t opt = 0);
|
||||
void DaliSendData(uint32_t adr, uint32_t cmd, uint32_t opt) {
|
||||
adr &= 0xFF;
|
||||
cmd &= 0xFF;
|
||||
|
||||
Dali->address = adr;
|
||||
Dali->command = cmd;
|
||||
DaliSaveState(adr, cmd);
|
||||
if (!opt) { // No DALI-2 24-bit command
|
||||
Dali->address = adr;
|
||||
Dali->command = cmd;
|
||||
DaliSaveState(adr, cmd);
|
||||
}
|
||||
|
||||
bool send_twice = false;
|
||||
if (adr & DALI_SELECTOR_BIT) { // Selector bit (command) or special command
|
||||
@ -547,11 +585,14 @@ void DaliSendData(uint32_t adr, uint32_t cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DALI_DEBUG
|
||||
AddLog(Dali->log_level, PSTR("DLI: Tx DT%d, Twice %d, Adr 0x%02X, Cmd 0x%02X"), Dali->device_type, send_twice, adr, cmd);
|
||||
#endif // DALI_DEBUG
|
||||
uint32_t send_dali_data = adr << 8 | cmd;
|
||||
if (opt & DALI_FORWARD_24BIT_FRAME) {
|
||||
send_dali_data = (send_dali_data << 8) | (opt & (DALI_FORWARD_24BIT_FRAME | 0xFF));
|
||||
}
|
||||
|
||||
uint16_t send_dali_data = adr << 8 | cmd;
|
||||
#ifdef DALI_DEBUG
|
||||
AddLog(Dali->log_level, PSTR("DLI: Tx 0x%08X, Twice %d, DT%d"), send_dali_data, send_twice, Dali->device_type);
|
||||
#endif // DALI_DEBUG
|
||||
|
||||
DaliDisableRxInterrupt();
|
||||
DaliSendDataOnce(send_dali_data); // Takes 14.7 ms
|
||||
@ -578,8 +619,8 @@ int DaliSendWaitResponse(uint32_t adr, uint32_t cmd, uint32_t timeout) {
|
||||
int result = -1; // DALI NO or no response
|
||||
if (Dali->available) {
|
||||
Dali->available = false; // DALI collision (-2) or valid data (>=0)
|
||||
bool collision = (Dali->received_dali_data &0x00010000);
|
||||
bool forward_frame = (Dali->received_dali_data &0x00020000);
|
||||
bool collision = (Dali->received_dali_data & DALI_COLLISION);
|
||||
bool forward_frame = (Dali->received_dali_data & DALI_FORWARD_FRAME);
|
||||
if (!forward_frame) {
|
||||
result = (collision) ? -2 : (Dali->received_dali_data &0xFF);
|
||||
}
|
||||
@ -587,7 +628,7 @@ int DaliSendWaitResponse(uint32_t adr, uint32_t cmd, uint32_t timeout) {
|
||||
Dali->response = false;
|
||||
|
||||
#ifdef DALI_DEBUG
|
||||
AddLog(Dali->log_level, PSTR("DLI: Rx 0x%05X Response"), result);
|
||||
AddLog(Dali->log_level, PSTR("DLI: Rx 0x%08X Response"), result);
|
||||
#endif // DALI_DEBUG
|
||||
|
||||
return result;
|
||||
@ -883,13 +924,16 @@ void ResponseDali(uint32_t index) {
|
||||
void DaliLoop(void) {
|
||||
if (!Dali->available || Dali->response) { return; }
|
||||
|
||||
bool collision = (Dali->received_dali_data &0x00010000);
|
||||
bool forward_frame = (Dali->received_dali_data &0x00020000);
|
||||
bool collision = (Dali->received_dali_data & DALI_COLLISION);
|
||||
bool forward_frame = (Dali->received_dali_data & DALI_FORWARD_FRAME);
|
||||
bool forward_24bit_frame = (Dali->received_dali_data & DALI_FORWARD_24BIT_FRAME);
|
||||
|
||||
AddLog((1 == Dali->probe) ? LOG_LEVEL_DEBUG : LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Rx 0x%05X%s"), Dali->received_dali_data, (!forward_frame)?" backward":"");
|
||||
AddLog((1 == Dali->probe) ? LOG_LEVEL_DEBUG : LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Rx 0x%08X %s"),
|
||||
Dali->received_dali_data, (collision)?"collision":(forward_frame)?"":"backward");
|
||||
|
||||
if (collision || // Rx collision
|
||||
!forward_frame || // We do not serve backward frames
|
||||
!forward_frame || // Skip backward frames
|
||||
forward_24bit_frame || // Currently no support for DALI-2 24-bit frame
|
||||
(1 == Dali->probe)) { // Probe only
|
||||
Dali->available = false;
|
||||
return;
|
||||
@ -900,10 +944,7 @@ void DaliLoop(void) {
|
||||
|
||||
#ifdef USE_LIGHT
|
||||
#ifdef DALI_LIGHT_COLOR_SUPPORT
|
||||
if (DALI_209_SET_TEMPORARY_RGBWAF_CONTROL == Dali->command) {
|
||||
Dali->color_sequence = millis(); // Indicate start of color sequence - See DaliSetChannels()
|
||||
}
|
||||
else if (DALI_102_SET_DTR0 == Dali->address) { Dali->dtr[0] = Dali->command; } // Might be Red / White
|
||||
if (DALI_102_SET_DTR0 == Dali->address) { Dali->dtr[0] = Dali->command; } // Might be Red / White
|
||||
else if (DALI_102_SET_DTR1 == Dali->address) { Dali->dtr[1] = Dali->command; } // Might be Green / Amber
|
||||
else if (DALI_102_SET_DTR2 == Dali->address) { Dali->dtr[2] = Dali->command; } // Might be Blue
|
||||
else if (DALI_209_SET_TEMPORARY_RGB_DIMLEVEL == Dali->command) {
|
||||
@ -916,7 +957,6 @@ void DaliLoop(void) {
|
||||
Dali->color[4] = Dali->dtr[0]; // Cold White
|
||||
}
|
||||
else if (DALI_209_ACTIVATE == Dali->command) {
|
||||
Dali->color_sequence = 0;
|
||||
uint32_t channels = Dali->Settings.light_type -8;
|
||||
if ((Dali->target_rgbwaf > 0) && (channels > 0)) { // Color control
|
||||
Dali->address &= 0xFE; // Reset DALI_SELECTOR_BIT set
|
||||
@ -943,7 +983,7 @@ void DaliLoop(void) {
|
||||
} else
|
||||
#endif // DALI_LIGHT_COLOR_SUPPORT
|
||||
#endif // USE_LIGHT
|
||||
if ((!(Dali->address & DALI_SELECTOR_BIT)) && !Dali->color_sequence) { // Address
|
||||
if (!(Dali->address & DALI_SELECTOR_BIT)) { // Address
|
||||
uint32_t index = DaliSaveState(Dali->address, Dali->command); // Update dimmer and power
|
||||
bool show_response = true;
|
||||
#ifdef USE_LIGHT
|
||||
@ -981,9 +1021,6 @@ void DaliEverySecond(void) {
|
||||
if (5 == TasmotaGlobal.uptime) {
|
||||
DaliInitLight();
|
||||
}
|
||||
if (Dali->color_sequence && TimeReached(Dali->color_sequence + 1000)) {
|
||||
Dali->color_sequence = 0; // Reset color sequence in case of incomplete DALI sequence
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
@ -1423,6 +1460,13 @@ void CmndDaliSend(void) {
|
||||
AddLog(Dali->log_level, PSTR("DLI: index %d, params %d, values %d,%d,%d,%d,%d"), XdrvMailbox.index, params, values[0], values[1], values[2], values[3], values[4]);
|
||||
#endif // DALI_DEBUG
|
||||
|
||||
if (255 == XdrvMailbox.index) { // DaliSend255 - Dali-2 24-bit frame
|
||||
if (params >= 3) {
|
||||
DaliSendData(values[0], values[1], values[2] | DALI_FORWARD_24BIT_FRAME);
|
||||
ResponseCmndDone();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (DALI_207_DEVICE_TYPE == XdrvMailbox.index) { // DaliSend6 - DT6 = 207 = Extended LED commands 224...236
|
||||
/*
|
||||
params 0 1 2
|
||||
|
||||
Loading…
Reference in New Issue
Block a user