Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions Controllers/ArcticController/ArcticController.cpp
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;
}
27 changes: 27 additions & 0 deletions Controllers/ArcticController/ArcticController.h
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;
};
49 changes: 49 additions & 0 deletions Controllers/ArcticController/ArcticControllerDetect.cpp
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 ) |
\*---------------------------------------------------------------------------------------------------------*/
128 changes: 128 additions & 0 deletions Controllers/ArcticController/RGBController_Arctic.cpp
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),
Copy link
Copy Markdown
Owner

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

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 {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The 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);
}
}
}
Loading