1
+ #include " wled.h"
2
+
3
+ #ifdef WLED_ENABLE_DMX_INPUT
4
+
5
+ #ifdef ESP8266
6
+ #error DMX input is only supported on ESP32
7
+ #endif
8
+
9
+ #include " dmx_input.h"
10
+ #include < rdm/responder.h>
11
+
12
+ void rdmPersonalityChangedCb (dmx_port_t dmxPort, const rdm_header_t *header,
13
+ void *context)
14
+ {
15
+ DMXInput *dmx = static_cast <DMXInput *>(context);
16
+
17
+ if (!dmx) {
18
+ DEBUG_PRINTLN (" DMX: Error: no context in rdmPersonalityChangedCb" );
19
+ return ;
20
+ }
21
+
22
+ if (header->cc == RDM_CC_SET_COMMAND_RESPONSE) {
23
+ const uint8_t personality = dmx_get_current_personality (dmx->inputPortNum );
24
+ DMXMode = std::min (DMX_MODE_PRESET, std::max (DMX_MODE_SINGLE_RGB, int (personality)));
25
+ doSerializeConfig = true ;
26
+ DEBUG_PRINTF (" DMX personality changed to to: %d\n " , DMXMode);
27
+ }
28
+ }
29
+
30
+ void rdmAddressChangedCb (dmx_port_t dmxPort, const rdm_header_t *header,
31
+ void *context)
32
+ {
33
+ DMXInput *dmx = static_cast <DMXInput *>(context);
34
+
35
+ if (!dmx) {
36
+ DEBUG_PRINTLN (" DMX: Error: no context in rdmAddressChangedCb" );
37
+ return ;
38
+ }
39
+
40
+ if (header->cc == RDM_CC_SET_COMMAND_RESPONSE) {
41
+ const uint16_t addr = dmx_get_start_address (dmx->inputPortNum );
42
+ DMXAddress = std::min (512 , int (addr));
43
+ doSerializeConfig = true ;
44
+ DEBUG_PRINTF (" DMX start addr changed to: %d\n " , DMXAddress);
45
+ }
46
+ }
47
+
48
+ static dmx_config_t createConfig ()
49
+ {
50
+ dmx_config_t config;
51
+ config.pd_size = 255 ;
52
+ config.dmx_start_address = DMXAddress;
53
+ config.model_id = 0 ;
54
+ config.product_category = RDM_PRODUCT_CATEGORY_FIXTURE;
55
+ config.software_version_id = VERSION;
56
+ strcpy (config.device_label , " WLED_MM" );
57
+
58
+ const std::string versionString = " WLED_V" + std::to_string (VERSION);
59
+ strncpy (config.software_version_label , versionString.c_str (), 32 );
60
+ config.software_version_label [32 ] = ' \0 ' ; // zero termination in case versionString string was longer than 32 chars
61
+
62
+ config.personalities [0 ].description = " SINGLE_RGB" ;
63
+ config.personalities [0 ].footprint = 3 ;
64
+ config.personalities [1 ].description = " SINGLE_DRGB" ;
65
+ config.personalities [1 ].footprint = 4 ;
66
+ config.personalities [2 ].description = " EFFECT" ;
67
+ config.personalities [2 ].footprint = 15 ;
68
+ config.personalities [3 ].description = " MULTIPLE_RGB" ;
69
+ config.personalities [3 ].footprint = std::min (512 , int (strip.getLengthTotal ()) * 3 );
70
+ config.personalities [4 ].description = " MULTIPLE_DRGB" ;
71
+ config.personalities [4 ].footprint = std::min (512 , int (strip.getLengthTotal ()) * 3 + 1 );
72
+ config.personalities [5 ].description = " MULTIPLE_RGBW" ;
73
+ config.personalities [5 ].footprint = std::min (512 , int (strip.getLengthTotal ()) * 4 );
74
+ config.personalities [6 ].description = " EFFECT_W" ;
75
+ config.personalities [6 ].footprint = 18 ;
76
+ config.personalities [7 ].description = " EFFECT_SEGMENT" ;
77
+ config.personalities [7 ].footprint = std::min (512 , strip.getSegmentsNum () * 15 );
78
+ config.personalities [8 ].description = " EFFECT_SEGMENT_W" ;
79
+ config.personalities [8 ].footprint = std::min (512 , strip.getSegmentsNum () * 18 );
80
+ config.personalities [9 ].description = " PRESET" ;
81
+ config.personalities [9 ].footprint = 1 ;
82
+
83
+ config.personality_count = 10 ;
84
+ // rdm personalities are numbered from 1, thus we can just set the DMXMode directly.
85
+ config.current_personality = DMXMode;
86
+
87
+ return config;
88
+ }
89
+
90
+ void dmxReceiverTask (void *context)
91
+ {
92
+ DMXInput *instance = static_cast <DMXInput *>(context);
93
+ if (instance == nullptr ) {
94
+ return ;
95
+ }
96
+
97
+ if (instance->installDriver ()) {
98
+ while (true ) {
99
+ instance->updateInternal ();
100
+ }
101
+ }
102
+ }
103
+
104
+ bool DMXInput::installDriver ()
105
+ {
106
+
107
+ const auto config = createConfig ();
108
+ DEBUG_PRINTF (" DMX port: %u\n " , inputPortNum);
109
+ if (!dmx_driver_install (inputPortNum, &config, DMX_INTR_FLAGS_DEFAULT)) {
110
+ DEBUG_PRINTF (" Error: Failed to install dmx driver\n " );
111
+ return false ;
112
+ }
113
+
114
+ DEBUG_PRINTF (" Listening for DMX on pin %u\n " , rxPin);
115
+ DEBUG_PRINTF (" Sending DMX on pin %u\n " , txPin);
116
+ DEBUG_PRINTF (" DMX enable pin is: %u\n " , enPin);
117
+ dmx_set_pin (inputPortNum, txPin, rxPin, enPin);
118
+
119
+ rdm_register_dmx_start_address (inputPortNum, rdmAddressChangedCb, this );
120
+ rdm_register_dmx_personality (inputPortNum, rdmPersonalityChangedCb, this );
121
+ initialized = true ;
122
+ return true ;
123
+ }
124
+
125
+ void DMXInput::init (uint8_t rxPin, uint8_t txPin, uint8_t enPin, uint8_t inputPortNum)
126
+ {
127
+
128
+ #ifdef WLED_ENABLE_DMX_OUTPUT
129
+ // TODO add again once dmx output has been merged
130
+ // if(inputPortNum == dmxOutputPort)
131
+ // {
132
+ // DEBUG_PRINTF("DMXInput: Error: Input port == output port");
133
+ // return;
134
+ // }
135
+ #endif
136
+
137
+ if (inputPortNum <= (SOC_UART_NUM - 1 ) && inputPortNum > 0 ) {
138
+ this ->inputPortNum = inputPortNum;
139
+ }
140
+ else {
141
+ DEBUG_PRINTF (" DMXInput: Error: invalid inputPortNum: %d\n " , inputPortNum);
142
+ return ;
143
+ }
144
+
145
+ if (rxPin > 0 && enPin > 0 && txPin > 0 ) {
146
+
147
+ const managed_pin_type pins[] = {
148
+ {(int8_t )txPin, false }, // these are not used as gpio pins, thus isOutput is always false.
149
+ {(int8_t )rxPin, false },
150
+ {(int8_t )enPin, false }};
151
+ const bool pinsAllocated = PinManager::allocateMultiplePins (pins, 3 , PinOwner::DMX_INPUT);
152
+ if (!pinsAllocated) {
153
+ DEBUG_PRINTF (" DMXInput: Error: Failed to allocate pins for DMX_INPUT. Pins already in use:\n " );
154
+ DEBUG_PRINTF (" rx in use by: %s\n " , pinManager.getPinOwnerText (rxPin).c_str ());
155
+ DEBUG_PRINTF (" tx in use by: %s\n " , pinManager.getPinOwnerText (txPin).c_str ());
156
+ DEBUG_PRINTF (" en in use by: %s\n " , pinManager.getPinOwnerText (enPin).c_str ());
157
+ return ;
158
+ }
159
+
160
+ this ->rxPin = rxPin;
161
+ this ->txPin = txPin;
162
+ this ->enPin = enPin;
163
+
164
+ // put dmx receiver into seperate task because it should not be blocked
165
+ // pin to core 0 because wled is running on core 1
166
+ xTaskCreatePinnedToCore (dmxReceiverTask, " DMX_RCV_TASK" , 10240 , this , 2 , &task, 0 );
167
+ if (!task) {
168
+ DEBUG_PRINTF (" Error: Failed to create dmx rcv task" );
169
+ }
170
+ }
171
+ else {
172
+ DEBUG_PRINTLN (" DMX input disabled due to rxPin, enPin or txPin not set" );
173
+ return ;
174
+ }
175
+ }
176
+
177
+ void DMXInput::updateInternal ()
178
+ {
179
+ if (!initialized) {
180
+ return ;
181
+ }
182
+
183
+ checkAndUpdateConfig ();
184
+
185
+ dmx_packet_t packet;
186
+ unsigned long now = millis ();
187
+ if (dmx_receive (inputPortNum, &packet, DMX_TIMEOUT_TICK)) {
188
+ if (!packet.err ) {
189
+ if (!connected) {
190
+ DEBUG_PRINTLN (" DMX Input - connected" );
191
+ }
192
+ connected = true ;
193
+ identify = isIdentifyOn ();
194
+ if (!packet.is_rdm ) {
195
+ const std::lock_guard<std::mutex> lock (dmxDataLock);
196
+ dmx_read (inputPortNum, dmxdata, packet.size );
197
+ }
198
+ }
199
+ else {
200
+ connected = false ;
201
+ }
202
+ }
203
+ else {
204
+ if (connected) {
205
+ DEBUG_PRINTLN (" DMX Input - disconnected" );
206
+ }
207
+ connected = false ;
208
+ }
209
+ }
210
+
211
+
212
+ void DMXInput::update ()
213
+ {
214
+ if (identify) {
215
+ turnOnAllLeds ();
216
+ }
217
+ else if (connected) {
218
+ const std::lock_guard<std::mutex> lock (dmxDataLock);
219
+ handleDMXData (1 , 512 , dmxdata, REALTIME_MODE_DMX, 0 );
220
+ }
221
+ }
222
+
223
+ void DMXInput::turnOnAllLeds ()
224
+ {
225
+ // TODO not sure if this is the correct way?
226
+ const uint16_t numPixels = strip.getLengthTotal ();
227
+ for (uint16_t i = 0 ; i < numPixels; ++i)
228
+ {
229
+ strip.setPixelColor (i, 255 , 255 , 255 , 255 );
230
+ }
231
+ strip.setBrightness (255 , true );
232
+ strip.show ();
233
+ }
234
+
235
+ void DMXInput::disable ()
236
+ {
237
+ if (initialized) {
238
+ dmx_driver_disable (inputPortNum);
239
+ }
240
+ }
241
+ void DMXInput::enable ()
242
+ {
243
+ if (initialized) {
244
+ dmx_driver_enable (inputPortNum);
245
+ }
246
+ }
247
+
248
+ bool DMXInput::isIdentifyOn () const
249
+ {
250
+
251
+ uint8_t identify = 0 ;
252
+ const bool gotIdentify = rdm_get_identify_device (inputPortNum, &identify);
253
+ // gotIdentify should never be false because it is a default parameter in rdm
254
+ // but just in case we check for it anyway
255
+ return bool (identify) && gotIdentify;
256
+ }
257
+
258
+ void DMXInput::checkAndUpdateConfig ()
259
+ {
260
+
261
+ /* *
262
+ * The global configuration variables are modified by the web interface.
263
+ * If they differ from the driver configuration, we have to update the driver
264
+ * configuration.
265
+ */
266
+
267
+ const uint8_t currentPersonality = dmx_get_current_personality (inputPortNum);
268
+ if (currentPersonality != DMXMode) {
269
+ DEBUG_PRINTF (" DMX personality has changed from %d to %d\n " , currentPersonality, DMXMode);
270
+ dmx_set_current_personality (inputPortNum, DMXMode);
271
+ }
272
+
273
+ const uint16_t currentAddr = dmx_get_start_address (inputPortNum);
274
+ if (currentAddr != DMXAddress) {
275
+ DEBUG_PRINTF (" DMX address has changed from %d to %d\n " , currentAddr, DMXAddress);
276
+ dmx_set_start_address (inputPortNum, DMXAddress);
277
+ }
278
+ }
279
+
280
+ #endif
0 commit comments