mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2025-12-30 10:47:50 -05:00
384 lines
14 KiB
C++
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]);
|
|
}
|