Skip to content

Commit d7b1bb3

Browse files
committed
DALI add send retry on collision detection
1 parent 413bb3b commit d7b1bb3

File tree

1 file changed

+126
-82
lines changed

1 file changed

+126
-82
lines changed

tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino

Lines changed: 126 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
--------------------------------------------------------------------------------------------
6666
Version yyyymmdd Action Description
6767
--------------------------------------------------------------------------------------------
68+
1.3.0.4 20251123 update - Add send retry on collision detection
69+
- Prep DALI-2 24-bit transceive
6870
1.3.0.3 20251122 update - Remove sleep dependency from frame handling
6971
- Change receive timeout from 50 ms to 20 ms (DALI protocol is 9.2 ms)
7072
- Add DALI DT8 RGBWAF Control Gear (receive) for Tasmota color light control
@@ -139,6 +141,14 @@
139141
#define DALI_DEBUG_PIN 4 // Debug GPIO
140142
#endif
141143

144+
/*********************************************************************************************/
145+
146+
#define DALI_COLLISION 0x10000000 // Collision data mask
147+
#define DALI_BACKWARD_FRAME 0x00000000 // Backward frame mask
148+
#define DALI_FORWARD_16BIT_FRAME 0x01000000 // DALI 16-bit forward frame mask
149+
#define DALI_FORWARD_24BIT_FRAME 0x02000000 // DALI-2 24-bit forward frame mask
150+
#define DALI_FORWARD_FRAME 0x03000000 // = DALI_FORWARD_16BIT_FRAME | DALI_FORWARD_24BIT_FRAME
151+
142152
#include "include/xdrv_75_dali.h"
143153

