Files
OpenRGB/Controllers/HYTENexusController/HYTENexusController.cpp
2024-12-02 19:15:16 -06:00

384 lines
14 KiB
C++

/*---------------------------------------------------------*\
| HYTENexusController.cpp |
| |
| Driver for HYTE Nexus |
| |
| Adam Honse (calcprogrammer1@gmail.com) 12 Nov 2024 |
| |
| This file is part of the OpenRGB project |
| SPDX-License-Identifier: GPL-2.0-only |
\*---------------------------------------------------------*/
#include "HYTENexusController.h"
#include "LogManager.h"
using namespace std::chrono_literals;
/*---------------------------------------------------------*\
| The protocol for the HYTE NP50 and Q60 are documented |
| on hackmd.io: |
| NP50: https://hackmd.io/3X_ojT77Sr-sLt5Fo2CYMQ |
| Q60: https://hackmd.io/7qUhUQfIQReQYNhdGLqO6g |
| |
| More information on the HYTE Nexus Playground site: |
| https://hyte.com/nexus/nexus-playground |
\*---------------------------------------------------------*/
HYTENexusController::HYTENexusController(char* port, unsigned short pid)
{
port_name = port;
device_pid = pid;
/*-----------------------------------------------------*\
| Initialize channels based on PID |
\*-----------------------------------------------------*/
memset(channels, 0, sizeof(channels));
switch(pid)
{
case HYTE_NEXUS_PORTAL_NP50_PID:
num_channels = 3;
channels[0].is_nexus_channel = true;
channels[1].is_nexus_channel = true;
channels[2].is_nexus_channel = true;
channels[0].has_6_led_logo = true;
break;
case HYTE_THICC_Q60_PID:
num_channels = 4;
channels[0].is_nexus_channel = true;
channels[1].is_nexus_channel = true;
channels[2].is_nexus_channel = false;
channels[3].is_nexus_channel = false;
channels[2].has_lcd_leds = true;
channels[3].has_4_led_logo = true;
break;
default:
num_channels = 0;
break;
}
/*-----------------------------------------------------*\
| Open the port |
| Baud rate doesn't matter for ACM device |
\*-----------------------------------------------------*/
serialport = new serial_port(port_name.c_str(), 2000000);
/*-----------------------------------------------------*\
| Get controller information and firmware version |
\*-----------------------------------------------------*/
ReadDeviceInfo();
ReadFirmwareVersion();
/*-----------------------------------------------------*\
| Get attached device information for all channels |
\*-----------------------------------------------------*/
for(unsigned int channel = 0; channel < num_channels; channel++)
{
if(channels[channel].is_nexus_channel)
{
ReadChannelInfo(channel);
}
}
keepalive_thread_run = true;
keepalive_thread = std::thread(&HYTENexusController::KeepaliveThreadFunction, this);
}
HYTENexusController::~HYTENexusController()
{
keepalive_thread_run = false;
keepalive_thread.join();
serialport->serial_close();
}
std::string HYTENexusController::GetFirmwareVersion()
{
return(firmware_version);
}
std::string HYTENexusController::GetLocation()
{
return(port_name);
}
void HYTENexusController::KeepaliveThreadFunction()
{
while(keepalive_thread_run.load())
{
if((std::chrono::steady_clock::now() - last_update_time) > 2500ms)
{
ReadDeviceInfo();
}
std::this_thread::sleep_for(1s);
}
}
std::string HYTENexusController::GetDeviceName(unsigned int device_type)
{
std::string device_name = "";
switch(device_type)
{
case HYTE_NEXUS_DEVICE_TYPE_LS10:
device_name = "LS10";
break;
case HYTE_NEXUS_DEVICE_TYPE_LS30:
device_name = "LS30";
break;
case HYTE_NEXUS_DEVICE_TYPE_FP12:
device_name = "FP12";
break;
case HYTE_NEXUS_DEVICE_TYPE_FP12_DUO:
device_name = "FP12 Duo";
break;
case HYTE_NEXUS_DEVICE_TYPE_FP12_TRIO:
device_name = "FP12 Trio";
break;
case HYTE_NEXUS_DEVICE_TYPE_LN4060:
device_name = "LN4060";
break;
case HYTE_NEXUS_DEVICE_TYPE_LN70:
device_name = "LN70";
break;
}
return(device_name);
}
void HYTENexusController::LEDStreaming(unsigned char channel, unsigned short led_count, RGBColor* colors)
{
/*-----------------------------------------------------*\
| Send LED Streaming command |
| Byte 0: FF |
| Byte 1: EE |
| Byte 2: 01 |
| Byte 3: Channel (Port1, Port2, or Port3) |
| Byte 4: LEDCount_H |
| Byte 5: LEDCount_L |
| Byte 6: Reserved |
| Byte 7: G |
| Byte 8: R |
| Byte 9: B |
| Repeat GRB pattern for remaining bytes |
\*-----------------------------------------------------*/
unsigned char command_buf[750];
memset(command_buf, 0, sizeof(command_buf));
command_buf[0] = 0xFF;
command_buf[1] = 0xEE;
command_buf[2] = 0x01;
command_buf[3] = (channel + 1);
command_buf[4] = (led_count >> 8);
command_buf[5] = (led_count & 0xFF);
for(unsigned int led_idx = 0; led_idx < led_count; led_idx++)
{
unsigned int offset = (led_idx * 3);
command_buf[7 + offset] = RGBGetGValue(colors[led_idx]);
command_buf[8 + offset] = RGBGetRValue(colors[led_idx]);
command_buf[9 + offset] = RGBGetBValue(colors[led_idx]);
}
/*-----------------------------------------------------*\
| The HYTE THICC Q60 requires 90 bytes to be sent for |
| the 4th channel (logo) even though it only has 4 LEDs |
\*-----------------------------------------------------*/
unsigned char bytes_to_send = ((led_count * 3) + 7);
if((device_pid == HYTE_THICC_Q60_PID)
&& (channel == 3)
&& (bytes_to_send < 90))
{
bytes_to_send = 90;
}
serialport->serial_write((char *)command_buf, bytes_to_send);
serialport->serial_flush_tx();
serialport->serial_flush_rx();
/*-----------------------------------------------------*\
| Wait 10ms for device process command |
\*-----------------------------------------------------*/
std::this_thread::sleep_for(5ms);
}
void HYTENexusController::ReadChannelInfo(unsigned char channel)
{
/*-----------------------------------------------------*\
| Send Get Channel Info command |
| Byte 0: FF |
| Byte 1: CC |
| Byte 2: 01 (Get Status) |
| Byte 3: Channel (Port1, Port2, or Port3) |
\*-----------------------------------------------------*/
unsigned char command_buf[4];
command_buf[0] = 0xFF;
command_buf[1] = 0xCC;
command_buf[2] = 0x01;
command_buf[3] = (channel + 1);
serialport->serial_write((char *)command_buf, sizeof(command_buf));
serialport->serial_flush_tx();
/*-----------------------------------------------------*\
| Wait 50ms for device to send response |
\*-----------------------------------------------------*/
std::this_thread::sleep_for(50ms);
/*-----------------------------------------------------*\
| Receive Channel Info |
| First Device Additional Devices |
| Byte 0: FF 00 |
| Byte 1: CC 00 |
| Byte 2: Device Count |
| Byte 3: Device Type |
| Byte 4: Hardware Version |
| Byte 5: LED Count |
| Byte 6: Fan Temp_H |
| Byte 7: Fan Temp_L |
| Byte 8: Fan RPM_H |
| Byte 9: Fan RPM_L |
| Byte 10: Fan Orientation |
| Byte 11: Touch |
| |
| Format repeats with offset of 12 * n for nth device |
| in the list, except for the first two bytes being |
| zero. |
\*-----------------------------------------------------*/
unsigned char receive_buf[240];
serialport->serial_read((char *)receive_buf, sizeof(receive_buf));
serialport->serial_flush_rx();
channels[channel].num_devices = 0;
memset(&channels[channel].devices, 0, sizeof(channels[channel].devices));
for(unsigned int device = 0; device < 19; device++)
{
unsigned int offset = 12 * device;
if(receive_buf[offset + 2] > 0)
{
channels[channel].num_devices++;
channels[channel].devices[device].device_type = receive_buf[offset + 3];
channels[channel].devices[device].hardware_version = receive_buf[offset + 4];
channels[channel].devices[device].led_count = receive_buf[offset + 5];
}
}
}
void HYTENexusController::ReadDeviceInfo()
{
/*-----------------------------------------------------*\
| Send Get Device Info command |
| Byte 0: FF |
| Byte 1: CC |
| Byte 2: 01 (Get Status) |
| Byte 3: 00 (NP50/Pump) |
\*-----------------------------------------------------*/
unsigned char command_buf[4];
command_buf[0] = 0xFF;
command_buf[1] = 0xCC;
command_buf[2] = 0x01;
command_buf[3] = 0x00;
serialport->serial_write((char *)command_buf, sizeof(command_buf));
serialport->serial_flush_tx();
/*-----------------------------------------------------*\
| Wait 50ms for device to send response |
\*-----------------------------------------------------*/
std::this_thread::sleep_for(50ms);
/*-----------------------------------------------------*\
| Receive Device Info |
| NP50 Q60 |
| Byte 0: FF FF |
| Byte 1: CC CC |
| Byte 2: 00 00 |
| Byte 3: Noise_H Reserve |
| Byte 4: Noise_L Reserve |
| Byte 5: Reserve In Liquid Temp_H |
| Byte 6: Reserve In Liquid Temp_L |
| Byte 7: Temp_H Out Liquid Temp_H |
| Byte 8: Temp_L Out Liquid Temp_L |
| Byte 9: RPM_H Pump RPM_H |
| Byte 10: RPM_L Pump RPM_L |
| Byte 11: Reserve Fan Exhaust/Intake |
| Byte 12: Current Cooling Mode Current Cooling Mode|
| Byte 13: Warning Warnings |
\*-----------------------------------------------------*/
unsigned char receive_buf[14];
serialport->serial_read((char *)receive_buf, sizeof(receive_buf));
serialport->serial_flush_rx();
/*-----------------------------------------------------*\
| Update last update time |
\*-----------------------------------------------------*/
last_update_time = std::chrono::steady_clock::now();
}
void HYTENexusController::ReadFirmwareVersion()
{
/*-----------------------------------------------------*\
| Send Get Firmware Version command |
| Byte 0: FF |
| Byte 1: DD |
| Byte 2: 02 |
| Byte 3: 00 (Reserve) |
\*-----------------------------------------------------*/
unsigned char command_buf[4];
command_buf[0] = 0xFF;
command_buf[1] = 0xDD;
command_buf[2] = 0x02;
command_buf[3] = 0x00;
serialport->serial_write((char *)command_buf, sizeof(command_buf));
serialport->serial_flush_tx();
/*-----------------------------------------------------*\
| Wait 50ms for device to send response |
\*-----------------------------------------------------*/
std::this_thread::sleep_for(50ms);
/*-----------------------------------------------------*\
| Receive Firmware Version |
| Byte 0: FF |
| Byte 1: DD |
| Byte 2: 02 |
| Byte 3: Large Version |
| Byte 4: Mid Version |
| Byte 5: Small Version |
| Byte 6: Hardware Version |
\*-----------------------------------------------------*/
unsigned char receive_buf[7];
serialport->serial_read((char *)receive_buf, sizeof(receive_buf));
serialport->serial_flush_rx();
/*-----------------------------------------------------*\
| Format Firmware Version string |
\*-----------------------------------------------------*/
firmware_version = "FW: " + std::to_string(receive_buf[3]) + "." + std::to_string(receive_buf[4]) + "." + std::to_string(receive_buf[5]) + ", HW: " + std::to_string(receive_buf[6]);
}