├── .vscode ├── settings.json └── extensions.json ├── .DS_Store ├── include ├── pinout.h └── README ├── lib ├── .DS_Store ├── radio │ ├── radio.h │ └── radio.cpp └── README ├── .gitignore ├── src └── main.cpp ├── README.md ├── test └── README ├── platformio.ini └── LICENSE /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benallen-dev/xiaomi-lightbar/HEAD/.DS_Store -------------------------------------------------------------------------------- /include/pinout.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PIN_RADIO_CE 9 4 | #define PIN_RADIO_CSN 10 -------------------------------------------------------------------------------- /lib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benallen-dev/xiaomi-lightbar/HEAD/lib/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | 7 | .vscode 8 | -------------------------------------------------------------------------------- /lib/radio/radio.h: -------------------------------------------------------------------------------- 1 | #ifndef RADIO_H 2 | #define RADIO_H 3 | 4 | void setupRadioScanner(); 5 | void updateRadioScanner(); 6 | 7 | void setupRadioTransmitter(); 8 | void sendCommand(); 9 | 10 | #endif -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ], 7 | "unwantedRecommendations": [ 8 | "ms-vscode.cpptools-extension-pack" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void setup() 6 | { 7 | // put your setup code here, to run once: 8 | Serial.begin(9600); 9 | printf_begin(); 10 | Serial.println("Serial interface initialised"); 11 | 12 | // setupRadioScanner(); 13 | setupRadioTransmitter(); 14 | } 15 | 16 | void loop() 17 | { 18 | // updateRadioScanner(); 19 | 20 | sendCommand(); 21 | delay(2000); 22 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xiaomi-lightbar 2 | Use an arduino nano and RF24 module to control a Xiaomi monitor light bar 3 | 4 | Currently just a proof of concept. 5 | 6 | ## Looking for something that's more than just me goofing around? 7 | 8 | I haven't tried it personally, but [frostworx](https://github.com/frostworx) pointed this project out to me, and it looks like they did a really great job on the reverse engineering: 9 | 10 | [https://github.com/lamperez/xiaomi-lightbar-nrf24](https://github.com/lamperez/xiaomi-lightbar-nrf24) 11 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | default_envs = nanoatmega328 13 | 14 | [env:nanoatmega328] 15 | platform = atmelavr 16 | board = nanoatmega328 17 | framework = arduino 18 | lib_deps = 19 | RF24 20 | build_flags = -I include 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ben Allen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /lib/radio/radio.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | //#include 4 | 5 | // pinout.h isn't being resolved for some reason so just defining here. 6 | #define PIN_RADIO_CE 9 7 | #define PIN_RADIO_CSN 10 8 | 9 | #define SEND_ATTEMPTS 20 10 | 11 | const static uint8_t RADIO_ID = 0; 12 | const static uint8_t DESTINATION_RADIO_ID = 1; 13 | 14 | RF24 radio(PIN_RADIO_CE, PIN_RADIO_CSN); 15 | const uint64_t address = 0xAAAAAAAAAAAA; 16 | 17 | uint8_t counter = 0; 18 | 19 | void setupRadioScanner() 20 | { 21 | radio.begin(); 22 | radio.openReadingPipe(0, address); 23 | 24 | radio.setChannel(68); 25 | radio.setDataRate(RF24_2MBPS); 26 | radio.disableCRC(); 27 | radio.disableDynamicPayloads(); 28 | radio.setPayloadSize(17); 29 | radio.setAutoAck(false); 30 | 31 | radio.startListening(); 32 | radio.printPrettyDetails(); 33 | } 34 | 35 | void updateRadioScanner() 36 | { 37 | if (radio.available()) 38 | { 39 | byte data[18] = {0}; 40 | 41 | radio.read(&data, sizeof(data)); 42 | 43 | // Byte 13 contains the command: 44 | // 0x20 on/off 45 | // 0x40 color temperature + (more blue) 46 | // 0x7F color temperature - (more red) 47 | // 0x80 brightness + 48 | // 0xBF brightness - 49 | 50 | if (data[0] == 0x67 && data[1] == 0x22) // Capture all events from the remote 51 | { 52 | Serial.println("========================="); 53 | Serial.println("Light Bar Packet received"); 54 | Serial.println("========================="); 55 | 56 | for (unsigned long i = 0; i < sizeof(data); i++) 57 | { 58 | if (i == 0) 59 | Serial.println("------ address"); 60 | if (i == 7) 61 | Serial.println("------ product serial"); 62 | if (i == 11) 63 | Serial.println("------ packet ID counter, I think. Avoids duplicate execution"); 64 | if (i == 13) 65 | Serial.println("------ action"); 66 | if (i == 14) 67 | Serial.println("------ checksum? parameters? noise? idk"); 68 | Serial.print(i, DEC); 69 | Serial.print(": 0x"); 70 | Serial.print(data[i], HEX); 71 | if (i == 13 && data[i] == 0x80) 72 | Serial.print(" | brightness +"); 73 | if (i == 13 && data[i] == 0xBF) 74 | Serial.print(" | brightness -"); 75 | if (i == 13 && data[i] == 0x20) 76 | Serial.print(" | on/off"); 77 | if (i == 13 && data[i] == 0x7F) 78 | Serial.print(" | temperature - "); 79 | if (i == 13 && data[i] == 0x40) 80 | Serial.print(" | temperature + "); 81 | Serial.println(); 82 | } 83 | 84 | Serial.println("========================="); 85 | Serial.println(); 86 | } 87 | } 88 | }; 89 | 90 | void setupRadioTransmitter() 91 | { 92 | 93 | radio.begin(); 94 | radio.openReadingPipe(0, address); 95 | 96 | Serial.println("Set to channel 68..."); 97 | radio.setChannel(68); 98 | 99 | Serial.println("Set data rate to 2MBPS..."); 100 | radio.setDataRate(RF24_2MBPS); 101 | 102 | Serial.println("Disable CRC..."); 103 | radio.disableCRC(); 104 | 105 | Serial.println("Disable dynamic payloads..."); 106 | radio.disableDynamicPayloads(); 107 | 108 | Serial.println("Disable auto-ACK..."); 109 | radio.setAutoAck(false); 110 | 111 | Serial.println("Set PA Level LOW..."); 112 | radio.setPALevel(RF24_PA_LOW); 113 | 114 | Serial.println("Set payload size"); 115 | radio.setPayloadSize(17); 116 | 117 | Serial.println("Set retries"); 118 | radio.setRetries(15, 15); 119 | 120 | Serial.println("Open writing pipe"); 121 | radio.openWritingPipe(address); // always uses pipe 0 122 | 123 | radio.printPrettyDetails(); 124 | 125 | Serial.println("Stop listening"); 126 | radio.stopListening(); 127 | } 128 | 129 | void sendCommand() 130 | { 131 | // Captured sequences from remote 132 | uint8_t commands[5][17] = { 133 | {0x67, 134 | 0x22, 135 | 0x9B, 136 | 0xA3, 137 | 0x89, 138 | 0x26, 139 | 0x82, 140 | 0x4A, 141 | 0x2D, 142 | 0xE4, 143 | 0x3F, 144 | 0xED, 145 | 0xA0, 146 | 0x20, 147 | 0x1B, 148 | 0xE5, 149 | 0x12}, 150 | {0x67, 151 | 0x22, 152 | 0x9B, 153 | 0xA3, 154 | 0x89, 155 | 0x26, 156 | 0x82, 157 | 0x4A, 158 | 0x2D, 159 | 0xE4, 160 | 0x3F, 161 | 0xF0, 162 | 0x40, 163 | 0x20, 164 | 0xA, 165 | 0x65, 166 | 0x13}, 167 | {0x67, 168 | 0x22, 169 | 0x9B, 170 | 0xA3, 171 | 0x89, 172 | 0x26, 173 | 0x82, 174 | 0x4A, 175 | 0x2D, 176 | 0xE4, 177 | 0x3F, 178 | 0xEE, 179 | 0x0, 180 | 0x20, 181 | 0x39, 182 | 0xC7, 183 | 0x66}, 184 | {0x67, 185 | 0x22, 186 | 0x9B, 187 | 0xA3, 188 | 0x89, 189 | 0x26, 190 | 0x82, 191 | 0x4A, 192 | 0x2D, 193 | 0xE4, 194 | 0x3F, 195 | 0xF5, 196 | 0x40, 197 | 0x20, 198 | 0xF, 199 | 0x89, 200 | 0xE3}, 201 | {0x67, 202 | 0x22, 203 | 0x9B, 204 | 0xA3, 205 | 0x89, 206 | 0x26, 207 | 0x82, 208 | 0x4A, 209 | 0x2D, 210 | 0xE4, 211 | 0x3F, 212 | 0xF7, 213 | 0x40, 214 | 0x20, 215 | 0x7, 216 | 0xE5, 217 | 0x93}}; 218 | 219 | counter++; 220 | 221 | // Send repeatedly, response is unreliable otherwise 222 | for (int i = 0; i < 17; i++) 223 | { 224 | bool report = radio.write(&commands[counter % 5], sizeof(commands[0]), true); 225 | 226 | if (!report) 227 | { 228 | Serial.println("write fail..."); 229 | } 230 | 231 | // Without the delay, it'll work some times and not others 232 | delay(20); 233 | } 234 | } 235 | --------------------------------------------------------------------------------