-
-
Notifications
You must be signed in to change notification settings - Fork 354
Add support for Arctic RGB Controller #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| /*-----------------------------------------*\ | ||
| | ArcticController.h | | ||
| | | | ||
| | Controller Interface for Arctic devices | | ||
| | | | ||
| | Armin Wolf (Wer-Wolf) 01/09/2023 | | ||
| \*-----------------------------------------*/ | ||
|
|
||
| #include "ArcticController.h" | ||
| #include <cstring> | ||
|
|
||
| using namespace std::chrono_literals; | ||
|
|
||
| #define ARCTIC_COMMAND_SET_RGB 0x00 | ||
| #define ARCTIC_COMMAND_IDENTIFY 0x5C | ||
|
|
||
| #define ARCTIC_RESPONSE_BUFFER_LENGTH 15 | ||
| #define ARCTIC_RESPONSE_COMMAND_OFFSET 0 | ||
| #define ARCTIC_RESPONSE_DATA_OFFSET 1 | ||
| #define ARCTIC_RESPONSE_DATA_LENGTH 12 | ||
| #define ARCTIC_RESPONSE_XOR_CSUM_OFFSET 13 | ||
| #define ARCTIC_RESPONSE_ADD_CSUM_OFFSET 14 | ||
|
|
||
| #define ARCTIC_COMMAND_BUFFER_LENGTH(payload_size) (sizeof(header) + 1 + payload_size) | ||
| #define ARCTIC_COMMAND_COMMAND_OFFSET (sizeof(header)) | ||
| #define ARCTIC_COMMAND_PAYLOAD_OFFSET (sizeof(header) + 1) | ||
|
|
||
| const unsigned char header[] = | ||
| { | ||
| 0x01, | ||
| 0x02, | ||
| 0x03, | ||
| 0xFF, | ||
| 0x05, | ||
| 0xFF, | ||
| 0x02, | ||
| 0x03 | ||
| }; | ||
|
|
||
| const unsigned char identify_payload[] = | ||
| { | ||
| 0x01, | ||
| 0xFE, | ||
| 0x01, | ||
| 0xFE | ||
| }; | ||
|
|
||
| ArcticController::ArcticController(const std::string &portname) | ||
| : port_name(portname), | ||
| serialport(portname.c_str(), 250000, SERIAL_PORT_PARITY_NONE, SERIAL_PORT_SIZE_8, SERIAL_PORT_STOP_BITS_2, false) | ||
| { | ||
| serialport.serial_set_dtr(true); | ||
| } | ||
|
|
||
| ArcticController::~ArcticController() | ||
| { | ||
| serialport.serial_set_dtr(false); | ||
| } | ||
|
|
||
| static void FormatCommandBuffer(char *buffer, char command) | ||
| { | ||
| std::memcpy(buffer, header, sizeof(header)); | ||
| buffer[ARCTIC_COMMAND_COMMAND_OFFSET] = command; | ||
| } | ||
|
|
||
| void ArcticController::SetChannels(std::vector<RGBColor> colors) | ||
| { | ||
| char buffer[ARCTIC_COMMAND_BUFFER_LENGTH(colors.size() * 3)]; | ||
|
|
||
| FormatCommandBuffer(buffer, ARCTIC_COMMAND_SET_RGB); | ||
|
|
||
| for(unsigned int channel = 0; channel < colors.size(); channel++) | ||
| { | ||
| const unsigned int offset = ARCTIC_COMMAND_PAYLOAD_OFFSET + channel * 3; | ||
|
|
||
| buffer[offset + 0x00] = std::min<unsigned int>(254, RGBGetRValue(colors[channel])); | ||
| buffer[offset + 0x01] = std::min<unsigned int>(254, RGBGetGValue(colors[channel])); | ||
| buffer[offset + 0x02] = std::min<unsigned int>(254, RGBGetBValue(colors[channel])); | ||
| } | ||
|
|
||
| serialport.serial_write(buffer, sizeof(buffer)); | ||
| } | ||
|
|
||
| static char XORChecksum(char *data, int length) | ||
| { | ||
| char sum = 0; | ||
|
|
||
| for(int i = 0; i < length; i++) | ||
| { | ||
| sum ^= data[i]; | ||
| } | ||
|
|
||
| return sum; | ||
| } | ||
|
|
||
| static char AddChecksum(char *data, int length) | ||
| { | ||
| char sum = 0; | ||
|
|
||
| for(int i = 0; i < length; i++) | ||
| { | ||
| sum = (char)(sum + data[i]); | ||
| } | ||
|
|
||
| return sum; | ||
| } | ||
|
|
||
| bool ArcticController::IsPresent() | ||
| { | ||
| char buffer[ARCTIC_COMMAND_BUFFER_LENGTH(sizeof(identify_payload))]; | ||
| char response[ARCTIC_RESPONSE_BUFFER_LENGTH]; | ||
| int ret; | ||
|
|
||
| FormatCommandBuffer(buffer, ARCTIC_COMMAND_IDENTIFY); | ||
| std::memcpy(buffer + ARCTIC_COMMAND_PAYLOAD_OFFSET, identify_payload, sizeof(identify_payload)); | ||
|
|
||
| serialport.serial_flush_rx(); | ||
| ret = serialport.serial_write(buffer, sizeof(buffer)); | ||
| if(ret != sizeof(buffer)) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| std::this_thread::sleep_for(100ms); | ||
|
|
||
| ret = serialport.serial_read(response, sizeof(response)); | ||
| if(ret != sizeof(response)) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| if(response[ARCTIC_RESPONSE_COMMAND_OFFSET] != ARCTIC_COMMAND_IDENTIFY) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| if(response[ARCTIC_RESPONSE_XOR_CSUM_OFFSET] != XORChecksum(&response[ARCTIC_RESPONSE_DATA_OFFSET], ARCTIC_RESPONSE_DATA_LENGTH)) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| if(response[ARCTIC_RESPONSE_ADD_CSUM_OFFSET] != AddChecksum(&response[ARCTIC_RESPONSE_DATA_OFFSET], ARCTIC_RESPONSE_DATA_LENGTH)) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| std::string ArcticController::GetLocation() | ||
| { | ||
| return port_name; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| /*-----------------------------------------*\ | ||
| | ArcticController.h | | ||
| | | | ||
| | Controller Interface for Arctic devices | | ||
| | | | ||
| | Armin Wolf (Wer-Wolf) 01/09/2023 | | ||
| \*-----------------------------------------*/ | ||
|
|
||
| #pragma once | ||
| #include "RGBController.h" | ||
| #include "serial_port.h" | ||
|
|
||
| class ArcticController | ||
| { | ||
| public: | ||
| ArcticController(const std::string &portname); | ||
| ~ArcticController(); | ||
|
|
||
| void SetChannels(std::vector<RGBColor> colors); | ||
| bool IsPresent(); | ||
|
|
||
| std::string GetLocation(); | ||
|
|
||
| private: | ||
| std::string port_name; | ||
| serial_port serialport; | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| /*-----------------------------------------*\ | ||
| | ArcticControllerDetect.cpp | | ||
| | | | ||
| | Detect Arctic RGB controllers | | ||
| | | | ||
| | Armin Wolf (Wer-Wolf) 01/09/2023 | | ||
| \*-----------------------------------------*/ | ||
|
|
||
| #include "Detector.h" | ||
| #include "ArcticController.h" | ||
| #include "RGBController.h" | ||
| #include "RGBController_Arctic.h" | ||
| #include "SettingsManager.h" | ||
| #include "find_usb_serial_port.h" | ||
| #include <vector> | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
|
|
||
| #define CH341_VID 0x1A86 | ||
| #define CH341_PID 0x7523 | ||
|
|
||
| void DetectArcticControllers() | ||
| { | ||
| std::vector<std::string *> ports = find_usb_serial_port(CH341_VID, CH341_PID); | ||
|
|
||
| for(unsigned int device = 0; device < ports.size(); device++) | ||
| { | ||
| ArcticController *controller = new ArcticController(*ports[device]); | ||
|
|
||
| if(controller->IsPresent()) | ||
| { | ||
| RGBController_Arctic *rgb_controller = new RGBController_Arctic(controller); | ||
| ResourceManager::get()->RegisterRGBController(rgb_controller); | ||
| } | ||
| else | ||
| { | ||
| delete controller; | ||
| } | ||
|
|
||
| delete ports[device]; | ||
| } | ||
| } | ||
|
|
||
| REGISTER_DETECTOR("Arctic RGB controller", DetectArcticControllers); | ||
| /*---------------------------------------------------------------------------------------------------------*\ | ||
| | Entries for dynamic UDEV rules | | ||
| | | | ||
| | DUMMY_DEVICE_DETECTOR("Arctic RGB controller", DetectArcticControllers, 0x1a86, 0x7523 ) | | ||
| \*---------------------------------------------------------------------------------------------------------*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| /*-----------------------------------------*\ | ||
| | RGBController_Arctic.h | | ||
| | | | ||
| | Generic RGB Interface for Arctic devices | | ||
| | | | ||
| | Armin Wolf (Wer-Wolf) 01/09/2023 | | ||
| \*-----------------------------------------*/ | ||
|
|
||
| #include "RGBController_Arctic.h" | ||
| #include <math.h> | ||
|
|
||
| using namespace std::chrono_literals; | ||
|
|
||
| #define ARCTIC_NUM_CHANNELS 4 | ||
| #define ARCTIC_SLEEP_THRESHOLD 100ms | ||
| #define ARCTIC_KEEPALIVE_PERIOD 500ms /* Device requires at least 1s */ | ||
|
|
||
| /**------------------------------------------------------------------*\ | ||
| @name Arctic RGB Controller Devices | ||
| @category LEDStrip | ||
| @type Serial | ||
| @save :x: | ||
| @direct :robot: | ||
| @effects :x: | ||
| @detectors DetectArcticControllers | ||
| @comment | ||
| \*-------------------------------------------------------------------*/ | ||
|
|
||
| RGBController_Arctic::RGBController_Arctic(ArcticController *arctic_controller) | ||
| : controller(arctic_controller), | ||
| keepalive_thread_run(true) | ||
| { | ||
| name = "Arctic RGB Controller"; | ||
| vendor = "Arctic"; | ||
| description = "Arctic 4-Channel RGB Controller"; | ||
| location = controller->GetLocation(); | ||
| type = DEVICE_TYPE_LEDSTRIP; | ||
|
|
||
| mode DirectMode; | ||
| DirectMode.name = "Direct"; | ||
| DirectMode.value = 0; | ||
| DirectMode.flags = MODE_FLAG_HAS_PER_LED_COLOR; | ||
| DirectMode.color_mode = MODE_COLORS_PER_LED; | ||
|
|
||
| modes.push_back(DirectMode); | ||
|
|
||
| SetupZones(); | ||
|
|
||
| keepalive_thread = std::thread(&RGBController_Arctic::KeepaliveThreadFunction, this); | ||
| } | ||
|
|
||
| RGBController_Arctic::~RGBController_Arctic() | ||
| { | ||
| keepalive_thread_run = false; | ||
| keepalive_thread.join(); | ||
| delete controller; | ||
| } | ||
|
|
||
| void RGBController_Arctic::SetupZones() | ||
| { | ||
| for(int channel = 0; channel < ARCTIC_NUM_CHANNELS; channel++) | ||
| { | ||
| zone LedZone; | ||
| LedZone.name = "LED Strip " + std::to_string(channel); | ||
| LedZone.type = ZONE_TYPE_SINGLE; | ||
| LedZone.leds_count = 1; | ||
| LedZone.leds_min = 1; | ||
| LedZone.leds_max = 1; | ||
| LedZone.matrix_map = nullptr; | ||
|
|
||
| led Led; | ||
| Led.name = LedZone.name + " LED"; | ||
| Led.value = channel; | ||
|
|
||
| zones.push_back(LedZone); | ||
| leds.push_back(Led); | ||
| } | ||
|
|
||
| SetupColors(); | ||
| } | ||
|
|
||
| void RGBController_Arctic::ResizeZone(int /* zone */, int /* new_size */) | ||
| { | ||
| /*---------------------------------------------------------*\ | ||
| | This device does not support resizing zones | | ||
| \*---------------------------------------------------------*/ | ||
| } | ||
|
|
||
| void RGBController_Arctic::DeviceUpdateLEDs() | ||
| { | ||
| last_update_time = std::chrono::steady_clock::now(); | ||
|
|
||
| controller->SetChannels(colors); | ||
| } | ||
|
|
||
| void RGBController_Arctic::UpdateZoneLEDs(int /* zone */) | ||
| { | ||
| DeviceUpdateLEDs(); | ||
| } | ||
|
|
||
| void RGBController_Arctic::UpdateSingleLED(int /* led */) | ||
| { | ||
| DeviceUpdateLEDs(); | ||
| } | ||
|
|
||
| void RGBController_Arctic::DeviceUpdateMode() | ||
| { | ||
| /*---------------------------------------------------------*\ | ||
| | This device does not support mode updates | | ||
| \*---------------------------------------------------------*/ | ||
| } | ||
|
|
||
| void RGBController_Arctic::KeepaliveThreadFunction() | ||
| { | ||
| std::chrono::nanoseconds sleep_time; | ||
|
|
||
| while(keepalive_thread_run.load()) | ||
| { | ||
| sleep_time = ARCTIC_KEEPALIVE_PERIOD - (std::chrono::steady_clock::now() - last_update_time); | ||
| if(sleep_time <= ARCTIC_SLEEP_THRESHOLD) | ||
| { | ||
| UpdateLEDs(); // Already protected thru a device update thread | ||
| std::this_thread::sleep_for(ARCTIC_KEEPALIVE_PERIOD); | ||
| } else { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Braces and else on own lines |
||
| std::this_thread::sleep_for(sleep_time); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Follow the convention of other controllers. The arctic_controller pointer should be renamed to controller_ptr and the initialization should be inside the function:
RGBController_Arctic::RGBController_Arctic(ArcticController* controller_ptr)
{
controller = controller_ptr;
Also make sure it's named controller_ptr in the .h file