├── .clang-format ├── .editorconfig ├── .github ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── support.md ├── dependabot.yml └── workflows │ ├── documentation.yml │ ├── lint.yml │ ├── push.yml │ └── stale.yaml ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── .clang-format ├── AdditionalFeatures │ └── AdditionalFeatures.ino ├── AmbientBacklight │ └── AmbientBacklight.ino ├── CommanderPRO │ ├── CommanderPRO.ino │ ├── PWMFan.cpp │ ├── PWMFan.h │ ├── SimpleFanController.cpp │ ├── SimpleFanController.h │ ├── ThermistorTemperatureController.cpp │ └── ThermistorTemperatureController.h ├── DebugSketch │ ├── DebugSketch.ino │ └── board.txt ├── DeviceIDTool │ └── DeviceIDTool.ino ├── HoodLoader2CLPBridge │ ├── CLPUSBSerialBridge.cpp │ ├── CLPUSBSerialBridge.h │ └── HoodLoader2CLPBridge.ino ├── HoodLoader2UnoMegaController │ └── HoodLoader2UnoMegaController.ino ├── LS100 │ └── LS100.ino ├── LT100 │ └── LT100.ino ├── LightingNodeCORE │ └── LightingNodeCORE.ino ├── LightingNodePRO │ └── LightingNodePRO.ino ├── MultipleFans │ └── MultipleFans.ino ├── NoEEPROM │ └── NoEEPROM.ino ├── NonAddressable │ └── NonAddressable.ino ├── RepeatAndScale │ └── RepeatAndScale.ino ├── SingleStripLightingNodePRO │ └── SingleStripLightingNodePRO.ino ├── TinyUSB │ └── TinyUSB.ino ├── TransformLLFansFormatToStrip │ └── TransformLLFansFormatToStrip.ino ├── UnitTests │ └── UnitTests.ino └── UnitTests2 │ └── UnitTests2.ino ├── extra ├── doxygen.conf └── images │ ├── OpenRGBBadge.png │ ├── RGBSyncDarkBadge.png │ ├── SignalRGBBadge.png │ ├── board-wiring-pico.jpg │ ├── board-wiring.jpg │ ├── iCUE.jpg │ ├── iCUEDarkBadge.png │ ├── open-example.png │ ├── overview.png │ ├── select-board-pico.png │ ├── select-board.png │ └── upload-sketch.png ├── keywords.txt ├── library.properties └── src ├── CLPAdditionalFeatures.cpp ├── CLPAdditionalFeatures.h ├── CLPUtils.cpp ├── CLPUtils.h ├── CorsairLightingFirmware.cpp ├── CorsairLightingFirmware.h ├── CorsairLightingFirmwareStorageEEPROM.cpp ├── CorsairLightingFirmwareStorageEEPROM.h ├── CorsairLightingFirmwareStorageStatic.cpp ├── CorsairLightingFirmwareStorageStatic.h ├── CorsairLightingProtocol.h ├── CorsairLightingProtocolConstants.h ├── CorsairLightingProtocolController.cpp ├── CorsairLightingProtocolController.h ├── CorsairLightingProtocolHID.cpp ├── CorsairLightingProtocolHID.h ├── CorsairLightingProtocolResponse.cpp ├── CorsairLightingProtocolResponse.h ├── CorsairLightingProtocolSerial.cpp ├── CorsairLightingProtocolSerial.h ├── CorsairLightingProtocolTinyUSBHID.cpp ├── CorsairLightingProtocolTinyUSBHID.h ├── FanController.cpp ├── FanController.h ├── FastLEDController.cpp ├── FastLEDController.h ├── FastLEDControllerStorage.h ├── FastLEDControllerStorageEEPROM.cpp ├── FastLEDControllerStorageEEPROM.h ├── FastLEDControllerUtils.cpp ├── FastLEDControllerUtils.h ├── IFanController.h ├── ILEDController.h ├── ITemperatureController.h ├── LEDController.cpp ├── LEDController.h ├── RawHID.cpp ├── RawHID.h ├── TemperatureController.cpp └── TemperatureController.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | ColumnLimit: 120 3 | IndentWidth: 4 4 | TabWidth: 4 5 | UseTab: ForContinuationAndIndentation 6 | AccessModifierOffset: -4 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = false 8 | insert_final_newline = true 9 | 10 | [*.{yml,yaml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | You can contribute in many different ways, for example report a bug or come up with an idea for improvement. 4 | If you are good at programming you could also provide a fix for bugs or Pull Requests with improvements. 5 | If you want to help others using CLP you are welcome to extend the [Wiki on GitHub](https://github.com/Legion2/CorsairLightingProtocol/wiki). 6 | 7 | ## Finding information 8 | 9 | General information can be found in the [Readme](https://github.com/Legion2/CorsairLightingProtocol) file at the root of the project. 10 | Information about specific topics are written in the [Wiki](https://github.com/Legion2/CorsairLightingProtocol/wiki). 11 | For developer there is also an [API documentation](https://legion2.github.io/CorsairLightingProtocol/) for all the public methods and types. 12 | 13 | ## Writing code 14 | 15 | For writing code I recommend [VS Code](https://code.visualstudio.com/) with the [Clang-Format](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) and [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extensions. 16 | For uploading to Arduino boards I use the ArduinoIDE. 17 | 18 | This project consists of two major parts, the CLP library itself, for which the source code can be found in `src/` directory and the example sketches in the `examples/` subdirectories. 19 | The main logic for processing commands and creating the LED colors is part of the library source code. 20 | The examples only show how to use the library and what can be configured. 21 | The library is modular so communication with iCUE and the processing of commands is decoupled and can be extended by other developers. 22 | Single parts of the library, such as the LEDController can also be used without iCUE, by just calling the public API of these classes. 23 | 24 | The examples are used by most users as a template which they only modified slightly, so the basic examples should be simple and explicitly show the main configuration options. 25 | Not all users are developers and may not understand the syntax or semantics of the code, they just alter some values and upload the sketches. 26 | Examples can also be used to show how some additional or special features can be used, these sketches are not meant to be used by the users directly. 27 | To verify that the library can be compiled on all supported platforms, all the examples are compiled in the CI pipeline for different boards. 28 | 29 | ## Coding conventions 30 | 31 | Public methods must be documented with JavaDoc style comments. 32 | When introducing new methods you should think about if the method should be part of the public API or only used internally. 33 | Always use descriptive names for variables, only use acronyms if they are well known and frequently used. 34 | The coding style is defined using ClangFormat and automatically checked when you submit a Pull Request. 35 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: Legion2 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: paypal.me/LeonKiefer 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Change to '...' 16 | 2. Upload '....' 17 | 3. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **System (please complete the following information):** 26 | - OS: [e.g. Windows 10] 27 | - Board: [e.g. Arduino Leonardo] 28 | - Device: [e.g. Lighting Node PRO] 29 | - Version of IDE: [e.g. Arduino IDE 1.8.13] 30 | - Version of CLP: [e.g. 0.15.0] 31 | - Version of CLP Boards: [e.g. 0.2.0] 32 | - Sketch: [e.g. RepeatAndScale] 33 | 34 | **Code changes** 35 | Did you changed the code of the library or the sketch? 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support 3 | about: You have a problem using this project and need help? 4 | title: '' 5 | labels: support 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the problem** 11 | A clear and concise description of your problem. 12 | 13 | **Screenshots** 14 | If applicable, add screenshots to help explain your problem. 15 | 16 | **System (please complete the following information):** 17 | - OS: [e.g. Windows 10] 18 | - Board: [e.g. Arduino Leonardo] 19 | - Device: [e.g. Lighting Node PRO] 20 | - Version of IDE: [e.g. Arduino IDE 1.8.13] 21 | - Version of CLP: [e.g. 0.15.0] 22 | - Version of CLP Boards: [e.g. 0.2.0] 23 | - Sketch: [e.g. RepeatAndScale] 24 | 25 | **Code changes** 26 | Did you changed the code of the library or the sketch? 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - dev 7 | 8 | jobs: 9 | doxygen: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Install doxygen 15 | run: sudo apt install doxygen graphviz 16 | - name: Fix Readme title 17 | run: sed -i '1s/ \[/\n\[/' README.md 18 | - name: Run doxygen 19 | run: doxygen extra/doxygen.conf 20 | - name: Deploy to gh-pages 21 | uses: peaceiris/actions-gh-pages@v3.9.2 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | publish_branch: gh-pages 25 | publish_dir: doxygen/html 26 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Check Code Format 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: Check src format 12 | uses: DoozyX/clang-format-lint-action@v0.18 13 | with: 14 | source: './src' 15 | extensions: 'h,cpp' 16 | clangFormatVersion: 9 17 | - name: Check examples format 18 | uses: DoozyX/clang-format-lint-action@v0.18 19 | with: 20 | source: './examples' 21 | extensions: 'h,cpp,ino' 22 | clangFormatVersion: 9 23 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request, workflow_dispatch] 2 | name: Test 3 | jobs: 4 | test: 5 | name: Test for Board ${{ matrix.board }} 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | board: 10 | [ 11 | "Legion2:avr:leonardoclp", 12 | "Legion2:avr:promicro5vclp", 13 | "Legion2:avr:promicro3vclp", 14 | ] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Build for ${{ matrix.board }} 18 | uses: ArminJo/arduino-test-compile@v3 19 | with: 20 | cli-version: 0.18.3 21 | arduino-board-fqbn: ${{ matrix.board }} 22 | platform-url: https://raw.githubusercontent.com/sparkfun/Arduino_Boards/master/IDE_Board_Manager/package_sparkfun_index.json,https://raw.githubusercontent.com/Legion2/CorsairLightingProtocolBoards/master/package_Legion2_CorsairLightingProtocolBoards_index.json 23 | arduino-platform: arduino:avr@1.8.3,SparkFun:avr@1.1.13,Legion2:avr@0.3.1 24 | required-libraries: FastLED@3.5.0 25 | sketch-names: LightingNodePRO.ino, 26 | SingleStripLightingNodePRO.ino, 27 | CommanderPRO.ino, 28 | DeviceIDTool.ino, 29 | RepeatAndScale.ino, 30 | TransformLLFansFormatToStrip.ino, 31 | LS100.ino, 32 | LT100.ino, 33 | LightingNodeCORE.ino, 34 | NonAddressable.ino, 35 | AdditionalFeatures.ino, 36 | AmbientBacklight.ino, 37 | MultipleFans.ino, 38 | DebugSketch.ino, 39 | NoEEPROM.ino 40 | build-properties: '{"DebugSketch": "-DDEBUG -DVERBOSE -DPRINT_COMMAND=true -DPRINT_RESPONSE=true -DPRINT_LOOP=true -DPRINT_UPDATE=true"}' 41 | testUnoMega: 42 | name: Test Arduino Uno/Mega sketches for Board ${{ matrix.board }} 43 | runs-on: ubuntu-latest 44 | strategy: 45 | matrix: 46 | board: ["arduino:avr:uno", "arduino:avr:mega:cpu=atmega2560"] 47 | steps: 48 | - uses: actions/checkout@v4 49 | - name: Build for ${{ matrix.board }} 50 | uses: ArminJo/arduino-test-compile@v3 51 | with: 52 | cli-version: 0.18.3 53 | arduino-board-fqbn: ${{ matrix.board }} 54 | arduino-platform: arduino:avr@1.8.3 55 | required-libraries: FastLED@3.5.0 56 | sketch-names: HoodLoader2UnoMegaController.ino 57 | test16u2: 58 | name: Test 16u2 sketch for Board ${{ matrix.board }} 59 | runs-on: ubuntu-latest 60 | strategy: 61 | matrix: 62 | board: ["Legion2:avr:HoodLoader2atmega16u2clp"] 63 | steps: 64 | - uses: actions/checkout@v4 65 | - name: Build for ${{ matrix.board }} 66 | uses: ArminJo/arduino-test-compile@v3 67 | with: 68 | cli-version: 0.18.3 69 | arduino-board-fqbn: ${{ matrix.board }} 70 | platform-url: https://raw.githubusercontent.com/NicoHood/HoodLoader2/master/package_NicoHood_HoodLoader2_index.json,https://raw.githubusercontent.com/Legion2/CorsairLightingProtocolBoards/master/package_Legion2_CorsairLightingProtocolBoards_index.json 71 | arduino-platform: arduino:avr@1.8.3,HoodLoader2:avr@2.0.5,Legion2:avr@0.3.1 72 | required-libraries: FastLED@3.5.0 73 | sketch-names: HoodLoader2CLPBridge.ino 74 | testAdafruitSAMD: 75 | name: Test for Board ${{ matrix.board }} 76 | runs-on: ubuntu-latest 77 | strategy: 78 | matrix: 79 | board: 80 | [ 81 | "adafruit:samd:adafruit_feather_m0:usbstack=tinyusb", 82 | "adafruit:samd:adafruit_metro_m0:usbstack=tinyusb", 83 | "adafruit:samd:adafruit_circuitplayground_m0:usbstack=tinyusb", 84 | "adafruit:samd:adafruit_gemma_m0:usbstack=tinyusb", 85 | "adafruit:samd:adafruit_trinket_m0:usbstack=tinyusb", 86 | "adafruit:samd:adafruit_qtpy_m0:usbstack=tinyusb", 87 | "adafruit:samd:adafruit_itsybitsy_m0:usbstack=tinyusb", 88 | "adafruit:samd:adafruit_itsybitsy_m4:usbstack=tinyusb", 89 | "adafruit:samd:adafruit_metro_m4_airliftlite:usbstack=tinyusb", 90 | # "adafruit:samd:adafruit_feather_m4:usbstack=tinyusb", #Should work but doesn't have the pins needed for the sketch 91 | "adafruit:samd:adafruit_matrixportal_m4:usbstack=tinyusb" 92 | ] 93 | steps: 94 | - name: Checkout 95 | uses: actions/checkout@v4 96 | - name: Build for ${{ matrix.board }} 97 | uses: ArminJo/arduino-test-compile@v3 98 | with: 99 | cli-version: 0.18.3 100 | arduino-board-fqbn: ${{ matrix.board }} 101 | platform-url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json 102 | arduino-platform: adafruit:samd@1.7.8 103 | required-libraries: FastLED@3.5.0,Adafruit TinyUSB Library@1.9.2 104 | sketch-names: TinyUSB.ino 105 | testAdafruitnRF52: 106 | name: Test for Board ${{ matrix.board }} 107 | runs-on: ubuntu-latest 108 | strategy: 109 | matrix: 110 | board: 111 | [ 112 | "adafruit:nrf52:feather52840", 113 | "adafruit:nrf52:itsybitsy52840", 114 | # "adafruit:nrf52:cplaynrf52840", #Should work but doesn't have the pins needed for the sketch 115 | "adafruit:nrf52:metro52840" 116 | ] 117 | steps: 118 | - name: Checkout 119 | uses: actions/checkout@v4 120 | - name: Get adafruit-nrfutil 121 | run: | 122 | pip3 install adafruit-nrfutil --user 123 | - name: Build for ${{ matrix.board }} 124 | uses: ArminJo/arduino-test-compile@v3 125 | with: 126 | cli-version: 0.18.3 127 | arduino-board-fqbn: ${{ matrix.board }} 128 | platform-url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json 129 | arduino-platform: adafruit:nrf52@1.3.0 130 | required-libraries: FastLED@3.5.0,Adafruit TinyUSB Library@1.9.2 131 | sketch-names: TinyUSB.ino -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9 11 | with: 12 | days-before-stale: 14 13 | days-before-close: 7 14 | exempt-issue-labels: bug,enhancement,refactoring,documentation 15 | stale-issue-message: > 16 | This issue has been automatically marked as stale because it has not had 17 | recent activity. It will be closed if no further activity occurs. Thank you 18 | for your contributions. 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | 263 | __vm/ 264 | 265 | # Visual Studio 266 | *.sln 267 | *.vcxproj 268 | *.vcxproj.filters 269 | *.vcxitems 270 | 271 | /doxygen 272 | 273 | # VS Code settings 274 | .vscode/ 275 | 276 | # Arduino CLI 277 | build/ 278 | -------------------------------------------------------------------------------- /examples/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | ColumnLimit: 120 3 | IndentWidth: 4 4 | TabWidth: 4 5 | UseTab: ForContinuationAndIndentation 6 | AccessModifierOffset: -4 7 | 8 | AllowShortBlocksOnASingleLine: false 9 | AllowShortFunctionsOnASingleLine: false 10 | AllowShortLambdasOnASingleLine: false 11 | -------------------------------------------------------------------------------- /examples/AdditionalFeatures/AdditionalFeatures.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define DATA_PIN_CHANNEL_1 2 20 | #define DATA_PIN_CHANNEL_2 3 21 | 22 | CRGB ledsChannel1[60]; 23 | CRGB ledsChannel2[60]; 24 | 25 | // Define a custom SerialNumber for the device 26 | const char mySerialNumber[] PROGMEM = "202B6949A967"; 27 | 28 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 29 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 30 | FastLEDControllerStorageEEPROM storage; 31 | FastLEDController ledController(&storage); 32 | CorsairLightingProtocolController cLP(&ledController, &firmware); 33 | // Set the SerialNumber here 34 | CorsairLightingProtocolHID cHID(&cLP, mySerialNumber); 35 | 36 | void setup() { 37 | // Disable the build in RX and TX LEDs of the Arduino 38 | CLP::disableBuildInLEDs(); 39 | // enable reset on DeviceId (FF FF FF FF) 40 | if (CLP::shouldReset(&firmware)) { 41 | // reset DeviceId and generate new one 42 | CLP::reset(&firmware); 43 | // reset the LEDController Settings 44 | ledController.reset(); 45 | } 46 | FastLED.addLeds(ledsChannel1, 60); 47 | FastLED.addLeds(ledsChannel2, 60); 48 | ledController.addLEDs(0, ledsChannel1, 60); 49 | ledController.addLEDs(1, ledsChannel2, 60); 50 | } 51 | 52 | void loop() { 53 | cHID.update(); 54 | 55 | if (ledController.updateLEDs()) { 56 | FastLED.show(); 57 | CLP::printFps(5000); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /examples/AmbientBacklight/AmbientBacklight.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | // Hint: The channels are swapped in iCUE, so the first channel in iCUE is here channel 2 20 | #define DATA_PIN_CHANNEL_1 2 // For the monitor backlight 21 | #define DATA_PIN_CHANNEL_2 3 22 | 23 | CRGB ledsChannel1[84]; 24 | CRGB ledsChannel2[105]; 25 | 26 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 27 | CorsairLightingFirmware firmware(CORSAIR_SMART_LIGHTING_CONTROLLER, &firmwareStorage); 28 | FastLEDControllerStorageEEPROM storage; 29 | FastLEDController ledController(&storage); 30 | CorsairLightingProtocolController cLP(&ledController, &firmware); 31 | CorsairLightingProtocolHID cHID(&cLP); 32 | 33 | void setup() { 34 | FastLED.addLeds(ledsChannel1, 84); 35 | FastLED.addLeds(ledsChannel2, 105); 36 | ledController.addLEDs(0, ledsChannel1, 84); 37 | ledController.addLEDs(1, ledsChannel2, 105); 38 | ledController.onUpdateHook(0, []() { 39 | // increase the brightness of channel 1 when using iCUE, because iCUE only set brightness to max 50% 40 | CLP::fixIcueBrightness(&ledController, 0); 41 | // gamma correction with gamma value 2.0. Use napplyGamma_video for other gamma values. 42 | CLP::gammaCorrection(&ledController, 0); 43 | // napplyGamma_video(ledsChannel1, 84, 2.2); 44 | }); 45 | } 46 | 47 | void loop() { 48 | cHID.update(); 49 | 50 | if (ledController.updateLEDs()) { 51 | FastLED.show(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/CommanderPRO/CommanderPRO.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #include "SimpleFanController.h" 20 | #include "ThermistorTemperatureController.h" 21 | 22 | #define DATA_PIN_CHANNEL_1 2 23 | #define DATA_PIN_CHANNEL_2 3 24 | 25 | #define TEMP_SENSOR_PIN_1 A6 26 | #define TEMP_SENSOR_PIN_2 A8 27 | 28 | #define FAN_UPDATE_RATE 500 29 | #define PWM_FAN_PIN_1 5 30 | #define PWM_FAN_PIN_2 6 31 | #define PWM_FAN_PIN_3 9 32 | #define PWM_FAN_PIN_4 10 33 | 34 | #define CHANNEL_LED_COUNT 96 35 | 36 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 37 | CorsairLightingFirmware firmware(CORSAIR_COMMANDER_PRO, &firmwareStorage); 38 | ThermistorTemperatureController temperatureController; 39 | FastLEDControllerStorageEEPROM storage; 40 | FastLEDController ledController(&storage); 41 | SimpleFanController fanController(&temperatureController, FAN_UPDATE_RATE, EEPROM_ADDRESS + storage.getEEPROMSize()); 42 | CorsairLightingProtocolController cLP(&ledController, &temperatureController, &fanController, &firmware); 43 | CorsairLightingProtocolHID cHID(&cLP); 44 | 45 | CRGB ledsChannel1[CHANNEL_LED_COUNT]; 46 | CRGB ledsChannel2[CHANNEL_LED_COUNT]; 47 | 48 | PWMFan fan1(PWM_FAN_PIN_1, 0, 2000); 49 | PWMFan fan2(PWM_FAN_PIN_2, 0, 2000); 50 | PWMFan fan3(PWM_FAN_PIN_3, 0, 2000); 51 | PWMFan fan4(PWM_FAN_PIN_4, 0, 2000); 52 | 53 | void setup() { 54 | CLP::disableBuildInLEDs(); 55 | FastLED.addLeds(ledsChannel1, CHANNEL_LED_COUNT); 56 | FastLED.addLeds(ledsChannel2, CHANNEL_LED_COUNT); 57 | ledController.addLEDs(0, ledsChannel1, CHANNEL_LED_COUNT); 58 | ledController.addLEDs(1, ledsChannel2, CHANNEL_LED_COUNT); 59 | temperatureController.addSensor(0, TEMP_SENSOR_PIN_1); 60 | temperatureController.addSensor(1, TEMP_SENSOR_PIN_2); 61 | fanController.addFan(0, &fan1); 62 | fanController.addFan(1, &fan2); 63 | fanController.addFan(2, &fan3); 64 | fanController.addFan(3, &fan4); 65 | } 66 | 67 | void loop() { 68 | cHID.update(); 69 | 70 | if (ledController.updateLEDs()) { 71 | FastLED.show(); 72 | } 73 | fanController.updateFans(); 74 | } 75 | -------------------------------------------------------------------------------- /examples/CommanderPRO/PWMFan.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "PWMFan.h" 17 | 18 | PWMFan::PWMFan(uint8_t pwmPin, uint16_t minRPM, uint16_t maxRPM) : pwmPin(pwmPin), minRPM(minRPM), maxRPM(maxRPM) { 19 | pinMode(pwmPin, OUTPUT); 20 | analogWrite(pwmPin, 0); 21 | switch (digitalPinToTimer(pwmPin)) { 22 | case TIMER0B: /* 3 */ 23 | #ifdef DEBUG 24 | Serial.println(F("Pin not supported as PWM fan pin")); 25 | Serial.println(F("We don't want to mess up Arduino time functions")); 26 | #endif // DEBUG 27 | break; 28 | case TIMER3A: /* 5 */ 29 | TCCR3B = (TCCR3B & B11111000) | 0x01; 30 | break; 31 | case TIMER4D: /* 6 */ 32 | // PLLFRQ = (PLLFRQ & B11001111) | (0x03 << PLLTM0); 33 | TCCR4B = (TCCR4B & B11110000) | 0x01; 34 | break; 35 | case TIMER1A: /* 9 */ 36 | TCCR1B = (TCCR1B & B11111000) | 0x01; 37 | break; 38 | case TIMER1B: /* 10 */ 39 | TCCR1B = (TCCR1B & B11111000) | 0x01; 40 | break; 41 | default: 42 | #ifdef DEBUG 43 | Serial.println(F("Pin not supported as PWM fan pin")); 44 | #endif // DEBUG 45 | break; 46 | } 47 | } 48 | 49 | void PWMFan::setPower(uint8_t percentage) { 50 | analogWrite(pwmPin, percentage); 51 | } 52 | 53 | uint8_t PWMFan::calculatePowerFromSpeed(uint16_t rpm) { 54 | rpm = constrain(rpm, minRPM, maxRPM); 55 | return ((float)(rpm - minRPM) / (float)(maxRPM - minRPM)) * 255; 56 | } 57 | 58 | uint16_t PWMFan::calculateSpeedFromPower(uint8_t power) { 59 | return map(power, 0, 255, minRPM, maxRPM); 60 | } 61 | -------------------------------------------------------------------------------- /examples/CommanderPRO/PWMFan.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | 20 | class PWMFan { 21 | public: 22 | /** 23 | * PWM fan which maps speed to power using linear interpolation. This fan does not read the real RPM values. The 24 | * Arduino timer for the given pin will be set to higher speed. 25 | * 26 | * @param pwmPin the Arduino pwm pin for this fan. Not all PWM pins are supported. 27 | * @param minRPM the speed in RPM at 0% power 28 | * @param maxRPM the speed in RPM at 100% power 29 | */ 30 | PWMFan(uint8_t pwmPin, uint16_t minRPM, uint16_t maxRPM); 31 | virtual void setPower(uint8_t percentage); 32 | virtual uint8_t calculatePowerFromSpeed(uint16_t rpm); 33 | virtual uint16_t calculateSpeedFromPower(uint8_t power); 34 | 35 | protected: 36 | const uint8_t pwmPin; 37 | const uint16_t minRPM; 38 | const uint16_t maxRPM; 39 | }; 40 | -------------------------------------------------------------------------------- /examples/CommanderPRO/SimpleFanController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "SimpleFanController.h" 17 | 18 | #include 19 | 20 | SimpleFanController::SimpleFanController(TemperatureController* temperatureController, uint16_t updateRate, 21 | uint16_t eEPROMAdress) 22 | : temperatureController(temperatureController), updateRate(updateRate), eEPROMAdress(eEPROMAdress) { 23 | load(); 24 | } 25 | 26 | void SimpleFanController::addFan(uint8_t index, PWMFan* fan) { 27 | if (index >= FAN_NUM) { 28 | return; 29 | } 30 | fans[index] = fan; 31 | switch (fanData[index].mode) { 32 | case FAN_CONTROL_MODE_FIXED_POWER: 33 | fanData[index].speed = fan->calculateSpeedFromPower(fanData[index].power); 34 | break; 35 | case FAN_CONTROL_MODE_FIXED_RPM: 36 | fanData[index].power = fan->calculatePowerFromSpeed(fanData[index].speed); 37 | break; 38 | } 39 | } 40 | 41 | bool SimpleFanController::updateFans() { 42 | unsigned long currentUpdate = millis(); 43 | unsigned long lastUpdateNumber = lastUpdate / updateRate; 44 | unsigned long currentUpdateNumber = currentUpdate / updateRate; 45 | lastUpdate = currentUpdate; 46 | if (lastUpdateNumber < currentUpdateNumber) { 47 | if (triggerSave) { 48 | triggerSave = false; 49 | save(); 50 | } 51 | 52 | for (uint8_t i = 0; i < FAN_NUM; i++) { 53 | if (fans[i] == nullptr) { 54 | continue; 55 | } 56 | if (fanData[i].mode == FAN_CONTROL_MODE_FIXED_RPM || fanData[i].mode == FAN_CONTROL_MODE_FIXED_POWER) { 57 | fans[i]->setPower(fanData[i].power); 58 | continue; 59 | } 60 | 61 | uint16_t temp; 62 | const uint8_t& group = fanData[i].tempGroup; 63 | if (group == FAN_CURVE_TEMP_GROUP_EXTERNAL) { 64 | temp = externalTemp[i]; 65 | } else if (group < TEMPERATURE_NUM) { 66 | temp = temperatureController->getTemperature(group); 67 | } 68 | 69 | const FanCurve& fanCurve = fanData[i].fanCurve; 70 | uint16_t speed; 71 | 72 | if (temp <= fanCurve.temperatures[0]) { 73 | speed = fanCurve.rpms[0]; 74 | } else if (temp > fanCurve.temperatures[FAN_CURVE_POINTS_NUM - 1]) { 75 | speed = fanCurve.rpms[FAN_CURVE_POINTS_NUM - 1]; 76 | } else { 77 | for (uint8_t p = 0; p < FAN_CURVE_POINTS_NUM - 1; p++) { 78 | if (temp > fanCurve.temperatures[p + 1]) { 79 | continue; 80 | } 81 | speed = map(temp, fanCurve.temperatures[p], fanCurve.temperatures[p + 1], fanCurve.rpms[p], 82 | fanCurve.rpms[p + 1]); 83 | break; 84 | } 85 | } 86 | 87 | fanData[i].speed = speed; 88 | fanData[i].power = fans[i]->calculatePowerFromSpeed(speed); 89 | fans[i]->setPower(fanData[i].power); 90 | } 91 | return true; 92 | } 93 | return false; 94 | } 95 | 96 | uint16_t SimpleFanController::getFanSpeed(uint8_t fan) { 97 | return fanData[fan].speed; 98 | } 99 | 100 | void SimpleFanController::setFanSpeed(uint8_t fan, uint16_t speed) { 101 | fanData[fan].speed = speed; 102 | fanData[fan].mode = FAN_CONTROL_MODE_FIXED_RPM; 103 | fanData[fan].power = fans[fan] != nullptr ? fans[fan]->calculatePowerFromSpeed(speed) : 0; 104 | triggerSave = true; 105 | } 106 | 107 | uint8_t SimpleFanController::getFanPower(uint8_t fan) { 108 | return fanData[fan].power; 109 | } 110 | 111 | void SimpleFanController::setFanPower(uint8_t fan, uint8_t percentage) { 112 | fanData[fan].power = percentage; 113 | fanData[fan].mode = FAN_CONTROL_MODE_FIXED_POWER; 114 | fanData[fan].speed = fans[fan] != nullptr ? fans[fan]->calculateSpeedFromPower(percentage) : 0; 115 | triggerSave = true; 116 | } 117 | 118 | void SimpleFanController::setFanCurve(uint8_t fan, uint8_t group, FanCurve& fanCurve) { 119 | fanData[fan].fanCurve = fanCurve; 120 | fanData[fan].tempGroup = group; 121 | fanData[fan].mode = FAN_CONTROL_MODE_CURVE; 122 | triggerSave = true; 123 | } 124 | 125 | void SimpleFanController::setFanExternalTemperature(uint8_t fan, uint16_t temp) { 126 | externalTemp[fan] = temp; 127 | } 128 | 129 | void SimpleFanController::setFanForce3PinMode(bool flag) { 130 | force3PinMode = flag; 131 | } 132 | 133 | FanDetectionType SimpleFanController::getFanDetectionType(uint8_t fan) { 134 | return fanData[fan].detectionType; 135 | } 136 | 137 | void SimpleFanController::setFanDetectionType(uint8_t fan, FanDetectionType type) { 138 | if (fanData[fan].detectionType != type) { 139 | fanData[fan].detectionType = type; 140 | triggerSave = true; 141 | } 142 | } 143 | 144 | bool SimpleFanController::load() { 145 | EEPROM.get(eEPROMAdress, fanData); 146 | return true; 147 | } 148 | 149 | bool SimpleFanController::save() { 150 | #ifdef DEBUG 151 | Serial.println(F("Save fan data to EEPROM.")); 152 | #endif 153 | EEPROM.put(eEPROMAdress, fanData); 154 | return true; 155 | } 156 | -------------------------------------------------------------------------------- /examples/CommanderPRO/SimpleFanController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "FanController.h" 20 | #include "PWMFan.h" 21 | #include "TemperatureController.h" 22 | 23 | #define FAN_CONTROL_MODE_FIXED_POWER 0 24 | #define FAN_CONTROL_MODE_FIXED_RPM 1 25 | #define FAN_CONTROL_MODE_CURVE 2 26 | 27 | struct FanData { 28 | uint8_t mode = FAN_CONTROL_MODE_FIXED_POWER; 29 | uint8_t power = 0; 30 | uint16_t speed = 0; 31 | FanDetectionType detectionType = FanDetectionType::Disconnected; 32 | uint8_t tempGroup; 33 | FanCurve fanCurve; 34 | }; 35 | 36 | /** 37 | * This simple Fan Controller implementation does not implement all features of a Fan Controller. 38 | * It should only demonstrate how to implement your own Fan Controller. 39 | */ 40 | class SimpleFanController : public FanController { 41 | public: 42 | /** 43 | * Fan Controller must use the EEPROM else on startup the fans can't be controlled 44 | * 45 | * @param temperatureController the TemperatureController used to get the temperature to control the fans 46 | * @param updateRate is the time between fan speed updates in ms 47 | * @param eEPROMAdress the address where the data is stored in EEPROM 48 | */ 49 | SimpleFanController(TemperatureController* temperatureController, uint16_t updateRate, uint16_t eEPROMAdress); 50 | /** 51 | * Add a fan to the Controller. 52 | * 53 | * @param index the index of the fan 54 | * @param fan the fan object 55 | */ 56 | void addFan(uint8_t index, PWMFan* fan); 57 | /** 58 | * Update the fan speeds based on the temperature and commands. 59 | */ 60 | virtual bool updateFans(); 61 | 62 | protected: 63 | virtual uint16_t getFanSpeed(uint8_t fan) override; 64 | virtual void setFanSpeed(uint8_t fan, uint16_t speed) override; 65 | virtual uint8_t getFanPower(uint8_t fan) override; 66 | virtual void setFanPower(uint8_t fan, uint8_t percentage) override; 67 | virtual void setFanCurve(uint8_t fan, uint8_t group, FanCurve& fanCurve) override; 68 | virtual void setFanExternalTemperature(uint8_t fan, uint16_t temp) override; 69 | virtual void setFanForce3PinMode(bool flag) override; 70 | virtual FanDetectionType getFanDetectionType(uint8_t fan) override; 71 | virtual void setFanDetectionType(uint8_t fan, FanDetectionType type) override; 72 | bool load(); 73 | bool save(); 74 | 75 | TemperatureController* const temperatureController; 76 | PWMFan* fans[FAN_NUM] = {nullptr}; 77 | bool force3PinMode = false; 78 | FanData fanData[FAN_NUM]; 79 | uint16_t externalTemp[FAN_NUM]; 80 | uint16_t updateRate; 81 | uint16_t eEPROMAdress; 82 | /** 83 | * Indicates that the configuration of the fans has been changed and should be saved. 84 | */ 85 | bool triggerSave = false; 86 | unsigned long lastUpdate = 0; 87 | }; 88 | -------------------------------------------------------------------------------- /examples/CommanderPRO/ThermistorTemperatureController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "ThermistorTemperatureController.h" 17 | 18 | #define B_COEFFICIENT 3950 19 | #define TEMPERATURE_REFERENCE (273.15 + 25) 20 | #define RESISTENCE_REFERENCE 10000 21 | #define RESISTENCE_DIVIDER 10000 22 | #define MAX_TEMP 150 23 | 24 | void ThermistorTemperatureController::addSensor(uint8_t index, uint8_t pin) { 25 | if (index < sizeof(sensorPins)) { 26 | pinMode(pin, INPUT); 27 | sensorPins[index] = pin; 28 | } 29 | } 30 | 31 | uint16_t ThermistorTemperatureController::getTemperatureValue(uint8_t temperatureSensor) { 32 | if (!isTemperatureSensorConnected(temperatureSensor)) { 33 | return 0; 34 | } 35 | int read = analogRead(sensorPins[temperatureSensor]); 36 | 37 | double resistence = (RESISTENCE_DIVIDER * (double)(1023 - read)) / read; 38 | 39 | double temp = resistence / RESISTENCE_REFERENCE; // (R/Ro) 40 | temp = log(temp); // ln(R/Ro) 41 | temp /= B_COEFFICIENT; // * (1/B) 42 | temp += 1.0 / TEMPERATURE_REFERENCE; // + (1/To) 43 | temp = 1.0 / temp; // invert 44 | temp -= 273.15; // convert to °C 45 | 46 | return constrain(temp, 0, MAX_TEMP) * 100; 47 | } 48 | 49 | bool ThermistorTemperatureController::isTemperatureSensorConnected(uint8_t temperatureSensor) { 50 | return sensorPins[temperatureSensor] != 0; 51 | } 52 | 53 | uint16_t ThermistorTemperatureController::getVoltageRail12V() { 54 | return 0; 55 | } 56 | 57 | uint16_t ThermistorTemperatureController::getVoltageRail5V() { 58 | return 0; 59 | } 60 | 61 | uint16_t ThermistorTemperatureController::getVoltageRail3V3() { 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /examples/CommanderPRO/ThermistorTemperatureController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "TemperatureController.h" 19 | /** 20 | * This TemperatureController uses Thermistors and Resistors to messure the temperature. It does not implement the 21 | * voltage rail measurements. 22 | * 23 | * Thermistor Schematic: 24 | *
25 |  *     | ---- [10k - Resistor] ---- | ---- [Thermistor] ---- |
26 |  *     |                            |                        |
27 |  *  [Ground]                    Analog Pin                 [+5v]
28 |  * 
29 | */ 30 | class ThermistorTemperatureController : public TemperatureController { 31 | public: 32 | /** 33 | * Add a Sensor to the TemperatureController using an Arduino analog pin connected as shown in {@link 34 | * ThermistorTemperatureController}. 35 | * 36 | * @param index the index of the sensorPins 37 | * @param pin the Arduino analog pin 38 | */ 39 | void addSensor(uint8_t index, uint8_t pin); 40 | 41 | protected: 42 | virtual uint16_t getTemperatureValue(uint8_t temperatureSensor) override; 43 | virtual bool isTemperatureSensorConnected(uint8_t temperatureSensor) override; 44 | virtual uint16_t getVoltageRail12V() override; 45 | virtual uint16_t getVoltageRail5V() override; 46 | virtual uint16_t getVoltageRail3V3() override; 47 | 48 | uint8_t sensorPins[TEMPERATURE_NUM] = {0}; 49 | }; 50 | -------------------------------------------------------------------------------- /examples/DebugSketch/DebugSketch.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define CHANNEL_LED_COUNT 96 20 | 21 | #define DATA_PIN_CHANNEL_1 2 22 | #define DATA_PIN_CHANNEL_2 3 23 | 24 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 25 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 26 | FastLEDControllerStorageEEPROM storage; 27 | FastLEDController ledController(&storage); 28 | CorsairLightingProtocolController cLP(&ledController, &firmware); 29 | CorsairLightingProtocolHID cLPS(&cLP); 30 | 31 | CRGB ledsChannel1[CHANNEL_LED_COUNT]; 32 | CRGB ledsChannel2[CHANNEL_LED_COUNT]; 33 | 34 | bool printLoop = PRINT_LOOP; 35 | bool printUpdate = PRINT_UPDATE; 36 | 37 | void setup() { 38 | Serial.begin(115200); 39 | Serial.setTimeout(100); 40 | FastLED.addLeds(ledsChannel1, CHANNEL_LED_COUNT); 41 | FastLED.addLeds(ledsChannel2, CHANNEL_LED_COUNT); 42 | ledController.addLEDs(0, ledsChannel1, CHANNEL_LED_COUNT); 43 | ledController.addLEDs(1, ledsChannel2, CHANNEL_LED_COUNT); 44 | } 45 | 46 | void loop() { 47 | if (printLoop) Serial.println(F("loop")); 48 | cLPS.update(); 49 | 50 | if (ledController.updateLEDs()) { 51 | if (printUpdate) Serial.println(F("updateLEDs")); 52 | FastLED.show(); 53 | CLP::printFps(5000); 54 | } 55 | 56 | if (Serial.available()) { 57 | static String cmd = ""; 58 | cmd = Serial.readStringUntil('\n'); 59 | processCommand(cmd); 60 | } 61 | } 62 | 63 | void processCommand(String& cmd) { 64 | if (cmd == F("print DeviceID")) { 65 | DeviceID deviceId; 66 | firmware.getDeviceID(deviceId); 67 | CLP::printDeviceID(deviceId); 68 | Serial.println(); 69 | } 70 | #ifdef VERBOSE 71 | else if (cmd == F("toggle command")) { 72 | printCommand = !printCommand; 73 | } else if (cmd == F("toggle response")) { 74 | printResponse = !printResponse; 75 | } 76 | #endif // VERBOSE 77 | else if (cmd == F("toggle loop")) { 78 | printLoop = !printLoop; 79 | } else if (cmd == F("toggle update")) { 80 | printUpdate = !printUpdate; 81 | } else { 82 | Serial.println(F("Unknown command")); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/DebugSketch/board.txt: -------------------------------------------------------------------------------- 1 | # CorsairLightingProtocol build property overrides 2 | 3 | build.vid=0x1b1c 4 | build.pid=0x0c0b 5 | build.usb_product="Lighting Node PRO" 6 | build.usb_manufacturer="Corsair" 7 | 8 | build.extra_flags={build.usb_flags} -DDEBUG -DVERBOSE -DPRINT_COMMAND=true -DPRINT_RESPONSE=true -DPRINT_LOOP=true -DPRINT_UPDATE=true 9 | -------------------------------------------------------------------------------- /examples/DeviceIDTool/DeviceIDTool.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | // 17 | // UPLOAD THIS TO THE ARDUINO AND OPEN SERIAL MONITOR WITH BOUDRATE 115200 18 | // 19 | #include 20 | #include 21 | 22 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 23 | 24 | void setup() { 25 | Serial.begin(115200); 26 | Serial.setTimeout(100); 27 | DeviceID deviceID; 28 | firmwareStorage.loadDeviceID(deviceID); 29 | 30 | while (!Serial) { 31 | ; // wait for serial port to connect. Needed for native USB 32 | } 33 | Serial.print(F("Current DeviceID: ")); 34 | CLP::printDeviceID(deviceID); 35 | Serial.println(); 36 | Serial.println( 37 | F("Set a new DeviceID by typing it in the Serial Monitor. The new DeviceID must be in same format as above. 4 " 38 | "Blocks with 2 digits separated by white space.")); 39 | Serial.println(); 40 | } 41 | 42 | void loop() { 43 | if (Serial.available()) { 44 | String inputString = Serial.readStringUntil('\n'); 45 | if (inputString.length() < 11) { 46 | Serial.println(F("Input is too short!")); 47 | Serial.println(F("Do not forget the leading zeroes!")); 48 | Serial.println(); 49 | } else { 50 | DeviceID newDeviceID; 51 | 52 | newDeviceID.data[0] = strtol(&inputString[0], nullptr, 16); 53 | newDeviceID.data[1] = strtol(&inputString[3], nullptr, 16); 54 | newDeviceID.data[2] = strtol(&inputString[6], nullptr, 16); 55 | newDeviceID.data[3] = strtol(&inputString[9], nullptr, 16); 56 | Serial.println(F("Set DeviceID to: ")); 57 | CLP::printDeviceID(newDeviceID); 58 | Serial.println(); 59 | if (CLP::isNullID(newDeviceID)) { 60 | Serial.println( 61 | F("This is a special DeviceID, it will generate a new random DeviceID next time iCUE controls the " 62 | "device!")); 63 | } 64 | if (CLP::isResetID(newDeviceID)) { 65 | Serial.println( 66 | F("This is a special DeviceID, it will reset the device and then generate a new DeviceID!")); 67 | } 68 | Serial.println(); 69 | firmwareStorage.saveDeviceID(newDeviceID); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /examples/HoodLoader2CLPBridge/CLPUSBSerialBridge.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "CLPUSBSerialBridge.h" 18 | 19 | #include 20 | #include 21 | 22 | void resetIOMCU() { 23 | digitalWrite(RESET_PIN, LOW); 24 | delay(10); 25 | digitalWrite(RESET_PIN, HIGH); 26 | #ifdef DEBUG 27 | Serial.println(F("R")); 28 | #endif // DEBUG 29 | } 30 | 31 | CLPUSBSerialBridge::CLPUSBSerialBridge(const char* serialNumber) { 32 | CLP::RawHID.setSerialNumber(serialNumber); 33 | } 34 | 35 | void CLPUSBSerialBridge::begin() { 36 | Serial1.begin(SERIAL_BAUD); 37 | CLP::RawHID.begin(rawHIDAndSerialBuffer, sizeof(rawHIDAndSerialBuffer)); 38 | } 39 | 40 | bool waitForSynchronization() { 41 | while (Serial1.available()) { 42 | Serial1.read(); 43 | } 44 | Serial1.setTimeout(SERIAL_SYNCHRONIZATION_TIMEOUT); 45 | byte value; 46 | size_t read = Serial1.readBytes(&value, 1); 47 | return read == 1 && value == 42; 48 | } 49 | 50 | void CLPUSBSerialBridge::sendError() { 51 | memset(rawHIDAndSerialBuffer, 0, RESPONSE_SIZE_16U2); 52 | rawHIDAndSerialBuffer[0] = PROTOCOL_RESPONSE_ERROR; 53 | sendResponse(); 54 | } 55 | 56 | void CLPUSBSerialBridge::sendResponse() { 57 | #ifdef DEBUG 58 | Serial.print(F("R")); 59 | Serial.println(rawHIDAndSerialBuffer[0], HEX); 60 | #endif // DEBUG 61 | CLP::RawHID.write(rawHIDAndSerialBuffer, RESPONSE_SIZE_16U2); 62 | // free the shared buffer to receive new data 63 | CLP::RawHID.enable(); 64 | } 65 | 66 | void CLPUSBSerialBridge::handleHID() { 67 | if (CLP::RawHID.available()) { 68 | #ifdef DEBUG 69 | Serial.print(F("C")); 70 | Serial.println(rawHIDAndSerialBuffer[0], HEX); 71 | unsigned long time = micros(); 72 | #endif // DEBUG 73 | if (!waitForSynchronization()) { 74 | #ifdef DEBUG 75 | Serial.println(F("S")); 76 | #endif // DEBUG 77 | sendError(); 78 | return; 79 | } 80 | 81 | Serial1.write(rawHIDAndSerialBuffer, COMMAND_SIZE); 82 | Serial1.setTimeout(SERIAL_RESPONSE_TIMEOUT); 83 | size_t read = Serial1.readBytes(rawHIDAndSerialBuffer, RESPONSE_SIZE); 84 | if (read != RESPONSE_SIZE) { 85 | #ifdef DEBUG 86 | Serial.print(F("T")); 87 | Serial.println(read); 88 | Serial.println(rawHIDAndSerialBuffer[0], HEX); 89 | #endif // DEBUG 90 | sendError(); 91 | return; 92 | } 93 | sendResponse(); 94 | 95 | #ifdef DEBUG 96 | unsigned long duration = micros() - time; 97 | Serial.print(F("D")); 98 | Serial.println(duration); 99 | #endif // DEBUG 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /examples/HoodLoader2CLPBridge/CLPUSBSerialBridge.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | #include "Arduino.h" 21 | 22 | #if (COMMAND_SIZE >= RESPONSE_SIZE) 23 | #define RAWHID_AND_SERIAL_BUFFER_SIZE COMMAND_SIZE 24 | #endif 25 | 26 | // Workaround for 16 byte responses don't work on 16U2 see https://github.com/Legion2/CorsairLightingProtocol/pull/152 27 | #define RESPONSE_SIZE_16U2 64 28 | 29 | #define SERIAL_SYNCHRONIZATION_TIMEOUT 20 30 | #define SERIAL_RESPONSE_TIMEOUT 10 31 | #define SERIAL_BAUD 1000000 32 | 33 | #define RESET_PIN IO_MCU_RESET_PIN 34 | 35 | class CLPUSBSerialBridge { 36 | public: 37 | /** 38 | * Create a new CLPUSBSerialBridge with the default Serial Number. 39 | */ 40 | CLPUSBSerialBridge(){}; 41 | /** 42 | * Create a new CLPUSBSerialBridge and set a Serial Number. 43 | * 44 | * @param serialNumber the Serial Number of the USB device 45 | */ 46 | CLPUSBSerialBridge(const char* serialNumber); 47 | /** 48 | * Setup the bridge connections. Must be called in the Arduino setup function. 49 | */ 50 | virtual void begin(); 51 | /** 52 | * Reads data from USB and sents it via Serial to the main MCU. Wait for the response and set it back to the USB 53 | * host. MUST be called in the Arduino loop function. 54 | */ 55 | virtual void handleHID(); 56 | 57 | private: 58 | byte rawHIDAndSerialBuffer[RAWHID_AND_SERIAL_BUFFER_SIZE]; 59 | 60 | void sendError(); 61 | void sendResponse(); 62 | }; 63 | -------------------------------------------------------------------------------- /examples/HoodLoader2CLPBridge/HoodLoader2CLPBridge.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CLPUSBSerialBridge.h" 17 | 18 | CLPUSBSerialBridge usbToSerialBridge; 19 | 20 | void setup() { 21 | #ifdef DEBUG 22 | Serial.begin(1000000); 23 | #endif // DEBUG 24 | usbToSerialBridge.begin(); 25 | } 26 | 27 | void loop() { 28 | usbToSerialBridge.handleHID(); 29 | } 30 | -------------------------------------------------------------------------------- /examples/HoodLoader2UnoMegaController/HoodLoader2UnoMegaController.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | // Hint: The Arduino Uno does not have as much memory as the Arduino Mega, it may be that problems occur when a higher 20 | // value is set here. 21 | #define CHANNEL_LED_COUNT 60 22 | 23 | #define DATA_PIN_CHANNEL_1 2 24 | #define DATA_PIN_CHANNEL_2 3 25 | 26 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 27 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 28 | FastLEDControllerStorageEEPROM storage; 29 | FastLEDController ledController(&storage); 30 | CorsairLightingProtocolController cLP(&ledController, &firmware); 31 | CorsairLightingProtocolSerial cLPS(&cLP); 32 | 33 | CRGB ledsChannel1[CHANNEL_LED_COUNT]; 34 | CRGB ledsChannel2[CHANNEL_LED_COUNT]; 35 | 36 | void setup() { 37 | /* 38 | YOU MUST NOT USE Serial! 39 | Serial is used by CorsairLightingProtocolSerial! 40 | */ 41 | cLPS.setup(); 42 | FastLED.addLeds(ledsChannel1, CHANNEL_LED_COUNT); 43 | FastLED.addLeds(ledsChannel2, CHANNEL_LED_COUNT); 44 | ledController.addLEDs(0, ledsChannel1, CHANNEL_LED_COUNT); 45 | ledController.addLEDs(1, ledsChannel2, CHANNEL_LED_COUNT); 46 | } 47 | 48 | void loop() { 49 | cLPS.update(); 50 | 51 | if (ledController.updateLEDs()) { 52 | FastLED.show(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/LS100/LS100.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | // Hint: The channels of the LS100 are swapped in iCUE, so the first channel in iCUE is here channel 2 20 | #define DATA_PIN_CHANNEL_1 2 21 | #define DATA_PIN_CHANNEL_2 3 22 | 23 | #define BUTTON_PIN 4 24 | 25 | // Hint: The ATmega32U4 does not have enough memory for 135 leds on both channels 26 | CRGB ledsChannel1[135]; 27 | CRGB ledsChannel2[54]; 28 | 29 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 30 | CorsairLightingFirmware firmware(CORSAIR_SMART_LIGHTING_CONTROLLER, &firmwareStorage); 31 | FastLEDControllerStorageEEPROM storage; 32 | FastLEDController ledController(&storage); 33 | CorsairLightingProtocolController cLP(&ledController, &firmware); 34 | CorsairLightingProtocolHID cHID(&cLP); 35 | 36 | void setup() { 37 | FastLED.addLeds(ledsChannel1, 135); 38 | FastLED.addLeds(ledsChannel2, 54); 39 | ledController.addLEDs(0, ledsChannel1, 135); 40 | ledController.addLEDs(1, ledsChannel2, 54); 41 | pinMode(BUTTON_PIN, INPUT_PULLUP); 42 | } 43 | 44 | void loop() { 45 | static bool lightingEnabled = true; 46 | cHID.update(); 47 | 48 | if (buttonClicked()) { 49 | lightingEnabled = !lightingEnabled; 50 | fill_solid(ledsChannel1, 135, CRGB::Black); 51 | fill_solid(ledsChannel2, 54, CRGB::Black); 52 | FastLED.show(); 53 | } 54 | 55 | if (lightingEnabled && ledController.updateLEDs()) { 56 | FastLED.show(); 57 | } 58 | } 59 | 60 | /** 61 | * Handle button of the LS100. The button is optional. 62 | * 63 | * @return true if the button was pressed and then released. 64 | */ 65 | bool buttonClicked() { 66 | static bool previousState = 1; 67 | bool state = digitalRead(BUTTON_PIN); 68 | if (previousState == 0 && state == 1) { 69 | previousState = state; 70 | return true; 71 | } 72 | previousState = state; 73 | return false; 74 | } 75 | -------------------------------------------------------------------------------- /examples/LT100/LT100.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define DATA_PIN_CHANNEL_1 2 20 | 21 | CRGB ledsChannel1[108]; 22 | 23 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 24 | CorsairLightingFirmware firmware(CORSAIR_SMART_LIGHTING_TOWERS, &firmwareStorage); 25 | FastLEDControllerStorageEEPROM storage; 26 | FastLEDController ledController(&storage); 27 | CorsairLightingProtocolController cLP(&ledController, &firmware); 28 | CorsairLightingProtocolHID cHID(&cLP); 29 | 30 | void setup() { 31 | FastLED.addLeds(ledsChannel1, 108); 32 | ledController.addLEDs(0, ledsChannel1, 108); 33 | } 34 | 35 | void loop() { 36 | cHID.update(); 37 | 38 | if (ledController.updateLEDs()) { 39 | FastLED.show(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/LightingNodeCORE/LightingNodeCORE.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define DATA_PIN_CHANNEL_1 2 20 | 21 | CRGB ledsChannel1[204]; 22 | 23 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 24 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_CORE, &firmwareStorage); 25 | FastLEDControllerStorageEEPROM storage; 26 | FastLEDController ledController(&storage); 27 | CorsairLightingProtocolController cLP(&ledController, &firmware); 28 | CorsairLightingProtocolHID cHID(&cLP); 29 | 30 | void setup() { 31 | FastLED.addLeds(ledsChannel1, 204); 32 | ledController.addLEDs(0, ledsChannel1, 204); 33 | } 34 | 35 | void loop() { 36 | cHID.update(); 37 | 38 | if (ledController.updateLEDs()) { 39 | FastLED.show(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/LightingNodePRO/LightingNodePRO.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define DATA_PIN_CHANNEL_1 2 20 | #define DATA_PIN_CHANNEL_2 3 21 | 22 | CRGB ledsChannel1[96]; 23 | CRGB ledsChannel2[96]; 24 | 25 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 26 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 27 | FastLEDControllerStorageEEPROM storage; 28 | FastLEDController ledController(&storage); 29 | CorsairLightingProtocolController cLP(&ledController, &firmware); 30 | CorsairLightingProtocolHID cHID(&cLP); 31 | 32 | void setup() { 33 | FastLED.addLeds(ledsChannel1, 96); 34 | FastLED.addLeds(ledsChannel2, 96); 35 | ledController.addLEDs(0, ledsChannel1, 96); 36 | ledController.addLEDs(1, ledsChannel2, 96); 37 | } 38 | 39 | void loop() { 40 | cHID.update(); 41 | 42 | if (ledController.updateLEDs()) { 43 | FastLED.show(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/MultipleFans/MultipleFans.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define NUMBER_OF_LEDS_PER_FAN 8 20 | 21 | #define DATA_PIN_FAN_1 2 22 | #define DATA_PIN_FAN_2 3 23 | #define DATA_PIN_FAN_3 4 24 | #define DATA_PIN_FAN_4 5 25 | #define DATA_PIN_FAN_5 6 26 | #define DATA_PIN_FAN_6 7 27 | #define DATA_PIN_CHANNEL_2 8 28 | 29 | CRGB ledsChannel1[96]; 30 | CRGB ledsChannel2[96]; 31 | 32 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 33 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 34 | FastLEDControllerStorageEEPROM storage; 35 | FastLEDController ledController(&storage); 36 | CorsairLightingProtocolController cLP(&ledController, &firmware); 37 | CorsairLightingProtocolHID cHID(&cLP); 38 | 39 | void setup() { 40 | // 6 fans on channel 1 41 | // FAN_PIN CRGB array offset number of leds per fan 42 | FastLED.addLeds(ledsChannel1, NUMBER_OF_LEDS_PER_FAN * 0, NUMBER_OF_LEDS_PER_FAN); 43 | FastLED.addLeds(ledsChannel1, NUMBER_OF_LEDS_PER_FAN * 1, NUMBER_OF_LEDS_PER_FAN); 44 | FastLED.addLeds(ledsChannel1, NUMBER_OF_LEDS_PER_FAN * 2, NUMBER_OF_LEDS_PER_FAN); 45 | FastLED.addLeds(ledsChannel1, NUMBER_OF_LEDS_PER_FAN * 3, NUMBER_OF_LEDS_PER_FAN); 46 | FastLED.addLeds(ledsChannel1, NUMBER_OF_LEDS_PER_FAN * 4, NUMBER_OF_LEDS_PER_FAN); 47 | FastLED.addLeds(ledsChannel1, NUMBER_OF_LEDS_PER_FAN * 5, NUMBER_OF_LEDS_PER_FAN); 48 | 49 | // normal strip on channel 2 50 | FastLED.addLeds(ledsChannel2, 96); 51 | ledController.addLEDs(0, ledsChannel1, 96); 52 | ledController.addLEDs(1, ledsChannel2, 96); 53 | } 54 | 55 | void loop() { 56 | cHID.update(); 57 | 58 | if (ledController.updateLEDs()) { 59 | FastLED.show(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/NoEEPROM/NoEEPROM.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define DATA_PIN_CHANNEL_1 2 20 | #define DATA_PIN_CHANNEL_2 3 21 | 22 | CRGB ledsChannel1[96]; 23 | CRGB ledsChannel2[96]; 24 | 25 | DeviceID deviceID = {0x9A, 0xDA, 0xA7, 0x8E}; 26 | CorsairLightingFirmwareStorageStatic firmwareStorage(deviceID); 27 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 28 | FastLEDController ledController(nullptr); 29 | CorsairLightingProtocolController cLP(&ledController, &firmware); 30 | CorsairLightingProtocolHID cHID(&cLP); 31 | 32 | void setup() { 33 | FastLED.addLeds(ledsChannel1, 96); 34 | FastLED.addLeds(ledsChannel2, 96); 35 | ledController.addLEDs(0, ledsChannel1, 96); 36 | ledController.addLEDs(1, ledsChannel2, 96); 37 | } 38 | 39 | void loop() { 40 | cHID.update(); 41 | 42 | if (ledController.updateLEDs()) { 43 | FastLED.show(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/NonAddressable/NonAddressable.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | // The Arduino pin where the physical LEDs are connected. 20 | // Must be PWM pins 21 | #define RED_PIN 3 22 | #define GREEN_PIN 5 23 | #define BLUE_PIN 6 24 | 25 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 26 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 27 | FastLEDControllerStorageEEPROM storage; 28 | FastLEDController ledController(&storage); 29 | CorsairLightingProtocolController cLP(&ledController, &firmware); 30 | CorsairLightingProtocolHID cHID(&cLP); 31 | 32 | CRGB ledsChannel1[10]; 33 | 34 | void setup() { 35 | pinMode(RED_PIN, OUTPUT); 36 | pinMode(GREEN_PIN, OUTPUT); 37 | pinMode(BLUE_PIN, OUTPUT); 38 | ledController.addLEDs(0, ledsChannel1, 10); 39 | ledController.onUpdateHook(0, []() { 40 | // use color of first LED of the first channel 41 | set4PinLEDs(ledsChannel1[0]); 42 | }); 43 | } 44 | 45 | void loop() { 46 | cHID.update(); 47 | ledController.updateLEDs(); 48 | } 49 | 50 | void set4PinLEDs(const CRGB& color) { 51 | analogWrite(RED_PIN, color.r); 52 | analogWrite(GREEN_PIN, color.g); 53 | analogWrite(BLUE_PIN, color.b); 54 | } 55 | -------------------------------------------------------------------------------- /examples/RepeatAndScale/RepeatAndScale.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define DATA_PIN_CHANNEL_1 2 20 | #define DATA_PIN_CHANNEL_2 3 21 | 22 | CRGB ledsChannel1[100]; 23 | CRGB ledsChannel2[144]; 24 | 25 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 26 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 27 | FastLEDControllerStorageEEPROM storage; 28 | FastLEDController ledController(&storage); 29 | CorsairLightingProtocolController cLP(&ledController, &firmware); 30 | CorsairLightingProtocolHID cHID(&cLP); 31 | 32 | void setup() { 33 | FastLED.addLeds(ledsChannel1, 100); 34 | FastLED.addLeds(ledsChannel2, 144); 35 | ledController.addLEDs(0, ledsChannel1, 50); 36 | ledController.addLEDs(1, ledsChannel2, 60); 37 | ledController.onUpdateHook(0, []() { 38 | CLP::repeat(&ledController, 0, 2); 39 | }); 40 | ledController.onUpdateHook(1, []() { 41 | CLP::scale(&ledController, 1, 144); 42 | }); 43 | } 44 | 45 | void loop() { 46 | cHID.update(); 47 | 48 | if (ledController.updateLEDs()) { 49 | FastLED.show(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/SingleStripLightingNodePRO/SingleStripLightingNodePRO.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | // The number of LEDs per channel. 20 | #define CHANNEL_LED_COUNT 50 21 | 22 | // Total count of LEDs on all channels, the value is calculated based on the LEDs per channel. 23 | #define NUM_LEDS (CHANNEL_LED_COUNT * 2) 24 | 25 | // The Arduino pin where the physical LEDs are connected. 26 | // You can use two pins, one for each channel. 27 | // In this example we use only one pin where both channel are connected in series. 28 | #define DATA_PIN 2 29 | 30 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 31 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 32 | FastLEDControllerStorageEEPROM storage; 33 | FastLEDController ledController(&storage); 34 | CorsairLightingProtocolController cLP(&ledController, &firmware); 35 | CorsairLightingProtocolHID cHID(&cLP); 36 | 37 | // This array conatins all RGB values for the LEDs of the both channels. 38 | CRGB leds[NUM_LEDS]; 39 | 40 | void setup() { 41 | #ifdef DEBUG 42 | Serial.begin(115200); 43 | #endif 44 | CLP::disableBuildInLEDs(); 45 | FastLED.addLeds(leds, NUM_LEDS); 46 | ledController.addLEDs(0, leds, CHANNEL_LED_COUNT); 47 | ledController.addLEDs(1, &(leds[CHANNEL_LED_COUNT]), CHANNEL_LED_COUNT); 48 | } 49 | 50 | void loop() { 51 | cHID.update(); 52 | 53 | if (ledController.updateLEDs()) { 54 | FastLED.show(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/TinyUSB/TinyUSB.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define DATA_PIN_CHANNEL_1 2 20 | #define DATA_PIN_CHANNEL_2 3 21 | 22 | CRGB ledsChannel1[96]; 23 | CRGB ledsChannel2[96]; 24 | 25 | // Most ARM devices do not contain an EEPROM; we will use static storage for the Device ID 26 | DeviceID deviceID = {0x9A, 0xDA, 0xA7, 0x8E}; 27 | CorsairLightingFirmwareStorageStatic firmwareStorage(deviceID); 28 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 29 | FastLEDController ledController(nullptr); 30 | CorsairLightingProtocolController cLP(&ledController, &firmware); 31 | CorsairLightingProtocolTinyUSBHID cHID(&cLP); 32 | 33 | void setup() { 34 | cHID.setup(); 35 | 36 | FastLED.addLeds(ledsChannel1, 96); 37 | FastLED.addLeds(ledsChannel2, 96); 38 | ledController.addLEDs(0, ledsChannel1, 96); 39 | ledController.addLEDs(1, ledsChannel2, 96); 40 | } 41 | 42 | void loop() { 43 | cHID.update(); 44 | 45 | if (ledController.updateLEDs()) { 46 | FastLED.show(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/TransformLLFansFormatToStrip/TransformLLFansFormatToStrip.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include 17 | #include 18 | 19 | #define DATA_PIN_CHANNEL_1 2 20 | #define DATA_PIN_CHANNEL_2 3 21 | 22 | CRGB ledsChannel1[96]; 23 | CRGB ledsChannel2[60]; 24 | 25 | CorsairLightingFirmwareStorageEEPROM firmwareStorage; 26 | CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); 27 | FastLEDControllerStorageEEPROM storage; 28 | FastLEDController ledController(&storage); 29 | CorsairLightingProtocolController cLP(&ledController, &firmware); 30 | CorsairLightingProtocolHID cHID(&cLP); 31 | 32 | void setup() { 33 | FastLED.addLeds(ledsChannel1, 96); 34 | FastLED.addLeds(ledsChannel2, 60); 35 | ledController.addLEDs(0, ledsChannel1, 96); 36 | ledController.addLEDs(1, ledsChannel2, 60); 37 | ledController.onUpdateHook(0, []() { 38 | CLP::transformLLFanToStrip(&ledController, 0); 39 | }); 40 | } 41 | 42 | void loop() { 43 | cHID.update(); 44 | 45 | if (ledController.updateLEDs()) { 46 | FastLED.show(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /examples/UnitTests/UnitTests.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #line 17 "UnitTests.ino" 17 | 18 | #include 19 | 20 | #include "FastLEDControllerUtils.h" 21 | 22 | using namespace aunit; 23 | 24 | class FastLEDControllerTest : public TestOnce { 25 | protected: 26 | void assertCRGB(const CRGB& actual, const CRGB& expected) { 27 | assertEqual(actual.r, expected.r); 28 | assertEqual(actual.g, expected.g); 29 | assertEqual(actual.b, expected.b); 30 | } 31 | void assertCRGBArray(const CRGB* const leds, int from, int to, const CRGB& expected) { 32 | for (int i = from; i <= to; i++) { 33 | assertCRGB(leds[i], expected); 34 | } 35 | } 36 | }; 37 | 38 | test(getLEDs) { 39 | CRGB leds[10]; 40 | FastLEDController ledController(nullptr); 41 | ledController.addLEDs(0, leds, 10); 42 | assertEqual(ledController.getLEDs(0), leds); 43 | assertEqual(ledController.getLEDs(1), nullptr); 44 | } 45 | 46 | testF(FastLEDControllerTest, simpleScaleUp) { 47 | CRGB leds[20]; 48 | FastLEDController ledController(nullptr); 49 | fill_solid(leds, 20, CRGB::Black); 50 | ledController.addLEDs(0, leds, 10); 51 | 52 | fill_solid(leds, 10, CRGB::White); 53 | CLP::scale(&ledController, 0, 20); 54 | 55 | assertCRGBArray(leds, 0, 19, CRGB::White); 56 | } 57 | 58 | testF(FastLEDControllerTest, simpleScaleDown) { 59 | CRGB leds[20]; 60 | FastLEDController ledController(nullptr); 61 | fill_solid(leds, 20, CRGB::Black); 62 | ledController.addLEDs(0, leds, 20); 63 | 64 | fill_solid(leds, 10, CRGB::White); 65 | CLP::scale(&ledController, 0, 10); 66 | 67 | assertCRGBArray(leds, 0, 4, CRGB::White); 68 | assertCRGBArray(leds, 5, 9, CRGB::Black); 69 | } 70 | 71 | testF(FastLEDControllerTest, simpleScaleDownBoundaries) { 72 | CRGB leds[20]; 73 | FastLEDController ledController(nullptr); 74 | fill_solid(leds, 20, CRGB::Black); 75 | ledController.addLEDs(0, leds, 20); 76 | 77 | leds[0] = CRGB::White; 78 | leds[19] = CRGB::Red; 79 | CLP::scale(&ledController, 0, 5); 80 | 81 | assertCRGBArray(leds, 0, 0, CRGB::White); 82 | assertCRGBArray(leds, 1, 3, CRGB::Black); 83 | assertCRGBArray(leds, 4, 4, CRGB::Red); 84 | } 85 | 86 | testF(FastLEDControllerTest, simpleScaleIdentity) { 87 | CRGB leds[20]; 88 | FastLEDController ledController(nullptr); 89 | fill_solid(leds, 20, CRGB::Black); 90 | ledController.addLEDs(0, leds, 10); 91 | 92 | fill_solid(leds, 10, CRGB::White); 93 | CLP::scale(&ledController, 0, 10); 94 | 95 | assertCRGBArray(leds, 0, 9, CRGB::White); 96 | assertCRGBArray(leds, 10, 19, CRGB::Black); 97 | } 98 | 99 | testF(FastLEDControllerTest, scaleLongStrip) { 100 | CRGB leds[41]; 101 | FastLEDController ledController(nullptr); 102 | fill_solid(leds, 41, CRGB::Black); 103 | ledController.addLEDs(0, leds, 27); 104 | 105 | fill_solid(leds, 27, CRGB::White); 106 | CLP::scale(&ledController, 0, 41); 107 | 108 | assertCRGBArray(leds, 0, 40, CRGB::White); 109 | } 110 | 111 | testF(FastLEDControllerTest, LT100) { 112 | CRGB leds[30]; 113 | FastLEDController ledController(nullptr); 114 | fill_solid(leds, 30, CRGB::Black); 115 | ledController.addLEDs(0, leds, 30); 116 | 117 | leds[0] = CRGB::White; 118 | fill_solid(leds + 1, 26, CRGB::Blue); 119 | CLP::SegmentScaling segments[2] = {{1, 4}, {26, 26}}; 120 | CLP::scaleSegments(&ledController, 0, segments, 2); 121 | 122 | assertCRGBArray(leds, 0, 3, CRGB::White); 123 | assertCRGBArray(leds, 4, 29, CRGB::Blue); 124 | } 125 | 126 | testF(FastLEDControllerTest, singleSegmentScaleUp) { 127 | CRGB leds[20]; 128 | FastLEDController ledController(nullptr); 129 | fill_solid(leds, 20, CRGB::Black); 130 | ledController.addLEDs(0, leds, 10); 131 | 132 | fill_solid(leds, 10, CRGB::White); 133 | CLP::SegmentScaling segments[] = {{10, 20}}; 134 | CLP::scaleSegments(&ledController, 0, segments, 1); 135 | 136 | assertCRGBArray(leds, 0, 19, CRGB::White); 137 | } 138 | 139 | testF(FastLEDControllerTest, multiScaleUp) { 140 | CRGB leds[30]; 141 | FastLEDController ledController(nullptr); 142 | fill_solid(leds, 30, CRGB::Black); 143 | ledController.addLEDs(0, leds, 10); 144 | 145 | fill_solid(leds + 5, 5, CRGB::White); 146 | CLP::SegmentScaling segments[] = {{5, 10}, {5, 20}}; 147 | CLP::scaleSegments(&ledController, 0, segments, 2); 148 | 149 | assertCRGBArray(leds, 0, 9, CRGB::Black); 150 | assertCRGBArray(leds, 10, 29, CRGB::White); 151 | } 152 | 153 | testF(FastLEDControllerTest, multiScaleDown) { 154 | CRGB leds[30]; 155 | FastLEDController ledController(nullptr); 156 | fill_solid(leds, 30, CRGB::Black); 157 | ledController.addLEDs(0, leds, 30); 158 | 159 | fill_solid(leds + 10, 20, CRGB::White); 160 | CLP::SegmentScaling segments[] = {{10, 5}, {20, 5}}; 161 | CLP::scaleSegments(&ledController, 0, segments, 2); 162 | 163 | assertCRGBArray(leds, 0, 4, CRGB::Black); 164 | assertCRGBArray(leds, 5, 9, CRGB::White); 165 | } 166 | 167 | testF(FastLEDControllerTest, singleSegmentScaleDown) { 168 | CRGB leds[20]; 169 | FastLEDController ledController(nullptr); 170 | fill_solid(leds, 20, CRGB::Black); 171 | ledController.addLEDs(0, leds, 20); 172 | 173 | fill_solid(leds, 10, CRGB::White); 174 | CLP::SegmentScaling segments[] = {{20, 10}}; 175 | CLP::scaleSegments(&ledController, 0, segments, 1); 176 | 177 | assertCRGBArray(leds, 0, 4, CRGB::White); 178 | assertCRGBArray(leds, 5, 9, CRGB::Black); 179 | } 180 | 181 | testF(FastLEDControllerTest, SegmentScaleOverlap) { 182 | CRGB leds[15]; 183 | FastLEDController ledController(nullptr); 184 | fill_solid(leds, 15, CRGB::Black); 185 | ledController.addLEDs(0, leds, 15); 186 | 187 | fill_solid(leds, 5, CRGB::White); 188 | CLP::SegmentScaling segments[] = {{5, 10}, {10, 5}}; 189 | CLP::scaleSegments(&ledController, 0, segments, 2); 190 | 191 | assertCRGBArray(leds, 0, 9, CRGB::White); 192 | assertCRGBArray(leds, 10, 14, CRGB::Black); 193 | } 194 | 195 | testF(FastLEDControllerTest, SegmentScaleOverlapInverted) { 196 | CRGB leds[15]; 197 | FastLEDController ledController(nullptr); 198 | fill_solid(leds, 15, CRGB::Black); 199 | ledController.addLEDs(0, leds, 15); 200 | 201 | fill_solid(leds, 10, CRGB::White); 202 | CLP::SegmentScaling segments[] = {{10, 5}, {5, 10}}; 203 | CLP::scaleSegments(&ledController, 0, segments, 2); 204 | 205 | assertCRGBArray(leds, 0, 4, CRGB::White); 206 | assertCRGBArray(leds, 5, 14, CRGB::Black); 207 | } 208 | 209 | testF(FastLEDControllerTest, SegmentScaleMix) { 210 | CRGB leds[30]; 211 | FastLEDController ledController(nullptr); 212 | fill_solid(leds, 30, CRGB::Black); 213 | ledController.addLEDs(0, leds, 30); 214 | 215 | fill_solid(leds, 5, CRGB::White); 216 | fill_solid(leds + 5, 20, CRGB::Red); 217 | fill_solid(leds + 25, 5, CRGB::Blue); 218 | CLP::SegmentScaling segments[] = {{5, 10}, {20, 5}, {5, 10}}; 219 | CLP::scaleSegments(&ledController, 0, segments, 3); 220 | 221 | assertCRGBArray(leds, 0, 9, CRGB::White); 222 | assertCRGBArray(leds, 10, 14, CRGB::Red); 223 | assertCRGBArray(leds, 15, 24, CRGB::Blue); 224 | } 225 | 226 | testF(FastLEDControllerTest, SegmentScaleMixInverted) { 227 | CRGB leds[30]; 228 | FastLEDController ledController(nullptr); 229 | fill_solid(leds, 30, CRGB::Black); 230 | ledController.addLEDs(0, leds, 25); 231 | 232 | fill_solid(leds, 10, CRGB::White); 233 | fill_solid(leds + 10, 5, CRGB::Red); 234 | fill_solid(leds + 15, 10, CRGB::Blue); 235 | CLP::SegmentScaling segments[] = {{10, 5}, {5, 20}, {10, 5}}; 236 | CLP::scaleSegments(&ledController, 0, segments, 3); 237 | 238 | assertCRGBArray(leds, 0, 4, CRGB::White); 239 | assertCRGBArray(leds, 5, 24, CRGB::Red); 240 | assertCRGBArray(leds, 25, 29, CRGB::Blue); 241 | } 242 | 243 | testF(FastLEDControllerTest, SegmentScaleMonitor) { 244 | CRGB leds[130]; 245 | FastLEDController ledController(nullptr); 246 | fill_solid(leds, 130, CRGB::Black); 247 | ledController.addLEDs(0, leds, 84); 248 | 249 | fill_solid(leds, 15, CRGB::White); 250 | fill_solid(leds + 15, 27, CRGB::Red); 251 | fill_solid(leds + 42, 15, CRGB::Blue); 252 | fill_solid(leds + 57, 27, CRGB::Green); 253 | CLP::SegmentScaling segments[] = {{15, 24}, {27, 41}, {15, 24}, {27, 41}}; 254 | CLP::scaleSegments(&ledController, 0, segments, 4); 255 | 256 | assertCRGBArray(leds, 0, 23, CRGB::White); 257 | assertCRGBArray(leds, 24, 64, CRGB::Red); 258 | assertCRGBArray(leds, 65, 88, CRGB::Blue); 259 | assertCRGBArray(leds, 89, 129, CRGB::Green); 260 | } 261 | 262 | testF(FastLEDControllerTest, SegmentScaleLongStrip) { 263 | CRGB leds[41]; 264 | FastLEDController ledController(nullptr); 265 | fill_solid(leds, 41, CRGB::Black); 266 | ledController.addLEDs(0, leds, 27); 267 | 268 | fill_solid(leds, 27, CRGB::White); 269 | CLP::SegmentScaling segments[] = {{27, 41}}; 270 | CLP::scaleSegments(&ledController, 0, segments, 1); 271 | 272 | assertCRGBArray(leds, 0, 40, CRGB::White); 273 | } 274 | 275 | void setup() { 276 | delay(1000); 277 | Serial.begin(115200); 278 | while (!Serial) 279 | ; 280 | } 281 | 282 | void loop() { 283 | TestRunner::run(); 284 | } 285 | -------------------------------------------------------------------------------- /examples/UnitTests2/UnitTests2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #line 17 "UnitTests2.ino" 17 | 18 | #include 19 | 20 | #include "FastLEDController.h" 21 | #include "FastLEDControllerUtils.h" 22 | 23 | using namespace aunit; 24 | 25 | class FastLEDControllerTest : public TestOnce { 26 | protected: 27 | void assertCRGB(const CRGB& actual, const CRGB& expected) { 28 | assertEqual(actual.r, expected.r); 29 | assertEqual(actual.g, expected.g); 30 | assertEqual(actual.b, expected.b); 31 | } 32 | void assertCRGBArray(const CRGB* const leds, int from, int to, const CRGB& expected) { 33 | for (int i = from; i <= to; i++) { 34 | assertCRGB(leds[i], expected); 35 | } 36 | } 37 | }; 38 | 39 | class TestFastLEDController : public FastLEDController { 40 | public: 41 | TestFastLEDController() : FastLEDController(nullptr) { 42 | } 43 | 44 | void setLastUpdate(unsigned long time) { 45 | lastUpdate = time; 46 | } 47 | 48 | void setCurrentUpdate(unsigned long time) { 49 | currentUpdate = time; 50 | } 51 | 52 | using FastLEDController::animation_step; 53 | using FastLEDController::animation_step_count; 54 | }; 55 | 56 | test(getLEDs) { 57 | CRGB leds[10]; 58 | FastLEDController ledController(nullptr); 59 | ledController.addLEDs(0, leds, 10); 60 | assertEqual(ledController.getLEDs(0), leds); 61 | assertEqual(ledController.getLEDs(1), nullptr); 62 | } 63 | 64 | testF(FastLEDControllerTest, simpleScaleUp) { 65 | CRGB leds[20]; 66 | FastLEDController ledController(nullptr); 67 | fill_solid(leds, 20, CRGB::Black); 68 | ledController.addLEDs(0, leds, 20); 69 | 70 | fill_solid(leds, 10, CRGB::White); 71 | CLP::reverse(&ledController, 0); 72 | 73 | assertCRGBArray(leds, 0, 9, CRGB::Black); 74 | assertCRGBArray(leds, 10, 19, CRGB::White); 75 | } 76 | 77 | test(animation_step) { 78 | TestFastLEDController ledController; 79 | ledController.setLastUpdate(155); 80 | ledController.setCurrentUpdate(165); 81 | assertEqual(ledController.animation_step(5, 100), 0); 82 | assertEqual(ledController.animation_step(10, 100), 50); 83 | assertEqual(ledController.animation_step(20, 100), 25); 84 | assertEqual(ledController.animation_step(40, 100), 12); 85 | assertEqual(ledController.animation_step(5000, 100), 3); 86 | } 87 | 88 | test(animation_step_long) { 89 | TestFastLEDController ledController; 90 | ledController.setLastUpdate(3000000000); 91 | ledController.setCurrentUpdate(300000005); 92 | assertEqual(ledController.animation_step(5, 100), 0); 93 | assertEqual(ledController.animation_step(10, 100), 50); 94 | assertEqual(ledController.animation_step(20, 100), 25); 95 | assertEqual(ledController.animation_step(40, 100), 12); 96 | assertEqual(ledController.animation_step(5000, 100), 0); 97 | } 98 | 99 | test(animation_step_count) { 100 | TestFastLEDController ledController; 101 | ledController.setLastUpdate(155); 102 | ledController.setCurrentUpdate(165); 103 | assertEqual(ledController.animation_step_count(5, 100), 200); 104 | assertEqual(ledController.animation_step_count(10, 100), 100); 105 | assertEqual(ledController.animation_step_count(20, 100), 50); 106 | assertEqual(ledController.animation_step_count(40, 100), 25); 107 | assertEqual(ledController.animation_step_count(5000, 100), 0); 108 | } 109 | 110 | test(animation_step_count_offset) { 111 | TestFastLEDController ledController; 112 | ledController.setLastUpdate(195); 113 | ledController.setCurrentUpdate(205); 114 | assertEqual(ledController.animation_step_count(10000, 100), 1); 115 | } 116 | 117 | test(animation_step_count_long) { 118 | TestFastLEDController ledController; 119 | ledController.setLastUpdate(3000000000); 120 | ledController.setCurrentUpdate(3000000010); 121 | assertEqual(ledController.animation_step_count(5, 100), 200); 122 | assertEqual(ledController.animation_step_count(10, 100), 100); 123 | assertEqual(ledController.animation_step_count(20, 100), 50); 124 | assertEqual(ledController.animation_step_count(40, 100), 25); 125 | assertEqual(ledController.animation_step_count(5000, 100), 0); 126 | } 127 | 128 | void setup() { 129 | delay(1000); 130 | Serial.begin(115200); 131 | while (!Serial) 132 | ; 133 | } 134 | 135 | void loop() { 136 | TestRunner::run(); 137 | } 138 | -------------------------------------------------------------------------------- /extra/images/OpenRGBBadge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/OpenRGBBadge.png -------------------------------------------------------------------------------- /extra/images/RGBSyncDarkBadge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/RGBSyncDarkBadge.png -------------------------------------------------------------------------------- /extra/images/SignalRGBBadge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/SignalRGBBadge.png -------------------------------------------------------------------------------- /extra/images/board-wiring-pico.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/board-wiring-pico.jpg -------------------------------------------------------------------------------- /extra/images/board-wiring.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/board-wiring.jpg -------------------------------------------------------------------------------- /extra/images/iCUE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/iCUE.jpg -------------------------------------------------------------------------------- /extra/images/iCUEDarkBadge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/iCUEDarkBadge.png -------------------------------------------------------------------------------- /extra/images/open-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/open-example.png -------------------------------------------------------------------------------- /extra/images/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/overview.png -------------------------------------------------------------------------------- /extra/images/select-board-pico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/select-board-pico.png -------------------------------------------------------------------------------- /extra/images/select-board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/select-board.png -------------------------------------------------------------------------------- /extra/images/upload-sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Legion2/CorsairLightingProtocol/ab045805303d5ae11fe7089fca9119dead7edf33/extra/images/upload-sketch.png -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Corsair Lighting Protocol 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | ILEDController KEYWORD1 10 | LEDController KEYWORD1 11 | FastLEDController KEYWORD1 12 | CorsairLightingProtocolController KEYWORD1 13 | CorsairLightingProtocolHID KEYWORD1 14 | CorsairLightingProtocolTinyUSBHID KEYWORD1 15 | CorsairLightingProtocolSerial KEYWORD1 16 | CorsairLightingProtocolResponse KEYWORD1 17 | CorsairLightingFirmware KEYWORD1 18 | IFanController KEYWORD1 19 | FanController KEYWORD1 20 | ITemperatureController KEYWORD1 21 | TemperatureController KEYWORD1 22 | 23 | ####################################### 24 | # Methods and Functions (KEYWORD2) 25 | ####################################### 26 | 27 | 28 | ####################################### 29 | # Instances (KEYWORD1) 30 | ####################################### 31 | 32 | 33 | ####################################### 34 | # Structures (KEYWORD3) 35 | ####################################### 36 | 37 | ChannelData KEYWORD3 38 | Command KEYWORD3 39 | FanCurve KEYWORD3 40 | LEDChannel KEYWORD3 41 | LEDGroup KEYWORD3 42 | SegmentScaling KEYWORD3 43 | 44 | ####################################### 45 | # Constants (LITERAL1) 46 | ####################################### 47 | 48 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Corsair Lighting Protocol 2 | version=0.15.0 3 | author=Leon Kiefer 4 | maintainer=Leon Kiefer 5 | sentence=Control LED strips via USB from a PC. 6 | paragraph=The library mimics Corsair LED Controller devices and can be controlled via USB in iCUE. 7 | category=Device Control 8 | url=https://github.com/Legion2/CorsairLightingProtocol 9 | architectures=avr,samd,rp2040 10 | includes=CorsairLightingProtocol.h 11 | depends=FastLED 12 | -------------------------------------------------------------------------------- /src/CLPAdditionalFeatures.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CLPAdditionalFeatures.h" 17 | 18 | #include "CLPUtils.h" 19 | 20 | bool CLP::shouldReset(const CorsairLightingFirmware* firmware) { 21 | DeviceID deviceId; 22 | firmware->getDeviceID(deviceId); 23 | return CLP::isResetID(deviceId); 24 | } 25 | 26 | void CLP::reset(CorsairLightingFirmware* firmware) { 27 | DeviceID deviceId = {0x00}; 28 | firmware->setDeviceID(deviceId); 29 | } 30 | -------------------------------------------------------------------------------- /src/CLPAdditionalFeatures.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "CorsairLightingFirmware.h" 20 | 21 | namespace CLP { 22 | /** 23 | * Check if the device should be reseted. The check is based on the DeviceID from the firmware. 24 | * 25 | * @param firmware the firmware used by this device 26 | * @return if the device should be reset 27 | */ 28 | bool shouldReset(const CorsairLightingFirmware* firmware); 29 | 30 | /** 31 | * Reset the DeviceID of the firmware. 32 | * 33 | * @param firmware reset this firmware 34 | */ 35 | void reset(CorsairLightingFirmware* firmware); 36 | } // namespace CLP 37 | -------------------------------------------------------------------------------- /src/CLPUtils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CLPUtils.h" 17 | 18 | uint16_t CLP::fromBigEndian(const byte& byte1, const byte& byte2) { 19 | uint16_t t = byte1; 20 | t = t << 8; 21 | t |= byte2; 22 | return t; 23 | } 24 | 25 | void CLP::disableBuildInLEDs() { 26 | #ifndef DEBUG 27 | #ifdef LED_BUILTIN_RX 28 | pinMode(LED_BUILTIN_RX, INPUT); 29 | #endif 30 | #ifdef LED_BUILTIN_TX 31 | pinMode(LED_BUILTIN_TX, INPUT); 32 | #endif 33 | #endif 34 | } 35 | 36 | bool CLP::isNullID(const DeviceID& deviceId) { 37 | return !(deviceId.data[0] | deviceId.data[1] | deviceId.data[2] | deviceId.data[3]); 38 | } 39 | 40 | bool CLP::isResetID(const DeviceID& deviceId) { 41 | return deviceId.data[0] == 0xFF && deviceId.data[1] == 0xFF && deviceId.data[2] == 0xFF && deviceId.data[3] == 0xFF; 42 | } 43 | 44 | void CLP::printDeviceID(const DeviceID& deviceId) { 45 | char tmp[16]; 46 | for (size_t i = 0; i < 4; i++) { 47 | sprintf(tmp, "%.2X", deviceId.data[i]); 48 | Serial.print(tmp); 49 | if (i < 3) Serial.print(F(" ")); 50 | } 51 | } 52 | 53 | void CLP::printFps(const int interval) { 54 | // Create static variables so that the code and variables can 55 | // all be declared inside a function 56 | static unsigned long lastMillis; 57 | static unsigned long frameCount; 58 | 59 | unsigned long now = millis(); 60 | frameCount++; 61 | if (now - lastMillis >= (unsigned int)interval) { 62 | double framesPerSecond = (frameCount * 1000.0) / interval; 63 | Serial.print(F("FPS: ")); 64 | Serial.println(framesPerSecond, 1); 65 | frameCount = 0; 66 | lastMillis = now; 67 | } 68 | } 69 | 70 | #if CLP_DEBUG 71 | #if defined(CLP_DEBUG_PORT) 72 | 73 | int CLP::printf(const char* __restrict format, ...) { 74 | char buf[64]; 75 | int len; 76 | va_list ap; 77 | va_start(ap, format); 78 | len = vsnprintf(buf, sizeof(buf), format, ap); 79 | CLP_DEBUG_PORT.write(buf); 80 | va_end(ap); 81 | return len; 82 | } 83 | 84 | int CLP::printf(const __FlashStringHelper* __restrict format, ...) { 85 | char buf[64]; 86 | char fmt[64]; 87 | int len; 88 | va_list ap; 89 | va_start(ap, format); 90 | strcpy_P(fmt, (const char*)format); 91 | len = vsnprintf(buf, sizeof(buf), fmt, ap); 92 | CLP_DEBUG_PORT.write(buf); 93 | va_end(ap); 94 | return len; 95 | } 96 | 97 | #endif // defined(CLP_DEBUG_PORT) 98 | 99 | void CLP::printData(uint8_t const* buf, uint32_t bufsize, bool address_table) { 100 | if (address_table) { 101 | clpPrintf("\t>>>> "); 102 | for (uint32_t i = 0; i < 16; i++) clpPrintf("0x%X ", i); 103 | clpPrintf("\r\n"); 104 | } 105 | for (uint32_t i = 0; i < bufsize; i++) { 106 | if (address_table && (i % 16 == 0)) clpPrintf("\t0x%02X ", (i / 16) << 4); 107 | clpPrintf(" %02X ", buf[i]); 108 | if ((i + 1) % 16 == 0 || (i + 1) == bufsize) clpPrintf("\r\n"); 109 | } 110 | } 111 | 112 | #endif // CLP_DEBUG 113 | -------------------------------------------------------------------------------- /src/CLPUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "CorsairLightingFirmware.h" 20 | 21 | #define toBigEndian(a) highByte(a), lowByte(a) 22 | 23 | #if CLP_DEBUG 24 | 25 | #define CLP_STRCAT(a, b) a##b 26 | #define CLP_STRCAT3(a, b, c) a##b##c 27 | 28 | #define CLP_XSTRCAT(a, b) CLP_STRCAT(a, b) 29 | #define CLP_XSTRCAT3(a, b, c) CLP_STRCAT3(a, b, c) 30 | 31 | #if defined(CLP_DEBUG_PORT) 32 | namespace CLP { 33 | extern int printf(const char* format, ...); 34 | extern int printf(const __FlashStringHelper* format, ...); 35 | } // namespace CLP 36 | #define clpPrintf CLP::printf 37 | #else 38 | #define clpPrintf printf 39 | #endif 40 | 41 | namespace CLP { 42 | static inline void printVar(uint8_t const* buf, uint32_t bufsize) { 43 | for (uint32_t i = 0; i < bufsize; i++) clpPrintf("%02X ", buf[i]); 44 | } 45 | extern void printData(uint8_t const* buf, uint32_t bufsize, bool address_table); 46 | } // namespace CLP 47 | 48 | /* clang-format off */ 49 | #define CLP_LOG(n, ...) CLP_XSTRCAT(CLP_LOG, n)(__VA_ARGS__) 50 | #define CLP_LOG_VAR(n, ...) CLP_XSTRCAT3(CLP_LOG, n, _VAR)(__VA_ARGS__) 51 | #define CLP_LOG_HEX(n, ...) CLP_XSTRCAT3(CLP_LOG, n, _HEX)(__VA_ARGS__) 52 | #define CLP_LOG_DAT(n, buf, size, at) CLP_XSTRCAT3(CLP_LOG, n, _DAT)(buf, size, at) 53 | #define CLP_LOG_LOCATION() clpPrintf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__) 54 | #define CLP_LOG_FAILED() clpPrintf("%s: %d: Failed\r\n", __PRETTY_FUNCTION__, __LINE__) 55 | #define CLP_LOG_FUNC(func) func 56 | 57 | // Log Level 1: Error 58 | #define CLP_LOG1 clpPrintf 59 | #define CLP_LOG1_VAR(_x) CLP::printVar((uint8_t const*)(_x), sizeof(*(_x))) 60 | #define CLP_LOG1_HEX(_x) clpPrintf(#_x " = %lX\r\n", (unsigned long) (_x) ) 61 | #define CLP_LOG1_DAT(_x, _y, _z) CLP::printData((uint8_t const*)(_x), _y, _z) 62 | 63 | // Log Level 2: Warn 64 | #if CLP_DEBUG >= 2 65 | #define CLP_LOG2 CLP_LOG1 66 | #define CLP_LOG2_VAR CLP_LOG1_VAR 67 | #define CLP_LOG2_HEX CLP_LOG1_HEX 68 | #define CLP_LOG2_DAT CLP_LOG1_DAT 69 | #endif 70 | 71 | // Log Level 3: Info 72 | #if CLP_DEBUG >= 3 73 | #define CLP_LOG3 CLP_LOG1 74 | #define CLP_LOG3_VAR CLP_LOG1_VAR 75 | #define CLP_LOG3_HEX CLP_LOG1_HEX 76 | #define CLP_LOG3_DAT CLP_LOG1_DAT 77 | #endif 78 | 79 | // Log Level 4: Data 80 | #if CLP_DEBUG >= 4 81 | #define CLP_LOG4 CLP_LOG1 82 | #define CLP_LOG4_VAR CLP_LOG1_VAR 83 | #define CLP_LOG4_HEX CLP_LOG1_HEX 84 | #define CLP_LOG4_DAT CLP_LOG1_DAT 85 | #endif 86 | /* clang-format on */ 87 | 88 | #endif // CLP_DEBUG 89 | 90 | #ifndef CLP_LOG 91 | #define CLP_LOG(n, ...) 92 | #define CLP_LOG_VAR(n, ...) 93 | #define CLP_LOG_HEX(n, ...) 94 | #define CLP_LOG_DAT(n, buf, size, at) 95 | #define CLP_LOG_FUNC(n) 96 | #endif 97 | 98 | #define CLP_LOG0(...) 99 | #define CLP_LOG0_VAR(...) 100 | #define CLP_LOG0_HEX(...) 101 | #define CLP_LOG0_DAT(...) 102 | 103 | #ifndef CLP_LOG1 104 | #define CLP_LOG1(...) 105 | #define CLP_LOG1_VAR(...) 106 | #define CLP_LOG1_HEX(...) 107 | #define CLP_LOG1_DAT(...) 108 | #endif 109 | 110 | #ifndef CLP_LOG2 111 | #define CLP_LOG2(...) 112 | #define CLP_LOG2_VAR(...) 113 | #define CLP_LOG2_HEX(...) 114 | #define CLP_LOG2_DAT(...) 115 | #endif 116 | 117 | #ifndef CLP_LOG3 118 | #define CLP_LOG3(...) 119 | #define CLP_LOG3_VAR(...) 120 | #define CLP_LOG3_HEX(...) 121 | #define CLP_LOG3_DAT(...) 122 | #endif 123 | 124 | #ifndef CLP_LOG4 125 | #define CLP_LOG4(...) 126 | #define CLP_LOG4_VAR(...) 127 | #define CLP_LOG4_HEX(...) 128 | #define CLP_LOG4_DAT(...) 129 | #endif 130 | 131 | namespace CLP { 132 | 133 | template 134 | void swap(T a, T b) { 135 | auto temp = *a; 136 | *a = *b; 137 | *b = temp; 138 | } 139 | 140 | template 141 | void reverse(T first, T last) { 142 | while ((first != last) && (first != --last)) { 143 | CLP::swap(first++, last); 144 | } 145 | } 146 | 147 | template 148 | void rotate(T first, T n_first, T last) { 149 | if (first == n_first) return; 150 | if (n_first == last) return; 151 | 152 | T read = n_first; 153 | T write = first; 154 | T next_read = first; 155 | 156 | while (read != last) { 157 | if (write == next_read) next_read = read; 158 | CLP::swap(read++, write++); 159 | } 160 | 161 | (rotate)(write, next_read, last); 162 | return; 163 | } 164 | 165 | uint16_t fromBigEndian(const byte& byte1, const byte& byte2); 166 | 167 | /** 168 | * Convert value from range 0-100 to 0-255 169 | * 170 | * @param value the value which should be converted 171 | */ 172 | inline uint8_t convert100To255(uint8_t value) { return value * 2.5546875f; } 173 | 174 | /** 175 | * Convert value from range 0-255 to 0-100. 176 | * 177 | * @param value the value which should be converted 178 | */ 179 | inline uint8_t convert255To100(uint8_t value) { return value / 2.5546875f; } 180 | 181 | /** 182 | * Check if a device id is the special null id (00 00 00 00). 183 | * 184 | * @param deviceId the device id to check 185 | */ 186 | bool isNullID(const DeviceID& deviceId); 187 | 188 | /** 189 | * Check if a device id is the special reset id (FF FF FF FF). 190 | * 191 | * @param deviceId the device id to check 192 | */ 193 | bool isResetID(const DeviceID& deviceId); 194 | 195 | /** 196 | * This will disable the RX and TX built in LEDs on Arduino Leonardo, Micro and Pro Micro. 197 | */ 198 | void disableBuildInLEDs(); 199 | 200 | /** 201 | * Print the given DeviceID to Serial 202 | * 203 | * @param deviceId the device id to print 204 | */ 205 | void printDeviceID(const DeviceID& deviceId); 206 | 207 | /* 208 | * Measure and print the framerate at the given interval in milliseconds. The higher this value the more precise the 209 | * result will be. This function should be called after FastLED.show() to count the FPS. 210 | * 211 | * @param interval the measurement interval in milliseconds 212 | */ 213 | void printFps(const int interval); 214 | } // namespace CLP 215 | -------------------------------------------------------------------------------- /src/CorsairLightingFirmware.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CorsairLightingFirmware.h" 17 | 18 | const uint8_t bootloader_version[] PROGMEM = {0x00, 0x02}; 19 | 20 | CorsairLightingFirmware::CorsairLightingFirmware(corsair_product_enum_t product, 21 | CorsairLightingFirmwareStorage* storage) 22 | : storage(storage), product(product) { 23 | storage->loadDeviceID(deviceId); 24 | } 25 | 26 | void CorsairLightingFirmware::handleFirmwareCommand(const Command& command, 27 | const CorsairLightingProtocolResponse* response) { 28 | switch (command.command) { 29 | case READ_STATUS: { 30 | uint8_t statusData[] = {status}; 31 | response->send(statusData, sizeof(statusData)); 32 | break; 33 | } 34 | case READ_FIRMWARE_VERSION: { 35 | response->send_P(firmwareVersions[product], FIRMWARE_VERSION_SIZE); 36 | break; 37 | } 38 | case READ_DEVICE_ID: { 39 | response->send(deviceId.data, sizeof(deviceId)); 40 | break; 41 | } 42 | case WRITE_DEVICE_ID: { 43 | DeviceID deviceID = {command.data[0], command.data[1], command.data[2], command.data[3]}; 44 | setDeviceID(deviceID); 45 | response->send(deviceId.data, sizeof(deviceId)); 46 | break; 47 | } 48 | case READ_BOOTLOADER_VERSION: { 49 | response->send_P(bootloader_version, sizeof(bootloader_version)); 50 | break; 51 | } 52 | default: { 53 | response->sendError(); 54 | } 55 | } 56 | } 57 | 58 | void CorsairLightingFirmware::getDeviceID(DeviceID& deviceID) const { deviceID = deviceId; } 59 | 60 | void CorsairLightingFirmware::setDeviceID(const DeviceID& deviceID) { 61 | deviceId = deviceID; 62 | storage->saveDeviceID(deviceID); 63 | } 64 | 65 | uint8_t CorsairLightingFirmware::getStatus() { return status; } 66 | 67 | void CorsairLightingFirmware::setStatus(uint8_t a_status) { status = a_status; } 68 | 69 | uint8_t CorsairLightingFirmware::getProduct() { return product; } 70 | -------------------------------------------------------------------------------- /src/CorsairLightingFirmware.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "CorsairLightingProtocolConstants.h" 20 | #include "CorsairLightingProtocolResponse.h" 21 | 22 | #define FIRMWARE_VERSION_SIZE 3 23 | 24 | const uint8_t corsairLightingNodePROFirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x00, 0x0A, 0x04}; 25 | 26 | #define corsairLightingNodeCOREFirmwareVersion corsairLightingNodePROFirmwareVersion 27 | 28 | #define corsairLS100FirmwareVersion corsairLightingNodePROFirmwareVersion 29 | 30 | const uint8_t corsairCommanderPROFirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x00, 0x09, 0xD4}; 31 | 32 | const uint8_t corsairLT100FirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x01, 0x01, 0x38}; 33 | 34 | const uint8_t corsairCommanderCOREFirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x02, 0x06, 0xC9}; 35 | 36 | const uint8_t* const firmwareVersions[] PROGMEM = { 37 | corsairLightingNodePROFirmwareVersion, corsairCommanderPROFirmwareVersion, corsairLightingNodeCOREFirmwareVersion, 38 | corsairLS100FirmwareVersion, corsairLT100FirmwareVersion, corsairCommanderCOREFirmwareVersion}; 39 | 40 | struct DeviceID { 41 | uint8_t data[4]; 42 | }; 43 | 44 | /** 45 | * Interface to store the device id 46 | */ 47 | class CorsairLightingFirmwareStorage { 48 | public: 49 | virtual void loadDeviceID(DeviceID& deviceID) const = 0; 50 | virtual void saveDeviceID(const DeviceID& deviceID) = 0; 51 | }; 52 | 53 | class CorsairLightingFirmware { 54 | public: 55 | CorsairLightingFirmware(corsair_product_enum_t product, CorsairLightingFirmwareStorage* storage); 56 | void handleFirmwareCommand(const Command& command, const CorsairLightingProtocolResponse* response); 57 | void getDeviceID(DeviceID& deviceID) const; 58 | void setDeviceID(const DeviceID& deviceID); 59 | uint8_t getStatus(); 60 | void setStatus(uint8_t status); 61 | uint8_t getProduct(); 62 | 63 | protected: 64 | CorsairLightingFirmwareStorage* const storage; 65 | const corsair_product_enum_t product; 66 | DeviceID deviceId; 67 | 68 | private: 69 | uint8_t status = PROTOCOL_STATUS_OK; 70 | }; 71 | -------------------------------------------------------------------------------- /src/CorsairLightingFirmwareStorageEEPROM.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CorsairLightingFirmwareStorageEEPROM.h" 17 | 18 | #if defined(ARDUINO_ARCH_AVR) 19 | 20 | #include 21 | 22 | void CorsairLightingFirmwareStorageEEPROM::loadDeviceID(DeviceID& deviceID) const { 23 | EEPROM.get(EEPROM_ADDRESS_DEVICE_ID, deviceID); 24 | } 25 | void CorsairLightingFirmwareStorageEEPROM::saveDeviceID(const DeviceID& deviceID) { 26 | EEPROM.put(EEPROM_ADDRESS_DEVICE_ID, deviceID); 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/CorsairLightingFirmwareStorageEEPROM.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #if defined(ARDUINO_ARCH_AVR) 19 | 20 | #include "CorsairLightingFirmware.h" 21 | 22 | #ifndef EEPROM_ADDRESS_DEVICE_ID 23 | #define EEPROM_ADDRESS_DEVICE_ID 0 24 | #endif 25 | 26 | class CorsairLightingFirmwareStorageEEPROM : public CorsairLightingFirmwareStorage { 27 | public: 28 | virtual void loadDeviceID(DeviceID& deviceID) const override; 29 | virtual void saveDeviceID(const DeviceID& deviceID) override; 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/CorsairLightingFirmwareStorageStatic.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CorsairLightingFirmwareStorageStatic.h" 17 | 18 | CorsairLightingFirmwareStorageStatic::CorsairLightingFirmwareStorageStatic(DeviceID& deviceID) : deviceID(deviceID) {} 19 | 20 | void CorsairLightingFirmwareStorageStatic::loadDeviceID(DeviceID& deviceID) const { deviceID = this->deviceID; } 21 | void CorsairLightingFirmwareStorageStatic::saveDeviceID(const DeviceID& deviceID) { 22 | // DeviceID can not be updated 23 | } 24 | -------------------------------------------------------------------------------- /src/CorsairLightingFirmwareStorageStatic.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "CorsairLightingFirmware.h" 19 | 20 | class CorsairLightingFirmwareStorageStatic : public CorsairLightingFirmwareStorage { 21 | private: 22 | const DeviceID deviceID; 23 | 24 | public: 25 | CorsairLightingFirmwareStorageStatic(DeviceID& deviceID); 26 | virtual void loadDeviceID(DeviceID& deviceID) const override; 27 | virtual void saveDeviceID(const DeviceID& deviceID) override; 28 | }; 29 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | /** 19 | * @file 20 | * The central include file for CorsairLightingProtocol. 21 | */ 22 | #include "CLPAdditionalFeatures.h" 23 | #include "CLPUtils.h" 24 | #include "CorsairLightingFirmware.h" 25 | #include "CorsairLightingFirmwareStorageEEPROM.h" 26 | #include "CorsairLightingFirmwareStorageStatic.h" 27 | #include "CorsairLightingProtocolConstants.h" 28 | #include "CorsairLightingProtocolController.h" 29 | #include "CorsairLightingProtocolHID.h" 30 | #include "CorsairLightingProtocolResponse.h" 31 | #include "CorsairLightingProtocolSerial.h" 32 | #include "CorsairLightingProtocolTinyUSBHID.h" 33 | #include "FanController.h" 34 | #include "FastLEDController.h" 35 | #include "FastLEDControllerStorage.h" 36 | #include "FastLEDControllerStorageEEPROM.h" 37 | #include "FastLEDControllerUtils.h" 38 | #include "IFanController.h" 39 | #include "ILEDController.h" 40 | #include "ITemperatureController.h" 41 | #include "LEDController.h" 42 | #include "TemperatureController.h" 43 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocolConstants.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | 20 | // CLP_DEBUG: 0=Off, 1=Error, 2=Warning, 3=Info, 4=Data 21 | // CLP_DEBUG_BAUD: Setting too low will severely affect performance depending on debug level; 22 | // 2000000 needed when CLP_DEBUG is 4 23 | #define CLP_DEBUG 0 24 | #define CLP_DEBUG_PORT Serial1 25 | #define CLP_DEBUG_BAUD 115200 26 | 27 | #define COMMAND_SIZE 64 28 | #define RESPONSE_SIZE 16 29 | 30 | #define READ_STATUS 0x01 31 | #define READ_FIRMWARE_VERSION 0x02 32 | #define READ_DEVICE_ID 0x03 33 | #define WRITE_DEVICE_ID 0x04 34 | #define START_FIRMWARE_UPDATE 0x05 35 | #define READ_BOOTLOADER_VERSION 0x06 36 | #define WRITE_TEST_FLAG 0x07 37 | 38 | #define READ_TEMPERATURE_MASK 0x10 39 | #define READ_TEMPERATURE_VALUE 0x11 40 | #define READ_VOLTAGE_VALUE 0x12 41 | 42 | #define READ_FAN_MASK 0x20 43 | #define READ_FAN_SPEED 0x21 44 | #define READ_FAN_POWER 0x22 45 | #define WRITE_FAN_POWER 0x23 46 | #define WRITE_FAN_SPEED 0x24 47 | #define WRITE_FAN_CURVE 0x25 48 | #define WRITE_FAN_EXTERNAL_TEMP 0x26 49 | #define WRITE_FAN_FORCE_THREE_PIN_MODE 0x27 50 | #define WRITE_FAN_DETECTION_TYPE 0x28 51 | #define READ_FAN_DETECTION_TYPE 0x29 52 | 53 | #define READ_LED_STRIP_MASK 0x30 54 | #define WRITE_LED_RGB_VALUE 0x31 55 | #define WRITE_LED_COLOR_VALUES 0x32 56 | #define WRITE_LED_TRIGGER 0x33 57 | #define WRITE_LED_CLEAR 0x34 58 | #define WRITE_LED_GROUP_SET 0x35 59 | #define WRITE_LED_EXTERNAL_TEMP 0x36 60 | #define WRITE_LED_GROUPS_CLEAR 0x37 61 | #define WRITE_LED_MODE 0x38 62 | #define WRITE_LED_BRIGHTNESS 0x39 63 | #define WRITE_LED_COUNT 0x3A 64 | #define WRITE_LED_PORT_TYPE 0x3B 65 | #define WRITE_LED_START_AUTODETECTION 0x3C 66 | #define READ_LED_AUTODETECTION_RESULTS 0x3D 67 | 68 | #define PROTOCOL_RESPONSE_OK 0x00 69 | #define PROTOCOL_RESPONSE_ERROR 0x01 70 | #define PROTOCOL_STATUS_OK 0x00 71 | #define PROTOCOL_STATUS_ERROR 0xFF 72 | 73 | #ifndef SERIAL_NUMBER 74 | #define SERIAL_NUMBER "FB66DF55421900F5" 75 | #endif 76 | 77 | #define CORSAIR_MANUFACTURER "Corsair" 78 | #define CORSAIR_VID 0x1B1C 79 | #define CORSAIR_LNP_PRODUCT "Lighting Node PRO" // Antigua 80 | #define CORSAIR_LNP_PID 0x0C0B 81 | #define CORSAIR_CP_PRODUCT "Commander PRO" // Barbuda 82 | #define CORSAIR_CP_PID 0x0C10 83 | #define CORSAIR_LNC_PRODUCT "Lighting Node CORE" // Kauai 84 | #define CORSAIR_LNC_PID 0x0C1A 85 | #define CORSAIR_SLC_PRODUCT "Smart Lighting Controller" // Borealis 86 | #define CORSAIR_SLC_PID 0x0C1E 87 | #define CORSAIR_SLT_PRODUCT "Smart Lighting Towers" // Lightsabers 88 | #define CORSAIR_SLT_PID 0x0C23 89 | #define CORSAIR_CC_PRODUCT "CORSAIR iCUE Commander CORE" 90 | #define CORSAIR_CC_PID 0x0C1C 91 | 92 | #define QL_FAN_LEDS 34 93 | #define LL_FAN_LEDS 16 94 | #define HD_FAN_LEDS 12 95 | #define EIGHT_LED_FAN_LEDS 8 96 | #define ML_PRO_FAN_LEDS 4 97 | 98 | #define LC100_LEDS 9 99 | #define LC100_FIRST_LED_OFFSET 3 100 | 101 | typedef enum { 102 | CORSAIR_LIGHTING_NODE_PRO = 0, 103 | CORSAIR_COMMANDER_PRO, 104 | CORSAIR_LIGHTING_NODE_CORE, 105 | CORSAIR_SMART_LIGHTING_CONTROLLER, 106 | CORSAIR_SMART_LIGHTING_TOWERS, 107 | CORSAIR_COMMANDER_CORE // Currently not functional 108 | } corsair_product_enum_t; 109 | 110 | struct Command { 111 | union { 112 | struct { 113 | uint8_t command; 114 | uint8_t data[COMMAND_SIZE - 1]; 115 | }; 116 | uint8_t raw[COMMAND_SIZE]; 117 | }; 118 | }; 119 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocolController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CorsairLightingProtocolController.h" 17 | 18 | #include "LEDController.h" 19 | 20 | CorsairLightingProtocolController::CorsairLightingProtocolController(ILEDController* aLEDController, 21 | CorsairLightingFirmware* corsairLightingFirmware) 22 | : corsairLightingFirmware(corsairLightingFirmware), 23 | ledController(aLEDController), 24 | temperatureController(nullptr), 25 | fanController(nullptr) {} 26 | 27 | CorsairLightingProtocolController::CorsairLightingProtocolController(ILEDController* aLEDController, 28 | ITemperatureController* temperatureController, 29 | IFanController* fanController, 30 | CorsairLightingFirmware* corsairLightingFirmware) 31 | : corsairLightingFirmware(corsairLightingFirmware), 32 | ledController(aLEDController), 33 | temperatureController(temperatureController), 34 | fanController(fanController) {} 35 | 36 | void CorsairLightingProtocolController::handleCommand(const Command& command, 37 | CorsairLightingProtocolResponse* response) { 38 | if (command.command < 0x10) { 39 | corsairLightingFirmware->handleFirmwareCommand(command, response); 40 | } else if (command.command >= 0x10 && command.command < 0x20) { 41 | if (temperatureController != nullptr) { 42 | temperatureController->handleTemperatureControl(command, response); 43 | } else { 44 | response->sendError(); 45 | } 46 | } else if (command.command >= 0x20 && command.command < 0x30) { 47 | if (fanController != nullptr) { 48 | fanController->handleFanControl(command, response); 49 | } else { 50 | response->sendError(); 51 | } 52 | } else if (command.command >= 0x30 && command.command < 0x40) { 53 | ledController->handleLEDControl(command, response); 54 | } else { 55 | response->sendError(); 56 | } 57 | } 58 | 59 | CorsairLightingFirmware* CorsairLightingProtocolController::getFirmware(void) { return corsairLightingFirmware; } -------------------------------------------------------------------------------- /src/CorsairLightingProtocolController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "CorsairLightingFirmware.h" 20 | #include "CorsairLightingProtocolConstants.h" 21 | #include "CorsairLightingProtocolResponse.h" 22 | #include "IFanController.h" 23 | #include "ILEDController.h" 24 | #include "ITemperatureController.h" 25 | 26 | /** 27 | * The central Controller which integrates all components. The main components of the CorsairLightingProtocolController 28 | * are the CorsairLightingFirmware and the LEDController. There can also be an optional TemperatureController and 29 | * FanController which are required if the device should be an Commander PRO. 30 | */ 31 | class CorsairLightingProtocolController { 32 | public: 33 | /** 34 | * The constructor used to create a Lighting only device. 35 | * 36 | * @param l The LEDController which should be used to control the LEDs of the created Lighting Node PRO 37 | * @param c The CorsairLightingFirmware used to handle Firmware related commands 38 | */ 39 | CorsairLightingProtocolController(ILEDController* l, CorsairLightingFirmware* c); 40 | /** 41 | * The constructor used to create a device with lighting, temperature and fan controller functionality (Commander 42 | * PRO). 43 | * 44 | * @param l The LEDController which should be used to control the LEDs of the created Commander PRO 45 | * @param t The TemperatureController which used to messure the temperature of the created Commander PRO 46 | * @param f The FanController used to control the fans of the created Commander PRO 47 | * @param c The CorsairLightingFirmware used to handle Firmware related commands 48 | */ 49 | CorsairLightingProtocolController(ILEDController* l, ITemperatureController* t, IFanController* f, 50 | CorsairLightingFirmware* c); 51 | /** 52 | * The only public function of the CorsairLightingProtocolController. It must be called to process a command which 53 | * was received from iCUE. This function is normally called by CorsairLightingProtocolHID and 54 | * CorsairLightingProtocolSerial adapters. 55 | * 56 | * @param command The command received from iCUE 57 | * @param response The response callback which can be called to response to the command 58 | */ 59 | void handleCommand(const Command& command, CorsairLightingProtocolResponse* response); 60 | 61 | CorsairLightingFirmware* getFirmware(void); 62 | 63 | private: 64 | CorsairLightingFirmware* const corsairLightingFirmware; 65 | ILEDController* const ledController; 66 | ITemperatureController* const temperatureController; 67 | IFanController* const fanController; 68 | }; 69 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocolHID.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CorsairLightingProtocolHID.h" 17 | 18 | #if defined(SUPPORT_RAW_HID) 19 | 20 | #if defined(DEBUG) && defined(VERBOSE) 21 | bool printCommand = PRINT_COMMAND; 22 | bool printResponse = PRINT_RESPONSE; 23 | #endif 24 | 25 | CorsairLightingProtocolHID::CorsairLightingProtocolHID(CorsairLightingProtocolController* controller) 26 | : controller(controller) { 27 | CLP::RawHID.begin(rawhidData, sizeof(rawhidData)); 28 | } 29 | 30 | CorsairLightingProtocolHID::CorsairLightingProtocolHID(CorsairLightingProtocolController* controller, 31 | const char* serialNumber) 32 | : CorsairLightingProtocolHID(controller) { 33 | CLP::RawHID.setSerialNumber(serialNumber); 34 | } 35 | 36 | void CorsairLightingProtocolHID::update() { 37 | if (available()) { 38 | Command command; 39 | getCommand(command); 40 | controller->handleCommand(command, this); 41 | } 42 | } 43 | 44 | bool CorsairLightingProtocolHID::available() const { return CLP::RawHID.available() > 0; } 45 | 46 | void CorsairLightingProtocolHID::getCommand(Command& command) { 47 | auto bytesAvailable = CLP::RawHID.available(); 48 | if (bytesAvailable) { 49 | CLP::RawHID.readBytes(command.raw, sizeof(command.raw)); 50 | #if defined(DEBUG) && defined(VERBOSE) 51 | if (printCommand) { 52 | Serial.print(F("Received Command: ")); 53 | Serial.print(command.command, HEX); 54 | Serial.print(" "); 55 | Serial.println(command.data[0], HEX); 56 | } 57 | #endif 58 | } 59 | } 60 | 61 | void CorsairLightingProtocolHID::sendX(const uint8_t* data, const size_t x) const { 62 | #if defined(DEBUG) && defined(VERBOSE) 63 | if (printResponse) { 64 | Serial.print(F("Send Response: ")); 65 | Serial.println(data[0], HEX); 66 | } 67 | #endif 68 | CLP::RawHID.write(data, x); 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocolHID.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "CorsairLightingProtocolConstants.h" 20 | #include "CorsairLightingProtocolController.h" 21 | #include "CorsairLightingProtocolResponse.h" 22 | #include "RawHID.h" 23 | 24 | #if defined(SUPPORT_RAW_HID) 25 | 26 | #if defined(DEBUG) && defined(VERBOSE) 27 | extern bool printCommand; 28 | extern bool printResponse; 29 | #endif 30 | 31 | /** 32 | * The HID Adapter for CorsairLightingProtocolController. This adapter uses the USB HID interface directly to mimic a 33 | * corsair device. This adapter can only be used when the USB interface is accessable by the sketch. 34 | */ 35 | class CorsairLightingProtocolHID : CorsairLightingProtocolResponse { 36 | public: 37 | /** 38 | * Create a new adapter for CorsairLightingProtocolController using the default Serial Number. 39 | * 40 | * @param controller the CorsairLightingProtocolController 41 | */ 42 | CorsairLightingProtocolHID(CorsairLightingProtocolController* controller); 43 | /** 44 | * Create a new adapter for using the given Serial Number for the usb interface. 45 | * 46 | * @param controller the CorsairLightingProtocolController 47 | * @param serialNumber the Serial Number used for the USB interface 48 | */ 49 | CorsairLightingProtocolHID(CorsairLightingProtocolController* controller, const char* serialNumber); 50 | /** 51 | * Read commands form HID interface and pass them to the contoller. This function must be called in loop. 52 | */ 53 | void update(); 54 | 55 | protected: 56 | uint8_t rawhidData[COMMAND_SIZE]; 57 | CorsairLightingProtocolController* const controller; 58 | 59 | bool available() const; 60 | void getCommand(Command& command); 61 | void sendX(const uint8_t* data, const size_t x) const override; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocolResponse.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CorsairLightingProtocolResponse.h" 17 | 18 | #include "CLPUtils.h" 19 | #include "CorsairLightingProtocolConstants.h" 20 | 21 | void CorsairLightingProtocolResponse::send(const uint8_t* data, size_t size) const { 22 | uint8_t response[RESPONSE_SIZE] = {0x00}; 23 | if (size + 1 > sizeof(response)) { 24 | return; 25 | } 26 | response[0] = PROTOCOL_RESPONSE_OK; 27 | memcpy(response + 1, data, size); 28 | sendX(response, sizeof(response)); 29 | CLP_LOG(3, F("Sent response %02X: OK\r\n"), response[0]); 30 | } 31 | 32 | void CorsairLightingProtocolResponse::sendError() const { 33 | uint8_t response[RESPONSE_SIZE] = {0x00}; 34 | response[0] = PROTOCOL_RESPONSE_ERROR; 35 | sendX(response, sizeof(response)); 36 | CLP_LOG(3, F("Sent response %02X: Error\r\n"), response[0]); 37 | } 38 | 39 | void CorsairLightingProtocolResponse::send_P(const uint8_t* data, size_t size) const { 40 | uint8_t response[RESPONSE_SIZE] = {0x00}; 41 | if (size + 1 > sizeof(response)) { 42 | return; 43 | } 44 | response[0] = PROTOCOL_RESPONSE_OK; 45 | memcpy_P(response + 1, data, size); 46 | sendX(response, sizeof(response)); 47 | CLP_LOG(3, F("Sent response %02X: OK\r\n"), response[0]); 48 | } 49 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocolResponse.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | 20 | class CorsairLightingProtocolResponse { 21 | public: 22 | /** 23 | * Send 64 bytes via the CorsairLightingProtocol. All unset bytes will be filled with zeros. 24 | * 25 | * @param data the array with the data 26 | * @param size the length of the array 27 | */ 28 | virtual void send(const uint8_t* data, size_t size) const; 29 | /** 30 | * Send an error. 31 | */ 32 | virtual void sendError() const; 33 | /** 34 | * Send data from program memory. 35 | * 36 | * @param data the array with the data, the pointer must point to program memory 37 | * @param size the length of the array which should be send. 38 | */ 39 | virtual void send_P(const uint8_t* data, size_t size) const; 40 | /** 41 | * Send some bytes data via the CorsairLightingProtocol. 42 | * 43 | * @param data the array with the data 44 | * @param x the length of the array which should be send. 45 | */ 46 | virtual void sendX(const uint8_t* data, const size_t x) const = 0; 47 | }; 48 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocolSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "CorsairLightingProtocolSerial.h" 17 | 18 | CorsairLightingProtocolSerial::CorsairLightingProtocolSerial(CorsairLightingProtocolController* controller) 19 | : controller(controller) {} 20 | 21 | void CorsairLightingProtocolSerial::setup() { 22 | Serial.begin(SERIAL_BAUD); 23 | Serial.setTimeout(SERIAL_TIMEOUT); 24 | } 25 | 26 | void CorsairLightingProtocolSerial::update() { 27 | bool available = handleSerial(); 28 | if (available) { 29 | Command command; 30 | memcpy(command.raw, rawCommand, sizeof(command.raw)); 31 | controller->handleCommand(command, this); 32 | } 33 | } 34 | 35 | bool CorsairLightingProtocolSerial::handleSerial() { 36 | if (Serial.available()) { 37 | delay(SERIAL_TIMEOUT); 38 | while (Serial.available()) { 39 | Serial.read(); 40 | } 41 | } 42 | 43 | Serial.write(42); 44 | Serial.flush(); 45 | delayMicroseconds(100); 46 | 47 | if (Serial.available()) { 48 | size_t read = Serial.readBytes((char*)rawCommand, sizeof(rawCommand)); 49 | if (read == sizeof(rawCommand)) { 50 | return true; 51 | } else { 52 | byte data[] = {PROTOCOL_RESPONSE_ERROR, (byte)read}; 53 | sendX(data, sizeof(data)); 54 | } 55 | } 56 | return false; 57 | } 58 | 59 | void CorsairLightingProtocolSerial::sendX(const uint8_t* data, const size_t x) const { Serial.write(data, x); } 60 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocolSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "CorsairLightingProtocolConstants.h" 19 | #include "CorsairLightingProtocolController.h" 20 | #include "CorsairLightingProtocolResponse.h" 21 | 22 | // The maximum time in milliseconds needed to receive 64-byte data 23 | #define SERIAL_TIMEOUT 2 24 | #define SERIAL_BAUD 1000000 25 | 26 | /** 27 | * The Serial adapter for CorsairLightingProtocolController. This adapter uses a Serial bridge to access 28 | * the USB interface of another Arduino board. This is usefull for boards that don't have the USB 29 | * functionality build in. 30 | */ 31 | class CorsairLightingProtocolSerial : CorsairLightingProtocolResponse { 32 | public: 33 | /** 34 | * Create a new adapter for the controller. 35 | * 36 | * @param controller the CorsairLightingProtocolController 37 | */ 38 | CorsairLightingProtocolSerial(CorsairLightingProtocolController* controller); 39 | /** 40 | * Setup the Serial connection. 41 | * This function must be called in setup. 42 | */ 43 | void setup(); 44 | /** 45 | * Read commands from Serial connection and pass them to the controller 46 | * This function must be called in loop. 47 | */ 48 | void update(); 49 | 50 | private: 51 | byte rawCommand[COMMAND_SIZE]; 52 | CorsairLightingProtocolController* const controller; 53 | 54 | bool handleSerial(); 55 | void sendX(const uint8_t* data, const size_t x) const override; 56 | }; 57 | -------------------------------------------------------------------------------- /src/CorsairLightingProtocolTinyUSBHID.cpp: -------------------------------------------------------------------------------- 1 | #include "CorsairLightingProtocolTinyUSBHID.h" 2 | 3 | #if defined(USE_TINYUSB) 4 | 5 | const char* corsairProducts[] = {CORSAIR_LNP_PRODUCT, CORSAIR_CP_PRODUCT, CORSAIR_LNC_PRODUCT, 6 | CORSAIR_SLC_PRODUCT, CORSAIR_SLT_PRODUCT, CORSAIR_CC_PRODUCT}; 7 | const int corsairPIDs[] = {CORSAIR_LNP_PID, CORSAIR_CP_PID, CORSAIR_LNC_PID, 8 | CORSAIR_SLC_PID, CORSAIR_SLT_PID, CORSAIR_CC_PID}; 9 | 10 | Command CorsairLightingProtocolTinyUSBHID::command; 11 | int CorsairLightingProtocolTinyUSBHID::newData; 12 | 13 | /* clang-format off */ 14 | uint8_t const hid_report[] = {HID_USAGE_PAGE_N (HID_USAGE_PAGE_VENDOR | 0xC0, 2), 15 | HID_USAGE_N (0x0C00, 2), 16 | HID_COLLECTION (HID_COLLECTION_APPLICATION ), 17 | HID_REPORT_SIZE (8 ), 18 | HID_LOGICAL_MIN (0x00 ), 19 | HID_LOGICAL_MAX_N(0xff, 2), 20 | HID_REPORT_COUNT (RESPONSE_SIZE ), 21 | HID_USAGE (0x01 ), 22 | HID_INPUT (HID_DATA | HID_VARIABLE | HID_ABSOLUTE), 23 | HID_REPORT_COUNT (COMMAND_SIZE ), 24 | HID_USAGE (0x02 ), 25 | HID_OUTPUT (HID_DATA | HID_VARIABLE | HID_ABSOLUTE), 26 | HID_COLLECTION_END}; 27 | /* clang-format on */ 28 | 29 | Adafruit_USBD_HID tudHid(hid_report, sizeof(hid_report), HID_ITF_PROTOCOL_NONE, 1, true); 30 | 31 | uint16_t get_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { 32 | (void)report_id; 33 | (void)report_type; 34 | (void)buffer; 35 | (void)reqlen; 36 | 37 | CLP_LOG(2, F("Get report callback!\r\n")); 38 | 39 | return 0; 40 | } 41 | 42 | void set_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { 43 | (void)report_id; 44 | (void)report_type; 45 | 46 | if (bufsize <= sizeof(Command)) { 47 | memcpy(&CorsairLightingProtocolTinyUSBHID::command.raw, buffer, bufsize); 48 | CorsairLightingProtocolTinyUSBHID::newData = 1; 49 | CLP_LOG(4, F("Data received:\r\n")); 50 | CLP_LOG_DAT(4, &CorsairLightingProtocolTinyUSBHID::command.raw, 51 | sizeof(CorsairLightingProtocolTinyUSBHID::command), true); 52 | } else { 53 | CLP_LOG(2, F("Command too large\r\n")); 54 | } 55 | } 56 | 57 | CorsairLightingProtocolTinyUSBHID::CorsairLightingProtocolTinyUSBHID(CorsairLightingProtocolController* controller) 58 | : controller(controller), serialNumber(SERIAL_NUMBER) {} 59 | 60 | CorsairLightingProtocolTinyUSBHID::CorsairLightingProtocolTinyUSBHID(CorsairLightingProtocolController* controller, 61 | const char* serialNumber) 62 | : controller(controller), serialNumber(serialNumber) {} 63 | 64 | void CorsairLightingProtocolTinyUSBHID::setup(void) { 65 | CLP_LOG_FUNC(CLP_DEBUG_PORT.begin(CLP_DEBUG_BAUD)); 66 | 67 | TinyUSBDevice.setManufacturerDescriptor(CORSAIR_MANUFACTURER); 68 | TinyUSBDevice.setProductDescriptor(corsairProducts[controller->getFirmware()->getProduct()]); 69 | TinyUSBDevice.setID(CORSAIR_VID, corsairPIDs[controller->getFirmware()->getProduct()]); 70 | TinyUSBDevice.setSerialDescriptor(serialNumber); 71 | 72 | tudHid.setReportCallback(get_report_callback, set_report_callback); 73 | tudHid.begin(); 74 | 75 | while (!TinyUSBDevice.mounted()) delay(1); 76 | } 77 | 78 | void CorsairLightingProtocolTinyUSBHID::update(void) { 79 | if (newData) { 80 | controller->handleCommand(command, this); 81 | newData = 0; 82 | } 83 | } 84 | 85 | void CorsairLightingProtocolTinyUSBHID::sendX(const uint8_t* data, const size_t x) const { 86 | tudHid.sendReport(0, data, x); 87 | CLP_LOG(4, F("Data sent:\r\n")); 88 | CLP_LOG_DAT(4, data, x, true); 89 | } 90 | 91 | #endif -------------------------------------------------------------------------------- /src/CorsairLightingProtocolTinyUSBHID.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CLPUtils.h" 4 | #include "CorsairLightingProtocolConstants.h" 5 | #include "CorsairLightingProtocolController.h" 6 | #include "CorsairLightingProtocolResponse.h" 7 | 8 | #if defined(USE_TINYUSB) 9 | 10 | #include "Adafruit_TinyUSB.h" 11 | 12 | class CorsairLightingProtocolTinyUSBHID : CorsairLightingProtocolResponse { 13 | public: 14 | /** 15 | * Create a new adapter for CorsairLightingProtocolController using the default Serial Number. 16 | * 17 | * @param controller the CorsairLightingProtocolController 18 | */ 19 | CorsairLightingProtocolTinyUSBHID(CorsairLightingProtocolController* controller); 20 | /** 21 | * Create a new adapter for CorsairLightingProtocolController using the given Serial Number for the usb interface. 22 | * 23 | * @param controller the CorsairLightingProtocolController 24 | * @param serialNumber the Serial Number used for the USB interface 25 | */ 26 | CorsairLightingProtocolTinyUSBHID(CorsairLightingProtocolController* controller, const char* serialNumber); 27 | /** 28 | * Setup the TinyUSB HID connection. 29 | * This function must be called in setup. 30 | */ 31 | void setup(void); 32 | /** 33 | * Read commands form HID interface and pass them to the contoller. This function must be called in loop. 34 | */ 35 | void update(void); 36 | 37 | static Command command; 38 | 39 | static int newData; 40 | 41 | protected: 42 | CorsairLightingProtocolController* const controller; 43 | const char* serialNumber; 44 | void sendX(const uint8_t* data, const size_t x) const override; 45 | }; 46 | 47 | #endif -------------------------------------------------------------------------------- /src/FanController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "FanController.h" 17 | 18 | #include "CLPUtils.h" 19 | 20 | bool isValidFanMask(const FanMask fanMask) { 21 | return fanMask == FanMask::Disconnected || fanMask == FanMask::ThreePin || fanMask == FanMask::FourPin; 22 | } 23 | 24 | bool isValidFanDetectionType(const FanDetectionType type) { 25 | return type == FanDetectionType::Auto || type == FanDetectionType::ThreePin || type == FanDetectionType::FourPin || 26 | type == FanDetectionType::Disconnected; 27 | } 28 | 29 | void FanController::handleFanControl(const Command& command, const CorsairLightingProtocolResponse* response) { 30 | CLP_LOG(3, F("Received fan command %02X: "), command.command); 31 | switch (command.command) { 32 | case READ_FAN_MASK: { 33 | CLP_LOG(3, F("Read mask\r\n")); 34 | FanMask mask[FAN_NUM]; 35 | for (uint8_t i = 0; i < FAN_NUM; i++) { 36 | switch (getFanDetectionType(i)) { 37 | case FanDetectionType::Auto: 38 | mask[i] = FanMask::Disconnected; // TODO 39 | break; 40 | case FanDetectionType::ThreePin: 41 | mask[i] = FanMask::ThreePin; 42 | break; 43 | case FanDetectionType::FourPin: 44 | mask[i] = FanMask::FourPin; 45 | break; 46 | case FanDetectionType::Disconnected: 47 | mask[i] = FanMask::Disconnected; 48 | break; 49 | } 50 | } 51 | response->send(reinterpret_cast(mask), sizeof(mask)); 52 | break; 53 | } 54 | case READ_FAN_SPEED: { 55 | CLP_LOG(3, F("Read speed\r\n")); 56 | const uint8_t& fan = command.data[0]; 57 | if (fan >= FAN_NUM) { 58 | CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); 59 | response->sendError(); 60 | return; 61 | } 62 | uint16_t speed = getFanSpeed(fan); 63 | uint8_t fanData[] = {toBigEndian(speed)}; 64 | response->send(fanData, sizeof(fanData)); 65 | break; 66 | } 67 | case READ_FAN_POWER: { 68 | CLP_LOG(3, F("Read power\r\n")); 69 | const uint8_t& fan = command.data[0]; 70 | if (fan >= FAN_NUM) { 71 | CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); 72 | response->sendError(); 73 | return; 74 | } 75 | uint8_t power = getFanPower(fan); 76 | uint8_t powerData[] = {CLP::convert255To100(power)}; 77 | response->send(powerData, sizeof(powerData)); 78 | break; 79 | } 80 | case WRITE_FAN_POWER: { 81 | CLP_LOG(3, F("Write power\r\n")); 82 | const uint8_t& fan = command.data[0]; 83 | if (fan >= FAN_NUM) { 84 | CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); 85 | response->sendError(); 86 | return; 87 | } 88 | uint8_t percentage = CLP::convert100To255(command.data[1]); 89 | setFanPower(fan, percentage); 90 | response->send(nullptr, 0); 91 | break; 92 | } 93 | case WRITE_FAN_SPEED: { 94 | CLP_LOG(3, F("Write speed\r\n")); 95 | const uint8_t& fan = command.data[0]; 96 | if (fan >= FAN_NUM) { 97 | CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); 98 | response->sendError(); 99 | return; 100 | } 101 | uint16_t speed = CLP::fromBigEndian(command.data[1], command.data[2]); 102 | setFanSpeed(fan, speed); 103 | response->send(nullptr, 0); 104 | break; 105 | } 106 | case WRITE_FAN_CURVE: { 107 | CLP_LOG(3, F("Write curve\r\n")); 108 | const uint8_t& fan = command.data[0]; 109 | if (fan >= FAN_NUM) { 110 | CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); 111 | response->sendError(); 112 | return; 113 | } 114 | const uint8_t& group = command.data[1]; 115 | FanCurve fanCurve; 116 | for (uint8_t i = 0; i < FAN_CURVE_POINTS_NUM; i++) { 117 | fanCurve.temperatures[i] = CLP::fromBigEndian(command.data[2 + i * 2], command.data[3 + i * 2]); 118 | fanCurve.rpms[i] = CLP::fromBigEndian(command.data[14 + i * 2], command.data[15 + i * 2]); 119 | } 120 | setFanCurve(fan, group, fanCurve); 121 | response->send(nullptr, 0); 122 | break; 123 | } 124 | case WRITE_FAN_EXTERNAL_TEMP: { 125 | CLP_LOG(3, F("Write external temp\r\n")); 126 | const uint8_t& fan = command.data[0]; 127 | if (fan >= FAN_NUM) { 128 | CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); 129 | response->sendError(); 130 | return; 131 | } 132 | uint16_t temp = CLP::fromBigEndian(command.data[1], command.data[2]); 133 | setFanExternalTemperature(fan, temp); 134 | response->send(nullptr, 0); 135 | break; 136 | } 137 | case WRITE_FAN_FORCE_THREE_PIN_MODE: { 138 | CLP_LOG(3, F("Write 3-pin mode\r\n")); 139 | if (command.data[0] == FAN_FORCE_THREE_PIN_MODE_ON) { 140 | setFanForce3PinMode(true); 141 | } else if (command.data[0] == FAN_FORCE_THREE_PIN_MODE_OFF) { 142 | setFanForce3PinMode(false); 143 | } else { 144 | response->sendError(); 145 | return; 146 | } 147 | 148 | response->send(nullptr, 0); 149 | break; 150 | } 151 | case WRITE_FAN_DETECTION_TYPE: { 152 | CLP_LOG(3, F("Write detect type\r\n")); 153 | if (command.data[0] != 0x02) { 154 | response->sendError(); 155 | return; 156 | } 157 | const uint8_t& fan = command.data[1]; 158 | if (fan >= FAN_NUM) { 159 | CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); 160 | response->sendError(); 161 | return; 162 | } 163 | const FanDetectionType type = static_cast(command.data[2]); 164 | if (!isValidFanDetectionType(type)) { 165 | CLP_LOG(1, F("Invalid fan detection type: %d\r\n"), type); 166 | response->sendError(); 167 | return; 168 | } 169 | setFanDetectionType(fan, type); 170 | response->send(nullptr, 0); 171 | break; 172 | } 173 | case READ_FAN_DETECTION_TYPE: { 174 | CLP_LOG(3, F("Read detect type\r\n")); 175 | if (command.data[0] != 0x01) { 176 | response->sendError(); 177 | return; 178 | } 179 | const uint8_t& fan = command.data[1]; 180 | if (fan >= FAN_NUM) { 181 | CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); 182 | response->sendError(); 183 | return; 184 | } 185 | FanDetectionType type = getFanDetectionType(fan); 186 | byte typeData[] = {static_cast(type)}; 187 | response->send(typeData, sizeof(typeData)); 188 | break; 189 | } 190 | default: 191 | CLP_LOG(1, F("Unkown command\r\n")); 192 | response->sendError(); 193 | return; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/FanController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | /** 19 | * @file 20 | * Defines types and constants of the fan protocol 21 | */ 22 | 23 | #include "Arduino.h" 24 | #include "IFanController.h" 25 | 26 | #define FAN_NUM 6 27 | 28 | /** 29 | * The actual state of a fan port. 30 | * 31 | * @see FanDetectionType 32 | */ 33 | enum class FanMask : byte { 34 | /** No fan connected */ 35 | Disconnected = 0x00, 36 | /** A three pin fan is connected */ 37 | ThreePin = 0x01, 38 | /** A four pin fan is connected */ 39 | FourPin = 0x02 40 | }; 41 | 42 | bool isValidFanMask(const FanMask fanMask); 43 | 44 | /** 45 | * The virtual state of a fan port. 46 | * 47 | * @see FanMask 48 | */ 49 | enum class FanDetectionType : byte { 50 | /** Auto detect the type of fan which is connected */ 51 | Auto = 0x00, 52 | /** A three pin fan is connected */ 53 | ThreePin = 0x01, 54 | /** A four pin fan is connected */ 55 | FourPin = 0x02, 56 | /** No fan connected */ 57 | Disconnected = 0x03 58 | }; 59 | 60 | bool isValidFanDetectionType(const FanDetectionType type); 61 | 62 | #define FAN_FORCE_THREE_PIN_MODE_ON 0x01 63 | #define FAN_FORCE_THREE_PIN_MODE_OFF 0x00 64 | 65 | #define FAN_CURVE_POINTS_NUM 6 66 | 67 | #define FAN_CURVE_TEMP_GROUP_EXTERNAL 255 68 | 69 | /** 70 | * The definition of a fan curve to control fan speeds depending on the temperature. 71 | * The fan curve is a mapping of temperature values to fan speed. 72 | */ 73 | struct FanCurve { 74 | /** 75 | * The temperatures in hundredths of a degree Celsius. 76 | */ 77 | uint16_t temperatures[FAN_CURVE_POINTS_NUM]; 78 | /** 79 | * The fan speeds in RPM. 80 | */ 81 | uint16_t rpms[FAN_CURVE_POINTS_NUM]; 82 | }; 83 | 84 | /** 85 | * The abstract implementation of IFanController. This implementation handles the parsing and interpretation of incoming 86 | * commands. 87 | */ 88 | class FanController : public IFanController { 89 | public: 90 | virtual void handleFanControl(const Command& command, const CorsairLightingProtocolResponse* response) override; 91 | 92 | protected: 93 | /** 94 | * Get the fan speed. 95 | * 96 | * @param fan index of the fan 97 | * @return fan speed in RPM. 98 | */ 99 | virtual uint16_t getFanSpeed(uint8_t fan) = 0; 100 | /** 101 | * Set the fan speed to a fixed value. 102 | * 103 | * @param fan index of the fan 104 | * @param speed fan speed in RPM. 105 | */ 106 | virtual void setFanSpeed(uint8_t fan, uint16_t speed) = 0; 107 | /** 108 | * Get the power percentage of a fan. 109 | * 110 | * @param fan index of the fan 111 | * @return power percentage, in range 0-255 with 255 mean 100%. 112 | */ 113 | virtual uint8_t getFanPower(uint8_t fan) = 0; 114 | /** 115 | * @param fan index of the fan 116 | * @param percentage 255 mean 100%. 117 | */ 118 | virtual void setFanPower(uint8_t fan, uint8_t percentage) = 0; 119 | /** 120 | * Set a FanCure for a fan. 121 | * 122 | * @param fan index of the fan 123 | * @param group the temperature group used for getting the temperature for this fan 124 | * @param fanCurve the fan curve data 125 | */ 126 | virtual void setFanCurve(uint8_t fan, uint8_t group, FanCurve& fanCurve) = 0; 127 | /** 128 | * Set the external temperature for a fan. The external temperature is provied by the iCUE. 129 | * 130 | * @param fan index of the fan 131 | * @param temp The temperature in hundredths of a degree Celsius. 132 | */ 133 | virtual void setFanExternalTemperature(uint8_t fan, uint16_t temp) = 0; 134 | /** 135 | * TODO not sure what this does, currently unused 136 | * 137 | * @param flag the value 138 | */ 139 | virtual void setFanForce3PinMode(bool flag) = 0; 140 | /** 141 | * Get the fan DetectionType for a fan. 142 | * 143 | * @param fan index of the fan 144 | * @return the FanDetectionType 145 | */ 146 | virtual FanDetectionType getFanDetectionType(uint8_t fan) = 0; 147 | /** 148 | * Set the fan DetectionType for a fan. 149 | * 150 | * @param fan index of the fan 151 | * @param type the FanDetectionType 152 | */ 153 | virtual void setFanDetectionType(uint8_t fan, FanDetectionType type) = 0; 154 | }; 155 | -------------------------------------------------------------------------------- /src/FastLEDController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include 19 | 20 | #include "Arduino.h" 21 | #include "FastLEDControllerStorage.h" 22 | #include "LEDController.h" 23 | #include "TemperatureController.h" 24 | 25 | #ifndef LED_CONTROLLER_TIMEOUT 26 | #define LED_CONTROLLER_TIMEOUT 30000 27 | #endif 28 | 29 | /** 30 | * The default LEDController. This controller uses the FastLED library to implement the Hardware Lighting effects. Also 31 | * all RGB values of the LEDs are stored into CRGB arrays which can be used by the FastLED library to show them on the 32 | * real LED strips. This controller can stores the internal state to EEPROM and support HW playback without USB 33 | * connection. 34 | * 35 | * @see FastLED 36 | */ 37 | class FastLEDController : public LEDController { 38 | /** 39 | * Internal data stored for each Channel. These data are not persistent. 40 | */ 41 | struct ChannelData { 42 | uint8_t ledCount = 0; 43 | CRGB* leds = nullptr; 44 | /** 45 | * store an array for each color, used for software playback 46 | */ 47 | uint8_t* valuesBuffer[3] = {nullptr}; 48 | /** 49 | * External temperature value used by this channel for temperature based lighting. 50 | */ 51 | uint16_t temp; 52 | void (*onUpdateCallback)(void) = nullptr; 53 | }; 54 | 55 | public: 56 | /** 57 | * Create a new FastLEDController and specify which storage should be used. The EEPROM of the Arduino can be used as 58 | * Storage. See the other contructor for more details. 59 | * 60 | * @param storage specify the storage which should be used, e.g. EEPROM 61 | */ 62 | FastLEDController(FastLEDControllerStorage* storage); 63 | /** 64 | * Create a new FastLEDController and specify which storage should be used. If the EEPROM of the Arduino should be 65 | * used to store persistent information like the Hardware Lighting use {@code FastLEDControllerStorageEEPROM}. If 66 | * enabled, the hardware lighting configured in iCUE works without a USB connection and even after a restart of the 67 | * Arduino. Also the the TemperatureController used for temperature related lighting can be passed here. 68 | * 69 | * @param temperatureController used for temperature based lighting 70 | * @param storage specify the storage which should be used, e.g. EEPROM 71 | */ 72 | FastLEDController(TemperatureController* temperatureController, FastLEDControllerStorage* storage); 73 | ~FastLEDController(); 74 | /** 75 | * Add a LED array on a channel with a given length. The length define how many LEDs iCUE can control. The actual 76 | * length of the array can be longer, but iCUE only writes up to the specified length. 77 | * 78 | * @param channel the index of the channel 79 | * @param leds the array to store the LED data in 80 | * @param length the length of the array used by iCUE to write LED data 81 | */ 82 | virtual void addLEDs(uint8_t channel, CRGB* leds, uint8_t length); 83 | /** 84 | * Get the LED data array for a channel. 85 | * 86 | * @param channel the index of the channel 87 | * @return the pointer to the LED array or nullptr if there is no array 88 | * @see getLEDCount() 89 | */ 90 | CRGB* getLEDs(uint8_t channel); 91 | /** 92 | * Get the length of the LED data array. 93 | * 94 | * @param channel the index of the channel 95 | * @return the length of the array 96 | * @see getLEDs() 97 | */ 98 | uint8_t getLEDCount(uint8_t channel); 99 | /** 100 | * Update the displayed RGB values for the LEDs. This will write to the LED data array of each Channel. 101 | * This method does not call {@code FastLED.show()}. This function must be called in loop. 102 | * 103 | * @return true if the LED data of a channel was updated, false otherwise 104 | */ 105 | virtual bool updateLEDs(); 106 | /** 107 | * Register an update hook, which is executed after a channel has been updated. This can be used to apply 108 | * transforamtions to the channel before the data is displayed by FastLED. 109 | * 110 | * @param channel the channel for which the hook is registered 111 | * @param callback the callback, which is executed after the update 112 | */ 113 | void onUpdateHook(uint8_t channel, void (*callback)(void)); 114 | 115 | protected: 116 | TemperatureController* const temperatureController; 117 | FastLEDControllerStorage* const storage; 118 | 119 | bool trigger_update = false; 120 | 121 | ChannelData channelData[CHANNEL_NUM]; 122 | 123 | unsigned long lastUpdate = 0; 124 | unsigned long currentUpdate = 0; 125 | 126 | int applySpeed(int duration, const GroupSpeed speed); 127 | /** 128 | * Calculates the index of the current step of the animation. 129 | * 130 | * @param duration the duration on the animation 131 | * @param steps the number of steps of the animation 132 | * @return the current step of the animation 133 | */ 134 | int animation_step(int duration, int steps); 135 | /** 136 | * Calculates the number of steps of the animation, since the last update of the animation. 137 | * 138 | * @param duration the duration on the animation 139 | * @param steps the number of steps of the animation 140 | * @return the number of steps since the last update 141 | */ 142 | int animation_step_count(int duration, int steps); 143 | 144 | /** 145 | * Generate a rainbow random color from predefined color palette. 146 | * 147 | * @return a random color 148 | */ 149 | CRGB randomColor(); 150 | 151 | bool renderRainbowWave(ChannelData& channelData, LEDGroup& group, int groupLedCount); 152 | bool renderColorShift(ChannelData& channelData, LEDGroup& group, int groupLedCount); 153 | bool renderColorPulse(ChannelData& channelData, LEDGroup& group, int groupLedCount); 154 | bool renderColorWave(ChannelData& channelData, LEDGroup& group, int groupLedCount); 155 | bool renderStatic(ChannelData& channelData, LEDGroup& group, int groupLedCount); 156 | bool renderTemperature(ChannelData& channelData, LEDGroup& group, int groupLedCount); 157 | bool renderVisor(ChannelData& channelData, LEDGroup& group, int groupLedCount); 158 | bool renderMarquee(ChannelData& channelData, LEDGroup& group, int groupLedCount); 159 | bool renderBlink(ChannelData& channelData, LEDGroup& group, int groupLedCount); 160 | bool renderSequential(ChannelData& channelData, LEDGroup& group, int groupLedCount); 161 | bool renderRainbow(ChannelData& channelData, LEDGroup& group, int groupLedCount); 162 | 163 | bool load() override; 164 | bool save() override; 165 | 166 | virtual void triggerLEDUpdate() override; 167 | virtual void setLEDExternalTemperature(uint8_t channel, uint16_t temp) override; 168 | virtual void setLEDColorValues(uint8_t channel, uint8_t color, uint8_t offset, const uint8_t* values, 169 | size_t len) override; 170 | virtual void clearLEDColorValues(uint8_t channel) override; 171 | virtual uint8_t getLEDAutodetectionResult(uint8_t channel) override; 172 | /** 173 | * This function is called when a timeout occurs. 174 | */ 175 | virtual void timeoutAction(); 176 | }; 177 | -------------------------------------------------------------------------------- /src/FastLEDControllerStorage.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "LEDController.h" 19 | 20 | class FastLEDControllerStorage { 21 | public: 22 | virtual bool load(const int index, LEDChannel& channel) = 0; 23 | virtual bool save(const int index, const LEDChannel& channel) = 0; 24 | }; 25 | -------------------------------------------------------------------------------- /src/FastLEDControllerStorageEEPROM.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "FastLEDControllerStorageEEPROM.h" 17 | 18 | #if defined(ARDUINO_ARCH_AVR) 19 | 20 | #include 21 | 22 | bool FastLEDControllerStorageEEPROM::load(const int index, LEDChannel& channel) { 23 | EEPROM.get(EEPROM_ADDRESS + (sizeof(LEDChannel) * index), channel); 24 | return true; 25 | } 26 | 27 | bool FastLEDControllerStorageEEPROM::save(const int index, const LEDChannel& channel) { 28 | CLP_LOG(3, F("Save to EEPROM.\r\n")); 29 | #ifdef DEBUG 30 | Serial.println(F("Save to EEPROM.")); 31 | #endif 32 | EEPROM.put(EEPROM_ADDRESS + (sizeof(LEDChannel) * index), channel); 33 | return true; 34 | } 35 | 36 | size_t FastLEDControllerStorageEEPROM::getEEPROMSize() { return sizeof(LEDChannel) * CHANNEL_NUM; } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/FastLEDControllerStorageEEPROM.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #if defined(ARDUINO_ARCH_AVR) 19 | 20 | #include "FastLEDControllerStorage.h" 21 | 22 | #ifndef EEPROM_ADDRESS 23 | #define EEPROM_ADDRESS 4 24 | #endif 25 | 26 | class FastLEDControllerStorageEEPROM : public FastLEDControllerStorage { 27 | public: 28 | virtual bool load(const int index, LEDChannel& channel) override; 29 | virtual bool save(const int index, const LEDChannel& channel) override; 30 | /** 31 | * Get the total size of all data stored in EEPROM by this LEDController. 32 | * 33 | * @return the size in bytes 34 | */ 35 | virtual size_t getEEPROMSize(); 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/FastLEDControllerUtils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "FastLEDControllerUtils.h" 17 | 18 | #include 19 | #include 20 | 21 | #include "CLPUtils.h" 22 | 23 | void CLP::transformLLFanToStrip(FastLEDController* controller, uint8_t channelIndex) { 24 | auto& channel = controller->getChannel(channelIndex); 25 | if (channel.mode == ChannelMode::SoftwarePlayback) { 26 | auto leds = controller->getLEDs(channelIndex); 27 | auto count = controller->getLEDCount(channelIndex); 28 | for (uint8_t fanIndex = 0; fanIndex < count / LL_FAN_LEDS; fanIndex++) { 29 | CLP::reverse(&leds[fanIndex * LL_FAN_LEDS], &leds[fanIndex * LL_FAN_LEDS + LL_FAN_LEDS - 1] + 1); 30 | } 31 | } 32 | } 33 | 34 | void CLP::transformLC100ToStrip(FastLEDController* controller, uint8_t channelIndex) { 35 | auto& channel = controller->getChannel(channelIndex); 36 | if (channel.mode == ChannelMode::SoftwarePlayback) { 37 | auto leds = controller->getLEDs(channelIndex); 38 | auto count = controller->getLEDCount(channelIndex); 39 | for (uint8_t lc100Index = 0; lc100Index < count / LC100_LEDS; lc100Index++) { 40 | CLP::rotate(&leds[lc100Index * LC100_LEDS], &leds[lc100Index * LC100_LEDS + LC100_FIRST_LED_OFFSET], 41 | &leds[lc100Index * LC100_LEDS + LC100_LEDS - 1] + 1); // End address after the last element 42 | } 43 | } 44 | } 45 | 46 | /** 47 | * @param index the index which should be scaled 48 | * @param scaleFactor the factor for the scaling 49 | * @return the scaled index 50 | */ 51 | inline int scaleIndex(const int index, const float scaleFactor) { return round(index * scaleFactor); } 52 | 53 | float scaleFactorOf(const int numberLEDsBefore, const int numberLEDsAfter) { 54 | return (float)(numberLEDsBefore - 1) / (numberLEDsAfter - 1); 55 | } 56 | 57 | void CLP::scale(FastLEDController* controller, uint8_t channelIndex, int scaleToSize) { 58 | auto leds = controller->getLEDs(channelIndex); 59 | const float scaleFactor = scaleFactorOf(controller->getLEDCount(channelIndex), scaleToSize); 60 | if (scaleFactor < 1.0f) { 61 | for (int ledIndex = scaleToSize - 1; ledIndex >= 0; ledIndex--) { 62 | leds[ledIndex] = leds[scaleIndex(ledIndex, scaleFactor)]; 63 | } 64 | } else { 65 | for (int ledIndex = 0; ledIndex < scaleToSize; ledIndex++) { 66 | leds[ledIndex] = leds[scaleIndex(ledIndex, scaleFactor)]; 67 | } 68 | } 69 | } 70 | 71 | void CLP::repeat(FastLEDController* controller, uint8_t channelIndex, uint8_t times) { 72 | auto leds = controller->getLEDs(channelIndex); 73 | auto count = controller->getLEDCount(channelIndex); 74 | // skip first iteration, because LEDs already contains the data at the first position 75 | for (int i = 1; i < times; i++) { 76 | memcpy(leds + (count * i), leds, sizeof(CRGB) * count); 77 | } 78 | } 79 | 80 | void CLP::scaleSegments(FastLEDController* controller, uint8_t channelIndex, const SegmentScaling* const segments, 81 | int segmentsCount) { 82 | auto leds = controller->getLEDs(channelIndex); 83 | int ledStripIndexAfterScaling = 0; 84 | int ledStripIndexBeforeScaling = 0; 85 | int totalLengthAfterScaling = 0; 86 | SegmentScaling downScaledSegments[segmentsCount]; 87 | // scale down segments and move all segments together so there is space for upscaling 88 | for (int i = 0; i < segmentsCount; i++) { 89 | const int segmentLength = segments[i].segmentLength; 90 | const int scaleToSize = min(segments[i].scaleToSize, segmentLength); 91 | const float scaleFactor = scaleFactorOf(segmentLength, scaleToSize); 92 | 93 | for (int ledIndex = 0; ledIndex < scaleToSize; ledIndex++) { 94 | leds[ledStripIndexAfterScaling + ledIndex] = 95 | leds[ledStripIndexBeforeScaling + scaleIndex(ledIndex, scaleFactor)]; 96 | } 97 | ledStripIndexAfterScaling += scaleToSize; 98 | ledStripIndexBeforeScaling += segmentLength; 99 | downScaledSegments[i].segmentLength = scaleToSize; 100 | downScaledSegments[i].scaleToSize = segments[i].scaleToSize; 101 | totalLengthAfterScaling += segments[i].scaleToSize; 102 | } 103 | 104 | ledStripIndexBeforeScaling = ledStripIndexAfterScaling; 105 | ledStripIndexAfterScaling = totalLengthAfterScaling; 106 | // scale up segments beginning with the last segment to not override other segments 107 | for (int i = segmentsCount - 1; i >= 0; i--) { 108 | const float scaleFactor = scaleFactorOf(downScaledSegments[i].segmentLength, downScaledSegments[i].scaleToSize); 109 | ledStripIndexAfterScaling -= downScaledSegments[i].scaleToSize; 110 | ledStripIndexBeforeScaling -= downScaledSegments[i].segmentLength; 111 | for (int ledIndex = downScaledSegments[i].scaleToSize - 1; ledIndex >= 0; ledIndex--) { 112 | leds[ledStripIndexAfterScaling + ledIndex] = 113 | leds[ledStripIndexBeforeScaling + scaleIndex(ledIndex, scaleFactor)]; 114 | } 115 | } 116 | } 117 | 118 | void CLP::reverse(FastLEDController* controller, uint8_t channelIndex) { 119 | auto leds = controller->getLEDs(channelIndex); 120 | auto maxIndex = controller->getLEDCount(channelIndex) - 1; 121 | for (int ledIndex = 0; ledIndex < maxIndex - ledIndex; ledIndex++) { 122 | CRGB temp = leds[ledIndex]; 123 | leds[ledIndex] = leds[maxIndex - ledIndex]; 124 | leds[maxIndex - ledIndex] = temp; 125 | } 126 | } 127 | 128 | void CLP::gammaCorrection(FastLEDController* controller, uint8_t channelIndex) { 129 | auto leds = controller->getLEDs(channelIndex); 130 | auto count = controller->getLEDCount(channelIndex); 131 | for (int ledIndex = 0; ledIndex < count; ledIndex++) { 132 | leds[ledIndex].r = dim8_video(leds[ledIndex].r); 133 | leds[ledIndex].g = dim8_video(leds[ledIndex].g); 134 | leds[ledIndex].b = dim8_video(leds[ledIndex].b); 135 | } 136 | } 137 | 138 | void CLP::fixIcueBrightness(FastLEDController* controller, uint8_t channelIndex) { 139 | auto& channel = controller->getChannel(channelIndex); 140 | if (channel.mode == ChannelMode::SoftwarePlayback) { 141 | auto leds = controller->getLEDs(channelIndex); 142 | auto count = controller->getLEDCount(channelIndex); 143 | for (int ledIndex = 0; ledIndex < count; ledIndex++) { 144 | leds[ledIndex].r = leds[ledIndex].r * 2; 145 | leds[ledIndex].g = leds[ledIndex].g * 2; 146 | leds[ledIndex].b = leds[ledIndex].b * 2; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/FastLEDControllerUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "FastLEDController.h" 20 | 21 | namespace CLP { 22 | /** 23 | * Make it possible to use up to 96 LEDs per channel from iCUE by using the LL Fan option. This function transforms the 24 | * LL fans layout to a normal strip layout. This function can be combined with scale and repeat function but must be 25 | * invoked first. 26 | * 27 | * @param controller the FastLEDController controlling the LEDs 28 | * @param channelIndex the index of the channel 29 | */ 30 | void transformLLFanToStrip(FastLEDController* controller, uint8_t channelIndex); 31 | /** 32 | * Puts the LC100 LEDs in order for use as a strip. 33 | * 34 | * @param controller the FastLEDController controlling the LEDs 35 | * @param channelIndex the index of the channel 36 | */ 37 | void transformLC100ToStrip(FastLEDController* controller, uint8_t channelIndex); 38 | /** 39 | * Scales a channel's length to a given size, the size can be larger or smaller than the default length given to the 40 | * FastLEDController::addLEDs function Integer scaling is used, so no interpolation between color values is done and the 41 | * animation don't look blurry. 42 | * 43 | * @param controller the FastLEDController controlling the LEDs 44 | * @param channelIndex the index of the channel 45 | * @param scaleToSize the final size after scaling 46 | */ 47 | void scale(FastLEDController* controller, uint8_t channelIndex, int scaleToSize); 48 | /** 49 | * Repeat a channel's LEDs color to control more LEDs than provided by iCUE. 50 | * 51 | * @param controller the FastLEDController controlling the LEDs 52 | * @param channelIndex the index of the channel 53 | * @param times the number of time the colors should be repeated 54 | */ 55 | void repeat(FastLEDController* controller, uint8_t channelIndex, uint8_t times); 56 | 57 | /** 58 | * Define the scaling information for a segment of a channel. A segment is a part of a channel that can be scale 59 | * independently of other segments. 60 | */ 61 | struct SegmentScaling { 62 | /** 63 | * The length of the segment in iCUE for example 10 for the normal LED strips 64 | */ 65 | int segmentLength; 66 | /** 67 | * The size to which the segment will be scaled using Integer scaling 68 | */ 69 | int scaleToSize; 70 | }; 71 | /** 72 | * Scales a channel's segments to given sizes. This can be used to apply different scaling factors to the different 73 | * parts of a LED strip. Integer scaling is used. 74 | * 75 | * @param controller the FastLEDController controlling the LEDs 76 | * @param channelIndex the index of the channel you want to scale the segments on 77 | * @param segments the segments defining the size before and after scaling 78 | * @param segmentsCount the number of segments 79 | */ 80 | void scaleSegments(FastLEDController* controller, uint8_t channelIndex, const SegmentScaling* const segments, 81 | int segmentsCount); 82 | 83 | /** 84 | * Reverse the LEDs of a channel, after this operation, the first LED is the last and the last is the first. 85 | * 86 | * @param controller the FastLEDController controlling the LEDs 87 | * @param channelIndex the index of the channel you want to reverse 88 | */ 89 | void reverse(FastLEDController* controller, uint8_t channelIndex); 90 | 91 | /** 92 | * Simple gamma correction with gamma value 2. This approximation of the gamma correction is sufficient for most LED 93 | * strips. 94 | * 95 | * @param controller the FastLEDController controlling the LEDs 96 | * @param channelIndex the index of the channel 97 | */ 98 | void gammaCorrection(FastLEDController* controller, uint8_t channelIndex); 99 | 100 | /** 101 | * Increase the brightness of a LED channel when using LS100 and LT100 with iCUE Software lighting, because iCUE only 102 | * send the RGB value in the range (0 - 127) which is only 50% of max possible brightness. This function doubles the 103 | * received RGB value. Only use this function with LS100 and LT100. 104 | * 105 | * @param controller the FastLEDController controlling the LEDs 106 | * @param channelIndex the index of the channel 107 | */ 108 | void fixIcueBrightness(FastLEDController* controller, uint8_t channelIndex); 109 | } // namespace CLP 110 | -------------------------------------------------------------------------------- /src/IFanController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "CorsairLightingProtocolConstants.h" 20 | #include "CorsairLightingProtocolResponse.h" 21 | 22 | /** 23 | * The interface of a FanController. 24 | */ 25 | class IFanController { 26 | public: 27 | /** 28 | * Handle a command and send back a response. This method is called if a new command for the FanController is 29 | * received. 30 | * 31 | * @param command the command which must be handled 32 | * @param response the callback used for the response 33 | */ 34 | virtual void handleFanControl(const Command& command, const CorsairLightingProtocolResponse* response) = 0; 35 | }; 36 | -------------------------------------------------------------------------------- /src/ILEDController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "CorsairLightingProtocolConstants.h" 20 | #include "CorsairLightingProtocolResponse.h" 21 | 22 | /** 23 | * The interface of a LEDController. 24 | */ 25 | class ILEDController { 26 | public: 27 | /** 28 | * Handle LED commands and send a response. This method is called for each received command. 29 | * 30 | * @param command the command which must be handled 31 | * @param response the callback for the response 32 | */ 33 | virtual void handleLEDControl(const Command& command, const CorsairLightingProtocolResponse* response) = 0; 34 | }; 35 | -------------------------------------------------------------------------------- /src/ITemperatureController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | #include "Arduino.h" 19 | #include "CorsairLightingProtocolConstants.h" 20 | #include "CorsairLightingProtocolResponse.h" 21 | 22 | /** 23 | * The interface of a TemperatureController. 24 | */ 25 | class ITemperatureController { 26 | public: 27 | /** 28 | * Handle a temperature command and send a response. 29 | * 30 | * @param command the command which should be handled 31 | * @param response the callback for the response 32 | */ 33 | virtual void handleTemperatureControl(const Command& command, const CorsairLightingProtocolResponse* response) = 0; 34 | }; 35 | -------------------------------------------------------------------------------- /src/LEDController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "LEDController.h" 17 | 18 | #include "TemperatureController.h" 19 | 20 | void LEDController::handleLEDControl(const Command& command, const CorsairLightingProtocolResponse* response) { 21 | lastCommand = millis(); 22 | auto& data = command.data; 23 | CLP_LOG(3, F("Received LED command %02X: "), command.command); 24 | if (command.command == WRITE_LED_TRIGGER) { 25 | CLP_LOG(3, F("Write trigger\r\n")); 26 | triggerLEDUpdate(); 27 | saveIfNeeded(); 28 | } else { 29 | if (data[0] >= CHANNEL_NUM) { 30 | response->sendError(); 31 | return; 32 | } 33 | const uint8_t& channel = data[0]; 34 | switch (command.command) { 35 | case READ_LED_STRIP_MASK: { 36 | CLP_LOG(3, F("Read strip mask\r\n")); 37 | uint8_t ledMask[GROUPS_NUM]; 38 | for (uint8_t i = 0; i < GROUPS_NUM; i++) { 39 | if (i < channels[channel].groupsSet) { 40 | ledMask[i] = getLEDStripMask(channel, i); 41 | } else { 42 | ledMask[i] = 0x00; 43 | } 44 | } 45 | response->send(ledMask, sizeof(ledMask)); 46 | // don't send default response 47 | return; 48 | } 49 | case WRITE_LED_RGB_VALUE: { 50 | CLP_LOG(1, F("Write RGB value!\r\n")); 51 | #ifdef DEBUG 52 | Serial.println(F("WriteLedRgbValue")); 53 | #endif 54 | // TODO 55 | response->sendError(); 56 | return; 57 | } 58 | case WRITE_LED_COLOR_VALUES: { 59 | CLP_LOG(3, F("Write color values\r\n")); 60 | const uint8_t offset = data[1]; 61 | const size_t inputLength = min((size_t)data[2], sizeof(data) - 4); 62 | const uint8_t color = data[3]; 63 | if (color >= 3) { 64 | CLP_LOG(1, F("Invalid color: %d\r\n"), color); 65 | response->sendError(); 66 | return; 67 | } 68 | setLEDColorValues(channel, color, offset, data + 4, inputLength); 69 | break; 70 | } 71 | case WRITE_LED_CLEAR: { 72 | CLP_LOG(3, F("Write clear\r\n")); 73 | clearLEDColorValues(channel); 74 | break; 75 | } 76 | case WRITE_LED_GROUP_SET: { 77 | CLP_LOG(3, F("Write group set\r\n")); 78 | if (channels[channel].groupsSet >= GROUPS_NUM) { 79 | CLP_LOG(1, F("Invalid LED group: %d\r\n"), channels[channel].groupsSet); 80 | response->sendError(); 81 | return; 82 | } 83 | LEDGroup group; 84 | group.ledIndex = data[1]; 85 | group.ledCount = data[2]; 86 | group.mode = data[3]; 87 | group.speed = static_cast(data[4]); 88 | group.direction = static_cast(data[5]); 89 | group.extra = static_cast(data[6]); 90 | group.tempGroup = data[7]; 91 | group.color1.r = data[8]; 92 | group.color1.g = data[9]; 93 | group.color1.b = data[10]; 94 | group.color2.r = data[11]; 95 | group.color2.g = data[12]; 96 | group.color2.b = data[13]; 97 | group.color3.r = data[14]; 98 | group.color3.g = data[15]; 99 | group.color3.b = data[16]; 100 | group.temp1 = CLP::fromBigEndian(data[17], data[18]); 101 | group.temp2 = CLP::fromBigEndian(data[19], data[20]); 102 | group.temp3 = CLP::fromBigEndian(data[21], data[22]); 103 | 104 | if (!isValidLEDGroup(group)) { 105 | CLP_LOG(1, F("Invalid LED config received\r\n")); 106 | response->sendError(); 107 | return; 108 | } 109 | 110 | triggerSave |= setLEDGroup(channel, channels[channel].groupsSet++, group); 111 | break; 112 | } 113 | case WRITE_LED_EXTERNAL_TEMP: { 114 | CLP_LOG(3, F("Write external temp\r\n")); 115 | setLEDExternalTemperature(channel, CLP::fromBigEndian(data[2], data[3])); 116 | break; 117 | } 118 | case WRITE_LED_GROUPS_CLEAR: { 119 | CLP_LOG(3, F("Write groups clear\r\n")); 120 | triggerSave |= clearLEDGroups(channel); 121 | break; 122 | } 123 | case WRITE_LED_MODE: { 124 | CLP_LOG(3, F("Write mode\r\n")); 125 | const ChannelMode mode = static_cast(data[1]); 126 | if (!isValidChannelMode(mode)) { 127 | CLP_LOG(1, F("Unkown LED channel mode: %02X\r\n"), data[1]); 128 | #ifdef DEBUG 129 | Serial.print(F("unkown LED channel mode: ")); 130 | Serial.print(data[1], HEX); 131 | Serial.println(); 132 | #endif 133 | response->sendError(); 134 | return; 135 | } 136 | 137 | triggerSave |= setLEDMode(channel, mode); 138 | break; 139 | } 140 | case WRITE_LED_BRIGHTNESS: { 141 | CLP_LOG(3, F("Write brightness\r\n")); 142 | uint8_t brightness = CLP::convert100To255(data[1]); 143 | triggerSave |= setLEDBrightness(channel, brightness); 144 | break; 145 | } 146 | case WRITE_LED_COUNT: { 147 | CLP_LOG(2, F("Write count!\r\n")); 148 | #ifdef DEBUG 149 | Serial.print(F("WRITE_LED_COUNT: ")); 150 | Serial.print(data[1], HEX); 151 | Serial.println(); 152 | #endif 153 | // TODO 154 | channels[channel].ledCount = data[1]; 155 | break; 156 | } 157 | case WRITE_LED_PORT_TYPE: { 158 | CLP_LOG(3, F("Write port type\r\n")); 159 | const PortType portType = static_cast(data[1]); 160 | if (!isValidPortType(portType)) { 161 | CLP_LOG(1, F("Invalid port type: %d\r\n"), portType); 162 | response->sendError(); 163 | return; 164 | } 165 | triggerSave |= setLEDPortType(channel, portType); 166 | break; 167 | } 168 | case WRITE_LED_START_AUTODETECTION: { 169 | CLP_LOG(3, F("Write start autodet\r\n")); 170 | startLEDAutodetection(channel); 171 | break; 172 | } 173 | case READ_LED_AUTODETECTION_RESULTS: { 174 | CLP_LOG(3, F("Write get autodet results\r\n")); 175 | const uint8_t result = getLEDAutodetectionResult(channel); 176 | uint8_t buffer[] = {result}; 177 | response->send(buffer, sizeof(buffer)); 178 | // don't send default response 179 | return; 180 | } 181 | default: { 182 | CLP_LOG(1, F("Unkown command\r\n")); 183 | #ifdef DEBUG 184 | Serial.print(F("unkown command: ")); 185 | Serial.print(command.command, HEX); 186 | Serial.println(); 187 | #endif 188 | response->sendError(); 189 | return; 190 | } 191 | } 192 | } 193 | response->send(nullptr, 0); 194 | } 195 | 196 | bool LEDController::isValidLEDChannel(const LEDChannel& ledChannel) { 197 | if (isValidChannelMode(ledChannel.mode) && isValidPortType(ledChannel.ledPortType) && 198 | ledChannel.groupsSet < GROUPS_NUM) { 199 | for (uint8_t i = 0; i < ledChannel.groupsSet; i++) { 200 | if (!isValidLEDGroup(ledChannel.groups[i])) { 201 | return false; 202 | } 203 | } 204 | return true; 205 | } 206 | return false; 207 | } 208 | 209 | bool LEDController::isValidLEDGroup(const LEDGroup& ledGroup) { 210 | return ledGroup.mode <= GROUP_MODE_Rainbow && isValidGroupSpeed(ledGroup.speed) && 211 | isValidGroupDirection(ledGroup.direction) && isValidGroupExtra(ledGroup.extra) && 212 | (ledGroup.tempGroup == GROUP_TEMP_GROUP_EXTERNAL || ledGroup.tempGroup < TEMPERATURE_NUM); 213 | } 214 | 215 | const LEDChannel& LEDController::getChannel(uint8_t channelIndex) { return channels[channelIndex]; } 216 | 217 | void LEDController::reset() { 218 | for (auto& channel : channels) { 219 | channel = LEDChannel(); 220 | } 221 | save(); 222 | } 223 | 224 | uint8_t LEDController::getLEDStripMask(uint8_t channel, uint8_t groupIndex) { 225 | return channels[channel].groups[groupIndex].ledCount; 226 | } 227 | 228 | bool LEDController::setLEDGroup(uint8_t channel, uint8_t groupIndex, LEDGroup& group) { 229 | channels[channel].groups[groupIndex] = group; 230 | return true; 231 | } 232 | 233 | bool LEDController::clearLEDGroups(uint8_t channel) { 234 | if (channels[channel].groupsSet != 0) { 235 | channels[channel].groupsSet = 0; 236 | return true; 237 | } 238 | return false; 239 | } 240 | 241 | bool LEDController::setLEDMode(uint8_t channel, ChannelMode mode) { 242 | if (channels[channel].mode != mode) { 243 | channels[channel].mode = mode; 244 | return true; 245 | } 246 | return false; 247 | } 248 | 249 | bool LEDController::setLEDBrightness(uint8_t channel, uint8_t brightness) { 250 | if (channels[channel].brightness != brightness) { 251 | channels[channel].brightness = brightness; 252 | return true; 253 | } 254 | return false; 255 | } 256 | 257 | bool LEDController::setLEDPortType(uint8_t channel, PortType ledPortType) { 258 | if (channels[channel].ledPortType != ledPortType) { 259 | channels[channel].ledPortType = ledPortType; 260 | return true; 261 | } 262 | return false; 263 | } 264 | 265 | void LEDController::startLEDAutodetection(uint8_t channel) { 266 | // Nothing to do here 267 | } 268 | 269 | bool LEDController::saveIfNeeded() { 270 | if (triggerSave) { 271 | triggerSave = false; 272 | return save(); 273 | } 274 | return false; 275 | } 276 | -------------------------------------------------------------------------------- /src/LEDController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | /** 19 | * @file 20 | * Defines types and constants of the LED part of the protocol 21 | */ 22 | 23 | #include 24 | 25 | #include "Arduino.h" 26 | #include "CLPUtils.h" 27 | #include "ILEDController.h" 28 | 29 | #define CHANNEL_NUM 2 30 | #define GROUPS_NUM 6 31 | 32 | /** 33 | * The mode of an LEDChannel. The mode describes how the LED lighting is done. 34 | * 35 | * @see LEDController#setLEDMode() 36 | */ 37 | enum class ChannelMode : byte { 38 | /** No lighting is active for the channel. The LEDs will not be updated. */ 39 | Disabled = 0x00, 40 | /** The Hardware Playback uses lighting effects defined by LEDGroups and LEDController renders the effects themself. 41 | This mode works even without an USB connection. */ 42 | HardwarePlayback = 0x01, 43 | /** All lighting effects are rendered by iCUE and only the RGB values are transferred via USB to the device. This 44 | requires an USB connection. */ 45 | SoftwarePlayback = 0x02 46 | }; 47 | 48 | bool inline isValidChannelMode(const ChannelMode channelMode) { 49 | return channelMode == ChannelMode::Disabled || channelMode == ChannelMode::HardwarePlayback || 50 | channelMode == ChannelMode::SoftwarePlayback; 51 | } 52 | 53 | /** 54 | * The type of LED Chipset connected to a channel. These are the types implicitly defined by iCUE when doing the 55 | * lighting setup in iCUE. These type is ignored by the LEDController. 56 | */ 57 | enum class PortType : byte { 58 | /** WS2812B used by all new Corsair devices */ 59 | WS2812B = 0x01, 60 | /** UCS1903 Only used for the SP fan */ 61 | UCS1903 = 0x02 62 | }; 63 | 64 | bool inline isValidPortType(const PortType portType) { 65 | return portType == PortType::WS2812B || portType == PortType::UCS1903; 66 | } 67 | 68 | // LED group mode 69 | #define GROUP_MODE_Rainbow_Wave 0x00 70 | #define GROUP_MODE_Color_Shift 0x01 71 | #define GROUP_MODE_Color_Pulse 0x02 72 | #define GROUP_MODE_Color_Wave 0x03 73 | #define GROUP_MODE_Static 0x04 74 | #define GROUP_MODE_Temperature 0x05 75 | #define GROUP_MODE_Visor 0x06 76 | #define GROUP_MODE_Marquee 0x07 77 | #define GROUP_MODE_Blink 0x08 78 | #define GROUP_MODE_Sequential 0x09 79 | #define GROUP_MODE_Rainbow 0x0A 80 | 81 | /** 82 | * The animation speed of a LEDGroup. 83 | */ 84 | enum class GroupSpeed : byte { High = 0x00, Medium = 0x01, Low = 0x02 }; 85 | 86 | bool inline isValidGroupSpeed(const GroupSpeed groupSpeed) { 87 | return groupSpeed == GroupSpeed::High || groupSpeed == GroupSpeed::Medium || groupSpeed == GroupSpeed::Low; 88 | } 89 | 90 | /** 91 | * The animation direction of a LEDGroup. 92 | */ 93 | enum class GroupDirection : byte { Backward = 0x00, Forward = 0x01 }; 94 | 95 | bool inline isValidGroupDirection(const GroupDirection groupDirection) { 96 | return groupDirection == GroupDirection::Backward || groupDirection == GroupDirection::Forward; 97 | } 98 | 99 | /** 100 | * Extra information for animations of a LEDGroup. 101 | */ 102 | enum class GroupExtra : byte { Alternating = 0x00, Random = 0x01 }; 103 | 104 | bool inline isValidGroupExtra(const GroupExtra groupExtra) { 105 | return groupExtra == GroupExtra::Alternating || groupExtra == GroupExtra::Random; 106 | } 107 | 108 | #define GROUP_TEMP_GROUP_EXTERNAL 255 109 | 110 | /** 111 | * A LEDGroup is a contiguous range of LEDs on a strip. The LEDGroup defines the size, position and lighting effects of 112 | * the LED range. 113 | */ 114 | struct LEDGroup { 115 | /** 116 | * start index of the LEDs of this group 117 | */ 118 | uint8_t ledIndex = 0; 119 | /** 120 | * number of LEDs in this group 121 | */ 122 | uint8_t ledCount = 0; 123 | byte mode = GROUP_MODE_Rainbow_Wave; 124 | GroupSpeed speed = GroupSpeed::High; 125 | GroupDirection direction = GroupDirection::Forward; 126 | GroupExtra extra = GroupExtra::Alternating; 127 | byte tempGroup = GROUP_TEMP_GROUP_EXTERNAL; 128 | 129 | CRGB color1; 130 | CRGB color2; 131 | CRGB color3; 132 | 133 | uint16_t temp1; 134 | uint16_t temp2; 135 | uint16_t temp3; 136 | }; 137 | 138 | /** 139 | * The definition of a Channel. 140 | */ 141 | struct LEDChannel { 142 | /** 143 | * Brightness of the channel in range 0-255. 144 | */ 145 | uint8_t brightness = 255; 146 | ChannelMode mode = ChannelMode::HardwarePlayback; 147 | /** 148 | * The number of LEDs on this channel. 149 | */ 150 | uint8_t ledCount = 0; 151 | PortType ledPortType = PortType::WS2812B; 152 | 153 | LEDGroup groups[GROUPS_NUM]; 154 | uint8_t groupsSet = 0; 155 | }; 156 | 157 | /** 158 | * The abstract implemenation of an LEDController. This implementation handles the parsing and interpretation of 159 | * incoming commands. It also defines the data model to store the all required data from the commands. 160 | */ 161 | class LEDController : public ILEDController { 162 | public: 163 | virtual void handleLEDControl(const Command& command, const CorsairLightingProtocolResponse* response) override; 164 | /** 165 | * Validates a LEDChannel by checking all constrains on the values. This function should be used after non type-safe 166 | * operations on a LEDChannel. 167 | * 168 | * @param ledChannel the LEDChannel to validate 169 | * @return true if the LEDChannel is valid, false otherwise 170 | */ 171 | virtual bool isValidLEDChannel(const LEDChannel& ledChannel); 172 | /** 173 | * Validates a LEDGroup by checking all constrains on the values. This function should be used after non type-safe 174 | * operations on a LEDGroup. 175 | * 176 | * @param ledGroup the LEDGroup to validate 177 | * @return true if the LEDGroup is valid, false otherwise 178 | */ 179 | virtual bool isValidLEDGroup(const LEDGroup& ledGroup); 180 | /** 181 | * Get the data of a Channel from this LEDController. 182 | * 183 | * @param channelIndex the index of the channel 184 | * @return a reference to the LEDChannel 185 | */ 186 | const LEDChannel& getChannel(uint8_t channelIndex); 187 | /** 188 | * Reset all persistent data to default values of the LEDController. 189 | */ 190 | virtual void reset(); 191 | 192 | protected: 193 | LEDChannel channels[CHANNEL_NUM]; 194 | /** 195 | * Indicates that the configuration of the channels has been changed and should be saved. 196 | */ 197 | bool triggerSave = false; 198 | /** 199 | * Stores the time at which the last command was received by the LEDController. 200 | */ 201 | unsigned long lastCommand = 0; 202 | 203 | /** 204 | * Trigger update of the LEDs 205 | */ 206 | virtual void triggerLEDUpdate() = 0; 207 | /** 208 | * Get the LED count of the group. 209 | * 210 | * @param channel the channel index 211 | * @param group the group index 212 | * @return the number of LEDs in the group 213 | */ 214 | virtual uint8_t getLEDStripMask(uint8_t channel, uint8_t group); 215 | /** 216 | * Set the external temperature for a channel. 217 | * 218 | * @param channel the channel index 219 | * @param temp the temperature in hundredths of a degree Celsius. 220 | */ 221 | virtual void setLEDExternalTemperature(uint8_t channel, uint16_t temp) = 0; 222 | virtual bool setLEDGroup(uint8_t channel, uint8_t groupIndex, LEDGroup& group); 223 | /** 224 | * Set the LED color values for one color-channel (red, green or blue) of the given channel. 225 | * 226 | * @param channel the channel index 227 | * @param color the color index to set the values for red(0), green(1), blue(2) 228 | * @param offset the offset in the LED colors buffer to write to 229 | * @param values the array of values to write 230 | * @param len the length of the array of values to write 231 | * @see clearLEDColorValues() 232 | */ 233 | virtual void setLEDColorValues(uint8_t channel, uint8_t color, uint8_t offset, const uint8_t* values, 234 | size_t len) = 0; 235 | /** 236 | * Set the Channel mode. 237 | * 238 | * @param channel the channel index 239 | * @param mode the new mode 240 | * @return true if the mode was changed, false otherwise 241 | */ 242 | virtual bool setLEDMode(uint8_t channel, ChannelMode mode); 243 | /** 244 | * The brightness of the channel. This only applies to HW lighting. 245 | * 246 | * @param channel the channel index 247 | * @param brightness the brightness in the range 0-255 248 | * @return true if the brightness was changed 249 | */ 250 | virtual bool setLEDBrightness(uint8_t channel, uint8_t brightness); 251 | /** 252 | * Set the type of LED chipset: WS2812B or UCS1903 253 | * 254 | * @param channel the channel index 255 | * @param ledPortType the port type 256 | * @return true if the port type was changed 257 | */ 258 | virtual bool setLEDPortType(uint8_t channel, PortType ledPortType); 259 | /** 260 | * Clear the LED color buffer for the given channel. 261 | * 262 | * @param channel the channel index 263 | * @see setLEDColorValues() 264 | */ 265 | virtual void clearLEDColorValues(uint8_t channel) = 0; 266 | virtual bool clearLEDGroups(uint8_t channel); 267 | /** 268 | * Start the potentially long running process to detect the current number of LEDs connected to given channel. 269 | * 270 | * @param channel the channel index 271 | * @see getLEDAutodetectionResult() 272 | */ 273 | virtual void startLEDAutodetection(uint8_t channel); 274 | /** 275 | * Get the result of the LED number autodetection on the given channel. 276 | * Potential values for LT100: 27, 54, 81, 108 277 | * 278 | * @param channel the channel index 279 | * @return the number of LEDs currently connected to the channel 280 | * @see startLEDAutodetection() 281 | */ 282 | virtual uint8_t getLEDAutodetectionResult(uint8_t channel) = 0; 283 | virtual bool save() = 0; 284 | virtual bool load() = 0; 285 | /** 286 | * Save if triggerSave is true and then reset triggerSave. 287 | */ 288 | bool saveIfNeeded(); 289 | }; 290 | -------------------------------------------------------------------------------- /src/RawHID.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2015 NicoHood 3 | modified by Leon Kiefer 4 | See the readme for credit to other people. 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 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | */ 21 | #include "RawHID.h" 22 | 23 | #if defined(ARDUINO_ARCH_AVR) && defined(USBCON) 24 | 25 | #ifndef HID_ENDPOINT_INTERVAL_RAWHID 26 | #define HID_ENDPOINT_INTERVAL_RAWHID 0x01 27 | #endif 28 | /* clang-format off */ 29 | static const uint8_t _hidReportDescriptorRawHID[] PROGMEM = { 30 | /* RAW HID */ 31 | 0x06, lowByte(RAWHID_USAGE_PAGE), highByte(RAWHID_USAGE_PAGE), /* 30 */ 32 | 0x0A, lowByte(RAWHID_USAGE), highByte(RAWHID_USAGE), 33 | 34 | 0xA1, 0x01, /* Collection 0x01 */ 35 | 0x75, 0x08, /* report size = 8 bits */ 36 | 0x15, 0x00, /* logical minimum = 0 */ 37 | 0x26, 0xFF, 0x00, /* logical maximum = 255 */ 38 | 39 | 0x95, RAWHID_TX_SIZE, /* report count TX */ 40 | 0x09, 0x01, /* usage */ 41 | 0x81, 0x02, /* Input (array) */ 42 | 43 | 0x95, RAWHID_RX_SIZE, /* report count RX */ 44 | 0x09, 0x02, /* usage */ 45 | 0x91, 0x02, /* Output (array) */ 46 | 0xC0 /* end collection */ 47 | }; 48 | /* clang-format on */ 49 | 50 | const char defaultSerialNumber[] PROGMEM = SERIAL_NUMBER; 51 | 52 | CLP::RawHID_::RawHID_(void) 53 | : PluggableUSBModule(ENDPOINT_COUNT, 1, epType), 54 | protocol(HID_REPORT_PROTOCOL), 55 | idle(1), 56 | dataLength(0), 57 | dataAvailable(0), 58 | data(nullptr), 59 | serialNumber(defaultSerialNumber), 60 | featureReport(nullptr), 61 | featureLength(0) { 62 | setTimeout(10); 63 | epType[0] = EP_TYPE_INTERRUPT_IN; 64 | PluggableUSB().plug(this); 65 | } 66 | 67 | void CLP::RawHID_::setSerialNumber(const char* argSerialNumber) { serialNumber = argSerialNumber; } 68 | 69 | int CLP::RawHID_::getInterface(uint8_t* interfaceCount) { 70 | // Maybe as optional device FastRawHID with different USAGE PAGE 71 | *interfaceCount += 1; // uses 1 72 | HIDDescriptor hidInterface = {D_INTERFACE(pluggedInterface, ENDPOINT_COUNT, USB_DEVICE_CLASS_HUMAN_INTERFACE, 73 | HID_SUBCLASS_NONE, HID_PROTOCOL_NONE), 74 | D_HIDREPORT(sizeof(_hidReportDescriptorRawHID)), 75 | D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, 76 | RAWHID_TX_SIZE, HID_ENDPOINT_INTERVAL_RAWHID)}; 77 | return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); 78 | } 79 | 80 | int CLP::RawHID_::getDescriptor(USBSetup& setup) { 81 | // Check if this is a HID Class Descriptor request 82 | if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { 83 | return 0; 84 | } 85 | if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { 86 | return 0; 87 | } 88 | 89 | // In a HID Class Descriptor wIndex cointains the interface number 90 | if (setup.wIndex != pluggedInterface) { 91 | return 0; 92 | } 93 | 94 | // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol 95 | // due to the USB specs, but Windows and Linux just assumes its in report mode. 96 | protocol = HID_REPORT_PROTOCOL; 97 | 98 | return USB_SendControl(TRANSFER_PGM, _hidReportDescriptorRawHID, sizeof(_hidReportDescriptorRawHID)); 99 | } 100 | 101 | bool CLP::RawHID_::setup(USBSetup& setup) { 102 | if (pluggedInterface != setup.wIndex) { 103 | return false; 104 | } 105 | 106 | uint8_t request = setup.bRequest; 107 | uint8_t requestType = setup.bmRequestType; 108 | 109 | if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) { 110 | if (request == HID_GET_REPORT) { 111 | // TODO: HID_GetReport(); 112 | return true; 113 | } 114 | if (request == HID_GET_PROTOCOL) { 115 | // TODO: Send8(protocol); 116 | return true; 117 | } 118 | } 119 | 120 | if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { 121 | if (request == HID_SET_PROTOCOL) { 122 | protocol = setup.wValueL; 123 | return true; 124 | } 125 | if (request == HID_SET_IDLE) { 126 | idle = setup.wValueL; 127 | return true; 128 | } 129 | if (request == HID_SET_REPORT) { 130 | // Check if data has the correct length afterwards 131 | int length = setup.wLength; 132 | 133 | // Feature (set feature report) 134 | if (setup.wValueH == HID_REPORT_TYPE_FEATURE) { 135 | // No need to check for negative featureLength values, 136 | // except the host tries to send more then 32k bytes. 137 | // We dont have that much ram anyways. 138 | if (length == featureLength) { 139 | USB_RecvControl(featureReport, featureLength); 140 | 141 | // Block until data is read (make length negative) 142 | disableFeatureReport(); 143 | return true; 144 | } 145 | } 146 | 147 | // Output (set out report) 148 | else if (setup.wValueH == HID_REPORT_TYPE_OUTPUT) { 149 | if (!dataAvailable && length <= dataLength) { 150 | // Write data to fit to the end (not the beginning) of the array 151 | USB_RecvControl(data + dataLength - length, length); 152 | dataAvailable = length; 153 | return true; 154 | } 155 | } 156 | } 157 | } 158 | 159 | return false; 160 | } 161 | 162 | uint8_t CLP::RawHID_::getShortName(char* name) { 163 | name[0] = '\0'; 164 | strncat_P(name, serialNumber, ISERIAL_MAX_LEN - 1); 165 | return strlen(name); 166 | } 167 | namespace CLP { 168 | RawHID_ RawHID; 169 | } 170 | #endif -------------------------------------------------------------------------------- /src/RawHID.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2014-2015 NicoHood 3 | modified by Leon Kiefer 4 | See the readme for credit to other people. 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 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | */ 21 | #pragma once 22 | 23 | #include "Arduino.h" 24 | #include "CorsairLightingProtocolConstants.h" 25 | 26 | #if defined(ARDUINO_ARCH_AVR) 27 | #include "HID.h" 28 | #endif 29 | 30 | #if defined(ARDUINO_ARCH_AVR) && defined(USBCON) 31 | #define SUPPORT_RAW_HID 32 | #endif 33 | 34 | #if defined(SUPPORT_RAW_HID) 35 | // RawHID might never work with multireports, because of OS problems 36 | // therefore we have to make it a single report with no idea. No other HID device will be supported then. 37 | #undef RAWHID_USAGE_PAGE 38 | #define RAWHID_USAGE_PAGE 0xFFC0 // recommended: 0xFF00 to 0xFFFF 39 | 40 | #undef RAWHID_USAGE 41 | #define RAWHID_USAGE 0x0C00 // recommended: 0x0100 to 0xFFFF 42 | 43 | #if defined(__AVR_ATmega16U2__) 44 | #define RAWHID_TX_SIZE 64 45 | #else 46 | #define RAWHID_TX_SIZE 16 47 | #endif 48 | #define RAWHID_RX_SIZE 64 49 | 50 | #endif 51 | 52 | #if defined(ARDUINO_ARCH_AVR) && defined(USBCON) // Arduino Core 53 | 54 | #define EPTYPE_DESCRIPTOR_SIZE uint8_t 55 | // HID Functional Characteristics HID1.11 Page 10 4.4 Interfaces 56 | // Interrupt Out Endpoint is optional, control endpoint is used by default 57 | #define ENDPOINT_COUNT 1 58 | namespace CLP { 59 | class RawHID_ : public PluggableUSBModule, public Stream { 60 | public: 61 | RawHID_(void); 62 | /** 63 | * Set the Serial Number of the USB device. Else a default Serial Number. 64 | * 65 | * @param serialNumber the Serial Number. The Serial Number string must be located in program space (flash). 66 | */ 67 | void setSerialNumber(const char* serialNumber); 68 | 69 | void setFeatureReport(void* report, int length) { 70 | if (length > 0) { 71 | featureReport = (uint8_t*)report; 72 | featureLength = length; 73 | 74 | // Disable feature report by default 75 | disableFeatureReport(); 76 | } 77 | } 78 | 79 | int availableFeatureReport(void) { 80 | if (featureLength < 0) { 81 | return featureLength & ~0x8000; 82 | } 83 | return 0; 84 | } 85 | 86 | void enableFeatureReport(void) { featureLength &= ~0x8000; } 87 | 88 | void disableFeatureReport(void) { featureLength |= 0x8000; } 89 | 90 | void begin(void* report, int length) { 91 | if (length > 0) { 92 | data = (uint8_t*)report; 93 | dataLength = length; 94 | dataAvailable = 0; 95 | } 96 | } 97 | 98 | void end(void) { 99 | disable(); 100 | dataLength = 0; 101 | } 102 | 103 | void enable(void) { dataAvailable = 0; } 104 | 105 | void disable(void) { dataAvailable = -1; } 106 | 107 | virtual int available(void) { 108 | if (dataAvailable < 0) { 109 | return 0; 110 | } 111 | return dataAvailable; 112 | } 113 | 114 | virtual int read() { 115 | // Check if we have data available 116 | if (dataAvailable > 0) { 117 | // Get next data byte (from the start to the end) 118 | return data[dataLength - dataAvailable--]; 119 | } 120 | return -1; 121 | } 122 | 123 | virtual int peek() { 124 | // Check if we have data available 125 | if (dataAvailable > 0) { 126 | return data[dataLength - dataAvailable]; 127 | } 128 | return -1; 129 | } 130 | 131 | virtual void flush(void) { 132 | // Writing will always flush by the USB driver 133 | } 134 | 135 | /** 136 | * Wrapper for a single byte 137 | */ 138 | virtual size_t write(uint8_t b) { return write(&b, 1); } 139 | 140 | virtual size_t write(const uint8_t* buffer, size_t size) { 141 | return USB_Send(pluggedEndpoint | TRANSFER_RELEASE, buffer, size); 142 | } 143 | 144 | protected: 145 | // Implementation of the PUSBListNode 146 | int getInterface(uint8_t* interfaceCount) override; 147 | int getDescriptor(USBSetup& setup) override; 148 | bool setup(USBSetup& setup) override; 149 | uint8_t getShortName(char* name) override; 150 | 151 | EPTYPE_DESCRIPTOR_SIZE epType[ENDPOINT_COUNT]; 152 | uint8_t protocol; 153 | uint8_t idle; 154 | 155 | // Buffer pointers to hold the received data 156 | int dataLength; 157 | int dataAvailable; 158 | uint8_t* data; 159 | const char* serialNumber; 160 | 161 | uint8_t* featureReport; 162 | int featureLength; 163 | }; 164 | extern RawHID_ RawHID; 165 | } // namespace CLP 166 | #endif -------------------------------------------------------------------------------- /src/TemperatureController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #include "TemperatureController.h" 17 | 18 | #include "CLPUtils.h" 19 | 20 | void TemperatureController::handleTemperatureControl(const Command& command, 21 | const CorsairLightingProtocolResponse* response) { 22 | CLP_LOG(3, F("Received command %02X: "), command.command); 23 | switch (command.command) { 24 | case READ_TEMPERATURE_MASK: { 25 | CLP_LOG(3, F("Read temp mask\r\n")); 26 | uint8_t mask[TEMPERATURE_NUM]; 27 | for (uint8_t i = 0; i < TEMPERATURE_NUM; i++) { 28 | mask[i] = isTemperatureSensorConnected(i) ? TEMPERATURE_MASK_CONNECTED : TEMPERATURE_MASK_NOT_CONNECTED; 29 | } 30 | response->send(mask, sizeof(mask)); 31 | break; 32 | } 33 | case READ_TEMPERATURE_VALUE: { 34 | CLP_LOG(3, F("Read temp value\r\n")); 35 | const uint8_t& tempSensor = command.data[0]; 36 | if (tempSensor >= TEMPERATURE_NUM) { 37 | CLP_LOG(1, F("Unknown temp sensor: %d\r\n"), tempSensor); 38 | response->sendError(); 39 | return; 40 | } 41 | uint16_t temp = getTemperatureValue(tempSensor); 42 | uint8_t tempData[] = {toBigEndian(temp)}; 43 | response->send(tempData, sizeof(tempData)); 44 | break; 45 | } 46 | case READ_VOLTAGE_VALUE: { 47 | CLP_LOG(3, F("Read voltage\r\n")); 48 | const uint8_t& rail = command.data[0]; 49 | uint16_t voltage; 50 | switch (rail) { 51 | case VOLTAGE_RAIL_12V: { 52 | voltage = getVoltageRail12V(); 53 | break; 54 | } 55 | case VOLTAGE_RAIL_5V: { 56 | voltage = getVoltageRail5V(); 57 | break; 58 | } 59 | case VOLTAGE_RAIL_3V3: { 60 | voltage = getVoltageRail3V3(); 61 | break; 62 | } 63 | default: { 64 | CLP_LOG(1, F("Unkown voltage rail: %d\r\n"), rail); 65 | response->sendError(); 66 | return; 67 | } 68 | } 69 | 70 | uint8_t voltageData[] = {toBigEndian(voltage)}; 71 | response->send(voltageData, sizeof(voltageData)); 72 | break; 73 | } 74 | default: 75 | CLP_LOG(1, F("Unkown command\r\n")); 76 | response->sendError(); 77 | return; 78 | } 79 | } 80 | 81 | uint16_t TemperatureController::getTemperature(uint8_t temperatureSensor) { 82 | if (temperatureSensor >= TEMPERATURE_NUM) { 83 | return 0; 84 | } 85 | return getTemperatureValue(temperatureSensor); 86 | } 87 | -------------------------------------------------------------------------------- /src/TemperatureController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Leon Kiefer 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | #pragma once 17 | 18 | /** 19 | * @file 20 | */ 21 | 22 | #include "Arduino.h" 23 | #include "ITemperatureController.h" 24 | 25 | #define TEMPERATURE_NUM 4 26 | 27 | #define TEMPERATURE_MASK_CONNECTED 0x01 28 | #define TEMPERATURE_MASK_NOT_CONNECTED 0x00 29 | 30 | #define VOLTAGE_RAIL_12V 0 31 | #define VOLTAGE_RAIL_5V 1 32 | #define VOLTAGE_RAIL_3V3 2 33 | 34 | /** 35 | * The abstract implementation of the ITemperatureController. This implementation handles the commands parsing and 36 | * processing. 37 | */ 38 | class TemperatureController : public ITemperatureController { 39 | public: 40 | virtual void handleTemperatureControl(const Command& command, 41 | const CorsairLightingProtocolResponse* response) override; 42 | /** 43 | * Get the temperature of a sensor. 44 | * 45 | * @param temperatureSensor the index of the temperature sensor 46 | * @return the temperature in hundredths of a degree Celsius. 47 | */ 48 | virtual uint16_t getTemperature(uint8_t temperatureSensor); 49 | 50 | protected: 51 | /** 52 | * Get the temperature of a sensor. 53 | * 54 | * @param temperatureSensor the index of the temperature sensor 55 | * @return the temperature in hundredths of a degree Celsius. 56 | */ 57 | virtual uint16_t getTemperatureValue(uint8_t temperatureSensor) = 0; 58 | /** 59 | * Check if a temperature sensor is connected. 60 | * 61 | * @param temperatureSensor the index of the temperature sensor 62 | * @return true if a temperature sensor is connected 63 | */ 64 | virtual bool isTemperatureSensorConnected(uint8_t temperatureSensor) = 0; 65 | /** 66 | * Get the voltage of the 12V rail. 67 | * 68 | * @return the voltage in mV 69 | */ 70 | virtual uint16_t getVoltageRail12V() = 0; 71 | /** 72 | * Get the voltage of the 5V rail. 73 | * 74 | * @return the voltage in mV 75 | */ 76 | virtual uint16_t getVoltageRail5V() = 0; 77 | /** 78 | * Get the voltage of the 3.3V rail. 79 | * 80 | * @return the voltage in mV 81 | */ 82 | virtual uint16_t getVoltageRail3V3() = 0; 83 | }; 84 | --------------------------------------------------------------------------------