144154
#define DALI_MAX_STORED 17 // Store broadcast and group states
@@ -159,7 +169,6 @@ struct DALI {
159169
uint32_t bit_cycles;
160170
uint32_t last_activity;
161171
uint32_t received_dali_data; // Data received from DALI bus
162-
uint32_t color_sequence;
163172
uint8_t pin_rx;
164173
uint8_t pin_tx;
165174
uint8_t max_short_address;
@@ -364,49 +373,61 @@ void DaliDisableRxInterrupt(void) {
364373
void IRAM_ATTR DaliReceiveData(void); // Fix ESP8266 ISR not in IRAM! exception
365374
void DaliReceiveData(void) {
366375
/*
376+
DALI-2 Forward frame (1 Start bit + 24 data bits) * 2 bits/bit (manchester encoding) + 2 * 2 Stop bits = 54 bits
377+
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
378+
Start and Stop bits 1 1 1
379+
Manchester data 01010101010101011010010110100110101010010110100110
380+
Stop bits 1111
381+
367382
Forward frame (1 Start bit + 16 data bits) * 2 bits/bit (manchester encoding) + 2 * 2 Stop bits = 38 bits
368-
DALI data 0xFE64 1 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0 Forward frame
383+
DALI data 0xFE64 1 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0 Forward frame - 16.2 ms
369384
Start and Stop bits 1 1 1
370385
Manchester data 0101010101010101101001011010011010
371386
Stop bits 1111
372387
373388
Backward frame (1 Start bit + 8 data bits) * 2 bits/bit (manchester encoding) + 2 * 2 Stop bits = 22 bits
374-
DALI data 0x64 0 1 1 0 0 1 0 0 Backward frame
389+
DALI data 0x64 0 1 1 0 0 1 0 0 Backward frame - 10 ms
375390
Start and Stop bits 1 1 1
376391
Manchester data 011001011010011010
377392
Stop bits 1111
378393
379-
Bit number 01234567890123456789012345678901234567
380-
1 2 3
394+
Bit number 0123456789012345678901234567890123456789012345678901234
395+
1 2 3 4 5
381396
*/
382397
if (Dali->available) { return; } // Skip if last input is not yet handled
383398
uint32_t gap_time = millis() - Dali->last_activity;
384399
uint32_t wait = ESP.getCycleCount() + (Dali->bit_cycles / 2);
385400
int bit_state = 0;
386401
bool dali_read;
387-
bool forward_frame = true;
402+
uint32_t frame_type = 2; // 0 = 8-bit backward, 1 = 16-bit forward, 2 = 24-bit forward
388403
uint32_t received_dali_data = 0;
389404
uint32_t bit_number = 0;
390-
while (bit_number < 38) {
405+
while (bit_number < 54) {
391406
while (ESP.getCycleCount() < wait);
392407
wait += Dali->bit_cycles; // Auto roll-over +1Te
393408
dali_read = (digitalRead(Dali->pin_rx) != Dali->invert_rx);
394409
#ifdef DALI_DEBUG
395410
digitalWrite(DALI_DEBUG_PIN, bit_number&1); // Add LogicAnalyzer poll indication
396411
#endif // DALI_DEBUG
397-
if (bit_number < 34) { // 34 manchester encoded bits
412+
if (bit_number < 50) { // 50 manchester encoded bits
398413
bit_state += (dali_read) ? 1 : -1;
399414
if (0 == bit_state) { // Manchester encoding total 2 bits is always 0
400415
if (bit_number > 2) { // Skip start bit
401416
received_dali_data <<= 1;
402417
received_dali_data |= dali_read;
403418
}
404419
}
405-
else if ((2 == bit_state) &&
420+
else if ((2 == bit_state) && // Invalid manchester data (might be stop bit)
406421
(bit_number == 19)) { // Possible backward frame detected - Chk stop bits
407422
bit_state = 0;
408-
bit_number = 35;
409-
forward_frame = false;
423+
bit_number = 51; // Continue receiving stop bits
424+
frame_type = 0; // 0 = 8-bit backward, 1 = 16-bit forward, 2 = 24-bit forward
425+
}
426+
else if ((2 == bit_state) && // Invalid manchester data (might be stop bit)
427+
(bit_number == 35)) { // Possible 16-bit forward frame detected - Chk stop bits
428+
bit_state = 0;
429+
bit_number = 51; // Continue receiving stop bits
430+
frame_type = 1; // 0 = 8-bit backward, 1 = 16-bit forward, 2 = 24-bit forward
410431
}
411432
else if (abs(bit_state) > 1) { // Invalid manchester data (too many 0 or 1)
412433
break;
@@ -424,11 +445,9 @@ void DaliReceiveData(void) {
424445
}
425446
Dali->last_activity = millis(); // Start Forward Frame delay time (>22Te)
426447

427-
if (forward_frame) { // Forward frame received
428-
received_dali_data |= 0x00020000; // Forward frame received
429-
}
448+
received_dali_data |= (frame_type << 24); // 0 = 8-bit backward, 1 = 16-bit forward, 2 = 24-bit forward
430449
if (bit_state != 0) { // Invalid Manchester encoding including start and stop bits
431-
received_dali_data |= 0x00010000; // Possible collision or invalid reply of repeated frame due to handling of first frame
450+
received_dali_data |= DALI_COLLISION; // Possible collision or invalid reply of repeated frame due to handling of first frame
432451
}
433452
if (Dali->response || // Response from last message send
434453
(Dali->received_dali_data != received_dali_data)) { // Skip duplicates
@@ -441,84 +460,103 @@ void DaliReceiveData(void) {
441460
* DALI send
442461
\*-------------------------------------------------------------------------------------------*/
443462

444-
void DaliSendDataOnce(uint16_t send_dali_data) {
463+
void DaliSendDataOnce(uint32_t send_dali_data) {
445464
/*
465+
DALI-2 protocol forward frame
466+
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
467+
Start and Stop bits 1 1 1
468+
Manchester data 01010101010101011010010110100110101010010110100110
469+
Stop bits 1111
470+
446471
DALI protocol forward frame
447472
DALI data 0xFE64 1 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0
448473
Start and Stop bits 1 1 1
449474
Manchester data 0101010101010101101001011010011010
450475
Stop bits 1111
451476
452-
Bit number 01234567890123456789012345678901234567
453-
1 2 3
477+
Bit number 012345678901234567890123456789012345678901234567890123
478+
1 2 3 4 5
454479
*/
455-
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
456-
while (!TimeReached(Dali->last_activity)) {
457-
delay(1); // Wait for bus to be free if needed
458-
}
459480
bool bit_value;
460481
bool pin_value;
461482
bool dali_read;
462-
bool collision = false;
463-
uint32_t bit_pos = 15;
464-
uint32_t bit_number = 0;
483+
uint32_t retry = 2;
484+
bool collision;
485+
do {
486+
collision = false;
487+
uint32_t send_data = send_dali_data;
488+
uint32_t bit_pos = 15; // 16-bit forward frame
489+
uint32_t max_bit_number = 34;
490+
if (send_data & DALI_FORWARD_24BIT_FRAME) {
491+
bit_pos = 23; // 24-bit forward frame
492+
max_bit_number = 50;
493+
}
494+
uint32_t bit_number = 0;
495+
496+
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
497+
while (!TimeReached(Dali->last_activity)) {
498+
delay(1); // Wait for bus to be free if needed
499+
}
465500

466501
#ifdef ESP32
467-
{portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
468-
portENTER_CRITICAL(&mux);
502+
{portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
503+
portENTER_CRITICAL(&mux);
469504
#endif
470505

471-
uint32_t wait = ESP.getCycleCount();
472-
while (bit_number < 35) { // 417 * 35 = 35Te = 14.7 ms
473-
if (!collision) {
474-
if (0 == (bit_number &1)) { // Even bit
475-
// Start bit, Stop bit, Data bits
476-
bit_value = (0 == bit_number) ? 1 : (34 == bit_number) ? 0 : (bool)((send_dali_data >> bit_pos--) &1); // MSB first
477-
} else { // Odd bit
478-
bit_value = !bit_value; // Complement bit
479-
}
480-
pin_value = bit_value ? LOW : HIGH; // Invert bit
481-
} else {
482-
if (34 == bit_number) {
483-
pin_value = HIGH; // Set to idle
506+
uint32_t wait = ESP.getCycleCount();
507+
while (bit_number <= max_bit_number) { // 417 * 35 = 35Te = 14.7 ms
508+
if (!collision) {
509+
if (0 == (bit_number &1)) { // Even bit
510+
// Start bit, Stop bit, Data bits
511+
bit_value = (0 == bit_number) ? 1 : (max_bit_number == bit_number) ? 0 : (bool)((send_data >> bit_pos--) &1); // MSB first
512+
} else { // Odd bit
513+
bit_value = !bit_value; // Complement bit
514+
}
515+
pin_value = bit_value ? LOW : HIGH; // Invert bit
516+
} else {
517+
if (max_bit_number == bit_number) {
518+
pin_value = HIGH; // Set to idle
519+
}
484520
}
485-
}
486521

487-
digitalWrite(Dali->pin_tx, (Dali->invert_tx) ? !pin_value : pin_value);
488-
wait += Dali->bit_cycles; // Auto roll-over
489-
while (ESP.getCycleCount() < wait);
490-
491-
if (!collision) {
492-
dali_read = (digitalRead(Dali->pin_rx) != Dali->invert_rx);
493-
if ((HIGH == pin_value) && (LOW == dali_read)) { // Collision if write is 1 and bus is 0
494-
collision = true;
495-
pin_value = LOW;
496-
bit_number = 29; // Keep bus low for 4 bits
497-
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Tx collision"));
522+
digitalWrite(Dali->pin_tx, (Dali->invert_tx) ? !pin_value : pin_value);
523+
wait += Dali->bit_cycles; // Auto roll-over
524+
while (ESP.getCycleCount() < wait);
525+
526+
if (!collision) {
527+
dali_read = (digitalRead(Dali->pin_rx) != Dali->invert_rx);
528+
if ((HIGH == pin_value) && (LOW == dali_read)) { // Collision if write is 1 and bus is 0
529+
collision = true;
530+
pin_value = LOW;
531+
bit_number = max_bit_number -5; // Keep bus low for 4 bits - break sequence
532+
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Tx collision"));
533+
}
498534
}
499-
}
500535

501-
bit_number++;
502-
}
536+
bit_number++;
537+
}
503538

504539
#ifdef ESP32
505-
portEXIT_CRITICAL(&mux);}
540+
portEXIT_CRITICAL(&mux);}
506541
#endif
507542

508-
// delayMicroseconds(1100); // Wait 3Te as sending stop bits - adds to total 15.8 ms
509-
Dali->last_activity = millis(); // Start Forward Frame delay time (>22Te)
510-
543+
// delayMicroseconds(1100); // Wait 3Te as sending stop bits - adds to total 15.8 ms
544+
Dali->last_activity = millis(); // Start Forward Frame delay time (>22Te)
545+
} while (retry-- && collision);
511546
}
512547

513548
/*-------------------------------------------------------------------------------------------*/
514549

515-
void DaliSendData(uint32_t adr, uint32_t cmd) {
550+
void DaliSendData(uint32_t adr, uint32_t cmd, uint32_t opt = 0);
551+
void DaliSendData(uint32_t adr, uint32_t cmd, uint32_t opt) {
516552
adr &= 0xFF;
517553
cmd &= 0xFF;
518554

519-
Dali->address = adr;
520-
Dali->command = cmd;
521-
DaliSaveState(adr, cmd);
555+
if (!opt) { // No DALI-2 24-bit command
556+
Dali->address = adr;
557+
Dali->command = cmd;
558+
DaliSaveState(adr, cmd);
559+
}
522560

523561
bool send_twice = false;
524562
if (adr & DALI_SELECTOR_BIT) { // Selector bit (command) or special command
@@ -547,12 +585,15 @@ void DaliSendData(uint32_t adr, uint32_t cmd) {
547585
}
548586
}
549587

588+
uint32_t send_dali_data = adr << 8 | cmd;
589+
if (opt & DALI_FORWARD_24BIT_FRAME) {
590+
send_dali_data = (send_dali_data << 8) | (opt & (DALI_FORWARD_24BIT_FRAME | 0xFF));
591+
}
592+
550593
#ifdef DALI_DEBUG
551-
AddLog(Dali->log_level, PSTR("DLI: Tx DT%d, Twice %d, Adr 0x%02X, Cmd 0x%02X"), Dali->device_type, send_twice, adr, cmd);
594+
AddLog(Dali->log_level, PSTR("DLI: Tx 0x%08X, Twice %d, DT%d"), send_dali_data, send_twice, Dali->device_type);
552595
#endif // DALI_DEBUG
553596

554-
uint16_t send_dali_data = adr << 8 | cmd;
555-
556597
DaliDisableRxInterrupt();
557598
DaliSendDataOnce(send_dali_data); // Takes 14.7 ms
558599
if (send_twice) {
@@ -578,16 +619,16 @@ int DaliSendWaitResponse(uint32_t adr, uint32_t cmd, uint32_t timeout) {
578619
int result = -1; // DALI NO or no response
579620
if (Dali->available) {
580621
Dali->available = false; // DALI collision (-2) or valid data (>=0)
581-
bool collision = (Dali->received_dali_data &0x00010000);
582-
bool forward_frame = (Dali->received_dali_data &0x00020000);
622+
bool collision = (Dali->received_dali_data & DALI_COLLISION);
623+
bool forward_frame = (Dali->received_dali_data & DALI_FORWARD_FRAME);
583624
if (!forward_frame) {
584625
result = (collision) ? -2 : (Dali->received_dali_data &0xFF);
585626
}
586627
}
587628
Dali->response = false;
588629

589630
#ifdef DALI_DEBUG
590-
AddLog(Dali->log_level, PSTR("DLI: Rx 0x%05X Response"), result);
631+
AddLog(Dali->log_level, PSTR("DLI: Rx 0x%08X Response"), result);
591632
#endif // DALI_DEBUG
592633

593634
return result;
@@ -883,13 +924,16 @@ void ResponseDali(uint32_t index) {
883924
void DaliLoop(void) {
884925
if (!Dali->available || Dali->response) { return; }
885926

886-
bool collision = (Dali->received_dali_data &0x00010000);
887-
bool forward_frame = (Dali->received_dali_data &0x00020000);
927+
bool collision = (Dali->received_dali_data & DALI_COLLISION);
928+
bool forward_frame = (Dali->received_dali_data & DALI_FORWARD_FRAME);
929+
bool forward_24bit_frame = (Dali->received_dali_data & DALI_FORWARD_24BIT_FRAME);
888930

889-
AddLog((1 == Dali->probe) ? LOG_LEVEL_DEBUG : LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Rx 0x%05X%s"), Dali->received_dali_data, (!forward_frame)?" backward":"");
931+
AddLog((1 == Dali->probe) ? LOG_LEVEL_DEBUG : LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Rx 0x%08X %s"),
932+
Dali->received_dali_data, (collision)?"collision":(forward_frame)?"":"backward");
890933

891934
if (collision || // Rx collision
892-
!forward_frame || // We do not serve backward frames
935+
!forward_frame || // Skip backward frames
936+
forward_24bit_frame || // Currently no support for DALI-2 24-bit frame
893937
(1 == Dali->probe)) { // Probe only
894938
Dali->available = false;
895939
return;
@@ -900,10 +944,7 @@ void DaliLoop(void) {
900944

901945
#ifdef USE_LIGHT
902946
#ifdef DALI_LIGHT_COLOR_SUPPORT
903-
if (DALI_209_SET_TEMPORARY_RGBWAF_CONTROL == Dali->command) {
904-
Dali->color_sequence = millis(); // Indicate start of color sequence - See DaliSetChannels()
905-
}
906-
else if (DALI_102_SET_DTR0 == Dali->address) { Dali->dtr[0] = Dali->command; } // Might be Red / White
947+
if (DALI_102_SET_DTR0 == Dali->address) { Dali->dtr[0] = Dali->command; } // Might be Red / White
907948
else if (DALI_102_SET_DTR1 == Dali->address) { Dali->dtr[1] = Dali->command; } // Might be Green / Amber
908949
else if (DALI_102_SET_DTR2 == Dali->address) { Dali->dtr[2] = Dali->command; } // Might be Blue
909950
else if (DALI_209_SET_TEMPORARY_RGB_DIMLEVEL == Dali->command) {
@@ -916,7 +957,6 @@ void DaliLoop(void) {
916957
Dali->color[4] = Dali->dtr[0]; // Cold White
917958
}
918959
else if (DALI_209_ACTIVATE == Dali->command) {
919-
Dali->color_sequence = 0;
920960
uint32_t channels = Dali->Settings.light_type -8;
921961
if ((Dali->target_rgbwaf > 0) && (channels > 0)) { // Color control
922962
Dali->address &= 0xFE; // Reset DALI_SELECTOR_BIT set
@@ -943,7 +983,7 @@ void DaliLoop(void) {
943983
} else
944984
#endif // DALI_LIGHT_COLOR_SUPPORT
945985
#endif // USE_LIGHT
946-
if ((!(Dali->address & DALI_SELECTOR_BIT)) && !Dali->color_sequence) { // Address
986+
if (!(Dali->address & DALI_SELECTOR_BIT)) { // Address
947987
uint32_t index = DaliSaveState(Dali->address, Dali->command); // Update dimmer and power
948988
bool show_response = true;
949989
#ifdef USE_LIGHT
@@ -981,9 +1021,6 @@ void DaliEverySecond(void) {
9811021
if (5 == TasmotaGlobal.uptime) {
9821022
DaliInitLight();
9831023
}
984-
if (Dali->color_sequence && TimeReached(Dali->color_sequence + 1000)) {
985-
Dali->color_sequence = 0; // Reset color sequence in case of incomplete DALI sequence
986-
}
9871024
}
9881025

9891026
/*********************************************************************************************\
@@ -1423,6 +1460,13 @@ void CmndDaliSend(void) {
14231460
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]);
14241461
#endif // DALI_DEBUG
14251462

1463+
if (255 == XdrvMailbox.index) { // DaliSend255 - Dali-2 24-bit frame
1464+
if (params >= 3) {
1465+
DaliSendData(values[0], values[1], values[2] | DALI_FORWARD_24BIT_FRAME);
1466+
ResponseCmndDone();
1467+
return;
1468+
}
1469+
}
14261470
if (DALI_207_DEVICE_TYPE == XdrvMailbox.index) { // DaliSend6 - DT6 = 207 = Extended LED commands 224...236
14271471
/*
14281472
params 0 1 2

0 commit comments

Comments
 (0)