2024-10-06 20:57:59 +01:00
/*
xdrv_75_dali . ino - DALI support for Tasmota
Copyright ( C ) 2022 Andrei Kazmirtsuk aka eeak and Theo Arends
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Version yyyymmdd Action Description
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2024-11-01 10:23:55 +00:00
1.1 .0 .1 20241101 update - Enable DALI if another light is already claimed
2024-10-31 16:01:36 +00:00
1.1 .0 .0 20241031 update - Add GUI sliders with feedback when ` DaliLight 0 `
- Add command ` DaliGroupSliders 0. .16 ` to show GUI sliders
2024-10-25 11:39:36 +01:00
1.0 .0 .2 20241025 update - Fix GPIO detection
- Fix ESP32 ( C3 ) transmit stability by disabling interrupts
2024-10-24 22:03:14 +01:00
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
2024-10-22 11:38:56 +01:00
1.0 .0 .0 20241022 update - Refactor commission
- Add receive collision detection
0.1 .0 .8 20241019 update - Rename command ` DaliCommission ` to ` DaliScan `
- Rename command ` DaliWeb ` to ` DaliLight `
- Add command ` DaliGear ` to set max found gear to speed up scan response
2024-10-17 16:44:05 +01:00
0.1 .0 .7 20241017 update - Add command ` DaliCommission 1 | 2 ` assigning short addresses
- Add command ` DaliTarget 0 , 1. .64 , 101. .116 ` to select light control address
- Add command ` DaliGroup1 . .16 + < devices > | - < devices > ` to add / remove devices from group
- Extend commands ` DaliPower ` and ` DaliDimmer ` with index to control short address or group
- Remove non - functional MQTT interface
2024-10-14 21:58:45 +01:00
0.1 .0 .6 20241014 update - Fix received light command loop
- Add send collision detection
2024-10-14 11:27:01 +01:00
0.1 .0 .5 20241014 update - Add command ` DaliSend [ repeat ] < address > , < command > `
- Add command ` DaliQuery [ repeat ] < address > , < command > `
- Send frame twice ( repeat ) for DALI defined commands
- Add support for receiving backward frame
2024-10-13 11:03:31 +01:00
0.1 .0 .4 20241013 update - Fix intermittent bad send timing
2024-10-10 16:49:11 +01:00
0.1 .0 .3 20241010 update - Change DaliDimmer range from 0. .254 to 0. .100
2024-10-14 11:27:01 +01:00
- Add command ` DaliWeb 0 | 1 ` to enable persistent Web light controls
2024-10-08 16:54:17 +01:00
0.1 .0 .2 20241008 update - Better receive error detection
2024-10-11 16:27:12 +01:00
0.1 .0 .1 20241007 update - To stablizie communication send DALI datagram twice like Busch - Jaeger does
2024-10-07 17:03:20 +01:00
- Change DaliPower 0. .2 to act like Tasmota Power ( Off , On , Toggle )
- Keep last Dimmer value as default power on
2024-10-06 20:57:59 +01:00
0.1 .0 .0 20241006 rewrite - Add support for ESP8266
2024-10-11 16:27:12 +01:00
- Fix decoding of received DALI data
2024-10-06 20:57:59 +01:00
- Refactor command ` DaliPower 0. .254 ` controlling Broadcast devices
- Add command ` DaliDimmer 0. .254 ` controlling Broadcast devices
0.0 .0 .1 20221027 publish - Initial version
*/
# ifdef USE_DALI
/*********************************************************************************************\
* DALI support for Tasmota
2024-10-17 16:44:05 +01:00
*
* Available commands :
2024-10-22 11:38:56 +01:00
* < byte1 > or < byte3 > = 0. .255 or 0x00 . .0 xFF + 256 / 0x100 for optional repeat ( send twice )
* < byte2 > or < byte4 > = 0. .255 or 0x00 . .0 xFF - Both decimal and hexadecimal is supported
2024-10-17 16:44:05 +01:00
* < broadcast > = 0 - DALI default
* < device > = 1. .64 - DALI short address + 1
* < group > = 101. .116 - DALI group + 101
2024-10-22 11:38:56 +01:00
* DaliSend < byte1 > , < byte2 > - Execute DALI code and do not expect a DALI backward frame
* DaliSend < 0xA3 > , < byte2 > , < byte3 > , < byte4 > - Set DALI parameter using DTR0 and do not expect a DALI backward frame
* DaliQuery < byte1 > , < byte2 > - Execute DALI code and report result ( DALI backward frame )
* DaliScan 1 | 2 - Reset ( 0 ) or ( 1 ) / and commission device short addresses
2024-10-25 11:39:36 +01:00
* DaliGear 1. .64 - Set max short address to speed up scanning - default 64
2024-10-17 16:44:05 +01:00
* DaliGroup < 1. .16 > [ + ] | - < device > , < device > . . . - Add ( + ) or Remove ( - ) devices to / from group
* DaliPower < broadcast > | < device > | < group > 0. .254 - Control power ( 0 = Off , 1 = Last dimmer , 2 = Toggle , 3. .254 = absolute light brightness )
* DaliDimmer < broadcast > | < device > | < group > 0. .100 - Control dimmer ( 0 = Off , 1. .100 = precentage of brightness )
2024-10-25 11:39:36 +01:00
* DaliLight 0 | 1 - Enable Tasmota light control for DaliTarget device - default 1
2024-10-22 11:38:56 +01:00
* DaliTarget < broadcast > | < device > | < group > - Set Tasmota light control device ( 0 , 1. .64 , 101. .116 ) - default 0
2024-10-17 16:44:05 +01:00
*
2024-10-22 11:38:56 +01:00
* DALI background information
2024-10-17 16:44:05 +01:00
* Address type Address byte
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Broadcast address 1111111 S
* 64 short address 0 AAAAAAS
* 16 group address 100 AAAAS
* Special command 101 CCCC1 to 110 CCCC1
* A = Address bit , S = 0 Direct Arc Power control , S = 1 Command , C = Special command
2024-10-24 22:03:14 +01:00
*
2024-10-25 11:39:36 +01:00
* Shelly DALI Dimmer Gen3 ( ESP32C3 - 8 M ) - GPIO3 controls DALI power . In following template it is always ON . Max output is 16 V / 10 mA ( = 5 DALI gear )
* Template { " NAME " : " Shelly DALI Dimmer Gen3 " , " GPIO " : [ 34 , 4736 , 0 , 3840 , 11360 , 11392 , 128 , 129 , 0 , 1 , 576 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 ] , " FLAG " : 0 , " BASE " : 1 }
2024-10-24 22:03:14 +01:00
* AdcGpio1 10000 , 10000 , 4000 < - Temperature parameters
2024-10-25 11:39:36 +01:00
* Backlog ButtonTopic 0 ; SetOption1 1 ; SetOption11 0 ; SetOption32 20 ; DimmerStep 5 ; LedTable 0
2024-10-24 22:03:14 +01:00
* 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
2024-10-06 20:57:59 +01:00
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-10-22 11:38:56 +01:00
# define XDRV_75 75
2024-10-06 20:57:59 +01:00
2024-10-07 17:08:58 +01:00
# ifndef DALI_INIT_STATE
2024-10-22 11:38:56 +01:00
# define DALI_INIT_STATE 50 // DALI init dimmer state 50/254
# endif
# ifndef DALI_INIT_FADE
# define DALI_INIT_FADE 1 // Fade between light states in number of seconds
2024-10-07 17:08:58 +01:00
# endif
2024-10-14 11:27:01 +01:00
# ifndef DALI_TIMEOUT
2024-10-22 11:38:56 +01:00
# define DALI_TIMEOUT 50 // DALI backward frame receive timeout (ms)
2024-10-14 11:27:01 +01:00
# endif
2024-10-06 20:57:59 +01:00
//#define DALI_DEBUG
# ifndef DALI_DEBUG_PIN
2024-10-22 11:38:56 +01:00
# define DALI_DEBUG_PIN 4 // Debug GPIO
2024-10-06 20:57:59 +01:00
# endif
2024-10-22 11:38:56 +01:00
// Control commands - Send as second byte without repeat
# define DALI_OFF 0x0000 // 0 - Turns off lighting.
# define DALI_UP 0x0001 // 1 - Increases the lighting control level for 200 ms according to the Fade rate.
# define DALI_DOWN 0x0002 // 2 - Decreases the lighting control level for 200 ms according to the Fade rate.
# define DALI_STEP_UP 0x0003 // 3 - Increments the lighting control level (without fade).
# define DALI_STEP_DOWN 0x0004 // 4 - Decrements the lighting control level (without fade).
# define DALI_RECALL_MAX_LEVEL 0x0005 // 5 - Maximizes the lighting control level (without fade).
# define DALI_RECALL_MIN_LEVEL 0x0006 // 6 - Minimizes the lighting control level (without fade)
# define DALI_STEP_DOWN_AND_OFF 0x0007 // 7 - Decrements the lighting control level and turns off lighting if the level is at the minimum (without fade).
# define DALI_ON_AND_STEP_UP 0x0008 // 8 - Increments the lighting control level and turns on lighting if lighting is off (with fade).
# define DALI_ENABLE_DAPC_SEQUENCE 0x0009 // 9 - It shows the repeat start of the DAPC command.
# define DALI_GO_TO_LAST_ACTIVE_LEVEL 0x000A // 10 - DALI-2 Adjusts the lighting control level to the last light control level according to the Fade time.
# define DALI_RESERVED11 0x000B // 11 - [Reserved]
# define DALI_RESERVED15 0x000F // 15 - [Reserved]
# define DALI_GO_TO_SCENE0 0x0010 // 16 - Adjusts the lighting control level for Scene XXXX according to the fade time.
# define DALI_GO_TO_SCENE15 0x001F // 31 - Adjusts the lighting control level for Scene XXXX according to the fade time.
// Configuration commands - Send as second byte with repeat
# define DALI_RESET 0x0020 // 32 - Makes a slave an RESET state.
# define DALI_STORE_ACTUAL_LEVEL_IN_THE_DTR0 0x0021 // 33 - Saves the current lighting control level to the DTR (DTR0).
# define DALI_SAVE_PERSISTENT_VARIABLES 0x0022 // 34 - DALI-2 Saves a variable in nonvolatile memory (NVM).
# define DALI_SET_OPERATING_MODE 0x0023 // 35 - DALI-2 Sets data of DTR0 as an operating mode.
# define DALI_RESET_MEMORY_BANK 0x0024 // 36 - DALI-2 Changes to the reset value the specified memory bank in DTR0.
# define DALI_IDENTIFY_DEVICE 0x0025 // 37 - DALI-2 Starts the identification state of the device.
# define DALI_RESERVED38 0x0026 // 38 - [Reserved]
# define DALI_RESERVED41 0x0029 // 41 - [Reserved]
# define DALI_SET_MAX_LEVEL 0x002A // 42 - Specifies the DTR data as the maximum lighting control level.
# define DALI_SET_MIN_LEVEL 0x002B // 43 - Specifies the DTR data as the minimum lighting control level.
# define DALI_SET_SYSTEM_FAILURE_LEVEL 0x002C // 44 - Specifies the DTR data as the "FAILURELEVEL".
# define DALI_SET_POWER_ON_LEVEL 0x002D // 45 - Specifies the DTR data as the "POWER ONLEVEL".
# define DALI_SET_FADE_TIME 0x002E // 46 - Specifies the DTR data as the Fade time.
# define DALI_SET_FADE_RATE 0x002F // 47 - Specifies the DTR data as the Fade rate.
# define DALI_SET_EXTENDED_FADE_TIME 0x0030 // 48 - DALI-2 Specifies the DTR data as the Extended Fade Time.
# define DALI_RESERVED49 0x0031 // 49 - [Reserved]
# define DALI_RESERVED63 0x003F // 63 - [Reserved]
# define DALI_SET_SCENE0 0x0040 // 64 - Specifies the DTR data as Scene XXXX.
# define DALI_SET_SCENE15 0x004F // 79 - Specifies the DTR data as Scene XXXX.
# define DALI_REMOVE_FROM_SCENE0 0x0050 // 80 - Deletes the Scene XXXX setting. (Specifies 1111 1111 for the scene register.)
# define DALI_REMOVE_FROM_SCENE15 0x005F // 95 - Deletes the Scene XXXX setting. (Specifies 1111 1111 for the scene register.)
# define DALI_ADD_TO_GROUP0 0x0060 // 96 - Adds the slave to Group XXXX.
# define DALI_ADD_TO_GROUP15 0x006F // 111 - Adds the slave to Group XXXX.
# define DALI_REMOVE_FROM_GROUP0 0x0070 // 112 - Deletes the slave from Group XXXX.
# define DALI_REMOVE_FROM_GROUP15 0x007F // 127 - Deletes the slave from Group XXXX.
# define DALI_SET_SHORT_ADDRESS 0x0080 // 128 - Specifies the DTR data as a Short Address.
# define DALI_ENABLE_WRITE_MEMORY 0x0081 // 129 - Allows writing of the memory bank.
# define DALI_RESERVED130 0x0082 // 130 - [Reserved]
# define DALI_RESERVED143 0x008F // 143 - [Reserved]
// Query commands - Send as second byte
# define DALI_QUERY_STATUS 0x0090 // 144 - Returns "STATUS INFORMATION"
# define DALI_QUERY_CONTROL_GEAR_PRESENT 0x0091 // 145 - Is there a slave that can communicate?
# define DALI_QUERY_LAMP_FAILURE 0x0092 // 146 - Is there a lamp problem?
# define DALI_QUERY_LAMP_POWER_ON 0x0093 // 147 - Is a lamp on?
# define DALI_QUERY_LIMIT_ERROR 0x0094 // 148 - Is the specified lighting control level out of the range from the minimum to the maximum values?
# define DALI_QUERY_RESET_STATE 0x0095 // 149 - Is the slave in 'RESET STATE'?
# define DALI_QUERY_MISSING_SHORT_ADDRESS 0x0096 // 150 - Does the slave not have a short address?
# define DALI_QUERY_VERSION_NUMBER 0x0097 // 151 - What is the corresponding IEC standard number?
# define DALI_QUERY_CONTENT_DTR0 0x0098 // 152 - What is the DTR content?
# define DALI_QUERY_DEVICE_TYPE 0x0099 // 153 - What is the device type? (fluorescent lamp:0000 0000) (IEC62386-207 is 6 fixed)
# define DALI_QUERY_PHYSICAL_MINIMUM_LEVEL 0x009A // 154 - What is the minimum lighting control level specified by the hardware?
# define DALI_QUERY_POWER_FAILURE 0x009B // 155 - Has the slave operated without the execution of reset-command or the adjustment of the lighting control level?
# define DALI_QUERY_CONTENT_DTR1 0x009C // 156 - What is the DTR1 content?
# define DALI_QUERY_CONTENT_DTR2 0x009D // 157 - What is the DTR2 content?
# define DALI_QUERY_OPERATING_MODE 0x009E // 158 - DALI-2 What is the Operating Mode?
# define DALI_QUERY_LIGHT_SOURCE_TYPE 0x009F // 159 - DALI-2 What is the Light source type?
# define DALI_QUERY_ACTUAL_LEVEL 0x00A0 // 160 - What is the "ACTUAL LEVEL" (the current lighting control level)?
# define DALI_QUERY_MAX_LEVEL 0x00A1 // 161 - What is the maximum lighting control level?
# define DALI_QUERY_MIN_LEVEL 0x00A2 // 162 - What is the minimum lighting control level?
# define DALI_QUERY_POWER_ON_LEVEL 0x00A3 // 163 - What is the "POWER ON LEVEL" (the lighting control level when the power is turned on)?
# define DALI_QUERY_SYSTEM_FAILURE_LEVEL 0x00A4 // 164 - What is the "SYSTEM FAILURE LEVEL" (the lighting control level when a failure occurs)?
# define DALI_QUERY_FADE_TIME_FADE_RATE 0x00A5 // 165 - What are the Fade time and Fade rate?
# define DALI_QUERY_MANUFACTURER_SPECIFIC_MODE 0x00A6 // 166 - DALI-2 What is the Specific Mode?
# define DALI_QUERY_NEXT_DEVICE_TYPE 0x00A7 // 167 - DALI-2 What is the next Device Type?
# define DALI_QUERY_EXTENDED_FADE_TIME 0x00A8 // 168 - DALI-2 What is the Extended Fade Time?
# define DALI_QUERY_CONTROL_GEAR_FAILURE 0x00A9 // 169 - DALI-2 Does a slave have the abnormality?
# define DALI_RESERVED170 0x00AA // 170 - [Reserved]
# define DALI_RESERVED175 0x00AF // 175 - [Reserved]
# define DALI_QUERY_SCENE0_LEVEL 0x00B0 // 176 - What is the lighting control level for SCENE XXXX?
# define DALI_QUERY_GROUPS_0_7 0x00C0 // 192 - Does the slave belong to a group among groups 0 to 7? (Each bit corresponds to a group.)
# define DALI_QUERY_GROUPS_8_15 0x00C1 // 193 - Does the slave belong to a group among groups 8 to 15? (Each bit corresponds to a group.)
# define DALI_QUERY_RANDOM_ADDRESS_H 0x00C2 // 194 - What are the higher 8 bits of the random address?
# define DALI_QUERY_RANDOM_ADDRESS_M 0x00C3 // 195 - What are the middle 8 bits of the random address?
# define DALI_QUERY_RANDOM_ADDRESS_L 0x00C4 // 196 - What are the lower 8 bits of the random address?
# define DALI_READ_MEMORY_LOCATION 0x00C5 // 197 - What is the memory location content?
# define DALI_RESERVED198 0x00C6 // 198 - [Reserved]
# define DALI_RESERVED223 0x00DF // 223 - [Reserved]
// Application extended configuration commands - Send as second byte
# define DALI_REFERENCE_SYSTEM_POWER 0x00E0 // 224 - IEC62386-207 Starts power measurement.
# define DALI_ENABLE_CURRENT_PROTECTOR 0x00E1 // 225 - IEC62386-207 Enables the current protection.
# define DALI_DISABLE_CURRENT_PROTECTOR 0x00E2 // 226 - IEC62386-207 Disables the current protection.
# define DALI_SELECT_DIMMING_CURVE 0x00E3 // 227 - IEC62386-207 Selects Dimming curve.
# define DALI_STORE_DTR_AS_FAST_FADE_TIME 0x00E4 // 228 - IEC62386-207 Sets the DTR of the data as Fast Fade Time.
# define DALI_RESERVED229 0x00E5 // 229 - [Reserved]
# define DALI_RESERVED236 0x00EC // 236 - [Reserved]
// Application extended query commands - Send as second byte
# define DALI_QUERY_GEAR_TYPE 0x00ED // 237 - IEC62386-207 Returns ‘ GEAR TYPE’
# define DALI_QUERY_DIMMING_CURVE 0x00EE // 238 - IEC62386-207 Returns ’ Dimming curve’ in use
# define DALI_QUERY_POSSIBLE_OPERATING_MODE 0x00EF // 239 - IEC62386-207 Returns ‘ POSSIBLE OPERATING MODE’
# define DALI_QUERY_FEATURES 0x00F0 // 240 - IEC62386-207 Returns ‘ FEATURES’
# define DALI_QUERY_FAILURE_STATUS 0x00F1 // 241 - IEC62386-207 Returns ‘ FAILURE STATUS’
# define DALI_QUERY_SHORT_CIRCUIT 0x00F2 // 242 - IEC62386-207 Returns bit0 short circuit of ‘ FAILURE STATUS’
# define DALI_QUERY_OPEN_CIRCUIT 0x00F3 // 243 - IEC62386-207 Returns bit1 open circuit of ‘ FAILURE STATUS’
# define DALI_QUERY_LOAD_DECREASE 0x00F4 // 244 - IEC62386-207 Returns bit2 load decrease of ‘ FAILURE STATUS’
# define DALI_QUERY_LOAD_INDREASE 0x00F5 // 245 - IEC62386-207 Returns bit3 load increase of‘ FAILURE STATUS’
# define DALI_QUERY_CURRENT_PROTECTOR_ACTIVE 0x00F6 // 246 - IEC62386-207 Returns bit4 current protector active of ‘ FAILURE STATUS’
# define DALI_QUERY_THERMAL_SHUTDOWN 0x00F7 // 247 - IEC62386-207 Returns bit5 thermal shut down of ‘ FAILURE STATUS’
# define DALI_QUERY_THERMAL_OVERLOAD 0x00F8 // 248 - IEC62386-207 Returns bit6 thermal overload with light level reduction of ‘ FAILURE STATUS’
# define DALI_QUERY_REFARENCE_RUNNING 0x00F9 // 249 - IEC62386-207 Returns whetherReference System Power is in operation.
# define DALI_QUERY_REFERENCE_MEASURMENT_FAILED 0x00FA // 250 - IEC62386-207 Returns bit7 reference measurement failed of ‘ FAILURE STATUS’
# define DALI_QUERY_CURRENT_PROTECTOR_ENABLE 0x00FB // 251 - IEC62386-207 Returns state of Curent protector
# define DALI_QUERY_OPERATING_MODE2 0x00FC // 252 - IEC62386-207 Returns ‘ OPERATING MODE’
# define DALI_QUERY_FAST_FADE_TIME 0x00FD // 253 - IEC62386-207 Returns set Fast fade time.
# define DALI_QUERY_MIN_FAST_FADE_TIME 0x00FE // 254 - IEC62386-207 Returns set Minimum fast fade time
# define DALI_QUERY_EXTENDED_VERSION_NUMBER 0x00FF // 255 - IEC62386-207 The version number of the extended support? IEC62386-207: 1, Other: NO(no response)
// Address types - Send as first byte
# define DALI_SHORT_ADDRESS0 0x0000 // 0b00000000 0 - First short address
# define DALI_SHORT_ADDRESS1 0x0002 // 0b00000010 1 - Next short address
# define DALI_SHORT_ADDRESS63 0x007E // 0b01111110 63 - Last short address
# define DALI_GROUP_ADDRESS0 0x0080 // 0b10000000 0 - First group address
# define DALI_GROUP_ADDRESS1 0x0082 // 0b10000010 1 - Next group address
# define DALI_GROUP_ADDRESS15 0x009E // 0b10011110 15 - Last group address
// Special commands - Send as first byte
# define DALI_TERMINATE 0x00A1 // 256 - Releases the INITIALISE state.
# define DALI_DATA_TRANSFER_REGISTER0 0x00A3 // 257 - Stores the data XXXX XXXX to the DTR(DTR0).
# define DALI_INITIALISE 0x01A5 // 258 REPEAT - Sets the slave to the INITIALISE status for15 minutes. Commands 259 to 270 are enabled only for a slave in this status.
# define DALI_RANDOMISE 0x01A7 // 259 REPEAT - Generates a random address.
# define DALI_COMPARE 0x00A9 // 260 - Is the random address smaller or equal to the search address?
# define DALI_WITHDRAW 0x00AB // 261 - Excludes slaves for which the random address and search address match from the Compare process.
# define DALI_RESERVED262 0x00AD // 262 - [Reserved]
# define DALI_PING 0x00AF // 263 - DALI-2 Ignores in the slave.
# define DALI_SEARCHADDRH 0x00B1 // 264 - Specifies the higher 8 bits of the search address.
# define DALI_SEARCHADDRM 0x00B3 // 265 - Specifies the middle 8 bits of the search address.
# define DALI_SEARCHADDRL 0x00B5 // 266 - Specifies the lower 8 bits of the search address.
# define DALI_PROGRAM_SHORT_ADDRESS 0x00B7 // 267 - The slave shall store the received 6-bit address (AAA AAA) as a short address if it is selected.
# define DALI_VERIFY_SHORT_ADDRESS 0x00B9 // 268 - Is the short address AAA AAA?
# define DALI_QUERY_SHORT_ADDRESS 0x00BB // 269 - What is the short address of the slaveNote 2being selected?
# define DALI_PHYSICAL_SELECTION 0x00BD // 270 - not DALI-2 Sets the slave to Physical Selection Mode and excludes the slave from the Compare process. (Excluding IEC62386-102ed2.0)
# define DALI_RESERVED271 0x00BF // 271 - [Reserved]
// Extending special commands - Send as first byte
# define DALI_ENABLE_DEVICE_TYPE_X 0x00C1 // 272 - Adds the device XXXX (a special device).
# define DALI_DATA_TRANSFER_REGISTER1 0x00C3 // 273 - Stores data XXXX into DTR1.
# define DALI_DATA_TRANSFER_REGISTER2 0x00C5 // 274 - Stores data XXXX into DTR2.
# define DALI_WRITE_MEMORY_LOCATION 0x00C7 // 275 - Write data into the specified address of the specified memory bank. (There is BW) (DTR(DTR0): address, DTR1: memory bank number)
# define DALI_WRITE_MEMORY_LOCATION_NO_REPLY 0x00C9 // 276 - DALI-2 Write data into the specified address of the specified memory bank. (There is no BW) (DTR(DTR0): address, TR1: memory bank number)
# define DALI_RESERVED277 0x00CB // 277 - [Reserved]
# define DALI_RESERVED349 0x00FD // 349 - [Reserved]
// Address type - Send as first byte
# define DALI_BROADCAST_DP 0x00FE // 0b11111110 254 - Broadcast address
2024-10-06 20:57:59 +01:00
2024-10-31 16:01:36 +00:00
# define DALI_MAX_STORED 17 // Store broadcast and group states
2024-10-06 20:57:59 +01:00
# define DALI_TOPIC "DALI"
# define D_PRFX_DALI "Dali"
2024-11-12 14:32:10 +00:00
/*********************************************************************************************/
2024-10-06 20:57:59 +01:00
const char kDALICommands [ ] PROGMEM = D_PRFX_DALI " | " // Prefix
2024-10-17 16:44:05 +01:00
" | " D_CMND_POWER " | " D_CMND_DIMMER " |Target "
2024-10-11 10:54:08 +01:00
# ifdef USE_LIGHT
2024-10-22 11:38:56 +01:00
" |Light "
2024-10-11 10:54:08 +01:00
# endif // USE_LIGHT
2024-10-31 16:01:36 +00:00
" |Send|Query|Scan|Group "
" |GroupSliders|Gear " ;
2024-10-06 20:57:59 +01:00
void ( * const DALICommand [ ] ) ( void ) PROGMEM = {
2024-10-17 16:44:05 +01:00
& CmndDali , & CmndDaliPower , & CmndDaliDimmer , & CmndDaliTarget ,
2024-10-11 10:54:08 +01:00
# ifdef USE_LIGHT
2024-10-22 11:38:56 +01:00
& CmndDaliLight ,
2024-10-11 10:54:08 +01:00
# endif // USE_LIGHT
2024-10-31 16:01:36 +00:00
& CmndDaliSend , & CmndDaliQuery , & CmndDaliScan , & CmndDaliGroup ,
& CmndDaliGroupSliders , & CmndDaliGear } ;
2024-10-06 20:57:59 +01:00
struct DALI {
2024-10-24 22:03:14 +01:00
uint32_t bit_cycles ;
2024-10-22 11:38:56 +01:00
uint32_t last_activity ;
uint32_t received_dali_data ; // Data received from DALI bus
2024-10-06 20:57:59 +01:00
uint8_t pin_rx ;
uint8_t pin_tx ;
2024-10-22 11:38:56 +01:00
uint8_t max_short_address ;
2024-10-06 21:40:28 +01:00
uint8_t address ;
uint8_t command ;
2024-10-31 16:01:36 +00:00
uint8_t last_dimmer ;
uint8_t dimmer [ DALI_MAX_STORED ] ;
uint8_t web_dimmer [ DALI_MAX_STORED ] ;
2024-10-17 16:44:05 +01:00
uint8_t target ;
2024-11-01 10:23:55 +00:00
bool allow_light ;
2024-10-31 16:01:36 +00:00
bool last_power ;
bool power [ DALI_MAX_STORED ] ;
2024-10-11 10:54:08 +01:00
bool available ;
2024-10-14 11:27:01 +01:00
bool response ;
2024-10-14 21:58:45 +01:00
bool light_sync ;
2024-10-22 11:38:56 +01:00
bool probe ;
2024-10-24 22:03:14 +01:00
bool invert_rx ;
bool invert_tx ;
2024-10-06 20:57:59 +01:00
} * Dali = nullptr ;
/*********************************************************************************************\
* DALI low level
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-10-31 16:01:36 +00:00
uint32_t DaliTarget2Address ( void ) {
2024-10-17 16:44:05 +01:00
// 1..64 = Short address
// 101..116 = Group address
// Others = Broadcast
2024-10-31 16:01:36 +00:00
if ( ( Dali - > target > = 1 ) & & ( Dali - > target < = 64 ) ) { // 1 .. 64
Dali - > target - = 1 ; // Short address
Dali - > target < < = 1 ;
2024-10-17 16:44:05 +01:00
}
2024-10-31 16:01:36 +00:00
else if ( ( Dali - > target > = 101 ) & & ( Dali - > target < = 116 ) ) { // 101 .. 116
Dali - > target - = 101 ;
Dali - > target < < = 1 ;
Dali - > target | = 0x80 ; // Group address
2024-10-17 16:44:05 +01:00
}
else { // Others
2024-10-31 16:01:36 +00:00
Dali - > target = DALI_BROADCAST_DP ; // Broadcast address
2024-10-17 16:44:05 +01:00
}
2024-10-31 16:01:36 +00:00
return Dali - > target & 0xFE ; // Direct Arc Power Control command
2024-10-17 16:44:05 +01:00
}
2024-10-22 11:38:56 +01:00
2024-10-17 16:44:05 +01:00
/*
uint32_t DaliAddress2Target ( uint32_t adr ) {
if ( adr > = 254 ) { // 0b1111111S
return 0 ; // Broadcast address (0)
}
else if ( ( adr > = 128 ) & & ( adr < = 159 ) ) { // 0b1000000S .. 0b1001111S
return ( adr > > 1 ) + 101 ; // Group address (101 .. 116)
}
return ( adr > > 1 ) + 1 ; // 0b0000000S .. 0b0111111S Short address (1 .. 64)
}
*/
2024-10-22 11:38:56 +01:00
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-31 16:01:36 +00:00
uint32_t DaliSaveState ( uint32_t adr , uint32_t cmd ) {
if ( adr & 0x01 ) { return 0 ; } // No address
int index = - 1 ;
if ( DALI_BROADCAST_DP = = adr ) { // Broadcast address
index = 0 ;
}
adr > > = 1 ;
if ( ( adr > = 0x40 ) & & ( adr < = 0x4F ) ) { // Group address 0 to 15
index = adr - 0x3F ;
}
if ( index > = 0 ) {
Dali - > last_power = Dali - > power [ index ] ;
Dali - > power [ index ] = ( cmd ) ; // State
if ( Dali - > power [ index ] ) {
Dali - > last_dimmer = Dali - > dimmer [ index ] ;
Dali - > dimmer [ index ] = cmd ; // Value
}
if ( ( 0 = = index ) & & ! Dali - > power [ 0 ] ) { // Only on Broadcast change to power Off
for ( uint32_t i = 0 ; i < DALI_MAX_STORED ; i + + ) {
Dali - > power [ i ] = false ; // Log all group power as Off when Broadcast is Off
}
}
} else {
index = 0 ; // Use broadcast
}
return index ;
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-06 20:57:59 +01:00
void DaliEnableRxInterrupt ( void ) {
2024-10-14 11:27:01 +01:00
Dali - > available = false ;
2024-10-24 22:03:14 +01:00
attachInterrupt ( Dali - > pin_rx , DaliReceiveData , ( Dali - > invert_rx ) ? RISING : FALLING ) ;
2024-10-06 20:57:59 +01:00
}
void DaliDisableRxInterrupt ( void ) {
detachInterrupt ( Dali - > pin_rx ) ;
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*\
* DALI receive
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2024-10-06 20:57:59 +01:00
2024-10-08 16:54:17 +01:00
void IRAM_ATTR DaliReceiveData ( void ) ; // Fix ESP8266 ISR not in IRAM! exception
2024-10-06 20:57:59 +01:00
void DaliReceiveData ( void ) {
2024-10-14 11:27:01 +01:00
/*
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
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
Start and Stop bits 1 1 1
Manchester data 011001011010011010
Stop bits 1111
Bit number 01234567 890123456789012345678901234567
1 2 3
*/
2024-10-11 10:54:08 +01:00
if ( Dali - > available ) { return ; } // Skip if last input is not yet handled
2024-10-22 11:38:56 +01:00
uint32_t gap_time = millis ( ) - Dali - > last_activity ;
2024-10-24 22:03:14 +01:00
uint32_t wait = ESP . getCycleCount ( ) + ( Dali - > bit_cycles / 2 ) ;
2024-10-06 20:57:59 +01:00
int bit_state = 0 ;
bool dali_read ;
uint32_t received_dali_data = 0 ;
2024-10-14 11:27:01 +01:00
uint32_t bit_number = 0 ;
while ( bit_number < 38 ) {
2024-10-08 16:54:17 +01:00
while ( ESP . getCycleCount ( ) < wait ) ;
2024-10-24 22:03:14 +01:00
wait + = Dali - > bit_cycles ; // Auto roll-over
dali_read = ( digitalRead ( Dali - > pin_rx ) ! = Dali - > invert_rx ) ;
2024-10-06 20:57:59 +01:00
# ifdef DALI_DEBUG
2024-10-14 11:27:01 +01:00
digitalWrite ( DALI_DEBUG_PIN , bit_number & 1 ) ; // Add LogicAnalyzer poll indication
2024-10-06 20:57:59 +01:00
# endif // DALI_DEBUG
2024-10-14 11:27:01 +01:00
if ( bit_number < 34 ) { // 34 manchester encoded bits
2024-10-06 20:57:59 +01:00
bit_state + = ( dali_read ) ? 1 : - 1 ;
2024-10-14 11:27:01 +01:00
if ( 0 = = bit_state ) { // Manchester encoding total 2 bits is always 0
if ( bit_number > 2 ) { // Skip start bit
received_dali_data < < = 1 ;
2024-10-14 22:33:46 +01:00
received_dali_data | = dali_read ;
2024-10-08 16:54:17 +01:00
}
2024-10-06 20:57:59 +01:00
}
2024-10-14 11:27:01 +01:00
else if ( ( 2 = = bit_state ) & &
( bit_number = = 19 ) ) { // Possible backward frame detected - Chk stop bits
bit_state = 0 ;
bit_number = 35 ;
}
2024-10-22 11:38:56 +01:00
else if ( abs ( bit_state ) > 1 ) { // Invalid manchester data (too many 0 or 1)
2024-10-14 11:27:01 +01:00
break ;
}
} else { // 4 high Stop bits
if ( bit_state ! = 0 ) { // Invalid manchester data
break ;
}
else if ( dali_read ! = 1 ) { // Invalid level of stop bit
bit_state = 1 ;
break ;
}
2024-10-06 20:57:59 +01:00
}
2024-10-14 11:27:01 +01:00
bit_number + + ;
2024-10-06 20:57:59 +01:00
}
2024-10-22 11:38:56 +01:00
Dali - > last_activity = millis ( ) ;
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
if ( Dali - > response ) { // Expect backward frame with no collision
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " DLI: Rx collision " ) ) ;
}
}
if ( Dali - > probe ) {
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " DLI: Rx %05X %d " ) , received_dali_data , gap_time ) ;
} else {
2024-10-14 11:27:01 +01:00
if ( Dali - > response | | // Response from last message send
( Dali - > received_dali_data ! = received_dali_data ) ) { // Skip duplicates
2024-10-08 16:54:17 +01:00
Dali - > received_dali_data = received_dali_data ;
2024-10-22 11:38:56 +01:00
Dali - > available = true ; // Any data received
2024-10-08 16:54:17 +01:00
}
2024-10-06 20:57:59 +01:00
}
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*\
* DALI send
\ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2024-10-06 20:57:59 +01:00
2024-10-07 17:03:20 +01:00
void DaliSendDataOnce ( uint16_t send_dali_data ) {
2024-10-14 11:27:01 +01:00
/*
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 01234567 890123456789012345678901234567
1 2 3
*/
2024-10-22 11:38:56 +01:00
Dali - > last_activity + = 14 ; // As suggested by DALI protocol (> 9.17 ms)
while ( ! TimeReached ( Dali - > last_activity ) ) {
delay ( 1 ) ; // Wait for bus to be free if needed
}
2024-10-08 16:54:17 +01:00
bool bit_value ;
2024-10-14 21:58:45 +01:00
bool pin_value ;
2024-10-14 22:33:46 +01:00
bool dali_read ;
2024-10-14 21:58:45 +01:00
bool collision = false ;
2024-10-08 16:54:17 +01:00
uint32_t bit_pos = 15 ;
2024-10-14 21:58:45 +01:00
uint32_t bit_number = 0 ;
2024-10-25 11:39:36 +01:00
# ifdef ESP32
{ portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED ;
portENTER_CRITICAL ( & mux ) ;
# endif
uint32_t wait = ESP . getCycleCount ( ) ;
2024-10-14 21:58:45 +01:00
while ( bit_number < 35 ) { // 417 * 35 = 14.7 ms
if ( ! collision ) {
if ( 0 = = ( bit_number & 1 ) ) { // Even bit
2024-10-14 22:33:46 +01:00
// Start bit, Stop bit, Data bits
2024-10-14 21:58:45 +01:00
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
}
2024-10-08 16:54:17 +01:00
}
2024-10-14 21:58:45 +01:00
2024-10-24 22:03:14 +01:00
digitalWrite ( Dali - > pin_tx , ( Dali - > invert_tx ) ? ! pin_value : pin_value ) ;
wait + = Dali - > bit_cycles ; // Auto roll-over
2024-10-13 11:03:31 +01:00
while ( ESP . getCycleCount ( ) < wait ) ;
2024-10-14 21:58:45 +01:00
if ( ! collision ) {
2024-10-24 22:03:14 +01:00
dali_read = ( digitalRead ( Dali - > pin_rx ) ! = Dali - > invert_rx ) ;
2024-10-14 22:33:46 +01:00
if ( ( HIGH = = pin_value ) & & ( LOW = = dali_read ) ) { // Collision if write is 1 and bus is 0
2024-10-14 21:58:45 +01:00
collision = true ;
pin_value = LOW ;
bit_number = 29 ; // Keep bus low for 4 bits
2024-10-22 11:38:56 +01:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " DLI: Tx collision " ) ) ;
2024-10-14 21:58:45 +01:00
}
}
bit_number + + ;
2024-10-06 20:57:59 +01:00
}
2024-10-25 11:39:36 +01:00
# ifdef ESP32
portEXIT_CRITICAL ( & mux ) ; }
# endif
2024-10-14 11:27:01 +01:00
// delayMicroseconds(1100); // Adds to total 15.8 ms
2024-10-22 11:38:56 +01:00
Dali - > last_activity = millis ( ) ;
2024-10-07 17:03:20 +01:00
}
2024-10-06 20:57:59 +01:00
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
2024-10-14 11:27:01 +01:00
void DaliSendData ( uint32_t adr , uint32_t cmd ) {
bool repeat = ( adr & 0x100 ) ; // Set repeat if bit 8 is set
adr & = 0xFF ;
cmd & = 0xFF ;
Dali - > address = adr ;
Dali - > command = cmd ;
2024-10-31 16:01:36 +00:00
DaliSaveState ( adr , cmd ) ;
2024-10-14 11:27:01 +01:00
if ( ! repeat & & ( adr & 0x01 ) ) { // YAAAAAA1 Commands where user didn't set repeat
if ( ( adr > = 0xA1 ) & & ( adr < = 0xFD ) ) { // Special commands
repeat = ( ( 0xA5 = = adr ) | | ( 0xA7 = = adr ) ) ;
} else {
// ((cmd >=0) && (cmd <= 31)) // Arc power control commands
repeat = ( ( ( cmd > = 32 ) & & ( cmd < = 143 ) ) | | // Configuration commands
( ( cmd > = 224 ) & & ( cmd < = 236 ) ) ) ; // Extended configuration commands
// ((cmd >=144) && (cmd <= 223)) // Query commands
// ((cmd >=237) && (cmd <= 255)) // Extended query commands
2024-10-07 17:03:20 +01:00
}
}
2024-10-14 11:27:01 +01:00
2024-10-17 16:44:05 +01:00
# ifdef DALI_DEBUG
2024-10-25 11:39:36 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " DLI: Tx 0x%d%02X%02X " ) , repeat , adr , cmd ) ;
2024-10-17 16:44:05 +01:00
# endif // DALI_DEBUG
2024-10-14 11:27:01 +01:00
uint16_t send_dali_data = adr < < 8 | cmd ;
2024-10-07 17:03:20 +01:00
DaliDisableRxInterrupt ( ) ;
2024-10-14 11:27:01 +01:00
DaliSendDataOnce ( send_dali_data ) ; // Takes 14.7 ms
if ( repeat ) {
2024-10-13 11:03:31 +01:00
DaliSendDataOnce ( send_dali_data ) ; // Takes 14.7 ms
}
2024-10-14 11:27:01 +01:00
delay ( 2 ) ; // Block response
2024-10-06 20:57:59 +01:00
DaliEnableRxInterrupt ( ) ;
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
2024-10-14 11:27:01 +01:00
int DaliSendWaitResponse ( uint32_t adr , uint32_t cmd , uint32_t timeout = DALI_TIMEOUT ) ;
int DaliSendWaitResponse ( uint32_t adr , uint32_t cmd , uint32_t timeout ) {
Dali - > response = true ;
DaliSendData ( adr , cmd ) ;
while ( ! Dali - > available & & timeout - - ) { // Expect backward frame within DALI_TIMEOUT ms
delay ( 1 ) ;
} ;
int result = - 1 ; // DALI NO or no response
if ( Dali - > available ) {
2024-10-22 11:38:56 +01:00
Dali - > available = false ; // DALI collision (-2) or valid data (>=0)
result = ( Dali - > received_dali_data & 0x00010000 ) ? - 2 : Dali - > received_dali_data ;
2024-10-14 11:27:01 +01:00
}
Dali - > response = false ;
2024-10-17 16:44:05 +01:00
# ifdef DALI_DEBUG
2024-10-25 11:39:36 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " DLI: Rx 0x%05X " ) , result ) ;
2024-10-17 16:44:05 +01:00
# endif // DALI_DEBUG
2024-10-14 11:27:01 +01:00
return result ;
}
2024-10-17 16:44:05 +01:00
/*********************************************************************************************\
2024-10-22 11:38:56 +01:00
* DALI tools
2024-10-17 16:44:05 +01:00
*
* Courtesy of https : //github.com/qqqlab/DALI-Lighting-Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-10-22 11:38:56 +01:00
bool DaliSetValue ( uint32_t adr , uint32_t getcmd , uint32_t setcmd , uint32_t v ) {
// Set a parameter value, returns true on success
int current_v = DaliSendWaitResponse ( adr , getcmd ) ; // Get current parameter value
if ( current_v = = v ) { return true ; } // Already set
DaliSendData ( DALI_DATA_TRANSFER_REGISTER0 , v ) ; // Store value in DTR
int dtr = DaliSendWaitResponse ( adr , DALI_QUERY_CONTENT_DTR0 ) ; // Get DTR value
if ( dtr ! = v ) { return false ; }
DaliSendData ( adr , setcmd ) ; // Set parameter value = DTR
current_v = DaliSendWaitResponse ( adr , getcmd ) ; // Get current parameter value
if ( current_v ! = v ) { return false ; } // Set failed
return true ;
}
2024-10-17 16:44:05 +01:00
2024-10-22 11:38:56 +01:00
bool DaliSetOperatingMode ( uint32_t adr , uint32_t v ) {
return DaliSetValue ( adr , DALI_QUERY_OPERATING_MODE , DALI_SET_OPERATING_MODE , v ) ;
}
bool DaliSetMaxLevel ( uint32_t adr , uint32_t v ) {
return DaliSetValue ( adr , DALI_QUERY_MAX_LEVEL , DALI_SET_MAX_LEVEL , v ) ;
}
bool DaliSetMinLevel ( uint32_t adr , uint32_t v ) {
return DaliSetValue ( adr , DALI_QUERY_MIN_LEVEL , DALI_SET_MIN_LEVEL , v ) ;
}
bool DaliSetSystemFailureLevel ( uint32_t adr , uint32_t v ) {
return DaliSetValue ( adr , DALI_QUERY_SYSTEM_FAILURE_LEVEL , DALI_SET_SYSTEM_FAILURE_LEVEL , v ) ;
}
bool DaliSetPowerOnLevel ( uint32_t adr , uint32_t v ) {
return DaliSetValue ( adr , DALI_QUERY_POWER_ON_LEVEL , DALI_SET_POWER_ON_LEVEL , v ) ;
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-22 11:38:56 +01:00
uint32_t DaliGearPresent ( void ) {
uint32_t count = 0 ;
for ( uint32_t sa = 0 ; sa < Dali - > max_short_address ; sa + + ) { // Scanning 64 addresses takes about 2500 ms
if ( DaliSendWaitResponse ( sa < < 1 | 1 , DALI_QUERY_CONTROL_GEAR_PRESENT , 20 ) > = 0 ) {
count + + ;
}
}
return count ;
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-22 11:38:56 +01:00
void DaliInitLight ( void ) {
2024-10-25 11:39:36 +01:00
// Taken from Shelly Dali Dimmer ;-)
2024-10-22 11:38:56 +01:00
DaliSendData ( DALI_DATA_TRANSFER_REGISTER0 , DALI_INIT_FADE ) ; // Fade x second
DaliSendData ( 0xFF , DALI_SET_FADE_TIME ) ;
DaliSendData ( DALI_DATA_TRANSFER_REGISTER0 , 0 ) ; // Power off after gear power restore
DaliSendData ( 0xFF , DALI_SET_POWER_ON_LEVEL ) ;
DaliSendData ( DALI_DATA_TRANSFER_REGISTER0 , 0xFE ) ; // Reset all but short circuit
DaliSendData ( 0xFF , DALI_SET_SYSTEM_FAILURE_LEVEL ) ;
}
/*********************************************************************************************\
* DALI commissioning short addresses
*
* Courtesy of https : //github.com/qqqlab/DALI-Lighting-Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-10-17 16:44:05 +01:00
void DaliSetSearchAddress ( uint32_t adr ) {
// Set search address
DaliSendData ( DALI_SEARCHADDRH , adr > > 16 ) ;
DaliSendData ( DALI_SEARCHADDRM , adr > > 8 ) ;
DaliSendData ( DALI_SEARCHADDRL , adr ) ;
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
2024-10-17 16:44:05 +01:00
void DaliSetSearchAddressDifference ( uint32_t adr_new , uint32_t adr_current ) {
// Set search address, but set only changed bytes (takes less time)
if ( ( uint8_t ) ( adr_new > > 16 ) ! = ( uint8_t ) ( adr_current > > 16 ) ) DaliSendData ( DALI_SEARCHADDRH , adr_new > > 16 ) ;
if ( ( uint8_t ) ( adr_new > > 8 ) ! = ( uint8_t ) ( adr_current > > 8 ) ) DaliSendData ( DALI_SEARCHADDRM , adr_new > > 8 ) ;
if ( ( uint8_t ) ( adr_new ) ! = ( uint8_t ) ( adr_current ) ) DaliSendData ( DALI_SEARCHADDRL , adr_new ) ;
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
2024-10-17 16:44:05 +01:00
bool DaliCompare ( ) {
// Is the random address smaller or equal to the search address?
// As more than one device can reply, the reply gets garbled
uint8_t retry = 2 ;
while ( retry > 0 ) {
// Compare is true if we received any activity on the bus as reply.
// Sometimes the reply is not registered... so only accept retry times 'no reply' as a real false compare
int rv = DaliSendWaitResponse ( DALI_COMPARE , 0x00 ) ;
if ( rv = = 0xFF ) return true ; // Yes reply
2024-10-22 11:38:56 +01:00
if ( rv = = - 2 ) return true ; // Reply but collision
2024-10-17 16:44:05 +01:00
retry - - ;
}
return false ;
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
2024-10-17 16:44:05 +01:00
uint32_t DaliFindAddress ( void ) {
// Find addr with binary search
uint32_t adr = 0x800000 ;
uint32_t addsub = 0x400000 ;
uint32_t adr_last = adr ;
DaliSetSearchAddress ( adr ) ;
while ( addsub ) {
DaliSetSearchAddressDifference ( adr , adr_last ) ;
adr_last = adr ;
if ( DaliCompare ( ) ) { // Returns true if searchadr > adr
adr - = addsub ;
} else {
adr + = addsub ;
}
addsub > > = 1 ;
}
DaliSetSearchAddressDifference ( adr , adr_last ) ;
adr_last = adr ;
if ( ! DaliCompare ( ) ) {
adr + + ;
DaliSetSearchAddressDifference ( adr , adr_last ) ;
}
return adr ;
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
2024-10-17 16:44:05 +01:00
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 ) ;
2024-10-24 22:03:14 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " DLI: Set short address %d " ) , shortadr + 1 ) ;
2024-10-17 16:44:05 +01:00
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
2024-10-17 16:44:05 +01:00
uint32_t DaliCommission ( uint8_t init_arg ) {
2024-10-22 11:38:56 +01:00
// Based on Shelly DALI Dimmer Gen3 received frames
2024-10-17 16:44:05 +01:00
// init_arg=11111111 : all without short address
// init_arg=00000000 : all
// init_arg=0AAAAAA1 : only for this shortadr
// returns number of new short addresses assigned
2024-10-22 11:38:56 +01:00
DaliSendData ( 0xFF , DALI_RESET ) ; // Turns ON all lights
2024-10-17 16:44:05 +01:00
uint8_t arr [ 64 ] ;
uint32_t sa ;
for ( sa = 0 ; sa < 64 ; sa + + ) {
arr [ sa ] = 0 ;
}
2024-10-22 11:38:56 +01:00
delay ( 450 ) ;
DaliSendData ( DALI_DATA_TRANSFER_REGISTER0 , 0xFF ) ;
DaliSendData ( 0xFF , DALI_SET_SHORT_ADDRESS ) ;
DaliSendData ( DALI_TERMINATE , 0x00 ) ; // Terminate the DALI_INITIALISE command
delay ( 15 ) ;
2024-10-17 16:44:05 +01:00
// Start commissioning
DaliSendData ( DALI_INITIALISE , init_arg ) ;
DaliSendData ( DALI_RANDOMISE , 0x00 ) ;
2024-10-22 11:38:56 +01:00
delay ( 65 ) ; // Need pause after RANDOMISE
2024-10-17 16:44:05 +01:00
uint32_t cnt = 0 ;
while ( true ) { // Find random addresses and assign unused short addresses
uint32_t adr = DaliFindAddress ( ) ;
if ( adr > 0xffffff ) { break ; } // No more random addresses found -> exit
for ( sa = 0 ; sa < 64 ; sa + + ) { // Find first unused short address
if ( 0 = = arr [ sa ] ) { break ; }
}
2024-10-22 11:38:56 +01:00
if ( sa > = 64 ) { break ; } // All 64 short addresses assigned -> exit
2024-10-17 16:44:05 +01:00
arr [ sa ] = 1 ; // Mark short address as used
cnt + + ;
DaliProgramShortAddress ( sa ) ; // Assign short address
DaliSendData ( DALI_WITHDRAW , 0x00 ) ; // Remove the device from the search
2024-10-22 11:38:56 +01:00
DaliSendData ( sa < < 1 , DALI_OFF ) ; // Turns OFF latest short address light
2024-10-17 16:44:05 +01:00
delay ( 1 ) ;
2024-10-22 11:38:56 +01:00
OsWatchLoop ( ) ; // Feed blocked-loop watchdog
2024-10-17 16:44:05 +01:00
}
2024-10-22 11:38:56 +01:00
delay ( 100 ) ;
2024-10-17 16:44:05 +01:00
DaliSendData ( DALI_TERMINATE , 0x00 ) ; // Terminate the DALI_INITIALISE command
2024-10-22 11:38:56 +01:00
# ifdef USE_LIGHT
DaliInitLight ( ) ;
2024-11-10 10:46:52 +00:00
uint32_t address = ( Settings - > sbflag1 . dali_light ) ? DaliTarget2Address ( ) : DALI_BROADCAST_DP ; // DaliLight 1
2024-10-31 16:01:36 +00:00
DaliSendData ( address , Dali - > power [ 0 ] ) ; // Restore lights
2024-10-22 11:38:56 +01:00
# else
2024-10-31 16:01:36 +00:00
DaliSendData ( DALI_BROADCAST_DP , Dali - > power [ 0 ] ) ; // Restore lights
2024-10-22 11:38:56 +01:00
# endif // USE_LIGHT
2024-10-17 16:44:05 +01:00
return cnt ;
2024-10-06 20:57:59 +01:00
}
2024-10-22 11:38:56 +01:00
/*********************************************************************************************/
2024-10-06 20:57:59 +01:00
2024-10-31 16:01:36 +00:00
void ResponseAppendDali ( uint32_t index ) {
char number [ 12 ] ;
uint8_t dimmer = changeUIntScale ( Dali - > dimmer [ index ] , 0 , 254 , 0 , 100 ) ;
ResponseAppend_P ( PSTR ( " \" DALI \" :{ \" Power%s \" : \" %s \" , \" Dimmer%s \" :%d, \" Address \" :%d, \" Command \" :%d} " ) ,
( 0 = = index ) ? " " : itoa ( index + 100 , number , 10 ) , GetStateText ( Dali - > power [ index ] ) ,
( 0 = = index ) ? " " : itoa ( index + 100 , number , 10 ) , dimmer ,
Dali - > address , Dali - > command ) ;
2024-10-06 21:40:28 +01:00
}
2024-10-31 16:01:36 +00:00
void ResponseDali ( uint32_t index ) {
2024-10-07 17:03:20 +01:00
Response_P ( PSTR ( " { " ) ) ;
2024-10-31 16:01:36 +00:00
ResponseAppendDali ( index ) ;
2024-10-07 17:03:20 +01:00
ResponseJsonEnd ( ) ;
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
void DaliLoop ( void ) {
2024-10-14 11:27:01 +01:00
if ( ! Dali - > available | | Dali - > response ) { return ; }
2024-10-24 22:03:14 +01:00
AddLog ( LOG_LEVEL_DEBUG_MORE , PSTR ( " DLI: Rx 0x%05X " ) , Dali - > received_dali_data ) ;
2024-10-22 11:38:56 +01:00
if ( Dali - > received_dali_data & 0x00010000 ) {
Dali - > available = false ;
return ; // Rx collision
}
2024-10-14 11:27:01 +01:00
Dali - > address = Dali - > received_dali_data > > 8 ;
Dali - > command = Dali - > received_dali_data ;
2024-10-31 16:01:36 +00:00
uint32_t index = DaliSaveState ( Dali - > address , Dali - > command ) ; // Update dimmer and power
2024-10-10 17:16:32 +01:00
2024-10-14 11:27:01 +01:00
bool show_response = true ;
2024-10-31 16:01:36 +00:00
# ifdef USE_LIGHT
2024-11-01 10:23:55 +00:00
if ( Dali - > allow_light & & ( DaliTarget2Address ( ) = = Dali - > address ) ) {
2024-10-22 11:38:56 +01:00
if ( Settings - > sbflag1 . dali_light ) { // DaliLight 1
2024-10-31 16:01:36 +00:00
uint8_t dim_old = changeUIntScale ( Dali - > last_dimmer , 0 , 254 , 0 , 100 ) ;
uint8_t dim_new = changeUIntScale ( Dali - > dimmer [ index ] , 0 , 254 , 0 , 100 ) ;
if ( Dali - > last_power ! = Dali - > power [ index ] ) {
2024-10-14 21:58:45 +01:00
Dali - > light_sync = true ; // Block local loop
2024-10-31 16:01:36 +00:00
ExecuteCommandPower ( LightDevice ( ) , Dali - > power [ index ] , SRC_SWITCH ) ;
2024-10-14 11:27:01 +01:00
}
2024-10-31 16:01:36 +00:00
else if ( dim_old ! = dim_new ) {
2024-10-14 11:27:01 +01:00
char scmnd [ 20 ] ;
2024-10-31 16:01:36 +00:00
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_DIMMER " %d " ) , dim_new ) ;
2024-10-14 21:58:45 +01:00
Dali - > light_sync = true ; // Block local loop
2024-10-14 11:27:01 +01:00
ExecuteCommand ( scmnd , SRC_SWITCH ) ;
2024-10-11 10:54:08 +01:00
}
2024-10-14 11:27:01 +01:00
show_response = false ;
2024-10-11 10:54:08 +01:00
}
2024-10-14 11:27:01 +01:00
}
2024-10-31 16:01:36 +00:00
# endif // USE_LIGHT
2024-10-14 11:27:01 +01:00
if ( show_response ) {
2024-10-31 16:01:36 +00:00
ResponseDali ( index ) ;
2024-10-11 10:54:08 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_PRFX_DALI ) ) ;
2024-10-14 11:27:01 +01:00
}
2024-10-11 10:54:08 +01:00
2024-10-14 11:27:01 +01:00
Dali - > available = false ;
2024-10-06 20:57:59 +01:00
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
void DaliEverySecond ( void ) {
2024-11-01 10:23:55 +00:00
if ( 5 = = TasmotaGlobal . uptime ) {
DaliInitLight ( ) ;
2024-10-22 11:38:56 +01:00
}
}
2024-11-01 10:23:55 +00:00
/*-------------------------------------------------------------------------------------------*/
# ifdef USE_LIGHT
2024-10-14 21:58:45 +01:00
bool DaliSetChannels ( void ) {
2024-10-22 11:38:56 +01:00
if ( Settings - > sbflag1 . dali_light ) { // DaliLight 1
Settings - > light_fade = 0 ; // Use Dali fading
2024-10-25 11:39:36 +01:00
Settings - > light_correction = 0 ; // Use Dali light correction
2024-10-14 21:58:45 +01:00
if ( Dali - > light_sync ) { // Block local loop
Dali - > light_sync = false ;
} else {
uint8_t value = ( ( uint8_t * ) XdrvMailbox . data ) [ 0 ] ;
if ( 255 = = value ) { value = 254 ; } // Max Dali value
2024-10-31 16:01:36 +00:00
DaliSendData ( DaliTarget2Address ( ) , value ) ;
2024-10-14 21:58:45 +01:00
}
}
return true ;
}
# endif // USE_LIGHT
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
2024-11-01 10:23:55 +00:00
bool DaliInit ( uint32_t function ) {
2024-10-28 09:13:43 +00:00
int pin_tx = - 1 ;
bool invert_tx = false ;
2024-10-24 22:03:14 +01:00
if ( PinUsed ( GPIO_DALI_TX ) ) {
2024-10-28 09:13:43 +00:00
pin_tx = Pin ( GPIO_DALI_TX ) ;
2024-10-24 22:03:14 +01:00
}
else if ( PinUsed ( GPIO_DALI_TX_INV ) ) {
2024-10-28 09:13:43 +00:00
pin_tx = Pin ( GPIO_DALI_TX_INV ) ;
invert_tx = true ;
2024-10-24 22:03:14 +01:00
}
2024-10-28 09:13:43 +00:00
int pin_rx = - 1 ;
bool invert_rx = false ;
2024-10-24 22:03:14 +01:00
if ( PinUsed ( GPIO_DALI_RX ) ) {
2024-10-28 09:13:43 +00:00
pin_rx = Pin ( GPIO_DALI_RX ) ;
2024-10-24 22:03:14 +01:00
}
else if ( PinUsed ( GPIO_DALI_RX_INV ) ) {
2024-10-28 09:13:43 +00:00
pin_rx = Pin ( GPIO_DALI_RX_INV ) ;
invert_rx = true ;
2024-10-24 22:03:14 +01:00
}
2024-10-28 09:13:43 +00:00
if ( ( - 1 = = pin_tx ) | | ( - 1 = = pin_rx ) ) { return false ; }
Dali = ( DALI * ) calloc ( sizeof ( DALI ) , 1 ) ;
if ( ! Dali ) { return false ; }
Dali - > pin_tx = pin_tx ;
Dali - > invert_tx = invert_tx ;
Dali - > pin_rx = pin_rx ;
Dali - > invert_rx = invert_rx ;
2024-10-06 20:57:59 +01:00
2024-11-01 10:23:55 +00:00
Dali - > allow_light = ( FUNC_MODULE_INIT = = function ) ; // Light control is possible
2024-11-10 10:46:52 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " DLI: GPIO%d(RX%s) and GPIO%d(TX%s)%s " ) ,
Dali - > pin_rx , ( Dali - > invert_rx ) ? " i " : " " , Dali - > pin_tx , ( Dali - > invert_tx ) ? " i " : " " , ( Dali - > allow_light ) ? " as light " : " " ) ;
2024-10-06 20:57:59 +01:00
pinMode ( Dali - > pin_tx , OUTPUT ) ;
2024-10-24 22:03:14 +01:00
digitalWrite ( Dali - > pin_tx , ( Dali - > invert_tx ) ? LOW : HIGH ) ; // Idle
2024-10-06 20:57:59 +01:00
pinMode ( Dali - > pin_rx , INPUT ) ;
# ifdef DALI_DEBUG
pinMode ( DALI_DEBUG_PIN , OUTPUT ) ;
digitalWrite ( DALI_DEBUG_PIN , HIGH ) ;
# endif // DALI_DEBUG
2024-10-24 22:03:14 +01:00
Dali - > max_short_address = 64 ;
2024-10-31 16:01:36 +00:00
for ( uint32_t i = 0 ; i < DALI_MAX_STORED ; i + + ) {
Dali - > dimmer [ i ] = DALI_INIT_STATE ;
}
2024-10-14 11:27:01 +01:00
// Manchester twice 1200 bps = 2400 bps = 417 (protocol 416.76 +/- 10%) us
2024-10-24 22:03:14 +01:00
Dali - > bit_cycles = ESP . getCpuFreqMHz ( ) * 1000000 / 2400 ;
2024-10-06 20:57:59 +01:00
DaliEnableRxInterrupt ( ) ;
2024-10-10 16:49:11 +01:00
2024-11-01 10:23:55 +00:00
if ( ! Dali - > allow_light ) {
Settings - > sbflag1 . dali_light = false ; // No light control possible
}
2024-10-11 10:54:08 +01:00
# ifdef USE_LIGHT
2024-10-22 11:38:56 +01:00
if ( ! Settings - > sbflag1 . dali_light ) { // DaliLight 0
2024-10-10 16:49:11 +01:00
return false ;
}
2024-10-22 11:38:56 +01:00
Settings - > light_fade = 0 ; // Use Dali fading instead
2024-10-25 11:39:36 +01:00
Settings - > light_correction = 0 ; // Use Dali light correction
2024-10-10 16:49:11 +01:00
UpdateDevicesPresent ( 1 ) ;
TasmotaGlobal . light_type = LT_SERIAL1 ; // Single channel
return true ;
2024-10-11 10:54:08 +01:00
# else
return false ;
# endif // USE_LIGHT
2024-10-10 16:49:11 +01:00
}
2024-10-06 21:40:28 +01:00
/*********************************************************************************************\
* Commands
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-10-06 20:57:59 +01:00
bool DaliJsonParse ( void ) {
// {"addr":254,"cmd":100}
// {"addr":2}
// {"dim":3}
bool served = false ;
JsonParser parser ( ( char * ) XdrvMailbox . data ) ;
JsonParserObject root = parser . getRootObject ( ) ;
if ( root ) {
int DALIindex = 0 ;
int ADRindex = 0 ;
int8_t DALIdim = - 1 ;
2024-10-14 11:27:01 +01:00
uint32_t DALIaddr = DALI_BROADCAST_DP ;
2024-10-06 20:57:59 +01:00
JsonParserToken val = root [ PSTR ( " cmd " ) ] ;
if ( val ) {
2024-10-14 11:27:01 +01:00
uint32_t cmd = val . getUInt ( ) ;
2024-10-06 20:57:59 +01:00
val = root [ PSTR ( " addr " ) ] ;
if ( val ) {
2024-10-14 11:27:01 +01:00
uint32_t addr = val . getUInt ( ) ;
2024-10-06 20:57:59 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " DLI: cmd = %d, addr = %d " ) , cmd , addr ) ;
DaliSendData ( addr , cmd ) ;
return true ;
} else {
return false ;
}
}
val = root [ PSTR ( " addr " ) ] ;
if ( val ) {
2024-10-14 11:27:01 +01:00
uint32_t addr = val . getUInt ( ) ;
2024-10-06 20:57:59 +01:00
if ( ( addr > = 0 ) & & ( addr < 64 ) ) {
DALIaddr = addr < < 1 ;
}
}
val = root [ PSTR ( " dim " ) ] ;
if ( val ) {
uint8_t dim = val . getUInt ( ) ;
if ( dim < 255 ) {
DALIdim = dim ;
}
}
DaliSendData ( DALIaddr , DALIdim ) ;
served = true ;
}
return served ;
}
2024-11-12 14:32:10 +00:00
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
2024-10-06 20:57:59 +01:00
void CmndDali ( void ) {
2024-10-07 17:03:20 +01:00
// Dali {"addr":254,"cmd":100} - Any address and/or command
2024-10-22 11:38:56 +01:00
// Dali 0|1 - Enable DALI receive probe
2024-10-06 20:57:59 +01:00
if ( XdrvMailbox . data_len > 0 ) {
2024-10-22 11:38:56 +01:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 1 ) ) {
Dali - > probe = XdrvMailbox . payload ;
ResponseCmndNumber ( Dali - > probe ) ;
return ;
}
2024-10-07 17:03:20 +01:00
DaliJsonParse ( ) ;
2024-10-06 20:57:59 +01:00
}
2024-10-31 16:01:36 +00:00
ResponseDali ( 0 ) ;
2024-10-06 20:57:59 +01:00
}
2024-10-22 11:38:56 +01:00
/*-------------------------------------------------------------------------------------------*/
2024-10-17 16:44:05 +01:00
void CmndDaliTarget ( void ) {
// DaliTarget - Set transmit target
// DaliTarget 0 - Set target to broadcast address
// DaliTarget 1..64 - Set target to short address
// DaliTarget 101..116 - Set target to group address
if ( ( ( XdrvMailbox . payload > = 1 ) & & ( XdrvMailbox . payload < = 64 ) ) | |
( ( XdrvMailbox . payload > = 101 ) & & ( XdrvMailbox . payload < = 116 ) ) | |
( XdrvMailbox . payload = = 0 ) ) {
Dali - > target = XdrvMailbox . payload ;
}
ResponseCmndNumber ( Dali - > target ) ;
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-06 20:57:59 +01:00
void CmndDaliPower ( void ) {
2024-10-17 16:44:05 +01:00
// DaliPower 0 - Broadcast power off
// DaliPower 1 - Broadcast power on to last dimmer state
// DaliPower 2 - Broadcast toggle power off or last dimmer state
// DaliPower 3..254 - Broadcast equals DaliDimmer command
// DaliPower 0..254 - Broadcast control
// DaliPower0 0..254 - Broadcast control (= DaliPower)
// DaliPower1 0..254 - Short address 0 control
// DaliPower3 0..254 - Short address 2 control
2024-10-31 16:01:36 +00:00
uint32_t index = 0 ; // Broadcast
if ( ( XdrvMailbox . index > = 101 ) & & ( XdrvMailbox . index < = 116 ) ) {
index = XdrvMailbox . index - 100 ; // Group1 to 16
}
2024-10-17 16:44:05 +01:00
if ( ( ( XdrvMailbox . index > = 0 ) & & ( XdrvMailbox . index < = 64 ) ) | |
( ( XdrvMailbox . index > = 101 ) & & ( XdrvMailbox . index < = 116 ) ) ) {
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 254 ) ) {
if ( XdrvMailbox . payload < = 2 ) {
if ( 2 = = XdrvMailbox . payload ) {
2024-10-31 16:01:36 +00:00
XdrvMailbox . payload = ( Dali - > power [ index ] ) ? 0 : 1 ;
2024-10-17 16:44:05 +01:00
}
if ( 1 = = XdrvMailbox . payload ) {
2024-10-31 16:01:36 +00:00
XdrvMailbox . payload = Dali - > dimmer [ index ] ;
2024-10-17 16:44:05 +01:00
}
}
uint32_t DALIaddr = DALI_BROADCAST_DP ;
if ( XdrvMailbox . index > = 101 ) {
DALIaddr = ( ( XdrvMailbox . index - 101 ) < < 1 ) | 0x80 ; // Group address
2024-10-07 17:03:20 +01:00
}
2024-10-17 16:44:05 +01:00
else if ( ( XdrvMailbox . index > 0 ) & & XdrvMailbox . usridx ) {
DALIaddr = ( XdrvMailbox . index - 1 ) < < 1 ; // Short address
2024-10-07 17:03:20 +01:00
}
2024-10-17 16:44:05 +01:00
DaliSendData ( DALIaddr , XdrvMailbox . payload ) ;
2024-10-07 17:03:20 +01:00
}
2024-10-06 20:57:59 +01:00
}
2024-10-31 16:01:36 +00:00
ResponseDali ( index ) ;
2024-10-06 20:57:59 +01:00
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-06 20:57:59 +01:00
void CmndDaliDimmer ( void ) {
2024-10-17 16:44:05 +01:00
// DaliDimmer 0..100 - Broadcast set power off or dimmer state
// DaliDimmer0 0..100 - Broadcast set power off or dimmer state
// DaliDimmer1 0..100 - Short address 0 set power off or dimmer state
// DaliDimmer3 0..100 - Short address 2 set power off or dimmer state
2024-10-31 16:01:36 +00:00
uint32_t index = 0 ; // Broadcast
if ( ( XdrvMailbox . index > = 101 ) & & ( XdrvMailbox . index < = 116 ) ) {
index = XdrvMailbox . index - 100 ; // Group1 to 16
}
2024-10-17 16:44:05 +01:00
if ( ( ( XdrvMailbox . index > = 0 ) & & ( XdrvMailbox . index < = 64 ) ) | |
( ( XdrvMailbox . index > = 101 ) & & ( XdrvMailbox . index < = 116 ) ) ) {
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 100 ) ) {
uint8_t dimmer = changeUIntScale ( XdrvMailbox . payload , 0 , 100 , 0 , 254 ) ;
uint32_t DALIaddr = DALI_BROADCAST_DP ;
if ( XdrvMailbox . index > = 101 ) {
DALIaddr = ( ( XdrvMailbox . index - 101 ) < < 1 ) | 0x80 ; // Group address
}
else if ( ( XdrvMailbox . index > 0 ) & & XdrvMailbox . usridx ) {
DALIaddr = ( XdrvMailbox . index - 1 ) < < 1 ; // Short address
}
DaliSendData ( DALIaddr , dimmer ) ;
}
2024-10-06 20:57:59 +01:00
}
2024-10-31 16:01:36 +00:00
ResponseDali ( index ) ;
2024-10-06 20:57:59 +01:00
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-17 16:44:05 +01:00
void CmndDaliGroup ( void ) {
// DaliGroup1 1,2 - Add device 1 and 2 to group 1
// DaliGroup1 -1,2 - Remove device 1 and 2 to group 1
if ( ( XdrvMailbox . index > = 1 ) & & ( XdrvMailbox . index < = 16 ) ) {
2024-10-22 11:38:56 +01:00
uint32_t group = XdrvMailbox . index - 1 ;
bool more = false ;
char temp [ 200 ] = { 0 } ;
2024-10-17 16:44:05 +01:00
if ( XdrvMailbox . data_len ) {
uint32_t command = DALI_ADD_TO_GROUP0 ;
2024-10-22 11:38:56 +01:00
temp [ 0 ] = ' + ' ;
2024-10-17 16:44:05 +01:00
if ( ' + ' = = XdrvMailbox . data [ 0 ] ) { // Add devices
XdrvMailbox . data + + ;
XdrvMailbox . data_len - - ;
}
else if ( ' - ' = = XdrvMailbox . data [ 0 ] ) { // Remove devices
command = DALI_REMOVE_FROM_GROUP0 ;
XdrvMailbox . data + + ;
XdrvMailbox . data_len - - ;
2024-10-22 11:38:56 +01:00
temp [ 0 ] = ' - ' ;
2024-10-17 16:44:05 +01:00
}
uint32_t argc = ArgC ( ) ; // Number of devices
if ( argc ) {
2024-10-22 11:38:56 +01:00
command | = group ;
2024-10-17 16:44:05 +01:00
uint32_t sas [ argc ] ;
ParseParameters ( argc , sas ) ;
for ( uint32_t arg = 0 ; arg < argc ; arg + + ) {
uint32_t sa = sas [ arg ] - 1 ;
2024-10-22 11:38:56 +01:00
if ( sa < 64 ) {
snprintf_P ( temp , sizeof ( temp ) , PSTR ( " %s%s%d " ) , temp , ( more ) ? " , " : " " , sa + 1 ) ;
more = true ;
DaliSendData ( sa < < 1 | 1 , command ) ;
2024-10-17 16:44:05 +01:00
}
}
2024-10-22 11:38:56 +01:00
ResponseCmndIdxChar ( temp ) ;
}
} else {
uint32_t command = DALI_QUERY_GROUPS_0_7 ;
uint32_t bitmask = 1 < < group ;
if ( group > 7 ) {
command = DALI_QUERY_GROUPS_8_15 ;
bitmask = 1 < < group - 8 ;
}
for ( uint32_t sa = 0 ; sa < Dali - > max_short_address ; sa + + ) { // Scanning 64 addresses takes about 2500 ms
int result = DaliSendWaitResponse ( sa < < 1 | 1 , command , 20 ) ;
if ( ( result > = 0 ) & & ( result & bitmask ) ) {
snprintf_P ( temp , sizeof ( temp ) , PSTR ( " %s%s%d " ) , temp , ( more ) ? " , " : " " , sa + 1 ) ;
more = true ;
}
}
if ( ! strlen ( temp ) ) {
snprintf_P ( temp , sizeof ( temp ) , PSTR ( " None " ) ) ;
2024-10-17 16:44:05 +01:00
}
2024-10-22 11:38:56 +01:00
ResponseCmndIdxChar ( temp ) ;
2024-10-17 16:44:05 +01:00
}
}
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-22 11:38:56 +01:00
void CmndDaliGear ( void ) {
if ( ( XdrvMailbox . payload > = 1 ) & & ( XdrvMailbox . payload < = 64 ) ) {
Dali - > max_short_address = XdrvMailbox . payload ;
}
uint32_t count = DaliGearPresent ( ) ;
ResponseCmnd ( ) ;
ResponseAppend_P ( PSTR ( " %d, \" Present \" :%d} " ) , Dali - > max_short_address , count ) ;
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-14 11:27:01 +01:00
void CmndDaliSend ( void ) {
// Send command
2024-10-17 16:44:05 +01:00
// Setting bit 8 will repeat command once
2024-10-14 11:27:01 +01:00
// DaliSend 0x1a5,255 - DALI Initialise (send twice)
2024-10-22 11:38:56 +01:00
// DaliSend 0x01,0xa3,0x2d,254 - Set Power On level
uint32_t values [ 4 ] = { 0 } ;
uint32_t params = ParseParameters ( 4 , values ) ;
2024-10-14 11:27:01 +01:00
if ( 2 = = params ) {
DaliSendData ( values [ 0 ] & 0x1FF , values [ 1 ] & 0xFF ) ;
ResponseCmndDone ( ) ;
}
2024-10-22 11:38:56 +01:00
else if ( 4 = = params ) {
if ( DaliSetValue ( values [ 0 ] & 0x1FF , values [ 1 ] & 0xFF , values [ 2 ] & 0xFF , values [ 3 ] & 0xFF ) ) {
ResponseCmndDone ( ) ;
} else {
ResponseCmndFailed ( ) ;
}
}
2024-10-14 11:27:01 +01:00
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-14 11:27:01 +01:00
void CmndDaliQuery ( void ) {
// Send command and return response or -1 (no response within DALI_TIMEOUT)
2024-10-17 16:44:05 +01:00
// Setting bit 8 will repeat command once
2024-10-14 11:27:01 +01:00
// DaliQuery 0xff,0x90 - DALI Query status
// DaliQuery 0xff,144 - DALI Query status
uint32_t values [ 2 ] = { 0 } ;
uint32_t params = ParseParameters ( 2 , values ) ;
if ( 2 = = params ) {
int result = DaliSendWaitResponse ( values [ 0 ] & 0x1FF , values [ 1 ] & 0xFF ) ;
ResponseCmndNumber ( result ) ;
}
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-22 11:38:56 +01:00
void CmndDaliScan ( void ) {
// Scan short addresses
// DaliScan 1 - Reset and commission short addresses
// DaliScan 2 - Commission unassigned short addresses
2024-10-17 16:44:05 +01:00
if ( ( XdrvMailbox . payload > = 1 ) & & ( XdrvMailbox . payload < = 2 ) ) {
uint32_t init_arg = 0x00 ; // Commission all
if ( 2 = = XdrvMailbox . payload ) {
init_arg = 0xFF ; // Commission all without short addresses
}
int result = DaliCommission ( init_arg ) ;
ResponseCmndNumber ( result ) ;
}
}
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-31 16:01:36 +00:00
void CmndDaliGroupSliders ( void ) {
2024-11-01 10:23:55 +00:00
// DaliGroupSliders 0..16 - Add group sliders
2024-10-31 16:01:36 +00:00
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 16 ) ) {
Settings - > mbflag2 . dali_group_sliders = XdrvMailbox . payload ;
TasmotaGlobal . restart_flag = 2 ; // Restart to update GUI
}
ResponseCmndNumber ( Settings - > mbflag2 . dali_group_sliders ) ;
}
2024-10-11 10:54:08 +01:00
# ifdef USE_LIGHT
2024-11-12 14:32:10 +00:00
/*-------------------------------------------------------------------------------------------*/
2024-10-22 11:38:56 +01:00
void CmndDaliLight ( void ) {
// DaliLight 0 - Disable light controls
// DaliLight 1 - Enable light controls
2024-11-01 10:23:55 +00:00
if ( Dali - > allow_light & & ( XdrvMailbox . data_len > 0 ) ) {
2024-11-10 10:46:52 +00:00
Settings - > sbflag1 . dali_light = XdrvMailbox . payload & 1 ; // DaliLight 0/1
2024-10-31 16:01:36 +00:00
TasmotaGlobal . restart_flag = 2 ; // Restart to update GUI
2024-10-10 16:49:11 +01:00
}
2024-11-10 10:46:52 +00:00
ResponseCmndStateText ( Settings - > sbflag1 . dali_light ) ; // DaliLight 0/1
2024-10-10 16:49:11 +01:00
}
2024-10-11 10:54:08 +01:00
# endif // USE_LIGHT
2024-10-10 16:49:11 +01:00
2024-10-06 20:57:59 +01:00
/*********************************************************************************************\
* Presentation
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2024-10-31 16:01:36 +00:00
# ifdef USE_WEBSERVER
const char HTTP_MSG_SLIDER_DALI [ ] PROGMEM =
" <tr> "
" <td style='width:15%%'><button id='k75%d' style='background:#%06x;' onclick='la( \" &k75=%d \" );'>%s%s</button></td> "
" <td style='width:85%%'><div class='r' style='background-image:linear-gradient(to right,#000,#FFF);'> "
" <input id='i75%d' type='range' min='1' max='100' value='%d' onchange='lc( \" i \" ,75%d,value)'></div></td> "
" </tr> " ;
void DaliWebAddMainSlider ( void ) {
WSContentSend_P ( HTTP_TABLE100 ) ;
char number [ 12 ] ;
2024-11-10 10:46:52 +00:00
for ( uint32_t i = Settings - > sbflag1 . dali_light ; i < = Settings - > mbflag2 . dali_group_sliders ; i + + ) { // DaliLight 0/1, DaliGroupSliders
2024-10-31 16:01:36 +00:00
Dali - > web_dimmer [ i ] = Dali - > dimmer [ i ] ;
WSContentSend_P ( HTTP_MSG_SLIDER_DALI , // Brightness - Black to White
i , // k75<i>
2024-11-11 21:44:18 +00:00
WebColor ( ( Dali - > power [ i ] ) ? COL_BUTTON : COL_BUTTON_OFF ) ,
2024-10-31 16:01:36 +00:00
i , // k75=<i>
( 0 = = i ) ? " B " : " G " , // B (Broadcast) or G1 to G16 (Group)
( 0 = = i ) ? " " : itoa ( i , number , 10 ) ,
i , // i75<i>
changeUIntScale ( Dali - > web_dimmer [ i ] , 0 , 254 , 0 , 100 ) ,
i // i75<i>
) ;
}
WSContentSend_P ( PSTR ( " </table> " ) ) ;
}
2024-11-12 14:32:10 +00:00
/*********************************************************************************************/
2024-10-31 16:01:36 +00:00
void DaliWebGetArg ( void ) {
char tmp [ 8 ] ; // WebGetArg numbers only
char svalue [ 32 ] ; // Command and number parameter
char webindex [ 8 ] ; // WebGetArg name
uint32_t index ;
2024-11-10 10:46:52 +00:00
for ( uint32_t i = Settings - > sbflag1 . dali_light ; i < = Settings - > mbflag2 . dali_group_sliders ; i + + ) { // DaliLight 0/1, DaliGroupSliders
2024-10-31 16:01:36 +00:00
snprintf_P ( webindex , sizeof ( webindex ) , PSTR ( " i75%d " ) , i ) ;
WebGetArg ( webindex , tmp , sizeof ( tmp ) ) ; // 0 - 100 percent
if ( strlen ( tmp ) ) {
index = i ;
if ( index > 0 ) { index + = 100 ; } // Group
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " DaliDimmer%d %s " ) , index , tmp ) ;
ExecuteWebCommand ( svalue ) ;
}
}
WebGetArg ( PSTR ( " k75 " ) , tmp , sizeof ( tmp ) ) ;
if ( strlen ( tmp ) ) {
index = atoi ( tmp ) ;
if ( index > 0 ) { index + = 100 ; } // Group
snprintf_P ( svalue , sizeof ( svalue ) , PSTR ( " DaliPower%d 2 " ) , index ) ;
ExecuteWebCommand ( svalue ) ;
}
}
2024-11-12 14:32:10 +00:00
/*********************************************************************************************/
void DaliWebShow ( void ) {
WSContentSend_P ( PSTR ( " </table> " ) ) ; // Terminate current {t}
WSContentSend_P ( HTTP_MSG_EXEC_JAVASCRIPT ) ; // "<img style='display:none;' src onerror=\""
for ( uint32_t i = Settings - > sbflag1 . dali_light ; i < = Settings - > mbflag2 . dali_group_sliders ; i + + ) { // DaliLight 0/1, DaliGroupSliders
WSContentSend_P ( PSTR ( " eb('k75%d').style='background:#%06x'; " ) ,
i , WebColor ( ( Dali - > power [ i ] ) ? COL_BUTTON : COL_BUTTON_OFF ) ) ;
if ( Dali - > dimmer [ i ] ! = Dali - > web_dimmer [ i ] ) {
if ( WebUpdateSliderTime ( ) ) {
Dali - > web_dimmer [ i ] = Dali - > dimmer [ i ] ;
2024-10-31 16:01:36 +00:00
}
2024-11-12 14:32:10 +00:00
WSContentSend_P ( PSTR ( " eb('i75%d').value='%d'; " ) ,
i , changeUIntScale ( Dali - > dimmer [ i ] , 0 , 254 , 0 , 100 ) ) ;
2024-10-31 16:01:36 +00:00
}
2024-10-06 21:40:28 +01:00
}
2024-11-12 14:32:10 +00:00
WSContentSend_P ( PSTR ( " \" >{t} " ) ) ; // Restart {t} = <table style='width:100%'>
WSContentSeparator ( 3 ) ; // Don't print separator on next WSContentSeparator(1)
2024-10-06 21:40:28 +01:00
}
2024-10-06 20:57:59 +01:00
2024-11-12 14:32:10 +00:00
# endif // USE_WEBSERVER
2024-10-06 20:57:59 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool Xdrv75 ( uint32_t function ) {
bool result = false ;
2024-11-01 10:23:55 +00:00
if ( FUNC_MODULE_INIT = = function ) { // Try to claim DALI as light
result = DaliInit ( function ) ;
}
else if ( ( FUNC_PRE_INIT = = function ) & & ! Dali ) { // If claim failed then use DALI controls only
DaliInit ( function ) ;
2024-10-06 20:57:59 +01:00
}
else if ( Dali ) {
switch ( function ) {
case FUNC_LOOP :
2024-10-22 11:38:56 +01:00
DaliLoop ( ) ;
2024-10-06 20:57:59 +01:00
break ;
2024-10-22 11:38:56 +01:00
case FUNC_EVERY_SECOND :
DaliEverySecond ( ) ;
break ;
2024-11-01 10:23:55 +00:00
# ifdef USE_LIGHT
2024-10-10 16:49:11 +01:00
case FUNC_SET_CHANNELS :
result = DaliSetChannels ( ) ;
break ;
2024-10-11 10:54:08 +01:00
# endif // USE_LIGHT
2024-10-06 21:40:28 +01:00
case FUNC_JSON_APPEND :
2024-11-12 14:32:10 +00:00
ResponseAppend_P ( PSTR ( " , " ) ) ;
ResponseAppendDali ( 0 ) ;
2024-10-06 21:40:28 +01:00
break ;
# ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR :
2024-11-12 14:32:10 +00:00
DaliWebShow ( ) ;
2024-10-31 16:01:36 +00:00
break ;
case FUNC_WEB_ADD_MAIN_BUTTON :
2024-11-01 10:23:55 +00:00
DaliWebAddMainSlider ( ) ;
2024-10-31 16:01:36 +00:00
break ;
case FUNC_WEB_GET_ARG :
2024-11-01 10:23:55 +00:00
DaliWebGetArg ( ) ;
2024-10-06 21:40:28 +01:00
break ;
# endif // USE_WEBSERVER
2024-10-06 20:57:59 +01:00
case FUNC_COMMAND :
result = DecodeCommand ( kDALICommands , DALICommand ) ;
break ;
case FUNC_ACTIVE :
result = true ;
break ;
}
}
return result ;
}
# endif // USE_DALI