diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 5c5053a6..dfef69f0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -25,9 +25,11 @@ If applicable, add screenshots to help explain your problem. **System (please complete the following information):** - OS: [e.g. Windows 10] - Board: [e.g. Arduino Leonardo] - - IDE: [e.g. Arduino IDE] - - Version: [e.g. 0.3.0] - - Sketch: [e.g. SingleStripLightingNodePRO] + - Device: [e.g. Lighting Node PRO] + - Version of IDE: [e.g. Arduino IDE 1.8.13] + - Version of CLP: [e.g. 0.15.0] + - Version of CLP Boards: [e.g. 0.2.0] + - Sketch: [e.g. RepeatAndScale] **Code changes** Did you changed the code of the library or the sketch? diff --git a/.github/ISSUE_TEMPLATE/support.md b/.github/ISSUE_TEMPLATE/support.md index eee6ad66..45ccfb70 100644 --- a/.github/ISSUE_TEMPLATE/support.md +++ b/.github/ISSUE_TEMPLATE/support.md @@ -16,9 +16,11 @@ If applicable, add screenshots to help explain your problem. **System (please complete the following information):** - OS: [e.g. Windows 10] - Board: [e.g. Arduino Leonardo] - - IDE: [e.g. Arduino IDE] - - Version: [e.g. 0.3.0] - - Sketch: [e.g. SingleStripLightingNodePRO] + - Device: [e.g. Lighting Node PRO] + - Version of IDE: [e.g. Arduino IDE 1.8.13] + - Version of CLP: [e.g. 0.15.0] + - Version of CLP Boards: [e.g. 0.2.0] + - Sketch: [e.g. RepeatAndScale] **Code changes** Did you changed the code of the library or the sketch? diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 760073d5..00000000 --- a/.github/stale.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 40 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - bug - - enhancement -# Label to use when marking an issue as stale -staleLabel: wontfix -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 361aeb45..05896cb6 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -18,7 +18,7 @@ jobs: - name: Run doxygen run: doxygen extra/doxygen.conf - name: Deploy to gh-pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v3.8.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_branch: gh-pages diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f556146b..fff292a4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -9,13 +9,13 @@ jobs: steps: - uses: actions/checkout@v2 - name: Check src format - uses: DoozyX/clang-format-lint-action@v0.9 + uses: DoozyX/clang-format-lint-action@v0.13 with: source: './src' extensions: 'h,cpp' clangFormatVersion: 9 - name: Check examples format - uses: DoozyX/clang-format-lint-action@v0.9 + uses: DoozyX/clang-format-lint-action@v0.13 with: source: './examples' extensions: 'h,cpp,ino' diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index be6370a6..91979378 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,8 +1,8 @@ -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] name: Test jobs: test: - name: Test for ${{ matrix.board }} + name: Test for Board ${{ matrix.board }} runs-on: ubuntu-latest strategy: matrix: @@ -15,12 +15,13 @@ jobs: steps: - uses: actions/checkout@v2 - name: Build for ${{ matrix.board }} - uses: ArminJo/arduino-test-compile@v2.6.0 + uses: ArminJo/arduino-test-compile@v3 with: + cli-version: 0.18.3 arduino-board-fqbn: ${{ matrix.board }} 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 - arduino-platform: arduino:avr@1.8.2,SparkFun:avr@1.1.5,Legion2:avr@0.1.3 - required-libraries: FastLED@3.3.3 + arduino-platform: arduino:avr@1.8.3,SparkFun:avr@1.1.13,Legion2:avr@0.3.1 + required-libraries: FastLED@3.5.0 sketch-names: LightingNodePRO.ino, SingleStripLightingNodePRO.ino, CommanderPRO.ino, @@ -34,10 +35,11 @@ jobs: AdditionalFeatures.ino, AmbientBacklight.ino, MultipleFans.ino, - DebugSketch.ino - examples-build-properties: '{"DebugSketch": "-DDEBUG -DVERBOSE -DPRINT_COMMAND=true -DPRINT_RESPONSE=true -DPRINT_LOOP=true -DPRINT_UPDATE=true"}' + DebugSketch.ino, + NoEEPROM.ino + build-properties: '{"DebugSketch": "-DDEBUG -DVERBOSE -DPRINT_COMMAND=true -DPRINT_RESPONSE=true -DPRINT_LOOP=true -DPRINT_UPDATE=true"}' testUnoMega: - name: Test UnoMega sketches for ${{ matrix.board }} + name: Test Arduino Uno/Mega sketches for Board ${{ matrix.board }} runs-on: ubuntu-latest strategy: matrix: @@ -45,14 +47,15 @@ jobs: steps: - uses: actions/checkout@v2 - name: Build for ${{ matrix.board }} - uses: ArminJo/arduino-test-compile@v2.6.0 + uses: ArminJo/arduino-test-compile@v3 with: + cli-version: 0.18.3 arduino-board-fqbn: ${{ matrix.board }} - arduino-platform: arduino:avr@1.8.2 - required-libraries: FastLED@3.3.3 + arduino-platform: arduino:avr@1.8.3 + required-libraries: FastLED@3.5.0 sketch-names: HoodLoader2UnoMegaController.ino test16u2: - name: Test 16u2 sketch for ${{ matrix.board }} + name: Test 16u2 sketch for Board ${{ matrix.board }} runs-on: ubuntu-latest strategy: matrix: @@ -60,10 +63,69 @@ jobs: steps: - uses: actions/checkout@v2 - name: Build for ${{ matrix.board }} - uses: ArminJo/arduino-test-compile@v2.6.0 + uses: ArminJo/arduino-test-compile@v3 with: + cli-version: 0.18.3 arduino-board-fqbn: ${{ matrix.board }} 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 - arduino-platform: arduino:avr@1.8.2,HoodLoader2:avr@2.0.5,Legion2:avr@0.1.3 - required-libraries: FastLED@3.3.3 + arduino-platform: arduino:avr@1.8.3,HoodLoader2:avr@2.0.5,Legion2:avr@0.3.1 + required-libraries: FastLED@3.5.0 sketch-names: HoodLoader2CLPBridge.ino + testAdafruitSAMD: + name: Test for Board ${{ matrix.board }} + runs-on: ubuntu-latest + strategy: + matrix: + board: + [ + "adafruit:samd:adafruit_feather_m0:usbstack=tinyusb", + "adafruit:samd:adafruit_metro_m0:usbstack=tinyusb", + "adafruit:samd:adafruit_circuitplayground_m0:usbstack=tinyusb", + "adafruit:samd:adafruit_gemma_m0:usbstack=tinyusb", + "adafruit:samd:adafruit_trinket_m0:usbstack=tinyusb", + "adafruit:samd:adafruit_qtpy_m0:usbstack=tinyusb", + "adafruit:samd:adafruit_itsybitsy_m0:usbstack=tinyusb", + "adafruit:samd:adafruit_itsybitsy_m4:usbstack=tinyusb", + "adafruit:samd:adafruit_metro_m4_airliftlite:usbstack=tinyusb", +# "adafruit:samd:adafruit_feather_m4:usbstack=tinyusb", #Should work but doesn't have the pins needed for the sketch + "adafruit:samd:adafruit_matrixportal_m4:usbstack=tinyusb" + ] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Build for ${{ matrix.board }} + uses: ArminJo/arduino-test-compile@v3 + with: + cli-version: 0.18.3 + arduino-board-fqbn: ${{ matrix.board }} + platform-url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json + arduino-platform: adafruit:samd@1.7.8 + required-libraries: FastLED@3.5.0,Adafruit TinyUSB Library@1.9.2 + sketch-names: TinyUSB.ino + testAdafruitnRF52: + name: Test for Board ${{ matrix.board }} + runs-on: ubuntu-latest + strategy: + matrix: + board: + [ + "adafruit:nrf52:feather52840", + "adafruit:nrf52:itsybitsy52840", +# "adafruit:nrf52:cplaynrf52840", #Should work but doesn't have the pins needed for the sketch + "adafruit:nrf52:metro52840" + ] + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Get adafruit-nrfutil + run: | + pip3 install adafruit-nrfutil --user + - name: Build for ${{ matrix.board }} + uses: ArminJo/arduino-test-compile@v3 + with: + cli-version: 0.18.3 + arduino-board-fqbn: ${{ matrix.board }} + platform-url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json + arduino-platform: adafruit:nrf52@1.3.0 + required-libraries: FastLED@3.5.0,Adafruit TinyUSB Library@1.9.2 + sketch-names: TinyUSB.ino \ No newline at end of file diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml new file mode 100644 index 00000000..f12c8fa8 --- /dev/null +++ b/.github/workflows/stale.yaml @@ -0,0 +1,18 @@ +name: 'Close stale issues and PRs' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v4 + with: + days-before-stale: 14 + days-before-close: 7 + exempt-issue-labels: bug,enhancement,refactoring,documentation + stale-issue-message: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. diff --git a/.gitignore b/.gitignore index f3640dd6..14108842 100644 --- a/.gitignore +++ b/.gitignore @@ -274,5 +274,4 @@ __vm/ .vscode/ # Arduino CLI -*.elf -*.hex +build/ diff --git a/README.md b/README.md index 12a299ba..b8735ed6 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,14 @@ OpenRGB **This library can be used to integrate custom/unofficial RGB strips with iCUE.** -_This is not an official corsair project._ +_This is not an official Corsair project._ ## Features * Add support of Corsair DIY device protocol to Arduino. * Control LEDs with the [Corsair iCUE software](https://www.corsair.com/icue). * [Support common LED chipsets](https://github.com/FastLED/FastLED/wiki/Overview#chipsets). (e.g. WS2812B, WS2801) * Support [FastLED](http://fastled.io/). -* Supported platform: Arduino AVR +* Supported platforms: Arduino AVR, [TinyUSB supported cores](https://github.com/adafruit/Adafruit_TinyUSB_Arduino#supported-cores) * Hardware Lighting mode. * Use multiple devices at the same time. * Repeat or scale LED channels to arbitrary size. @@ -33,14 +33,19 @@ This project provides example sketches for easy use with Arduino IDE. - [Requirements](#requirements) - [Install the libraries](#install-the-libraries) -- [Create a Lighting Node PRO](#create-a-lighting-node-pro) +- [Create a Lighting Node PRO with AVR](#create-a-lighting-node-pro-with-avr) +- [Create a Lighting Node PRO with TinyUSB](#create-a-lighting-node-pro-for-a-raspberry-pi-pico-with-tinyusb) - [Use the Lighting Node PRO](#use-the-lighting-node-pro) ## Requirements The library is compatible with all boards using the MCU ATmega32U4. -This includes **Arduino Leonardo**, **SparkFun Pro Micro**, and **Arduino Micro**. +This includes **Arduino Leonardo**, **SparkFun Pro Micro**, **Arduino Micro**, and **Adafruit 32u4 AVR Boards**. It also supports the Arduino Uno and Arduino Mega, **but** this requires the [HoodLoader2](https://github.com/NicoHood/HoodLoader2) bootloader, see [this wiki](https://github.com/Legion2/CorsairLightingProtocol/wiki/How-to-use-on-Arduino-Uno-and-Arduino-Mega) for more details. -It is **not** compatible with ATmega328 (Arduino Nano), STM8S103F3, teensy, ESP8266 and ESP32 see [list of architecture/platform](https://github.com/Legion2/CorsairLightingProtocol/issues?q=is%3Aissue+label%3Aarchitecture%2Fplatform) for a detailed description why they are not supported. + +In addition, any board compatible with **Adafruit TinyUSB for Arduino** is also supported without the use of custom board definitions. Be sure to define USE_TINYUSB, which is done automatically when using a supported core and selecting TinyUSB for the USB Stack. See the TinyUSB example for implementation details. + +It is **not** compatible with ATmega328 (Arduino Nano), STM8S103F3, teensy, or ESP8266 see [list of architecture/platform](https://github.com/Legion2/CorsairLightingProtocol/issues?q=is%3Aissue+label%3Aarchitecture%2Fplatform) for a detailed description why they are not supported. + In the rest of the documentation "Arduino" is used as a synonym for all supported boards regardless of the manufacturer. When you have problems with a board not listed here, please open an [Issue](https://github.com/Legion2/CorsairLightingProtocol/issues). @@ -51,8 +56,9 @@ Open the Library-Manager in Arduino IDE via Tools->Manage Libraries... Search for "Corsair Lighting Protocol" and install the Corsair Lighting Protocol library. This library also requires the [FastLED](http://fastled.io/) library. Search for "FastLED" in the Library-Manager and install the FastLED library. +If using TinyUSB, also install the latest "Adafruit TinyUSB Library" as it supersedes some of the core versions. -## Create a Lighting Node PRO +## Create a Lighting Node PRO with AVR This guide will teach you how to create a Lighting Node PRO with an Arduino Leonardo compatible board. If you have an Arduino Uno or Mega, see the [other guide](https://github.com/Legion2/CorsairLightingProtocol/wiki/How-to-use-on-Arduino-Uno-and-Arduino-Mega). @@ -63,13 +69,14 @@ If you have an Arduino Uno or Mega, see the [other guide](https://github.com/Leg 1. Install the [CLP Boards](https://github.com/Legion2/CorsairLightingProtocolBoards). They can be installed by following the [CLP Boards installation guide](https://github.com/Legion2/CorsairLightingProtocolBoards#how-to-use-these-boards-in-arduino). After installation it should be possible to select the CLP Boards in the Arduino IDE as shown in the screenshot below. + If your are using a Sparkfun Pro Micro also install the [SparkFun Boards definition](https://github.com/sparkfun/Arduino_Boards#installation-instructions). ![select CLP Board](extra/images/select-board.png) 1. Upload the "LightingNodePRO" sketch to your Arduino. ![upload sketch](extra/images/upload-sketch.png) 1. Do the wiring. - For more information on [how to wire the leds](https://github.com/FastLED/FastLED/wiki/Wiring-leds) and [how to set up the LEDs in the code](https://github.com/FastLED/FastLED/wiki/Basic-usage#setting-up-the-leds) see the links. + For more information on [how to wire the LEDs](https://github.com/FastLED/FastLED/wiki/Wiring-leds) and [how to set up the LEDs in the code](https://github.com/FastLED/FastLED/wiki/Basic-usage#setting-up-the-leds) see the links. ![the wiring](extra/images/board-wiring.jpg) 1. Verify your device works as expected. @@ -77,6 +84,35 @@ If you have an Arduino Uno or Mega, see the [other guide](https://github.com/Leg Somewhere in the list of devices, there should be a device called "Lighting Node PRO". 1. Now open [iCUE](https://www.corsair.com/icue) there you should see the "Lighting Node PRO". +> If you have any problem during setup you may find the solution in the [Troubleshooting section](https://github.com/Legion2/CorsairLightingProtocol/wiki/Troubleshooting). + +## Create a Lighting Node PRO for a Raspberry Pi Pico with TinyUSB + +This guide will teach you how to create a Lighting Node PRO with a Raspberry Pi Pico. + +**Note:** FastLED currently does not support the RP2040 natively. You must manually merge support by modifying your library to include the [6 RP2040 platform files](https://github.com/FastLED/FastLED/pull/1261/files#diff-fda1710ad90fcc4b2f07be21a834da7d24b00008867655232c84fb0369cfc74b) in the FastLED/src/platforms/arm/rp2040 folder and `#elif defined(ARDUINO_ARCH_RP2040)` / `#include` statements in [led_sysdefs.h](https://github.com/FastLED/FastLED/pull/1261/files#diff-95f6b43a0e6b0e58988e1be3bc6415ded5284082a4f2ce2aaa90f5931d4194af) and [platforms.h](https://github.com/FastLED/FastLED/pull/1261/files#diff-255ea38a6573ed237ea1fe164d5e87ca46811eef21ba6e2cef120fda47c6e62f). + +1. Install the [Raspberry Pi Pico Arduino core](https://github.com/earlephilhower/arduino-pico#installing-via-arduino-boards-manager). + +1. Open the example "TinyUSB", you can find it in Arduino IDE in the File menu->Examples->Corsair Lighting Protocol->TinyUSB. + If you can't open the LightingNodePRO example the Corsair Lighting Protocol library is not installed correctly. + +1. Select the Raspberry Pi Pico as shown in the screenshot below. Be sure to select the "Adafruit TinyUSB" USB Stack. + + ![select Raspberry Pi Pico](extra/images/select-board-pico.png) +1. Upload the "TinyUSB" sketch to your Pico. + +1. Do the wiring. + For more information on [how to wire the LEDs](https://github.com/FastLED/FastLED/wiki/Wiring-leds) and [how to set up the LEDs in the code](https://github.com/FastLED/FastLED/wiki/Basic-usage#setting-up-the-leds) see the links. + + A level shifter or buffer, like [this one](https://www.ti.com/product/SN74AHCT1G126), is recommended in between the Pico and LEDs to translate the 3.3v logic level of the Pico IO to the 5v logic level of the LEDs. Your setup may not work reliably without one. + + ![the wiring](extra/images/board-wiring-pico.jpg) +1. Verify your device works as expected. + Open the Windows settings->devices->Other devices. + Somewhere in the list of devices, there should be a device called "Lighting Node PRO". +1. Now open [iCUE](https://www.corsair.com/icue) there you should see the "Lighting Node PRO". + ## Use the Lighting Node PRO ![iCUE RGB Strip example](extra/images/iCUE.jpg) @@ -118,7 +154,7 @@ The Serial Number MAY only consist of HEX characters (0-9 and A-F). The DeviceID can be set with the `setDeviceID` function of `CorsairLightingFirmware`. ```C++ void setup() { - byte deviceId[4] = { 0x9A, 0xDA, 0xA7, 0x8E }; + DeviceID deviceId = { 0x9A, 0xDA, 0xA7, 0x8E }; firmware.setDeviceID(deviceId); ... } @@ -158,11 +194,19 @@ ledController.onUpdateHook(0, []() { }); ``` +## Reverse direction of LED Strip +If you want to change the direction of the LEDs of the Strip without physically change the strip, the `CLP::reverse` function can be used. +The reverse function must be called be for scaling. +```C++ +ledController.onUpdateHook(0, []() { + CLP::reverse(&ledController, 0); +}); +``` ## Hardware Lighting mode The [Hardware Lighting mode](https://forum.corsair.com/v3/showthread.php?t=182874) can be configured in iCUE. It allows you the set lighting effects that will be active when iCUE **is not** running. This is the case when the PC is off, in sleep mode, booting or the user is logged out. -So if you want to have lighing effects in all these situations, use the Hardware Lighting mode. +So if you want to have lighting effects in all these situations, use the Hardware Lighting mode. If you don't want it, configure a static black color. # License diff --git a/examples/AdditionalFeatures/AdditionalFeatures.ino b/examples/AdditionalFeatures/AdditionalFeatures.ino index 77b8af60..191b934e 100644 --- a/examples/AdditionalFeatures/AdditionalFeatures.ino +++ b/examples/AdditionalFeatures/AdditionalFeatures.ino @@ -25,8 +25,10 @@ CRGB ledsChannel2[60]; // Define a custom SerialNumber for the device const char mySerialNumber[] PROGMEM = "202B6949A967"; -CorsairLightingFirmware firmware = corsairLightingNodePROFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); // Set the SerialNumber here CorsairLightingProtocolHID cHID(&cLP, mySerialNumber); diff --git a/examples/AmbientBacklight/AmbientBacklight.ino b/examples/AmbientBacklight/AmbientBacklight.ino index 93fedd65..bc31ecba 100644 --- a/examples/AmbientBacklight/AmbientBacklight.ino +++ b/examples/AmbientBacklight/AmbientBacklight.ino @@ -23,8 +23,10 @@ CRGB ledsChannel1[84]; CRGB ledsChannel2[105]; -CorsairLightingFirmware firmware = corsairLS100Firmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_SMART_LIGHTING_CONTROLLER, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/CommanderPRO/CommanderPRO.ino b/examples/CommanderPRO/CommanderPRO.ino index 9ebff631..f3429488 100644 --- a/examples/CommanderPRO/CommanderPRO.ino +++ b/examples/CommanderPRO/CommanderPRO.ino @@ -33,11 +33,12 @@ #define CHANNEL_LED_COUNT 96 -CorsairLightingFirmware firmware = corsairCommanderPROFirmware(); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_COMMANDER_PRO, &firmwareStorage); ThermistorTemperatureController temperatureController; -FastLEDController ledController(&temperatureController, true); -SimpleFanController fanController(&temperatureController, FAN_UPDATE_RATE, - EEPROM_ADDRESS + ledController.getEEPROMSize()); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); +SimpleFanController fanController(&temperatureController, FAN_UPDATE_RATE, EEPROM_ADDRESS + storage.getEEPROMSize()); CorsairLightingProtocolController cLP(&ledController, &temperatureController, &fanController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/CommanderPRO/SimpleFanController.cpp b/examples/CommanderPRO/SimpleFanController.cpp index b910ec5c..da1915c1 100644 --- a/examples/CommanderPRO/SimpleFanController.cpp +++ b/examples/CommanderPRO/SimpleFanController.cpp @@ -39,9 +39,9 @@ void SimpleFanController::addFan(uint8_t index, PWMFan* fan) { } bool SimpleFanController::updateFans() { - long currentUpdate = millis(); - long lastUpdateNumber = lastUpdate / updateRate; - long currentUpdateNumber = currentUpdate / updateRate; + unsigned long currentUpdate = millis(); + unsigned long lastUpdateNumber = lastUpdate / updateRate; + unsigned long currentUpdateNumber = currentUpdate / updateRate; lastUpdate = currentUpdate; if (lastUpdateNumber < currentUpdateNumber) { if (triggerSave) { diff --git a/examples/CommanderPRO/SimpleFanController.h b/examples/CommanderPRO/SimpleFanController.h index 61eb2999..a523b745 100644 --- a/examples/CommanderPRO/SimpleFanController.h +++ b/examples/CommanderPRO/SimpleFanController.h @@ -83,5 +83,5 @@ class SimpleFanController : public FanController { * Indicates that the configuration of the fans has been changed and should be saved. */ bool triggerSave = false; - long lastUpdate = 0; + unsigned long lastUpdate = 0; }; diff --git a/examples/DebugSketch/DebugSketch.ino b/examples/DebugSketch/DebugSketch.ino index 1a7281df..9cf56657 100644 --- a/examples/DebugSketch/DebugSketch.ino +++ b/examples/DebugSketch/DebugSketch.ino @@ -21,8 +21,10 @@ #define DATA_PIN_CHANNEL_1 2 #define DATA_PIN_CHANNEL_2 3 -CorsairLightingFirmware firmware = corsairLightingNodePROFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cLPS(&cLP); @@ -60,7 +62,7 @@ void loop() { void processCommand(String& cmd) { if (cmd == F("print DeviceID")) { - byte deviceId[4]; + DeviceID deviceId; firmware.getDeviceID(deviceId); CLP::printDeviceID(deviceId); Serial.println(); diff --git a/examples/DeviceIDTool/DeviceIDTool.ino b/examples/DeviceIDTool/DeviceIDTool.ino index 5d8aac87..0e9ac201 100644 --- a/examples/DeviceIDTool/DeviceIDTool.ino +++ b/examples/DeviceIDTool/DeviceIDTool.ino @@ -17,15 +17,15 @@ // UPLOAD THIS TO THE ARDUINO AND OPEN SERIAL MONITOR WITH BOUDRATE 115200 // #include -#include +#include -#define EEPROM_ADDRESS_DEVICE_ID 0 +CorsairLightingFirmwareStorageEEPROM firmwareStorage; void setup() { Serial.begin(115200); Serial.setTimeout(100); - uint8_t deviceID[4]; - EEPROM.get(EEPROM_ADDRESS_DEVICE_ID, deviceID); + DeviceID deviceID; + firmwareStorage.loadDeviceID(deviceID); while (!Serial) { ; // wait for serial port to connect. Needed for native USB @@ -47,12 +47,12 @@ void loop() { Serial.println(F("Do not forget the leading zeroes!")); Serial.println(); } else { - uint8_t newDeviceID[4]; + DeviceID newDeviceID; - newDeviceID[0] = strtol(&inputString[0], nullptr, 16); - newDeviceID[1] = strtol(&inputString[3], nullptr, 16); - newDeviceID[2] = strtol(&inputString[6], nullptr, 16); - newDeviceID[3] = strtol(&inputString[9], nullptr, 16); + newDeviceID.data[0] = strtol(&inputString[0], nullptr, 16); + newDeviceID.data[1] = strtol(&inputString[3], nullptr, 16); + newDeviceID.data[2] = strtol(&inputString[6], nullptr, 16); + newDeviceID.data[3] = strtol(&inputString[9], nullptr, 16); Serial.println(F("Set DeviceID to: ")); CLP::printDeviceID(newDeviceID); Serial.println(); @@ -66,7 +66,7 @@ void loop() { F("This is a special DeviceID, it will reset the device and then generate a new DeviceID!")); } Serial.println(); - EEPROM.put(EEPROM_ADDRESS_DEVICE_ID, newDeviceID); + firmwareStorage.saveDeviceID(newDeviceID); } } } \ No newline at end of file diff --git a/examples/HoodLoader2CLPBridge/CLPUSBSerialBridge.cpp b/examples/HoodLoader2CLPBridge/CLPUSBSerialBridge.cpp index 71688bf4..42069116 100644 --- a/examples/HoodLoader2CLPBridge/CLPUSBSerialBridge.cpp +++ b/examples/HoodLoader2CLPBridge/CLPUSBSerialBridge.cpp @@ -68,7 +68,7 @@ void CLPUSBSerialBridge::handleHID() { #ifdef DEBUG Serial.print(F("C")); Serial.println(rawHIDAndSerialBuffer[0], HEX); - long time = micros(); + unsigned long time = micros(); #endif // DEBUG if (!waitForSynchronization()) { #ifdef DEBUG @@ -93,7 +93,7 @@ void CLPUSBSerialBridge::handleHID() { sendResponse(); #ifdef DEBUG - long duration = micros() - time; + unsigned long duration = micros() - time; Serial.print(F("D")); Serial.println(duration); #endif // DEBUG diff --git a/examples/HoodLoader2UnoMegaController/HoodLoader2UnoMegaController.ino b/examples/HoodLoader2UnoMegaController/HoodLoader2UnoMegaController.ino index a3331fe3..cbbb0b00 100644 --- a/examples/HoodLoader2UnoMegaController/HoodLoader2UnoMegaController.ino +++ b/examples/HoodLoader2UnoMegaController/HoodLoader2UnoMegaController.ino @@ -23,8 +23,10 @@ #define DATA_PIN_CHANNEL_1 2 #define DATA_PIN_CHANNEL_2 3 -CorsairLightingFirmware firmware = corsairLightingNodePROFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolSerial cLPS(&cLP); diff --git a/examples/LS100/LS100.ino b/examples/LS100/LS100.ino index 415f22c8..c4444408 100644 --- a/examples/LS100/LS100.ino +++ b/examples/LS100/LS100.ino @@ -26,8 +26,10 @@ CRGB ledsChannel1[135]; CRGB ledsChannel2[54]; -CorsairLightingFirmware firmware = corsairLS100Firmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_SMART_LIGHTING_CONTROLLER, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/LT100/LT100.ino b/examples/LT100/LT100.ino index bcb04981..81235090 100644 --- a/examples/LT100/LT100.ino +++ b/examples/LT100/LT100.ino @@ -20,8 +20,10 @@ CRGB ledsChannel1[108]; -CorsairLightingFirmware firmware = corsairLT100Firmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_SMART_LIGHTING_TOWERS, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/LightingNodeCORE/LightingNodeCORE.ino b/examples/LightingNodeCORE/LightingNodeCORE.ino index 04e3f888..0dd5bb64 100644 --- a/examples/LightingNodeCORE/LightingNodeCORE.ino +++ b/examples/LightingNodeCORE/LightingNodeCORE.ino @@ -20,8 +20,10 @@ CRGB ledsChannel1[204]; -CorsairLightingFirmware firmware = corsairLightingNodeCOREFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_CORE, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/LightingNodePRO/LightingNodePRO.ino b/examples/LightingNodePRO/LightingNodePRO.ino index 040f49f3..6891b88b 100644 --- a/examples/LightingNodePRO/LightingNodePRO.ino +++ b/examples/LightingNodePRO/LightingNodePRO.ino @@ -22,8 +22,10 @@ CRGB ledsChannel1[96]; CRGB ledsChannel2[96]; -CorsairLightingFirmware firmware = corsairLightingNodePROFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/MultipleFans/MultipleFans.ino b/examples/MultipleFans/MultipleFans.ino index f06dba51..1e1132ec 100644 --- a/examples/MultipleFans/MultipleFans.ino +++ b/examples/MultipleFans/MultipleFans.ino @@ -29,8 +29,10 @@ CRGB ledsChannel1[96]; CRGB ledsChannel2[96]; -CorsairLightingFirmware firmware = corsairLightingNodePROFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/NoEEPROM/NoEEPROM.ino b/examples/NoEEPROM/NoEEPROM.ino new file mode 100644 index 00000000..1135392b --- /dev/null +++ b/examples/NoEEPROM/NoEEPROM.ino @@ -0,0 +1,45 @@ +/* + Copyright 2021 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include +#include + +#define DATA_PIN_CHANNEL_1 2 +#define DATA_PIN_CHANNEL_2 3 + +CRGB ledsChannel1[96]; +CRGB ledsChannel2[96]; + +DeviceID deviceID = {0x9A, 0xDA, 0xA7, 0x8E}; +CorsairLightingFirmwareStorageStatic firmwareStorage(deviceID); +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDController ledController(nullptr); +CorsairLightingProtocolController cLP(&ledController, &firmware); +CorsairLightingProtocolHID cHID(&cLP); + +void setup() { + FastLED.addLeds(ledsChannel1, 96); + FastLED.addLeds(ledsChannel2, 96); + ledController.addLEDs(0, ledsChannel1, 96); + ledController.addLEDs(1, ledsChannel2, 96); +} + +void loop() { + cHID.update(); + + if (ledController.updateLEDs()) { + FastLED.show(); + } +} diff --git a/examples/NonAddressable/NonAddressable.ino b/examples/NonAddressable/NonAddressable.ino index 0c960c6c..17c0466f 100644 --- a/examples/NonAddressable/NonAddressable.ino +++ b/examples/NonAddressable/NonAddressable.ino @@ -22,8 +22,10 @@ #define GREEN_PIN 5 #define BLUE_PIN 6 -CorsairLightingFirmware firmware = corsairLightingNodePROFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/RepeatAndScale/RepeatAndScale.ino b/examples/RepeatAndScale/RepeatAndScale.ino index c227b642..5814bed8 100644 --- a/examples/RepeatAndScale/RepeatAndScale.ino +++ b/examples/RepeatAndScale/RepeatAndScale.ino @@ -22,8 +22,10 @@ CRGB ledsChannel1[100]; CRGB ledsChannel2[144]; -CorsairLightingFirmware firmware = corsairLightingNodePROFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/SingleStripLightingNodePRO/SingleStripLightingNodePRO.ino b/examples/SingleStripLightingNodePRO/SingleStripLightingNodePRO.ino index 94a3086b..15fc8ef9 100644 --- a/examples/SingleStripLightingNodePRO/SingleStripLightingNodePRO.ino +++ b/examples/SingleStripLightingNodePRO/SingleStripLightingNodePRO.ino @@ -27,8 +27,10 @@ // In this example we use only one pin where both channel are connected in series. #define DATA_PIN 2 -CorsairLightingFirmware firmware = corsairLightingNodePROFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/TinyUSB/TinyUSB.ino b/examples/TinyUSB/TinyUSB.ino new file mode 100644 index 00000000..556dccc5 --- /dev/null +++ b/examples/TinyUSB/TinyUSB.ino @@ -0,0 +1,48 @@ +/* + Copyright 2021 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include +#include + +#define DATA_PIN_CHANNEL_1 2 +#define DATA_PIN_CHANNEL_2 3 + +CRGB ledsChannel1[96]; +CRGB ledsChannel2[96]; + +// Most ARM devices do not contain an EEPROM; we will use static storage for the Device ID +DeviceID deviceID = {0x9A, 0xDA, 0xA7, 0x8E}; +CorsairLightingFirmwareStorageStatic firmwareStorage(deviceID); +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDController ledController(nullptr); +CorsairLightingProtocolController cLP(&ledController, &firmware); +CorsairLightingProtocolTinyUSBHID cHID(&cLP); + +void setup() { + cHID.setup(); + + FastLED.addLeds(ledsChannel1, 96); + FastLED.addLeds(ledsChannel2, 96); + ledController.addLEDs(0, ledsChannel1, 96); + ledController.addLEDs(1, ledsChannel2, 96); +} + +void loop() { + cHID.update(); + + if (ledController.updateLEDs()) { + FastLED.show(); + } +} diff --git a/examples/TransformLLFansFormatToStrip/TransformLLFansFormatToStrip.ino b/examples/TransformLLFansFormatToStrip/TransformLLFansFormatToStrip.ino index 2f284b0b..49e7851b 100644 --- a/examples/TransformLLFansFormatToStrip/TransformLLFansFormatToStrip.ino +++ b/examples/TransformLLFansFormatToStrip/TransformLLFansFormatToStrip.ino @@ -22,8 +22,10 @@ CRGB ledsChannel1[96]; CRGB ledsChannel2[60]; -CorsairLightingFirmware firmware = corsairLightingNodePROFirmware(); -FastLEDController ledController(true); +CorsairLightingFirmwareStorageEEPROM firmwareStorage; +CorsairLightingFirmware firmware(CORSAIR_LIGHTING_NODE_PRO, &firmwareStorage); +FastLEDControllerStorageEEPROM storage; +FastLEDController ledController(&storage); CorsairLightingProtocolController cLP(&ledController, &firmware); CorsairLightingProtocolHID cHID(&cLP); diff --git a/examples/UnitTests/UnitTests.ino b/examples/UnitTests/UnitTests.ino index 57a33458..096386aa 100644 --- a/examples/UnitTests/UnitTests.ino +++ b/examples/UnitTests/UnitTests.ino @@ -37,7 +37,7 @@ protected: test(getLEDs) { CRGB leds[10]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); ledController.addLEDs(0, leds, 10); assertEqual(ledController.getLEDs(0), leds); assertEqual(ledController.getLEDs(1), nullptr); @@ -45,19 +45,19 @@ test(getLEDs) { testF(FastLEDControllerTest, simpleScaleUp) { CRGB leds[20]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 20, CRGB::Black); ledController.addLEDs(0, leds, 10); fill_solid(leds, 10, CRGB::White); CLP::scale(&ledController, 0, 20); - assertCRGBArray(leds, 0, 9, CRGB::White); + assertCRGBArray(leds, 0, 19, CRGB::White); } testF(FastLEDControllerTest, simpleScaleDown) { CRGB leds[20]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 20, CRGB::Black); ledController.addLEDs(0, leds, 20); @@ -70,7 +70,7 @@ testF(FastLEDControllerTest, simpleScaleDown) { testF(FastLEDControllerTest, simpleScaleDownBoundaries) { CRGB leds[20]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 20, CRGB::Black); ledController.addLEDs(0, leds, 20); @@ -85,7 +85,7 @@ testF(FastLEDControllerTest, simpleScaleDownBoundaries) { testF(FastLEDControllerTest, simpleScaleIdentity) { CRGB leds[20]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 20, CRGB::Black); ledController.addLEDs(0, leds, 10); @@ -98,7 +98,7 @@ testF(FastLEDControllerTest, simpleScaleIdentity) { testF(FastLEDControllerTest, scaleLongStrip) { CRGB leds[41]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 41, CRGB::Black); ledController.addLEDs(0, leds, 27); @@ -110,7 +110,7 @@ testF(FastLEDControllerTest, scaleLongStrip) { testF(FastLEDControllerTest, LT100) { CRGB leds[30]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 30, CRGB::Black); ledController.addLEDs(0, leds, 30); @@ -125,7 +125,7 @@ testF(FastLEDControllerTest, LT100) { testF(FastLEDControllerTest, singleSegmentScaleUp) { CRGB leds[20]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 20, CRGB::Black); ledController.addLEDs(0, leds, 10); @@ -138,7 +138,7 @@ testF(FastLEDControllerTest, singleSegmentScaleUp) { testF(FastLEDControllerTest, multiScaleUp) { CRGB leds[30]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 30, CRGB::Black); ledController.addLEDs(0, leds, 10); @@ -152,7 +152,7 @@ testF(FastLEDControllerTest, multiScaleUp) { testF(FastLEDControllerTest, multiScaleDown) { CRGB leds[30]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 30, CRGB::Black); ledController.addLEDs(0, leds, 30); @@ -166,7 +166,7 @@ testF(FastLEDControllerTest, multiScaleDown) { testF(FastLEDControllerTest, singleSegmentScaleDown) { CRGB leds[20]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 20, CRGB::Black); ledController.addLEDs(0, leds, 20); @@ -180,7 +180,7 @@ testF(FastLEDControllerTest, singleSegmentScaleDown) { testF(FastLEDControllerTest, SegmentScaleOverlap) { CRGB leds[15]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 15, CRGB::Black); ledController.addLEDs(0, leds, 15); @@ -194,7 +194,7 @@ testF(FastLEDControllerTest, SegmentScaleOverlap) { testF(FastLEDControllerTest, SegmentScaleOverlapInverted) { CRGB leds[15]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 15, CRGB::Black); ledController.addLEDs(0, leds, 15); @@ -208,7 +208,7 @@ testF(FastLEDControllerTest, SegmentScaleOverlapInverted) { testF(FastLEDControllerTest, SegmentScaleMix) { CRGB leds[30]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 30, CRGB::Black); ledController.addLEDs(0, leds, 30); @@ -225,7 +225,7 @@ testF(FastLEDControllerTest, SegmentScaleMix) { testF(FastLEDControllerTest, SegmentScaleMixInverted) { CRGB leds[30]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 30, CRGB::Black); ledController.addLEDs(0, leds, 25); @@ -242,7 +242,7 @@ testF(FastLEDControllerTest, SegmentScaleMixInverted) { testF(FastLEDControllerTest, SegmentScaleMonitor) { CRGB leds[130]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 130, CRGB::Black); ledController.addLEDs(0, leds, 84); @@ -261,7 +261,7 @@ testF(FastLEDControllerTest, SegmentScaleMonitor) { testF(FastLEDControllerTest, SegmentScaleLongStrip) { CRGB leds[41]; - FastLEDController ledController(false); + FastLEDController ledController(nullptr); fill_solid(leds, 41, CRGB::Black); ledController.addLEDs(0, leds, 27); diff --git a/examples/UnitTests2/UnitTests2.ino b/examples/UnitTests2/UnitTests2.ino new file mode 100644 index 00000000..6dd35c77 --- /dev/null +++ b/examples/UnitTests2/UnitTests2.ino @@ -0,0 +1,137 @@ +/* + Copyright 2020 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#line 17 "UnitTests2.ino" + +#include + +#include "FastLEDController.h" +#include "FastLEDControllerUtils.h" + +using namespace aunit; + +class FastLEDControllerTest : public TestOnce { +protected: + void assertCRGB(const CRGB& actual, const CRGB& expected) { + assertEqual(actual.r, expected.r); + assertEqual(actual.g, expected.g); + assertEqual(actual.b, expected.b); + } + void assertCRGBArray(const CRGB* const leds, int from, int to, const CRGB& expected) { + for (int i = from; i <= to; i++) { + assertCRGB(leds[i], expected); + } + } +}; + +class TestFastLEDController : public FastLEDController { +public: + TestFastLEDController() : FastLEDController(nullptr) { + } + + void setLastUpdate(unsigned long time) { + lastUpdate = time; + } + + void setCurrentUpdate(unsigned long time) { + currentUpdate = time; + } + + using FastLEDController::animation_step; + using FastLEDController::animation_step_count; +}; + +test(getLEDs) { + CRGB leds[10]; + FastLEDController ledController(nullptr); + ledController.addLEDs(0, leds, 10); + assertEqual(ledController.getLEDs(0), leds); + assertEqual(ledController.getLEDs(1), nullptr); +} + +testF(FastLEDControllerTest, simpleScaleUp) { + CRGB leds[20]; + FastLEDController ledController(nullptr); + fill_solid(leds, 20, CRGB::Black); + ledController.addLEDs(0, leds, 20); + + fill_solid(leds, 10, CRGB::White); + CLP::reverse(&ledController, 0); + + assertCRGBArray(leds, 0, 9, CRGB::Black); + assertCRGBArray(leds, 10, 19, CRGB::White); +} + +test(animation_step) { + TestFastLEDController ledController; + ledController.setLastUpdate(155); + ledController.setCurrentUpdate(165); + assertEqual(ledController.animation_step(5, 100), 0); + assertEqual(ledController.animation_step(10, 100), 50); + assertEqual(ledController.animation_step(20, 100), 25); + assertEqual(ledController.animation_step(40, 100), 12); + assertEqual(ledController.animation_step(5000, 100), 3); +} + +test(animation_step_long) { + TestFastLEDController ledController; + ledController.setLastUpdate(3000000000); + ledController.setCurrentUpdate(300000005); + assertEqual(ledController.animation_step(5, 100), 0); + assertEqual(ledController.animation_step(10, 100), 50); + assertEqual(ledController.animation_step(20, 100), 25); + assertEqual(ledController.animation_step(40, 100), 12); + assertEqual(ledController.animation_step(5000, 100), 0); +} + +test(animation_step_count) { + TestFastLEDController ledController; + ledController.setLastUpdate(155); + ledController.setCurrentUpdate(165); + assertEqual(ledController.animation_step_count(5, 100), 200); + assertEqual(ledController.animation_step_count(10, 100), 100); + assertEqual(ledController.animation_step_count(20, 100), 50); + assertEqual(ledController.animation_step_count(40, 100), 25); + assertEqual(ledController.animation_step_count(5000, 100), 0); +} + +test(animation_step_count_offset) { + TestFastLEDController ledController; + ledController.setLastUpdate(195); + ledController.setCurrentUpdate(205); + assertEqual(ledController.animation_step_count(10000, 100), 1); +} + +test(animation_step_count_long) { + TestFastLEDController ledController; + ledController.setLastUpdate(3000000000); + ledController.setCurrentUpdate(3000000010); + assertEqual(ledController.animation_step_count(5, 100), 200); + assertEqual(ledController.animation_step_count(10, 100), 100); + assertEqual(ledController.animation_step_count(20, 100), 50); + assertEqual(ledController.animation_step_count(40, 100), 25); + assertEqual(ledController.animation_step_count(5000, 100), 0); +} + +void setup() { + delay(1000); + Serial.begin(115200); + while (!Serial) + ; +} + +void loop() { + TestRunner::run(); +} diff --git a/extra/doxygen.conf b/extra/doxygen.conf index 326f5c86..c167f413 100644 --- a/extra/doxygen.conf +++ b/extra/doxygen.conf @@ -38,7 +38,7 @@ PROJECT_NAME = "Corsair Lighting Protocol" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.14.2 +PROJECT_NUMBER = 0.15.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/extra/images/board-wiring-pico.jpg b/extra/images/board-wiring-pico.jpg new file mode 100644 index 00000000..9a89eeb4 Binary files /dev/null and b/extra/images/board-wiring-pico.jpg differ diff --git a/extra/images/select-board-pico.png b/extra/images/select-board-pico.png new file mode 100644 index 00000000..cd7074e3 Binary files /dev/null and b/extra/images/select-board-pico.png differ diff --git a/keywords.txt b/keywords.txt index 8f0d66f7..e370f4c5 100644 --- a/keywords.txt +++ b/keywords.txt @@ -11,6 +11,7 @@ LEDController KEYWORD1 FastLEDController KEYWORD1 CorsairLightingProtocolController KEYWORD1 CorsairLightingProtocolHID KEYWORD1 +CorsairLightingProtocolTinyUSBHID KEYWORD1 CorsairLightingProtocolSerial KEYWORD1 CorsairLightingProtocolResponse KEYWORD1 CorsairLightingFirmware KEYWORD1 diff --git a/library.properties b/library.properties index 42785776..4d2cb98a 100644 --- a/library.properties +++ b/library.properties @@ -1,11 +1,11 @@ name=Corsair Lighting Protocol -version=0.14.2 +version=0.15.0 author=Leon Kiefer maintainer=Leon Kiefer sentence=Control LED strips via USB from a PC. paragraph=The library mimics Corsair LED Controller devices and can be controlled via USB in iCUE. category=Device Control url=https://github.com/Legion2/CorsairLightingProtocol -architectures=avr +architectures=avr,samd,rp2040 includes=CorsairLightingProtocol.h depends=FastLED diff --git a/src/CLPAdditionalFeatures.cpp b/src/CLPAdditionalFeatures.cpp index f180c218..7d3191dc 100644 --- a/src/CLPAdditionalFeatures.cpp +++ b/src/CLPAdditionalFeatures.cpp @@ -18,12 +18,12 @@ #include "CLPUtils.h" bool CLP::shouldReset(const CorsairLightingFirmware* firmware) { - byte deviceId[4]; + DeviceID deviceId; firmware->getDeviceID(deviceId); return CLP::isResetID(deviceId); } void CLP::reset(CorsairLightingFirmware* firmware) { - byte deviceId[4] = {0x00}; + DeviceID deviceId = {0x00}; firmware->setDeviceID(deviceId); } diff --git a/src/CLPUtils.cpp b/src/CLPUtils.cpp index fbde0dd6..1738225b 100644 --- a/src/CLPUtils.cpp +++ b/src/CLPUtils.cpp @@ -33,16 +33,18 @@ void CLP::disableBuildInLEDs() { #endif } -bool CLP::isNullID(const uint8_t* deviceId) { return !(deviceId[0] | deviceId[1] | deviceId[2] | deviceId[3]); } +bool CLP::isNullID(const DeviceID& deviceId) { + return !(deviceId.data[0] | deviceId.data[1] | deviceId.data[2] | deviceId.data[3]); +} -bool CLP::isResetID(const uint8_t* deviceId) { - return deviceId[0] == 0xFF && deviceId[1] == 0xFF && deviceId[2] == 0xFF && deviceId[3] == 0xFF; +bool CLP::isResetID(const DeviceID& deviceId) { + return deviceId.data[0] == 0xFF && deviceId.data[1] == 0xFF && deviceId.data[2] == 0xFF && deviceId.data[3] == 0xFF; } -void CLP::printDeviceID(const uint8_t* deviceId) { +void CLP::printDeviceID(const DeviceID& deviceId) { char tmp[16]; for (size_t i = 0; i < 4; i++) { - sprintf(tmp, "%.2X", deviceId[i]); + sprintf(tmp, "%.2X", deviceId.data[i]); Serial.print(tmp); if (i < 3) Serial.print(F(" ")); } @@ -56,7 +58,7 @@ void CLP::printFps(const int interval) { unsigned long now = millis(); frameCount++; - if (now - lastMillis >= interval) { + if (now - lastMillis >= (unsigned int)interval) { double framesPerSecond = (frameCount * 1000.0) / interval; Serial.print(F("FPS: ")); Serial.println(framesPerSecond, 1); @@ -64,3 +66,47 @@ void CLP::printFps(const int interval) { lastMillis = now; } } + +#if CLP_DEBUG +#if defined(CLP_DEBUG_PORT) + +int CLP::printf(const char* __restrict format, ...) { + char buf[64]; + int len; + va_list ap; + va_start(ap, format); + len = vsnprintf(buf, sizeof(buf), format, ap); + CLP_DEBUG_PORT.write(buf); + va_end(ap); + return len; +} + +int CLP::printf(const __FlashStringHelper* __restrict format, ...) { + char buf[64]; + char fmt[64]; + int len; + va_list ap; + va_start(ap, format); + strcpy_P(fmt, (const char*)format); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + CLP_DEBUG_PORT.write(buf); + va_end(ap); + return len; +} + +#endif // defined(CLP_DEBUG_PORT) + +void CLP::printData(uint8_t const* buf, uint32_t bufsize, bool address_table) { + if (address_table) { + clpPrintf(">>>> "); + for (uint32_t i = 0; i < 16; i++) clpPrintf("0x%X ", i); + clpPrintf("\r\n"); + } + for (uint32_t i = 0; i < bufsize; i++) { + if (address_table && (i % 16 == 0)) clpPrintf("0x%02X ", (i / 16) << 4); + clpPrintf(" %02X ", buf[i]); + if ((i + 1) % 16 == 0 || (i + 1) == bufsize) clpPrintf("\r\n"); + } +} + +#endif // CLP_DEBUG diff --git a/src/CLPUtils.h b/src/CLPUtils.h index 005d0f3e..9b6a068e 100644 --- a/src/CLPUtils.h +++ b/src/CLPUtils.h @@ -16,9 +16,118 @@ #pragma once #include "Arduino.h" +#include "CorsairLightingFirmware.h" #define toBigEndian(a) highByte(a), lowByte(a) +#if CLP_DEBUG + +#define CLP_STRCAT(a, b) a##b +#define CLP_STRCAT3(a, b, c) a##b##c + +#define CLP_XSTRCAT(a, b) CLP_STRCAT(a, b) +#define CLP_XSTRCAT3(a, b, c) CLP_STRCAT3(a, b, c) + +#if defined(CLP_DEBUG_PORT) +namespace CLP { +extern int printf(const char* format, ...); +extern int printf(const __FlashStringHelper* format, ...); +} // namespace CLP +#define clpPrintf CLP::printf +#else +#define clpPrintf printf +#endif + +namespace CLP { +static inline void printVar(uint8_t const* buf, uint32_t bufsize) { + for (uint32_t i = 0; i < bufsize; i++) clpPrintf("%02X ", buf[i]); +} +extern void printData(uint8_t const* buf, uint32_t bufsize, bool address_table); +} // namespace CLP + +/* clang-format off */ +#define CLP_LOG(n, ...) CLP_XSTRCAT(CLP_LOG, n)(__VA_ARGS__) +#define CLP_LOG_VAR(n, ...) CLP_XSTRCAT3(CLP_LOG, n, _VAR)(__VA_ARGS__) +#define CLP_LOG_HEX(n, ...) CLP_XSTRCAT3(CLP_LOG, n, _HEX)(__VA_ARGS__) +#define CLP_LOG_DAT(n, buf, size, at) CLP_XSTRCAT3(CLP_LOG, n, _DAT)(buf, size, at) +#define CLP_LOG_LOCATION() clpPrintf("%s: %d:\r\n", __PRETTY_FUNCTION__, __LINE__) +#define CLP_LOG_FAILED() clpPrintf("%s: %d: Failed\r\n", __PRETTY_FUNCTION__, __LINE__) +#define CLP_LOG_FUNC(func) func + +// Log Level 1: Error +#define CLP_LOG1 clpPrintf +#define CLP_LOG1_VAR(_x) CLP::printVar((uint8_t const*)(_x), sizeof(*(_x))) +#define CLP_LOG1_HEX(_x) clpPrintf(#_x " = %lX\r\n", (unsigned long) (_x) ) +#define CLP_LOG1_DAT(_x, _y, _z) CLP::printData((uint8_t const*)(_x), _y, _z) + +// Log Level 2: Warn +#if CLP_DEBUG >= 2 +#define CLP_LOG2 CLP_LOG1 +#define CLP_LOG2_VAR CLP_LOG1_VAR +#define CLP_LOG2_HEX CLP_LOG1_HEX +#define CLP_LOG2_DAT CLP_LOG1_DAT +#endif + +// Log Level 3: Info +#if CLP_DEBUG >= 3 +#define CLP_LOG3 CLP_LOG1 +#define CLP_LOG3_VAR CLP_LOG1_VAR +#define CLP_LOG3_HEX CLP_LOG1_HEX +#define CLP_LOG3_DAT CLP_LOG1_DAT +#endif + +// Log Level 4: Data +#if CLP_DEBUG >= 4 +#define CLP_LOG4 CLP_LOG1 +#define CLP_LOG4_VAR CLP_LOG1_VAR +#define CLP_LOG4_HEX CLP_LOG1_HEX +#define CLP_LOG4_DAT CLP_LOG1_DAT +#endif +/* clang-format on */ + +#endif // CLP_DEBUG + +#ifndef CLP_LOG +#define CLP_LOG(n, ...) +#define CLP_LOG_VAR(n, ...) +#define CLP_LOG_HEX(n, ...) +#define CLP_LOG_DAT(n, buf, size, at) +#define CLP_LOG_FUNC(n) +#endif + +#define CLP_LOG0(...) +#define CLP_LOG0_VAR(...) +#define CLP_LOG0_HEX(...) +#define CLP_LOG0_DAT(...) + +#ifndef CLP_LOG1 +#define CLP_LOG1(...) +#define CLP_LOG1_VAR(...) +#define CLP_LOG1_HEX(...) +#define CLP_LOG1_DAT(...) +#endif + +#ifndef CLP_LOG2 +#define CLP_LOG2(...) +#define CLP_LOG2_VAR(...) +#define CLP_LOG2_HEX(...) +#define CLP_LOG2_DAT(...) +#endif + +#ifndef CLP_LOG3 +#define CLP_LOG3(...) +#define CLP_LOG3_VAR(...) +#define CLP_LOG3_HEX(...) +#define CLP_LOG3_DAT(...) +#endif + +#ifndef CLP_LOG4 +#define CLP_LOG4(...) +#define CLP_LOG4_VAR(...) +#define CLP_LOG4_HEX(...) +#define CLP_LOG4_DAT(...) +#endif + namespace CLP { uint16_t fromBigEndian(const byte& byte1, const byte& byte2); @@ -41,14 +150,14 @@ inline uint8_t convert255To100(uint8_t value) { return value / 2.5546875f; } * * @param deviceId the device id to check */ -bool isNullID(const uint8_t* deviceId); +bool isNullID(const DeviceID& deviceId); /** * Check if a device id is the special reset id (FF FF FF FF). * * @param deviceId the device id to check */ -bool isResetID(const uint8_t* deviceId); +bool isResetID(const DeviceID& deviceId); /** * This will disable the RX and TX built in LEDs on Arduino Leonardo, Micro and Pro Micro. @@ -60,7 +169,7 @@ void disableBuildInLEDs(); * * @param deviceId the device id to print */ -void printDeviceID(const uint8_t* deviceId); +void printDeviceID(const DeviceID& deviceId); /* * Measure and print the framerate at the given interval in milliseconds. The higher this value the more precise the diff --git a/src/CorsairLightingFirmware.cpp b/src/CorsairLightingFirmware.cpp index ea9c4f53..69a15fbd 100644 --- a/src/CorsairLightingFirmware.cpp +++ b/src/CorsairLightingFirmware.cpp @@ -15,18 +15,12 @@ */ #include "CorsairLightingFirmware.h" -#include - const uint8_t bootloader_version[] PROGMEM = {0x00, 0x02}; -const uint8_t corsairLightingNodePROFirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x00, 0x0A, 0x04}; - -const uint8_t corsairCommanderPROFirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x00, 0x09, 0xD4}; - -const uint8_t corsairLT100FirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x01, 0x01, 0x38}; - -CorsairLightingFirmware::CorsairLightingFirmware(const uint8_t* firmwareVersion) : firmwareVersion(firmwareVersion) { - EEPROM.get(EEPROM_ADDRESS_DEVICE_ID, deviceId); +CorsairLightingFirmware::CorsairLightingFirmware(corsair_product_enum_t product, + CorsairLightingFirmwareStorage* storage) + : storage(storage), product(product) { + storage->loadDeviceID(deviceId); } void CorsairLightingFirmware::handleFirmwareCommand(const Command& command, @@ -38,16 +32,17 @@ void CorsairLightingFirmware::handleFirmwareCommand(const Command& command, break; } case READ_FIRMWARE_VERSION: { - response->send_P(firmwareVersion, FIRMWARE_VERSION_SIZE); + response->send_P(firmwareVersions[product], FIRMWARE_VERSION_SIZE); break; } case READ_DEVICE_ID: { - response->send(deviceId, sizeof(deviceId)); + response->send(deviceId.data, sizeof(deviceId)); break; } case WRITE_DEVICE_ID: { - setDeviceID(command.data); - response->send(deviceId, sizeof(deviceId)); + DeviceID deviceID = {command.data[0], command.data[1], command.data[2], command.data[3]}; + setDeviceID(deviceID); + response->send(deviceId.data, sizeof(deviceId)); break; } case READ_BOOTLOADER_VERSION: { @@ -60,31 +55,15 @@ void CorsairLightingFirmware::handleFirmwareCommand(const Command& command, } } -void CorsairLightingFirmware::getDeviceID(uint8_t* deviceID) const { memcpy(deviceID, deviceId, sizeof(deviceId)); } +void CorsairLightingFirmware::getDeviceID(DeviceID& deviceID) const { deviceID = deviceId; } -void CorsairLightingFirmware::setDeviceID(const uint8_t* deviceID) { - memcpy(deviceId, deviceID, sizeof(deviceId)); - EEPROM.put(EEPROM_ADDRESS_DEVICE_ID, deviceId); +void CorsairLightingFirmware::setDeviceID(const DeviceID& deviceID) { + deviceId = deviceID; + storage->saveDeviceID(deviceID); } uint8_t CorsairLightingFirmware::getStatus() { return status; } void CorsairLightingFirmware::setStatus(uint8_t a_status) { status = a_status; } -CorsairLightingFirmware corsairLightingNodePROFirmware() { - return CorsairLightingFirmware(corsairLightingNodePROFirmwareVersion); -} - -CorsairLightingFirmware corsairLightingNodeCOREFirmware() { - return CorsairLightingFirmware(corsairLightingNodePROFirmwareVersion); -} - -CorsairLightingFirmware corsairLS100Firmware() { - return CorsairLightingFirmware(corsairLightingNodePROFirmwareVersion); -} - -CorsairLightingFirmware corsairCommanderPROFirmware() { - return CorsairLightingFirmware(corsairCommanderPROFirmwareVersion); -} - -CorsairLightingFirmware corsairLT100Firmware() { return CorsairLightingFirmware(corsairLT100FirmwareVersion); } +uint8_t CorsairLightingFirmware::getProduct() { return product; } diff --git a/src/CorsairLightingFirmware.h b/src/CorsairLightingFirmware.h index ac2e2a2b..da310193 100644 --- a/src/CorsairLightingFirmware.h +++ b/src/CorsairLightingFirmware.h @@ -19,38 +19,52 @@ #include "CorsairLightingProtocolConstants.h" #include "CorsairLightingProtocolResponse.h" -#ifndef EEPROM_ADDRESS_DEVICE_ID -#define EEPROM_ADDRESS_DEVICE_ID 0 -#endif +#define FIRMWARE_VERSION_SIZE 3 -#define PROTOCOL_STATUS_OK 0x00 -#define PROTOCOL_STATUS_ERROR 0xFF +const uint8_t corsairLightingNodePROFirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x00, 0x0A, 0x04}; -#define FIRMWARE_VERSION_SIZE 3 +#define corsairLightingNodeCOREFirmwareVersion corsairLightingNodePROFirmwareVersion + +#define corsairLS100FirmwareVersion corsairLightingNodePROFirmwareVersion + +const uint8_t corsairCommanderPROFirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x00, 0x09, 0xD4}; + +const uint8_t corsairLT100FirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x01, 0x01, 0x38}; + +const uint8_t corsairCommanderCOREFirmwareVersion[FIRMWARE_VERSION_SIZE] PROGMEM = {0x02, 0x06, 0xC9}; + +const uint8_t* const firmwareVersions[] PROGMEM = { + corsairLightingNodePROFirmwareVersion, corsairCommanderPROFirmwareVersion, corsairLightingNodeCOREFirmwareVersion, + corsairLS100FirmwareVersion, corsairLT100FirmwareVersion, corsairCommanderCOREFirmwareVersion}; + +struct DeviceID { + uint8_t data[4]; +}; + +/** + * Interface to store the device id + */ +class CorsairLightingFirmwareStorage { +public: + virtual void loadDeviceID(DeviceID& deviceID) const = 0; + virtual void saveDeviceID(const DeviceID& deviceID) = 0; +}; class CorsairLightingFirmware { public: - CorsairLightingFirmware(const uint8_t* firmwareVersion); + CorsairLightingFirmware(corsair_product_enum_t product, CorsairLightingFirmwareStorage* storage); void handleFirmwareCommand(const Command& command, const CorsairLightingProtocolResponse* response); - void getDeviceID(uint8_t* deviceID) const; - void setDeviceID(const uint8_t* deviceID); + void getDeviceID(DeviceID& deviceID) const; + void setDeviceID(const DeviceID& deviceID); uint8_t getStatus(); void setStatus(uint8_t status); + uint8_t getProduct(); protected: - const uint8_t* firmwareVersion; - uint8_t deviceId[4]; + CorsairLightingFirmwareStorage* const storage; + const corsair_product_enum_t product; + DeviceID deviceId; private: uint8_t status = PROTOCOL_STATUS_OK; }; - -CorsairLightingFirmware corsairLightingNodePROFirmware(); - -CorsairLightingFirmware corsairLightingNodeCOREFirmware(); - -CorsairLightingFirmware corsairLS100Firmware(); - -CorsairLightingFirmware corsairCommanderPROFirmware(); - -CorsairLightingFirmware corsairLT100Firmware(); diff --git a/src/CorsairLightingFirmwareStorageEEPROM.cpp b/src/CorsairLightingFirmwareStorageEEPROM.cpp new file mode 100644 index 00000000..e45e21e8 --- /dev/null +++ b/src/CorsairLightingFirmwareStorageEEPROM.cpp @@ -0,0 +1,29 @@ +/* + Copyright 2021 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include "CorsairLightingFirmwareStorageEEPROM.h" + +#if defined(ARDUINO_ARCH_AVR) + +#include + +void CorsairLightingFirmwareStorageEEPROM::loadDeviceID(DeviceID& deviceID) const { + EEPROM.get(EEPROM_ADDRESS_DEVICE_ID, deviceID); +} +void CorsairLightingFirmwareStorageEEPROM::saveDeviceID(const DeviceID& deviceID) { + EEPROM.put(EEPROM_ADDRESS_DEVICE_ID, deviceID); +} + +#endif diff --git a/src/CorsairLightingFirmwareStorageEEPROM.h b/src/CorsairLightingFirmwareStorageEEPROM.h new file mode 100644 index 00000000..66074162 --- /dev/null +++ b/src/CorsairLightingFirmwareStorageEEPROM.h @@ -0,0 +1,32 @@ +/* + Copyright 2021 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#pragma once + +#if defined(ARDUINO_ARCH_AVR) + +#include "CorsairLightingFirmware.h" + +#ifndef EEPROM_ADDRESS_DEVICE_ID +#define EEPROM_ADDRESS_DEVICE_ID 0 +#endif + +class CorsairLightingFirmwareStorageEEPROM : public CorsairLightingFirmwareStorage { +public: + virtual void loadDeviceID(DeviceID& deviceID) const override; + virtual void saveDeviceID(const DeviceID& deviceID) override; +}; + +#endif diff --git a/src/CorsairLightingFirmwareStorageStatic.cpp b/src/CorsairLightingFirmwareStorageStatic.cpp new file mode 100644 index 00000000..f80e7ac5 --- /dev/null +++ b/src/CorsairLightingFirmwareStorageStatic.cpp @@ -0,0 +1,23 @@ +/* + Copyright 2021 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include "CorsairLightingFirmwareStorageStatic.h" + +CorsairLightingFirmwareStorageStatic::CorsairLightingFirmwareStorageStatic(DeviceID& deviceID) : deviceID(deviceID) {} + +void CorsairLightingFirmwareStorageStatic::loadDeviceID(DeviceID& deviceID) const { deviceID = this->deviceID; } +void CorsairLightingFirmwareStorageStatic::saveDeviceID(const DeviceID& deviceID) { + // DeviceID can not be updated +} diff --git a/src/CorsairLightingFirmwareStorageStatic.h b/src/CorsairLightingFirmwareStorageStatic.h new file mode 100644 index 00000000..e3c8f983 --- /dev/null +++ b/src/CorsairLightingFirmwareStorageStatic.h @@ -0,0 +1,28 @@ +/* + Copyright 2021 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#pragma once + +#include "CorsairLightingFirmware.h" + +class CorsairLightingFirmwareStorageStatic : public CorsairLightingFirmwareStorage { +private: + const DeviceID deviceID; + +public: + CorsairLightingFirmwareStorageStatic(DeviceID& deviceID); + virtual void loadDeviceID(DeviceID& deviceID) const override; + virtual void saveDeviceID(const DeviceID& deviceID) override; +}; diff --git a/src/CorsairLightingProtocol.h b/src/CorsairLightingProtocol.h index 0374bce1..af4c6404 100644 --- a/src/CorsairLightingProtocol.h +++ b/src/CorsairLightingProtocol.h @@ -22,13 +22,18 @@ #include "CLPAdditionalFeatures.h" #include "CLPUtils.h" #include "CorsairLightingFirmware.h" +#include "CorsairLightingFirmwareStorageEEPROM.h" +#include "CorsairLightingFirmwareStorageStatic.h" #include "CorsairLightingProtocolConstants.h" #include "CorsairLightingProtocolController.h" #include "CorsairLightingProtocolHID.h" #include "CorsairLightingProtocolResponse.h" #include "CorsairLightingProtocolSerial.h" +#include "CorsairLightingProtocolTinyUSBHID.h" #include "FanController.h" #include "FastLEDController.h" +#include "FastLEDControllerStorage.h" +#include "FastLEDControllerStorageEEPROM.h" #include "FastLEDControllerUtils.h" #include "IFanController.h" #include "ILEDController.h" diff --git a/src/CorsairLightingProtocolConstants.h b/src/CorsairLightingProtocolConstants.h index ef50fb7b..0ead66ba 100644 --- a/src/CorsairLightingProtocolConstants.h +++ b/src/CorsairLightingProtocolConstants.h @@ -17,6 +17,13 @@ #include "Arduino.h" +// CLP_DEBUG: 0=Off, 1=Error, 2=Warning, 3=Info, 4=Data (Only for TinyUSB) +// CLP_DEBUG_BAUD: Setting too low will severely affect performance depending on debug level; +// 2000000 needed when CLP_DEBUG is 4 +#define CLP_DEBUG 0 +#define CLP_DEBUG_PORT Serial1 +#define CLP_DEBUG_BAUD 115200 + #define COMMAND_SIZE 64 #define RESPONSE_SIZE 16 @@ -60,6 +67,36 @@ #define PROTOCOL_RESPONSE_OK 0x00 #define PROTOCOL_RESPONSE_ERROR 0x01 +#define PROTOCOL_STATUS_OK 0x00 +#define PROTOCOL_STATUS_ERROR 0xFF + +#ifndef SERIAL_NUMBER +#define SERIAL_NUMBER "FB66DF55421900F5" +#endif + +#define CORSAIR_MANUFACTURER "Corsair" +#define CORSAIR_VID 0x1B1C +#define CORSAIR_LNP_PRODUCT "Lighting Node PRO" +#define CORSAIR_LNP_PID 0x0C0B +#define CORSAIR_CP_PRODUCT "Commander PRO" +#define CORSAIR_CP_PID 0x0C10 +#define CORSAIR_LNC_PRODUCT "Lighting Node CORE" +#define CORSAIR_LNC_PID 0x0C1A +#define CORSAIR_SLC_PRODUCT "Smart Lighting Controller" +#define CORSAIR_SLC_PID 0x0C1E +#define CORSAIR_SLT_PRODUCT "Smart Lighting Towers" +#define CORSAIR_SLT_PID 0x0C23 +#define CORSAIR_CC_PRODUCT "CORSAIR iCUE Commander CORE" +#define CORSAIR_CC_PID 0x0C1C + +typedef enum { + CORSAIR_LIGHTING_NODE_PRO = 0, + CORSAIR_COMMANDER_PRO, + CORSAIR_LIGHTING_NODE_CORE, + CORSAIR_SMART_LIGHTING_CONTROLLER, + CORSAIR_SMART_LIGHTING_TOWERS, + CORSAIR_COMMANDER_CORE // Currently not functional +} corsair_product_enum_t; struct Command { union { diff --git a/src/CorsairLightingProtocolController.cpp b/src/CorsairLightingProtocolController.cpp index 3f4de55e..7da95368 100644 --- a/src/CorsairLightingProtocolController.cpp +++ b/src/CorsairLightingProtocolController.cpp @@ -55,3 +55,5 @@ void CorsairLightingProtocolController::handleCommand(const Command& command, response->sendError(); } } + +CorsairLightingFirmware* CorsairLightingProtocolController::getFirmware(void) { return corsairLightingFirmware; } \ No newline at end of file diff --git a/src/CorsairLightingProtocolController.h b/src/CorsairLightingProtocolController.h index a05c9412..ee279d41 100644 --- a/src/CorsairLightingProtocolController.h +++ b/src/CorsairLightingProtocolController.h @@ -58,6 +58,8 @@ class CorsairLightingProtocolController { */ void handleCommand(const Command& command, CorsairLightingProtocolResponse* response); + CorsairLightingFirmware* getFirmware(void); + private: CorsairLightingFirmware* const corsairLightingFirmware; ILEDController* const ledController; diff --git a/src/CorsairLightingProtocolSerial.cpp b/src/CorsairLightingProtocolSerial.cpp index d7c9d00a..878b3647 100644 --- a/src/CorsairLightingProtocolSerial.cpp +++ b/src/CorsairLightingProtocolSerial.cpp @@ -45,7 +45,7 @@ bool CorsairLightingProtocolSerial::handleSerial() { delayMicroseconds(100); if (Serial.available()) { - size_t read = Serial.readBytes(rawCommand, sizeof(rawCommand)); + size_t read = Serial.readBytes((char*)rawCommand, sizeof(rawCommand)); if (read == sizeof(rawCommand)) { return true; } else { diff --git a/src/CorsairLightingProtocolTinyUSBHID.cpp b/src/CorsairLightingProtocolTinyUSBHID.cpp new file mode 100644 index 00000000..b8b17bef --- /dev/null +++ b/src/CorsairLightingProtocolTinyUSBHID.cpp @@ -0,0 +1,93 @@ +#include "CorsairLightingProtocolTinyUSBHID.h" + +#if defined(USE_TINYUSB) + +const char* corsairProducts[] = {CORSAIR_LNP_PRODUCT, CORSAIR_CP_PRODUCT, CORSAIR_LNC_PRODUCT, + CORSAIR_SLC_PRODUCT, CORSAIR_SLT_PRODUCT, CORSAIR_CC_PRODUCT}; +const int corsairPIDs[] = {CORSAIR_LNP_PID, CORSAIR_CP_PID, CORSAIR_LNC_PID, + CORSAIR_SLC_PID, CORSAIR_SLT_PID, CORSAIR_CC_PID}; + +Command CorsairLightingProtocolTinyUSBHID::command; +int CorsairLightingProtocolTinyUSBHID::newData; + +/* clang-format off */ +uint8_t const hid_report[] = {HID_USAGE_PAGE_N (HID_USAGE_PAGE_VENDOR | 0xC0, 2), + HID_USAGE_N (0x0C00, 2), + HID_COLLECTION (HID_COLLECTION_APPLICATION ), + HID_REPORT_SIZE (8 ), + HID_LOGICAL_MIN (0x00 ), + HID_LOGICAL_MAX_N(0xff, 2), + HID_REPORT_COUNT (RESPONSE_SIZE ), + HID_USAGE (0x01 ), + HID_INPUT (HID_DATA | HID_VARIABLE | HID_ABSOLUTE), + HID_REPORT_COUNT (COMMAND_SIZE ), + HID_USAGE (0x02 ), + HID_OUTPUT (HID_DATA | HID_VARIABLE | HID_ABSOLUTE), + HID_COLLECTION_END}; +/* clang-format on */ + +Adafruit_USBD_HID tudHid(hid_report, sizeof(hid_report), HID_ITF_PROTOCOL_NONE, 1, true); + +uint16_t get_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { + (void)report_id; + (void)report_type; + (void)buffer; + (void)reqlen; + + CLP_LOG(2, F("Get report callback!\r\n")); + + return 0; +} + +void set_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { + (void)report_id; + (void)report_type; + + if (bufsize <= sizeof(Command)) { + memcpy(&CorsairLightingProtocolTinyUSBHID::command.raw, buffer, bufsize); + CorsairLightingProtocolTinyUSBHID::newData = 1; + + CLP_LOG(3, F("Received command: %02X\r\n"), CorsairLightingProtocolTinyUSBHID::command.command); + CLP_LOG_DAT(4, &CorsairLightingProtocolTinyUSBHID::command.raw, + sizeof(CorsairLightingProtocolTinyUSBHID::command), true); + } else { + CLP_LOG(2, F("Command too large\r\n")); + } +} + +CorsairLightingProtocolTinyUSBHID::CorsairLightingProtocolTinyUSBHID(CorsairLightingProtocolController* controller) + : controller(controller), serialNumber(SERIAL_NUMBER) {} + +CorsairLightingProtocolTinyUSBHID::CorsairLightingProtocolTinyUSBHID(CorsairLightingProtocolController* controller, + const char* serialNumber) + : controller(controller), serialNumber(serialNumber) {} + +void CorsairLightingProtocolTinyUSBHID::setup(void) { + CLP_LOG_FUNC(CLP_DEBUG_PORT.begin(CLP_DEBUG_BAUD)); + + TinyUSBDevice.setManufacturerDescriptor(CORSAIR_MANUFACTURER); + TinyUSBDevice.setProductDescriptor(corsairProducts[controller->getFirmware()->getProduct()]); + TinyUSBDevice.setID(CORSAIR_VID, corsairPIDs[controller->getFirmware()->getProduct()]); + TinyUSBDevice.setSerialDescriptor(serialNumber); + + tudHid.setReportCallback(get_report_callback, set_report_callback); + tudHid.begin(); + + while (!TinyUSBDevice.mounted()) delay(1); +} + +void CorsairLightingProtocolTinyUSBHID::update(void) { + if (newData) { + controller->handleCommand(command, this); + newData = 0; + } +} + +void CorsairLightingProtocolTinyUSBHID::sendX(const uint8_t* data, const size_t x) const { + tudHid.sendReport(0, data, x); + + CLP_LOG(3, F("Sent response: %02X\r\n"), data[0]); + CLP_LOG_DAT(4, data, x, true); +} + +#endif \ No newline at end of file diff --git a/src/CorsairLightingProtocolTinyUSBHID.h b/src/CorsairLightingProtocolTinyUSBHID.h new file mode 100644 index 00000000..c8383833 --- /dev/null +++ b/src/CorsairLightingProtocolTinyUSBHID.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Arduino.h" +#include "CLPUtils.h" +#include "CorsairLightingProtocolConstants.h" +#include "CorsairLightingProtocolController.h" +#include "CorsairLightingProtocolResponse.h" + +#if defined(USE_TINYUSB) + +#include "Adafruit_TinyUSB.h" + +class CorsairLightingProtocolTinyUSBHID : CorsairLightingProtocolResponse { +public: + /** + * Create a new adapter for CorsairLightingProtocolController using the default Serial Number. + * + * @param controller the CorsairLightingProtocolController + */ + CorsairLightingProtocolTinyUSBHID(CorsairLightingProtocolController* controller); + /** + * Create a new adapter for CorsairLightingProtocolController using the given Serial Number for the usb interface. + * + * @param controller the CorsairLightingProtocolController + * @param serialNumber the Serial Number used for the USB interface + */ + CorsairLightingProtocolTinyUSBHID(CorsairLightingProtocolController* controller, const char* serialNumber); + /** + * Setup the TinyUSB HID connection. + * This function must be called in setup. + */ + void setup(void); + /** + * Read commands form HID interface and pass them to the contoller. This function must be called in loop. + */ + void update(void); + + static Command command; + + static int newData; + +protected: + CorsairLightingProtocolController* const controller; + const char* serialNumber; + void sendX(const uint8_t* data, const size_t x) const override; +}; + +#endif \ No newline at end of file diff --git a/src/FanController.cpp b/src/FanController.cpp index e8e5e874..d68beee6 100644 --- a/src/FanController.cpp +++ b/src/FanController.cpp @@ -29,6 +29,7 @@ bool isValidFanDetectionType(const FanDetectionType type) { void FanController::handleFanControl(const Command& command, const CorsairLightingProtocolResponse* response) { switch (command.command) { case READ_FAN_MASK: { + CLP_LOG(3, F("Read fan mask\r\n")); FanMask mask[FAN_NUM]; for (uint8_t i = 0; i < FAN_NUM; i++) { switch (getFanDetectionType(i)) { @@ -50,8 +51,10 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } case READ_FAN_SPEED: { + CLP_LOG(3, F("Read fan speed\r\n")); const uint8_t& fan = command.data[0]; if (fan >= FAN_NUM) { + CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); response->sendError(); return; } @@ -61,8 +64,10 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } case READ_FAN_POWER: { + CLP_LOG(3, F("Read fan power\r\n")); const uint8_t& fan = command.data[0]; if (fan >= FAN_NUM) { + CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); response->sendError(); return; } @@ -72,8 +77,10 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } case WRITE_FAN_POWER: { + CLP_LOG(3, F("Write fan power\r\n")); const uint8_t& fan = command.data[0]; if (fan >= FAN_NUM) { + CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); response->sendError(); return; } @@ -83,8 +90,10 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } case WRITE_FAN_SPEED: { + CLP_LOG(3, F("Write fan speed\r\n")); const uint8_t& fan = command.data[0]; if (fan >= FAN_NUM) { + CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); response->sendError(); return; } @@ -94,8 +103,10 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } case WRITE_FAN_CURVE: { + CLP_LOG(3, F("Write fan curve\r\n")); const uint8_t& fan = command.data[0]; if (fan >= FAN_NUM) { + CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); response->sendError(); return; } @@ -110,8 +121,10 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } case WRITE_FAN_EXTERNAL_TEMP: { + CLP_LOG(3, F("Write fan external temp\r\n")); const uint8_t& fan = command.data[0]; if (fan >= FAN_NUM) { + CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); response->sendError(); return; } @@ -121,6 +134,7 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } case WRITE_FAN_FORCE_THREE_PIN_MODE: { + CLP_LOG(3, F("Write fan 3-pin mode\r\n")); if (command.data[0] == FAN_FORCE_THREE_PIN_MODE_ON) { setFanForce3PinMode(true); } else if (command.data[0] == FAN_FORCE_THREE_PIN_MODE_OFF) { @@ -134,17 +148,20 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } case WRITE_FAN_DETECTION_TYPE: { + CLP_LOG(3, F("Write fan detect type\r\n")); if (command.data[0] != 0x02) { response->sendError(); return; } const uint8_t& fan = command.data[1]; if (fan >= FAN_NUM) { + CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); response->sendError(); return; } const FanDetectionType type = static_cast(command.data[2]); if (!isValidFanDetectionType(type)) { + CLP_LOG(1, F("Invalid fan detection type: %d\r\n"), type); response->sendError(); return; } @@ -153,12 +170,14 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } case READ_FAN_DETECTION_TYPE: { + CLP_LOG(3, F("Read fan detect type\r\n")); if (command.data[0] != 0x01) { response->sendError(); return; } const uint8_t& fan = command.data[1]; if (fan >= FAN_NUM) { + CLP_LOG(1, F("Invalid fan: %d\r\n"), fan); response->sendError(); return; } @@ -168,6 +187,8 @@ void FanController::handleFanControl(const Command& command, const CorsairLighti break; } default: + CLP_LOG(1, F("Unkown command: %02X\r\n"), command.command); response->sendError(); + return; } } diff --git a/src/FastLEDController.cpp b/src/FastLEDController.cpp index a7620e6d..8bea4673 100644 --- a/src/FastLEDController.cpp +++ b/src/FastLEDController.cpp @@ -15,12 +15,13 @@ */ #include "FastLEDController.h" -#include - -FastLEDController::FastLEDController(bool useEEPROM) : temperatureController(nullptr), useEEPROM(useEEPROM) { load(); } +FastLEDController::FastLEDController(FastLEDControllerStorage* storage) + : temperatureController(nullptr), storage(storage) { + load(); +} -FastLEDController::FastLEDController(TemperatureController* temperatureController, bool useEEPROM) - : temperatureController(temperatureController), useEEPROM(useEEPROM) { +FastLEDController::FastLEDController(TemperatureController* temperatureController, FastLEDControllerStorage* storage) + : temperatureController(temperatureController), storage(storage) { load(); } @@ -71,15 +72,15 @@ int FastLEDController::applySpeed(int duration, const GroupSpeed speed) { } int FastLEDController::animation_step(int duration, int steps) { - int currentStep = ((currentUpdate % duration) / ((float)duration)) * steps; + int currentStep = ((currentUpdate % (unsigned int)duration) / ((float)duration)) * steps; return currentStep; } int FastLEDController::animation_step_count(int duration, int steps) { - long lastAnimationNumber = lastUpdate / duration; - long currentAnimationNumber = currentUpdate / duration; - int lastStep = ((lastUpdate % duration) / ((float)duration)) * steps; - int currentStep = ((currentUpdate % duration) / ((float)duration)) * steps; + unsigned long lastAnimationNumber = lastUpdate / (unsigned int)duration; + unsigned long currentAnimationNumber = currentUpdate / (unsigned int)duration; + int lastStep = ((lastUpdate % (unsigned int)duration) / ((float)duration)) * steps; + int currentStep = ((currentUpdate % (unsigned int)duration) / ((float)duration)) * steps; return currentStep - lastStep + (currentAnimationNumber - lastAnimationNumber) * steps; } @@ -418,6 +419,7 @@ bool FastLEDController::updateLEDs() { break; } default: { + CLP_LOG(3, F("Unkown group mode: %02X\r\n"), group.mode); #ifdef DEBUG Serial.print(F("unkown group mode: ")); Serial.print(group.mode, HEX); @@ -455,34 +457,36 @@ bool FastLEDController::updateLEDs() { return anyUpdate; } -size_t FastLEDController::getEEPROMSize() { return sizeof(channels); } - void FastLEDController::onUpdateHook(uint8_t channel, void (*callback)(void)) { channelData[channel].onUpdateCallback = callback; } bool FastLEDController::load() { - if (useEEPROM) { - EEPROM.get(EEPROM_ADDRESS, channels); - for (LEDChannel& channel : channels) { - if (!isValidLEDChannel(channel)) { - channel = LEDChannel(); - } + if (storage == nullptr) { + return false; + } + + for (int i = 0; i < CHANNEL_NUM; i++) { + storage->load(i, channels[i]); + } + + for (LEDChannel& channel : channels) { + if (!isValidLEDChannel(channel)) { + channel = LEDChannel(); } - return true; } - return false; + return true; } bool FastLEDController::save() { - if (useEEPROM) { -#ifdef DEBUG - Serial.println(F("Save to EEPROM.")); -#endif - EEPROM.put(EEPROM_ADDRESS, channels); - return true; + if (storage == nullptr) { + return false; } - return false; + + for (int i = 0; i < CHANNEL_NUM; i++) { + storage->save(i, channels[i]); + } + return true; } void FastLEDController::triggerLEDUpdate() { trigger_update = true; } diff --git a/src/FastLEDController.h b/src/FastLEDController.h index a520f45b..77f066a9 100644 --- a/src/FastLEDController.h +++ b/src/FastLEDController.h @@ -18,13 +18,10 @@ #include #include "Arduino.h" +#include "FastLEDControllerStorage.h" #include "LEDController.h" #include "TemperatureController.h" -#ifndef EEPROM_ADDRESS -#define EEPROM_ADDRESS 4 -#endif - #ifndef LED_CONTROLLER_TIMEOUT #define LED_CONTROLLER_TIMEOUT 30000 #endif @@ -57,22 +54,22 @@ class FastLEDController : public LEDController { public: /** - * Create a new FastLEDController and specify if the EEPROM of the Arduino should be used. See the other contructor - * for more details. + * Create a new FastLEDController and specify which storage should be used. The EEPROM of the Arduino can be used as + * Storage. See the other contructor for more details. * - * @param useEEPROM specify if the EEPROM should be used + * @param storage specify the storage which should be used, e.g. EEPROM */ - FastLEDController(bool useEEPROM); + FastLEDController(FastLEDControllerStorage* storage); /** - * Create a new FastLEDController and specify if the EEPROM of the Arduino should be used to store persistent - * information like the Hardware Lighting. If enabled, the hardware lighting configured in iCUE works without a USB - * connection and even after a restart of the Arduino. Also the the TemperatureController used for temperature - * related lighting can be passed here. + * Create a new FastLEDController and specify which storage should be used. If the EEPROM of the Arduino should be + * used to store persistent information like the Hardware Lighting use {@code FastLEDControllerStorageEEPROM}. If + * enabled, the hardware lighting configured in iCUE works without a USB connection and even after a restart of the + * Arduino. Also the the TemperatureController used for temperature related lighting can be passed here. * * @param temperatureController used for temperature based lighting - * @param useEEPROM specify if the EEPROM should be used + * @param storage specify the storage which should be used, e.g. EEPROM */ - FastLEDController(TemperatureController* temperatureController, bool useEEPROM); + FastLEDController(TemperatureController* temperatureController, FastLEDControllerStorage* storage); ~FastLEDController(); /** * Add a LED array on a channel with a given length. The length define how many LEDs iCUE can control. The actual @@ -106,12 +103,6 @@ class FastLEDController : public LEDController { * @return true if the LED data of a channel was updated, false otherwise */ virtual bool updateLEDs(); - /** - * Get the total size of all data stored in EEPROM by this LEDController. - * - * @return the size in bytes - */ - virtual size_t getEEPROMSize(); /** * Register an update hook, which is executed after a channel has been updated. This can be used to apply * transforamtions to the channel before the data is displayed by FastLED. @@ -123,13 +114,14 @@ class FastLEDController : public LEDController { protected: TemperatureController* const temperatureController; + FastLEDControllerStorage* const storage; bool trigger_update = false; ChannelData channelData[CHANNEL_NUM]; - long lastUpdate = 0; - long currentUpdate = 0; + unsigned long lastUpdate = 0; + unsigned long currentUpdate = 0; int applySpeed(int duration, const GroupSpeed speed); /** @@ -168,7 +160,6 @@ class FastLEDController : public LEDController { bool renderSequential(ChannelData& channelData, LEDGroup& group, int groupLedCount); bool renderRainbow(ChannelData& channelData, LEDGroup& group, int groupLedCount); - const bool useEEPROM; bool load() override; bool save() override; diff --git a/src/FastLEDControllerStorage.h b/src/FastLEDControllerStorage.h new file mode 100644 index 00000000..366458cc --- /dev/null +++ b/src/FastLEDControllerStorage.h @@ -0,0 +1,24 @@ +/* + Copyright 2021 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#pragma once + +#include "LEDController.h" + +class FastLEDControllerStorage { +public: + virtual bool load(const int index, LEDChannel& channel) = 0; + virtual bool save(const int index, const LEDChannel& channel) = 0; +}; diff --git a/src/FastLEDControllerStorageEEPROM.cpp b/src/FastLEDControllerStorageEEPROM.cpp new file mode 100644 index 00000000..c96ba30d --- /dev/null +++ b/src/FastLEDControllerStorageEEPROM.cpp @@ -0,0 +1,38 @@ +/* + Copyright 2021 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include "FastLEDControllerStorageEEPROM.h" + +#if defined(ARDUINO_ARCH_AVR) + +#include + +bool FastLEDControllerStorageEEPROM::load(const int index, LEDChannel& channel) { + EEPROM.get(EEPROM_ADDRESS + (sizeof(LEDChannel) * index), channel); + return true; +} + +bool FastLEDControllerStorageEEPROM::save(const int index, const LEDChannel& channel) { + CLP_LOG(3, F("Save to EEPROM.\r\n")); +#ifdef DEBUG + Serial.println(F("Save to EEPROM.")); +#endif + EEPROM.put(EEPROM_ADDRESS + (sizeof(LEDChannel) * index), channel); + return true; +} + +size_t FastLEDControllerStorageEEPROM::getEEPROMSize() { return sizeof(LEDChannel) * CHANNEL_NUM; } + +#endif diff --git a/src/FastLEDControllerStorageEEPROM.h b/src/FastLEDControllerStorageEEPROM.h new file mode 100644 index 00000000..a50a8ff1 --- /dev/null +++ b/src/FastLEDControllerStorageEEPROM.h @@ -0,0 +1,38 @@ +/* + Copyright 2021 Leon Kiefer + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#pragma once + +#if defined(ARDUINO_ARCH_AVR) + +#include "FastLEDControllerStorage.h" + +#ifndef EEPROM_ADDRESS +#define EEPROM_ADDRESS 4 +#endif + +class FastLEDControllerStorageEEPROM : public FastLEDControllerStorage { +public: + virtual bool load(const int index, LEDChannel& channel) override; + virtual bool save(const int index, const LEDChannel& channel) override; + /** + * Get the total size of all data stored in EEPROM by this LEDController. + * + * @return the size in bytes + */ + virtual size_t getEEPROMSize(); +}; + +#endif diff --git a/src/LEDController.cpp b/src/LEDController.cpp index c0423329..e38897f7 100644 --- a/src/LEDController.cpp +++ b/src/LEDController.cpp @@ -21,6 +21,7 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti lastCommand = millis(); auto& data = command.data; if (command.command == WRITE_LED_TRIGGER) { + CLP_LOG(3, F("Write LED trigger\r\n")); triggerLEDUpdate(); saveIfNeeded(); } else { @@ -31,6 +32,7 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti const uint8_t& channel = data[0]; switch (command.command) { case READ_LED_STRIP_MASK: { + CLP_LOG(3, F("Read LED strip mask\r\n")); uint8_t ledMask[GROUPS_NUM]; for (uint8_t i = 0; i < GROUPS_NUM; i++) { if (i < channels[channel].groupsSet) { @@ -45,6 +47,7 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti break; } case WRITE_LED_RGB_VALUE: { + CLP_LOG(1, F("Write LED RGB value!\r\n")); #ifdef DEBUG Serial.println(F("WriteLedRgbValue")); #endif @@ -54,10 +57,12 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti break; } case WRITE_LED_COLOR_VALUES: { + CLP_LOG(3, F("Write LED color values\r\n")); const uint8_t offset = data[1]; const size_t inputLength = min((size_t)data[2], sizeof(data) - 4); const uint8_t color = data[3]; if (color >= 3) { + CLP_LOG(1, F("Invalid color: %d\r\n"), color); response->sendError(); return; } @@ -65,11 +70,14 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti break; } case WRITE_LED_CLEAR: { + CLP_LOG(3, F("Write LED clear\r\n")); clearLEDColorValues(channel); break; } case WRITE_LED_GROUP_SET: { + CLP_LOG(3, F("Write LED group set\r\n")); if (channels[channel].groupsSet >= GROUPS_NUM) { + CLP_LOG(1, F("Invalid LED group: %d\r\n"), channels[channel].groupsSet); response->sendError(); return; } @@ -95,6 +103,7 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti group.temp3 = CLP::fromBigEndian(data[21], data[22]); if (!isValidLEDGroup(group)) { + CLP_LOG(1, F("Invalid LED config received\r\n")); response->sendError(); return; } @@ -103,16 +112,20 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti break; } case WRITE_LED_EXTERNAL_TEMP: { + CLP_LOG(3, F("Write LED external temp\r\n")); setLEDExternalTemperature(channel, CLP::fromBigEndian(data[2], data[3])); break; } case WRITE_LED_GROUPS_CLEAR: { + CLP_LOG(3, F("Write LED groups clear\r\n")); triggerSave |= clearLEDGroups(channel); break; } case WRITE_LED_MODE: { + CLP_LOG(3, F("Write LED mode\r\n")); const ChannelMode mode = static_cast(data[1]); if (!isValidChannelMode(mode)) { + CLP_LOG(1, F("Unkown LED channel mode: %02X\r\n"), data[1]); #ifdef DEBUG Serial.print(F("unkown LED channel mode: ")); Serial.print(data[1], HEX); @@ -126,11 +139,13 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti break; } case WRITE_LED_BRIGHTNESS: { + CLP_LOG(3, F("Write LED brightness\r\n")); uint8_t brightness = CLP::convert100To255(data[1]); triggerSave |= setLEDBrightness(channel, brightness); break; } case WRITE_LED_COUNT: { + CLP_LOG(2, F("Write LED count!\r\n")); #ifdef DEBUG Serial.print(F("WRITE_LED_COUNT: ")); Serial.print(data[1], HEX); @@ -141,8 +156,10 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti break; } case WRITE_LED_PORT_TYPE: { + CLP_LOG(3, F("Write LED port type\r\n")); const PortType portType = static_cast(data[1]); if (!isValidPortType(portType)) { + CLP_LOG(1, F("Invalid port type: %d\r\n"), portType); response->sendError(); return; } @@ -150,10 +167,12 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti break; } case WRITE_LED_START_AUTODETECTION: { + CLP_LOG(3, F("Write LED start autodet\r\n")); startLEDAutodetection(channel); break; } case READ_LED_AUTODETECTION_RESULTS: { + CLP_LOG(3, F("Write LED get autodet results\r\n")); const uint8_t result = getLEDAutodetectionResult(channel); uint8_t buffer[] = {result}; response->send(buffer, sizeof(buffer)); @@ -162,6 +181,7 @@ void LEDController::handleLEDControl(const Command& command, const CorsairLighti break; } default: { + CLP_LOG(1, F("Unkown command: %02X\r\n"), command.command); #ifdef DEBUG Serial.print(F("unkown command: ")); Serial.print(command.command, HEX); @@ -251,6 +271,7 @@ void LEDController::startLEDAutodetection(uint8_t channel) { bool LEDController::saveIfNeeded() { if (triggerSave) { triggerSave = false; - save(); + return save(); } + return false; } diff --git a/src/LEDController.h b/src/LEDController.h index ddef3f87..6007e273 100644 --- a/src/LEDController.h +++ b/src/LEDController.h @@ -198,7 +198,7 @@ class LEDController : public ILEDController { /** * Stores the time at which the last command was received by the LEDController. */ - long lastCommand = 0; + unsigned long lastCommand = 0; /** * Trigger update of the LEDs diff --git a/src/RawHID.cpp b/src/RawHID.cpp index efe991df..e7537c03 100644 --- a/src/RawHID.cpp +++ b/src/RawHID.cpp @@ -2,17 +2,14 @@ Copyright (c) 2014-2015 NicoHood modified by Leon Kiefer See the readme for credit to other people. - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -49,9 +46,6 @@ static const uint8_t _hidReportDescriptorRawHID[] PROGMEM = { 0xC0 /* end collection */ }; /* clang-format on */ -#ifndef SERIAL_NUMBER -#define SERIAL_NUMBER "FB66DF55421900F5" -#endif const char defaultSerialNumber[] PROGMEM = SERIAL_NUMBER; @@ -173,4 +167,4 @@ uint8_t CLP::RawHID_::getShortName(char* name) { namespace CLP { RawHID_ RawHID; } -#endif +#endif \ No newline at end of file diff --git a/src/RawHID.h b/src/RawHID.h index 2cb0b87d..d7771f02 100644 --- a/src/RawHID.h +++ b/src/RawHID.h @@ -2,17 +2,14 @@ Copyright (c) 2014-2015 NicoHood modified by Leon Kiefer See the readme for credit to other people. - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,10 +20,11 @@ THE SOFTWARE. */ #pragma once -#include +#include "Arduino.h" +#include "CorsairLightingProtocolConstants.h" #if defined(ARDUINO_ARCH_AVR) -#include +#include "HID.h" #endif #if defined(ARDUINO_ARCH_AVR) && defined(USBCON) @@ -165,4 +163,4 @@ class RawHID_ : public PluggableUSBModule, public Stream { }; extern RawHID_ RawHID; } // namespace CLP -#endif +#endif \ No newline at end of file diff --git a/src/TemperatureController.cpp b/src/TemperatureController.cpp index ffc84760..e14b9c2e 100644 --- a/src/TemperatureController.cpp +++ b/src/TemperatureController.cpp @@ -21,6 +21,7 @@ void TemperatureController::handleTemperatureControl(const Command& command, const CorsairLightingProtocolResponse* response) { switch (command.command) { case READ_TEMPERATURE_MASK: { + CLP_LOG(3, F("Read temp mask\r\n")); uint8_t mask[TEMPERATURE_NUM]; for (uint8_t i = 0; i < TEMPERATURE_NUM; i++) { mask[i] = isTemperatureSensorConnected(i) ? TEMPERATURE_MASK_CONNECTED : TEMPERATURE_MASK_NOT_CONNECTED; @@ -29,8 +30,10 @@ void TemperatureController::handleTemperatureControl(const Command& command, break; } case READ_TEMPERATURE_VALUE: { + CLP_LOG(3, F("Read temp value\r\n")); const uint8_t& tempSensor = command.data[0]; if (tempSensor >= TEMPERATURE_NUM) { + CLP_LOG(1, F("Unknown temp sensor: %d\r\n"), tempSensor); response->sendError(); return; } @@ -40,6 +43,7 @@ void TemperatureController::handleTemperatureControl(const Command& command, break; } case READ_VOLTAGE_VALUE: { + CLP_LOG(3, F("Read voltage\r\n")); const uint8_t& rail = command.data[0]; uint16_t voltage; switch (rail) { @@ -56,6 +60,7 @@ void TemperatureController::handleTemperatureControl(const Command& command, break; } default: { + CLP_LOG(1, F("Unkown voltage rail: %d\r\n"), rail); response->sendError(); return; } @@ -66,7 +71,9 @@ void TemperatureController::handleTemperatureControl(const Command& command, break; } default: + CLP_LOG(1, F("Unkown command: %02X\r\n"), command.command); response->sendError(); + return; } }