{"id":499,"date":"2022-05-26T16:06:15","date_gmt":"2022-05-26T14:06:15","guid":{"rendered":"https:\/\/oscarnet.eu\/?p=499"},"modified":"2023-11-16T11:21:48","modified_gmt":"2023-11-16T09:21:48","slug":"how-the-cheap-wireless-dmx-boards-use-the-nrf24l01-protocol","status":"publish","type":"post","link":"https:\/\/oscarnet.eu\/ru\/how-the-cheap-wireless-dmx-boards-use-the-nrf24l01-protocol\/","title":{"rendered":"How the cheap wireless DMX boards use the NRF24L01 protocol"},"content":{"rendered":"<p>This is a copypaste from\u00a0https:\/\/juskihackery.wordpress.com\/ in case if this article will get lost.<\/p>\n<p>In my previous post I started telling the sorry tale of how I came to using cheap wireless DMX boards with my lightshow, and how I became motivated to figuring out how they work.<\/p>\n<p>In this post I will reveal my findings, explain exactly what it all means \u2013 and I\u2019ll ultimately even give some example Arduino code to let YOU make your own compatible wireless DMX transmitter\/receiver.<\/p>\n<p>I captured a wireless DMX board\u2019s SPI communications from powering up, to acquiring a \u2018lock\u2019 on a DMX transmitter. Here\u2019s what Pulseview showed me was going on:<\/p>\n<pre class=\"wp-block-code has-small-font-size\"><code>10657954-10658020 nRF24L01(+): Commands: Cmd W_REGISTER: CONFIG = \"3F\"  0x00111111 RX mode, Power Up, EN_CRC, 2 byte CRC\r\n10659814-10659880 nRF24L01(+): Commands: Cmd W_REGISTER: EN_AA = \"00\" No auto Ack\r\n10661674-10661740 nRF24L01(+): Commands: Cmd W_REGISTER: EN_RXADDR = \"01\" Enable data pipe 1\r\n10663534-10663600 nRF24L01(+): Commands: Cmd W_REGISTER: SETUP_AW = \"03\" Use 5 byte addresses\r\n10665394-10665460 nRF24L01(+): Commands: Cmd W_REGISTER: SETUP_RETR = \"00\" no auto-retransmit delay\r\n10667254-10667318 nRF24L01(+): Commands: Cmd W_REGISTER: RF_CH = \"00\"\r\n10669120-10669184 nRF24L01(+): Commands: Cmd W_REGISTER: RF_SETUP = \"26\" 0x00100110  250kbps, 0dBm\r\n10670979-10671045 nRF24L01(+): Commands: Cmd W_REGISTER: STATUS = \"7E\"\r\n10672839-10672905 nRF24L01(+): Commands: Cmd W_REGISTER: RX_PW_P0 = \"20\" 32 byte payload\r\n10676251-10676496 nRF24L01(+): Commands: Cmd W_REGISTER: RX_ADDR_P0 = \"01FEFF0100\"\r\n10676670-10676916 nRF24L01(+): Commands: Cmd W_REGISTER: TX_ADDR = \"01FEFF0100\"\r\n11055714-11055780 nRF24L01(+): Commands: Cmd W_REGISTER: STATUS = \"7E\"\r\n11056686-11056706 nRF24L01(+): Commands: Cmd FLUSH_RX\r\n11057929-11057995 nRF24L01(+): Commands: Cmd W_REGISTER: RF_CH = \"2A\"\r\n11061353-11061598 nRF24L01(+): Commands: Cmd W_REGISTER: RX_ADDR_P0 = \"2BFED5012A\"\r\n11061772-11062018 nRF24L01(+): Commands: Cmd W_REGISTER: TX_ADDR = \"2BFED5012A\"\r\n11455904-11455968 nRF24L01(+): Commands: Cmd W_REGISTER: STATUS = \"7E\"\r\n11456875-11456897 nRF24L01(+): Commands: Cmd FLUSH_RX\r\n11458116-11458182 nRF24L01(+): Commands: Cmd W_REGISTER: RF_CH = \"54\"\r\n11461540-11461785 nRF24L01(+): Commands: Cmd W_REGISTER: RX_ADDR_P0 = \"55FEAB0154\"\r\n11461959-11462205 nRF24L01(+): Commands: Cmd W_REGISTER: TX_ADDR = \"55FEAB0154\"\r\n11856095-11856161 nRF24L01(+): Commands: Cmd W_REGISTER: STATUS = \"7E\"\r\n11857067-11857089 nRF24L01(+): Commands: Cmd FLUSH_RX<\/code><\/pre>\n<p>So there\u2019s the preamble which tells us the transmission parameters \u2013 no CRC, using data pipe 1, 5 byte addresses, no retransmit attempting, no auto ACK.. oh and the data rate is 256kbps &#038; payloads are 32 bytes long.<\/p>\n<p>After the first few lines it can be seen that the board is setting the RF channel to a value, then setting the RX &#038; TX 5 byte addresses to SOMETHING, then reading the status register &#038; flushing the RX buffer. What is this SOMETHING that it\u2019s setting the address to? I captured a few scan attempts from the board acting as a receiver, using different unit IDs to see if I could spot any pattern in the 5 byte addresses the micro was putting into the NRF radio chip. Note: a wireless DMX board like this (and the ones made by Donner too, presumably) can be set to one of 7 IDs. According to many sellers\u2019 product descriptions this is \u2018to avoid interference\u2019. Ha!<\/p>\n<p>It took me a while to work it out but each of the 5 address bytes corresponds to the RF channel and the unit\u2019s ID.<\/p>\n<pre class=\"wp-block-preformatted\">Byte 0: RF_CH + UnitID\r\nByte 1: 255 - UnitID\r\nByte 2: 255 - RF_CH\r\nByte 3: UnitID\r\nByte 4: RF_CH<\/pre>\n<p>Without knowing this address it wouldn\u2019t exactly be child\u2019s play to sniff the protocol blind (apparently though it is, by setting the NRF radio to use only 1 byte addressing &#038; dumping the rest out as part of a data payload LOL).<\/p>\n<p>In order to find out how each payload is constructed, and how they relate to actual DMX channels I set some easy to spot values in my DMX program on certain channels. If you\u2019ve never encountered DEADBEEF let me tell you it\u2019s a really handy little hex string you can use whenever you need something to stand out.<\/p>\n<p>By setting various groups of channels to certain values &#038; looking at the received payloads in Pulseview I was able to glean that each payload works like this:<\/p>\n<p>0x80 is always the first byte. For a 512-channel set of payloads the next two bytes are always 0xFF 0x01 (0x1FF == 511 \u2013 or 0-511 == 512 channels). The next 28 bytes of the payload are DMX channel data.<\/p>\n<pre class=\"wp-block-preformatted\">80 00 FF 01 DE AD BE EF 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ..\r\n80 01 FF 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ..\r\nthru\r\n80 12 FF 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ..<\/pre>\n<p>Payload 0 carries DMX channels 1-28. Payload 1 carries channels 29-57 .. and in the last payload (0x12 or 18 decimal) rather than waste the last 8 bytes or whatever, channels 1 to 8 are sent again:<\/p>\n<pre class=\"wp-block-preformatted\">80 12 FF 01 00 00 00 00 00 00 00 00 00 DE AD BE EF 11 22 33 44 ..<\/pre>\n<p>Great! So now we can code a receiver for an Arduino board? Why yes, we can!<\/p>\n<p>Now, before we get too far ahead of ourselves, me posting the following code is based on the assumption that YOU ARE A GROWN-UP AND ARE CAPABLE OF CONNECTING AN NRF24L01 module to an Arduino without blowing it or yourself to smithereens. The SPI connections on the NRF24 module go to SPI connections on the Arduino simply enough but the two you can most easily get wrong are CE &#038; CSN. In my code CE is connected to D1 (GPIO5) and CSN is hooked up to D8 (GPIO15). Get either of these wrong, you\u2019ll see \u201cERROR: failed to start radio\u201d when the Arduino starts up. The code should build just fine \u2013 just make sure you have the required libraries installed. NB \u2013 I\u2019m not helping you with that, or with any whim you might have to make the NRF module talk to the International Space Station via BLE or whatever crazy-arse plan you might have!!<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n<div id=\"highlighter_292889\" class=\"syntaxhighlighter cpp\">\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td class=\"gutter\">\n<div class=\"line number1 index0 alt2\">1<\/div>\n<div class=\"line number2 index1 alt1\">2<\/div>\n<div class=\"line number3 index2 alt2\">3<\/div>\n<div class=\"line number4 index3 alt1\">4<\/div>\n<div class=\"line number5 index4 alt2\">5<\/div>\n<div class=\"line number6 index5 alt1\">6<\/div>\n<div class=\"line number7 index6 alt2\">7<\/div>\n<div class=\"line number8 index7 alt1\">8<\/div>\n<div class=\"line number9 index8 alt2\">9<\/div>\n<div class=\"line number10 index9 alt1\">10<\/div>\n<div class=\"line number11 index10 alt2\">11<\/div>\n<div class=\"line number12 index11 alt1\">12<\/div>\n<div class=\"line number13 index12 alt2\">13<\/div>\n<div class=\"line number14 index13 alt1\">14<\/div>\n<div class=\"line number15 index14 alt2\">15<\/div>\n<div class=\"line number16 index15 alt1\">16<\/div>\n<div class=\"line number17 index16 alt2\">17<\/div>\n<div class=\"line number18 index17 alt1\">18<\/div>\n<div class=\"line number19 index18 alt2\">19<\/div>\n<div class=\"line number20 index19 alt1\">20<\/div>\n<div class=\"line number21 index20 alt2\">21<\/div>\n<div class=\"line number22 index21 alt1\">22<\/div>\n<div class=\"line number23 index22 alt2\">23<\/div>\n<div class=\"line number24 index23 alt1\">24<\/div>\n<div class=\"line number25 index24 alt2\">25<\/div>\n<div class=\"line number26 index25 alt1\">26<\/div>\n<div class=\"line number27 index26 alt2\">27<\/div>\n<div class=\"line number28 index27 alt1\">28<\/div>\n<div class=\"line number29 index28 alt2\">29<\/div>\n<div class=\"line number30 index29 alt1\">30<\/div>\n<div class=\"line number31 index30 alt2\">31<\/div>\n<div class=\"line number32 index31 alt1\">32<\/div>\n<div class=\"line number33 index32 alt2\">33<\/div>\n<div class=\"line number34 index33 alt1\">34<\/div>\n<div class=\"line number35 index34 alt2\">35<\/div>\n<div class=\"line number36 index35 alt1\">36<\/div>\n<div class=\"line number37 index36 alt2\">37<\/div>\n<div class=\"line number38 index37 alt1\">38<\/div>\n<div class=\"line number39 index38 alt2\">39<\/div>\n<div class=\"line number40 index39 alt1\">40<\/div>\n<div class=\"line number41 index40 alt2\">41<\/div>\n<div class=\"line number42 index41 alt1\">42<\/div>\n<div class=\"line number43 index42 alt2\">43<\/div>\n<div class=\"line number44 index43 alt1\">44<\/div>\n<div class=\"line number45 index44 alt2\">45<\/div>\n<div class=\"line number46 index45 alt1\">46<\/div>\n<div class=\"line number47 index46 alt2\">47<\/div>\n<div class=\"line number48 index47 alt1\">48<\/div>\n<div class=\"line number49 index48 alt2\">49<\/div>\n<div class=\"line number50 index49 alt1\">50<\/div>\n<div class=\"line number51 index50 alt2\">51<\/div>\n<div class=\"line number52 index51 alt1\">52<\/div>\n<div class=\"line number53 index52 alt2\">53<\/div>\n<div class=\"line number54 index53 alt1\">54<\/div>\n<div class=\"line number55 index54 alt2\">55<\/div>\n<div class=\"line number56 index55 alt1\">56<\/div>\n<div class=\"line number57 index56 alt2\">57<\/div>\n<div class=\"line number58 index57 alt1\">58<\/div>\n<div class=\"line number59 index58 alt2\">59<\/div>\n<div class=\"line number60 index59 alt1\">60<\/div>\n<div class=\"line number61 index60 alt2\">61<\/div>\n<div class=\"line number62 index61 alt1\">62<\/div>\n<div class=\"line number63 index62 alt2\">63<\/div>\n<div class=\"line number64 index63 alt1\">64<\/div>\n<div class=\"line number65 index64 alt2\">65<\/div>\n<div class=\"line number66 index65 alt1\">66<\/div>\n<div class=\"line number67 index66 alt2\">67<\/div>\n<div class=\"line number68 index67 alt1\">68<\/div>\n<div class=\"line number69 index68 alt2\">69<\/div>\n<div class=\"line number70 index69 alt1\">70<\/div>\n<div class=\"line number71 index70 alt2\">71<\/div>\n<div class=\"line number72 index71 alt1\">72<\/div>\n<div class=\"line number73 index72 alt2\">73<\/div>\n<div class=\"line number74 index73 alt1\">74<\/div>\n<div class=\"line number75 index74 alt2\">75<\/div>\n<div class=\"line number76 index75 alt1\">76<\/div>\n<div class=\"line number77 index76 alt2\">77<\/div>\n<div class=\"line number78 index77 alt1\">78<\/div>\n<div class=\"line number79 index78 alt2\">79<\/div>\n<div class=\"line number80 index79 alt1\">80<\/div>\n<div class=\"line number81 index80 alt2\">81<\/div>\n<div class=\"line number82 index81 alt1\">82<\/div>\n<div class=\"line number83 index82 alt2\">83<\/div>\n<div class=\"line number84 index83 alt1\">84<\/div>\n<div class=\"line number85 index84 alt2\">85<\/div>\n<div class=\"line number86 index85 alt1\">86<\/div>\n<div class=\"line number87 index86 alt2\">87<\/div>\n<div class=\"line number88 index87 alt1\">88<\/div>\n<div class=\"line number89 index88 alt2\">89<\/div>\n<div class=\"line number90 index89 alt1\">90<\/div>\n<div class=\"line number91 index90 alt2\">91<\/div>\n<div class=\"line number92 index91 alt1\">92<\/div>\n<div class=\"line number93 index92 alt2\">93<\/div>\n<div class=\"line number94 index93 alt1\">94<\/div>\n<div class=\"line number95 index94 alt2\">95<\/div>\n<div class=\"line number96 index95 alt1\">96<\/div>\n<div class=\"line number97 index96 alt2\">97<\/div>\n<div class=\"line number98 index97 alt1\">98<\/div>\n<div class=\"line number99 index98 alt2\">99<\/div>\n<div class=\"line number100 index99 alt1\">100<\/div>\n<div class=\"line number101 index100 alt2\">101<\/div>\n<div class=\"line number102 index101 alt1\">102<\/div>\n<div class=\"line number103 index102 alt2\">103<\/div>\n<div class=\"line number104 index103 alt1\">104<\/div>\n<div class=\"line number105 index104 alt2\">105<\/div>\n<div class=\"line number106 index105 alt1\">106<\/div>\n<div class=\"line number107 index106 alt2\">107<\/div>\n<div class=\"line number108 index107 alt1\">108<\/div>\n<div class=\"line number109 index108 alt2\">109<\/div>\n<div class=\"line number110 index109 alt1\">110<\/div>\n<div class=\"line number111 index110 alt2\">111<\/div>\n<div class=\"line number112 index111 alt1\">112<\/div>\n<div class=\"line number113 index112 alt2\">113<\/div>\n<div class=\"line number114 index113 alt1\">114<\/div>\n<div class=\"line number115 index114 alt2\">115<\/div>\n<div class=\"line number116 index115 alt1\">116<\/div>\n<div class=\"line number117 index116 alt2\">117<\/div>\n<div class=\"line number118 index117 alt1\">118<\/div>\n<div class=\"line number119 index118 alt2\">119<\/div>\n<div class=\"line number120 index119 alt1\">120<\/div>\n<div class=\"line number121 index120 alt2\">121<\/div>\n<div class=\"line number122 index121 alt1\">122<\/div>\n<div class=\"line number123 index122 alt2\">123<\/div>\n<div class=\"line number124 index123 alt1\">124<\/div>\n<div class=\"line number125 index124 alt2\">125<\/div>\n<div class=\"line number126 index125 alt1\">126<\/div>\n<div class=\"line number127 index126 alt2\">127<\/div>\n<div class=\"line number128 index127 alt1\">128<\/div>\n<div class=\"line number129 index128 alt2\">129<\/div>\n<div class=\"line number130 index129 alt1\">130<\/div>\n<div class=\"line number131 index130 alt2\">131<\/div>\n<div class=\"line number132 index131 alt1\">132<\/div>\n<div class=\"line number133 index132 alt2\">133<\/div>\n<div class=\"line number134 index133 alt1\">134<\/div>\n<div class=\"line number135 index134 alt2\">135<\/div>\n<div class=\"line number136 index135 alt1\">136<\/div>\n<div class=\"line number137 index136 alt2\">137<\/div>\n<div class=\"line number138 index137 alt1\">138<\/div>\n<div class=\"line number139 index138 alt2\">139<\/div>\n<div class=\"line number140 index139 alt1\">140<\/div>\n<div class=\"line number141 index140 alt2\">141<\/div>\n<div class=\"line number142 index141 alt1\">142<\/div>\n<div class=\"line number143 index142 alt2\">143<\/div>\n<div class=\"line number144 index143 alt1\">144<\/div>\n<div class=\"line number145 index144 alt2\">145<\/div>\n<div class=\"line number146 index145 alt1\">146<\/div>\n<div class=\"line number147 index146 alt2\">147<\/div>\n<div class=\"line number148 index147 alt1\">148<\/div>\n<div class=\"line number149 index148 alt2\">149<\/div>\n<div class=\"line number150 index149 alt1\">150<\/div>\n<div class=\"line number151 index150 alt2\">151<\/div>\n<div class=\"line number152 index151 alt1\">152<\/div>\n<div class=\"line number153 index152 alt2\">153<\/div>\n<div class=\"line number154 index153 alt1\">154<\/div>\n<div class=\"line number155 index154 alt2\">155<\/div>\n<div class=\"line number156 index155 alt1\">156<\/div>\n<div class=\"line number157 index156 alt2\">157<\/div>\n<div class=\"line number158 index157 alt1\">158<\/div>\n<div class=\"line number159 index158 alt2\">159<\/div>\n<div class=\"line number160 index159 alt1\">160<\/div>\n<div class=\"line number161 index160 alt2\">161<\/div>\n<div class=\"line number162 index161 alt1\">162<\/div>\n<div class=\"line number163 index162 alt2\">163<\/div>\n<div class=\"line number164 index163 alt1\">164<\/div>\n<div class=\"line number165 index164 alt2\">165<\/div>\n<div class=\"line number166 index165 alt1\">166<\/div>\n<div class=\"line number167 index166 alt2\">167<\/div>\n<div class=\"line number168 index167 alt1\">168<\/div>\n<div class=\"line number169 index168 alt2\">169<\/div>\n<div class=\"line number170 index169 alt1\">170<\/div>\n<div class=\"line number171 index170 alt2\">171<\/div>\n<div class=\"line number172 index171 alt1\">172<\/div>\n<div class=\"line number173 index172 alt2\">173<\/div>\n<div class=\"line number174 index173 alt1\">174<\/div>\n<div class=\"line number175 index174 alt2\">175<\/div>\n<div class=\"line number176 index175 alt1\">176<\/div>\n<div class=\"line number177 index176 alt2\">177<\/div>\n<div class=\"line number178 index177 alt1\">178<\/div>\n<div class=\"line number179 index178 alt2\">179<\/div>\n<div class=\"line number180 index179 alt1\">180<\/div>\n<div class=\"line number181 index180 alt2\">181<\/div>\n<div class=\"line number182 index181 alt1\">182<\/div>\n<div class=\"line number183 index182 alt2\">183<\/div>\n<div class=\"line number184 index183 alt1\">184<\/div>\n<div class=\"line number185 index184 alt2\">185<\/div>\n<div class=\"line number186 index185 alt1\">186<\/div>\n<div class=\"line number187 index186 alt2\">187<\/div>\n<div class=\"line number188 index187 alt1\">188<\/div>\n<div class=\"line number189 index188 alt2\">189<\/div>\n<div class=\"line number190 index189 alt1\">190<\/div>\n<div class=\"line number191 index190 alt2\">191<\/div>\n<div class=\"line number192 index191 alt1\">192<\/div>\n<div class=\"line number193 index192 alt2\">193<\/div>\n<div class=\"line number194 index193 alt1\">194<\/div>\n<div class=\"line number195 index194 alt2\">195<\/div>\n<div class=\"line number196 index195 alt1\">196<\/div>\n<div class=\"line number197 index196 alt2\">197<\/div>\n<div class=\"line number198 index197 alt1\">198<\/div>\n<\/td>\n<td class=\"code\">\n<div class=\"container\">\n<div class=\"line number1 index0 alt2\">\n<p>\/\/ Sample Arduino code to use an NRF24L01 module as a receiver\/transmitter<br \/>\n\/\/ which is compatible with many cheap &#038; cheerful wireless DMX products<br \/>\n\/\/ Feel free to base your own code on these findings<br \/>\n\/\/<\/p>\n<p>#include <SPI.h><br \/>\n#include <nRF24L01.h><br \/>\n#include <RF24.h><br \/>\n#include <DMXSerial.h><br \/>\n\/\/#include &#8220;ESP8266WiFi.h&#8221;<br \/>\n\/\/#define VERBOSE<\/p>\n<p>\/\/ UnitID 1 &#8211; 7<br \/>\n\/\/ 5 byte address string<br \/>\n\/\/ Byte 0 &#8211; Unit ID (0x01 &#8211; 0x07) + RF Channel<br \/>\n\/\/ Byte 1 &#8211; 255 &#8211; UnitID<br \/>\n\/\/ Byte 2 &#8211; 255 &#8211; RF Channel<br \/>\n\/\/ Byte 3 &#8211; UnitID<br \/>\n\/\/ Byte 4 &#8211; RF Channel<br \/>\n\/\/<br \/>\n\/\/ CONFIG = &#8220;3F&#8221; 0x00111111 RX mode, Power Up, EN_CRC<br \/>\n\/\/ EN_AA = &#8220;00&#8221; No CRC, No auto Ack<br \/>\n\/\/ EN_RXADDR = &#8220;01&#8221; Enable data pipe 1<br \/>\n\/\/ SETUP_AW = &#8220;03&#8221; Use 5 byte addresses<br \/>\n\/\/ SETUP_RETR = &#8220;00&#8221; no auto-retransmit delay<br \/>\n\/\/ RF_CH = &#8220;00&#8221;<br \/>\n\/\/ RF_SETUP = &#8220;26&#8221; 0x00100110 256kbps, 0dBm<br \/>\n\/\/ STATUS = &#8220;7E&#8221;<br \/>\n\/\/ RX_PW_P0 = &#8220;20&#8221; 32 byte payload<br \/>\n\/\/<br \/>\n\/\/ example payload DMX ch 1-<br \/>\n\/\/ byte DE AD BE EF 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32<br \/>\n\/\/ 80 00 FF 01 DE AD BE EF 05 06 07 08 09 0A 00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1A 1B 1C<br \/>\n\/\/ 80 payloadID FF 01 then 28 DMX channels<br \/>\n\/\/ thru<br \/>\n\/\/ 80 12 FF 01 XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX 01FF = 511<br \/>\n\/\/<\/p>\n<p>\/\/ Receiver code goes as follows<br \/>\n\/\/ 1. setup the radio gear<br \/>\n\/\/ 2. set receive address.<br \/>\n\/\/ 3. listen on a channel<br \/>\n\/\/ 4. if signal found, start taking payloads into DMX buffer<br \/>\n\/\/ 5. if no signal found, loop back to 2<br \/>\n\/\/<br \/>\nint D7 = 7;<br \/>\nint D8 = 8;<\/p>\n<p>uint8_t dmxBuf[512]; \/\/ initialise DMX buffer<br \/>\nuint8_t myAddress[5]; \/\/ initialise address<br \/>\nunsigned long flashTimer, receiveTimer, lastPayloadTime, chaseTimer;<\/p>\n<p>int step = 1;<br \/>\nbool txparamsSet;<br \/>\nbool gotLock;<br \/>\nRF24 radio(D7,D8); \/\/ CE, CSN<\/p>\n<p>bool transmitter = false; \/\/ = true;<br \/>\nbool configMode = false;<\/p>\n<p>int rfCH = 70; \/\/temporarily set rfCH<br \/>\nint unitID = 1; \/\/ set initial unit ID.<br \/>\nconst int dmxPerPayload = 28; \/\/<br \/>\nconst int numPayloads = 19; \/\/ 0x00 &#8211; 0x12 == 19 payloads<\/p>\n<p>int counter;<br \/>\nvoid setup() {<br \/>\nDMXSerial.init(DMXController);<\/p>\n<p>clearDMX(); \/\/ set dmxBuf to all zeros<\/p>\n<p>#ifdef VERBOSE<br \/>\nSerial.begin(115200);<br \/>\n#endif<\/p>\n<p>#ifdef VERBOSE<br \/>\n\/\/if (WiFi.mode(WIFI_OFF)){<br \/>\n\/\/ Serial.println(&#8220;wifi radio disabled&#8221;);<br \/>\n\/\/ }<br \/>\n#endif<\/p>\n<p>if (!radio.begin()){<br \/>\n#ifdef VERBOSE<br \/>\nSerial.println(&#8220;ERROR: failed to start radio&#8221;);<br \/>\n#endif<br \/>\n}<br \/>\ndelay(100);<br \/>\n#ifdef VERBOSE<br \/>\nSerial.println(&#8220;Starting up!&#8221;);<br \/>\n#endif<br \/>\nradio.setDataRate(RF24_250KBPS);<br \/>\nradio.setCRCLength(RF24_CRC_16);<br \/>\nradio.setPALevel(RF24_PA_MAX);<br \/>\nradio.setAutoAck(false);<br \/>\nradio.setPayloadSize(32);<br \/>\nradio.setChannel(0);<br \/>\n}<\/p>\n<p>void getAddress(int ID, int channel) {<br \/>\nmyAddress[4] = ID + channel;<br \/>\nmyAddress[3] = 255 &#8211; ID;<br \/>\nmyAddress[2] = 255 &#8211; channel;<br \/>\nmyAddress[1] = ID;<br \/>\nmyAddress[0] = channel;<br \/>\n}<\/p>\n<p>void clearDMX() {<br \/>\nfor (int i = 0; i<512; i++) {\ndmxBuf[i] = 0;\n}\n}\n\nvoid doScan() {\n\/\/Clear all dmx signals while scanning\nfor (int i = 0; i<512; i++) {\nDMXSerial.write(i,0);\n}\nfor ( int rfCH = 0; rfCH < 126; rfCH++ ) {\ngetAddress(unitID, rfCH);\n\/\/delay(1);\nradio.flush_rx();\nradio.openReadingPipe(0,myAddress);\nradio.startListening();\nradio.setChannel(rfCH);\n#ifdef VERBOSE\nSerial.print(\"Trying channel \");\nSerial.println(radio.getChannel());\n#endif\nunsigned long started_waiting_at = micros(); \/\/ timeout setup\nbool timeout = false;\nwhile ( ! radio.available() ){ \/\/ While nothing is received\nif (micros() - started_waiting_at > 10000 ){ \/\/ If waited longer than 10ms, indicate timeout and exit while loop<br \/>\ntimeout = true;<br \/>\nbreak;<br \/>\n}<br \/>\n}<br \/>\nif (!timeout ){<br \/>\nuint8_t buf[32];<br \/>\nradio.read(buf, sizeof(buf));<br \/>\nif (buf[0] == 0x80) { gotLock = true; }<br \/>\n}<br \/>\nif (gotLock) {<br \/>\n#ifdef VERBOSE<br \/>\nSerial.print(&#8220;Found a transmitter on channel &#8220;);<br \/>\nSerial.println(rfCH);<br \/>\nSerial.print(&#8220;And unit ID &#8220;);<br \/>\nSerial.println(unitID);<br \/>\n#endif<br \/>\nbreak; }<br \/>\n}<br \/>\n}<\/p>\n<p>void loop() {<br \/>\nchaseTimer=millis();<br \/>\nuint8_t buf[32];<\/p>\n<p>if (!transmitter) { \/\/ Receive Mode<br \/>\nif (!gotLock) { doScan(); } \/\/ if we haven&#8217;t seen any payloads<br \/>\nelse { \/\/ receive code starts here<br \/>\nwhile (radio.available()) {<br \/>\nlastPayloadTime = millis();<br \/>\nradio.read(buf, sizeof(buf));<br \/>\nint numChansA = buf[2]; \/\/ Byte 3 of payload is MSB of payload count<br \/>\nint numChansB = buf[3]; \/\/ Byte 4 of payload is LSB of payload count<br \/>\nint numChans = (numChansA+1)*2 + numChansB;<br \/>\nint numPayloads = (numChans \/ 28) + 1; \/\/ 28 channels per payload, minimum one payload<br \/>\nint payloadID = buf[1];<br \/>\nint channelPlace = payloadID * 28;<\/p>\n<p>\/\/Serial.print(&#8220;Test value: &#8220;);<br \/>\n\/\/Serial.println(channelPlace);<\/p>\n<p>for (int place = 4; place < 32; place++) { \/\/ put payload into dmx buffer\n\/\/dmxBuf[channelPlace + place-4 &#038;&#038; 512] = buf[place];\n\/\/Here we can get a dmx value of any channel\nint channel = channelPlace + place-4;\n\n#ifdef VERBOSE\nSerial.print(\"Chan: \");\nSerial.println(channel+1);\nSerial.print(\"Val: \");\nSerial.println(buf[place]);\n#endif\nDMXSerial.write(channel+1, buf[place]);\n\n}\ndelayMicroseconds (100);\n}\n}\n\/\/Check if there were no data received for 1 second\nif (millis() - lastPayloadTime > 1000) {<br \/>\ngotLock = false; \/\/Serial.println (&#8220;Not received a payload for > 1s&#8221;);<br \/>\nlastPayloadTime = millis();<br \/>\n}<br \/>\n\/\/}\/\/ receive code ends here<br \/>\n}<br \/>\nelse { \/\/ Transmit code starts here<\/p>\n<p>getAddress(unitID,rfCH);<br \/>\nif (!txparamsSet) {<br \/>\nradio.setChannel(rfCH);<br \/>\nradio.openWritingPipe(myAddress);<br \/>\nradio.stopListening();<br \/>\ntxparamsSet = true;<br \/>\n}<\/p>\n<p>for (int payloadID = 0; payloadID < numPayloads; payloadID++) {\nuint8_t payloadTx[32];\npayloadTx[0] = 0x80;\npayloadTx[1] = payloadID;\npayloadTx[2] = 0xff; \/\/ Assume 512 channels of payloads\npayloadTx[3] = 0x01; \/\/ Assume 512 channels of payloads\nint payloadOffset = payloadID * 28;\nfor (int ch = 0; ch < 28; ch++) {\npayloadTx[ch + 4] = dmxBuf[(payloadOffset + ch)&#038;511]; \/\/ clip dmxBuf index\n}\n\/\/ now send the payload\nradio.write(payloadTx,32);\n\n}\n} \/\/Transmit code ends here\n} \/\/end of main loop\n\n<\/div>\n<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>That should be enough to get on with for now. I eventually turned my attention to making my old blue wireless board be a transmitter, and sniffed the SPI comms the same way. The result of that didn\u2019t surprise me much \u2013 a transmitter briefly scans a few channels looking for a carrier &#038; quickly selects one to broadcast on, then starts stuffing payloads into the NRF chip using the scheme I\u2019ve already described above.<\/p>\n<h2>Further notes &#038; observations<\/h2>\n<p>The first byte (byte 0) of every payload always seems to be 0x80 (128 decimal). Right now I have absolutely no idea whether this is significant, or if it bears any relation to other transmission or reception modes that might be possible.<\/p>\n<p>Bytes 1 &#038; 2 of the payload are always 0xFF &#038; 0x01 when a wireless DMX board is given a full 512 channels of DMX frame. I experimented further by changing the number of channels QLC+ outputs to 256 (and later 128) &#038; found that a board in transmit mode seems to count the number of DMX channels in each frame and acts accordingly. A full set of 19 payloads takes 19 * 1.5ms to send, making the update rate approximately 35Hz. Pro tip: Send less than a full universe to get a better update rate across the wireless link\u00a0\ud83d\ude09\u00a0How many mobile DJs \/ small scale stage lighting techs actually need 512 channels anyway?<\/p>\n<p>Some online sellers use phrases like \u20182.4ghz wifi\u2019 or \u2018uses FHSS\u2019. This is of course utter rubbish! WiFi is definitely NOT what these boards use to chat to each other. As for FHSS \u2013 frequency hopping spread spectrum \u2013 NOPE! The ONLY time a transmitter or receiver changes its operating frequency is when it\u2019s scanning for a transmitter or a clear frequency to transmit on. During operation the RF channel is NEVER changed by the micro &#038; besides that there doesn\u2019t seem to be a mechanism in place for a transmitter to tell a receiver which channel to change to.<\/p>\n<p>Then there\u2019s the legality of these things. Are they operating within the laws of the country\/state where you live? Most of the 2.4GHz ISM band is designated for unlicenced use. That is to say, things can transmit within the band without needing any licence with certain limitations &#038; caveats \u2013 namely that the nature of the transmissions have to be within certain parameters. Power level limits are specified depending on the type of transmission used &#038; could potentially be enforced against anybody found to be exceeding them.<\/p>\n<p>In theory, a genuine NRF24L01 chip can output a maximum power output of 0dB \u2013 1mW. However the presence of the PA\/LNA IC can increase this by up to 22dB, pushing the power output level to 100mW. Is this legal in the UK? Yes but only just! Add an antenna with any kind of positive gain &#038; a simple wireless DMX transmitter could possibly break the law, as well as upset other users of the 2.4GHz ISM band (bluetooth, WiFi, remote controls, wireless computer accessories etc etc etc). The vast majority of wireless DMX transmitter solutions out there offer end users no means to change the RF output power level. In ideal, line-of-sight conditions in free space, over 700 metres of range is possible with these things. Do most of us really need that?<\/p>\n<p>Finally, where on Earth did this quite simple &#038; resilient method of broadcasting DMX wirelessly come from? A few companies are known to make &#038; sell products which are very similar in operation to the generic wireless DMX boards for sale on eBay &#038; AliExpress \u2013 and most are said to be 100% compatible \u2013 which leaves me pondering whose was first?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a copypaste from\u00a0https:\/\/juskihackery.wordpress.com\/ in case if this article will get lost. In my previous post I started telling the sorry tale of how I came to using cheap wireless DMX boards with my lightshow, and how I became motivated to figuring out how they work. In this post I will reveal my findings,&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/posts\/499"}],"collection":[{"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/comments?post=499"}],"version-history":[{"count":4,"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/posts\/499\/revisions"}],"predecessor-version":[{"id":584,"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/posts\/499\/revisions\/584"}],"wp:attachment":[{"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/media?parent=499"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/categories?post=499"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oscarnet.eu\/ru\/wp-json\/wp\/v2\/tags?post=499"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}