Jump to content


  • Posts

  • Joined


13 Good

About Thatualle1970

  • Birthday 04/14/1970


  • Interests
    Medicine, Writing
  1. That looks like Manchester encoding in the data packet.
  2. The Lighting node was documented here pretty completely: http://forum.corsair.com/forums/showpost.php?p=675996&postcount=33
  3. Yes. In a nutshell, there are three colours, cool, warm and hot. The temperature thresholds are defined in 09 and are 8.8 signed fixed-point. The colours are simple RGB values for each point and reside in register 0A. To set the temperature colour points, you'd do something like: for(int i=0; i<3; i++) { temp[i * 2] = (unsigned char)(temp.value[i] * 256.0f); temp[i * 2 + 1] = (unsigned char)temp_value[i]; } When you write the temperature to 08 while under temperature controlled mode, the H80i will interpolate that temperature to within the cool-warm-hot range and determine the RGB value automatically.
  4. Meh, to heck with it, I'll leave this up here as is. If anyone's interested in this: http://i.imgur.com/x7n6BP9.jpg You can download the source code from here and compile it with Visual C# Express: OpenHardwareMonitor.7z
  5. What part of the protocols are we missing? Confirmation from Corsair that we're right? They've already said they won't do that.
  6. You're welcome, and good luck! Regrading C#, if it had direct-to-x86 compilation it would be the perfect language to write in, but having to pass through the CLR all the time makes it imperfect for low latency communication. Ultimately, you'd be better off writing a C/C++ layer that handles the basic communication loop and pass that up to higher code. For example, in C# in Windows using HidLibrary, I get around 12ms per packet, whereas rewriting the low level code in ANSI C using direct Win32 calls and plugging it into LUA, I was able to easily hit 1ms per packet (the limit Windows/USB2.0 will allow).
  7. PID is always 0x1B1C VID is 0x0C04 for H80i and H100i VID is 0x0C02 for the USB Commander They use the same basic protocol, except the Commander's don't have the extra length byte at the very beginning. Here's what I have working so far (C# code, this depends on HidLibrary which you can get from NuGet): [b]public [/b]List<Int32> ListPorts() { [b]int [/b]packetIndex = 1; [color="Green"]// skip the report id field (always zero)[/color] [b]byte[/b][] packet = [b]new byte[/b][65]; [color="Green"]// size of the packet plus report id[/color] [b]if [/b](newProtocol) packet[packetIndex++] = 2; [color="Green"]// newProtocol = (pid == 0xC04)[/color] packet[packetIndex++] = ([b]byte[/b])index; [color="Green"]// write the index[/color] [b]if [/b](index < 255) index++; [b]else [/b]index = 20; [color="Green"]// and cycle it[/color] packet[packetIndex++] = 0x4F; [color="Green"]// command to get the port status[/color] [color="Green"]// perform the io[/color] device.Write(packet, 500); [b]var [/b]inpacket = device.Read(500); [color="Green"]// and collect our list of active channels[/color] List<Int32> ports = new List<Int32>(); [b]for [/b]([b]int [/b]portIndex = 0; portIndex < 8; portIndex++) { [color="Green"]// any port set to 0xFF is not present, anything else is 'live'[/color] [b]if [/b](inpacket.Data[3 + portIndex] != 0xFF) ports.Add(portIndex); } [b]return [/b]ports; } [color="Green"]// reportid, length, index, command, register, len // 0 1 2 3 4 5 <- block // 0 1 2 3 4 <- byte/word // ^-- omitted with old USB Commander (0xC02) // reportid, index, command/status, data... // 0 1 2 3 // ^-- omitted with USB Commander[/color] [b]public [/b][b]byte[/b][] ReadData([b]int [/b]reg, [b]int [/b]len = 1) { [b]int [/b]i = (newProtocol) ? 2 : 1; [color="Green"]// fill in the length when we're done[/color] [b]byte[/b][] packet = [b]new byte[/b][65]; [color="Green"]// our standard packet length[/color] [b]int [/b]command = (Port << 4) | 1; [color="Green"]// port fills the high nibble, 1 is 'read'[/color] [b]if [/b](len == 1) command |= 6; [color="Green"]// read byte[/color] [b]else if [/b](len == 2) command |= 8; [color="Green"]// read short[/color] [b]else [/b]command |= 10; [color="Green"]// read any length[/color] packet[i++] = ([b]byte[/b])index; [color="Green"]// obligatory index field[/color] [b]if [/b](index < 255) index++; [b]else [/b]index = 20; [color="Green"]// and cycle[/color] packet[i++] = ([b]byte[/b])command; [color="Green"]// command byte[/color] packet[i++] = ([b]byte[/b])reg; [color="Green"]// the register we'd like to read[/color] if (len > 2) packet[i++] = ([b]byte[/b])len; [color="Green"]// arbitrary length require the length[/color] [b]if [/b](newProtocol) packet[1] = ([b]byte[/b])(index - 1); [color="Green"]// add in our total length[/color] [color="Green"]// do the io[/color] device.Write(packet, 500); [b]var [/b]inpacket = device.Read(500); [b]if [/b](inpacket != null && inpacket.Status == HidDeviceData.ReadStatus.Success) { [color="Green"]// ignore bad packets (handle the null from the caller side)[/color] [b]if [/b](inpacket.Data[1] != packet[(newProtocol) ? 2 : 1]) [b]return [/b][b]null[/b]; [b]if [/b]((inpacket.Data[2] & 240) != 0) [b]return [/b][b]null[/b]; [color="Green"]// grab our returned data from within the packet[/color] [b]byte[/b][] data = [b]new byte[/b][len]; Buffer.BlockCopy(inpacket.Data, 3, data, 0, len); [b]return [/b]data; } [b]else [/b][b]return [/b][b]null[/b]; } [color="Green"]// reportid, length, index, command, register, len, data... // 0 1 2 3 4 5, 6... <- block // 0 1 2 3 4 5... <- byte/word // ^-- omitted with USB Commander (0xC02)[/color] [b]public [/b][b]bool [/b]WriteData([b]int [/b]reg, [b]byte[/b][] data) { [b]int [/b]i = (newProtocol) ? 2 : 1; [color="Green"]// fill in the length when we're done[/color] [b]byte[/b][] packet = [b]new byte[/b][65]; [color="Green"]// our standard packet length[/color] [b]int [/b]command = (Port << 4); [color="Green"]// port fills the high nibble, no low bit for write[/color] [b]if [/b](data.Length == 1) command |= 6; [color="Green"]// byte[/color] [b]else if [/b](data.Length == 2) command |= 8; [color="Green"]// short[/color] [b]else [/b]command |= 10; [color="Green"]// arbitrary length[/color] packet[i++] = ([b]byte[/b])index; [color="Green"]// index and cycle[/color] [b]if [/b](index < 255) index++; [b]else [/b]index = 20; packet[i++] = ([b]byte[/b])command; [color="Green"]// command field[/color] packet[i++] = ([b]byte[/b])reg; [color="Green"]// register we're writing too[/color] [b]if [/b](data.Length > 2) packet[i++] = ([b]byte[/b])(data.Length); [color="Green"]// length, if req'd[/color] Buffer.BlockCopy(data, 0, packet, i, data.Length); [color="Green"]// data we're writing[/color] i += data.Length; [color="Green"]// position in the packet[/color] [b]if [/b](newProtocol) packet[1] = (byte)(index - 1); [color="Green"]// prefix packet length[/color] device.Write(packet, 500); [color="Green"]// do the io[/color] [b]var [/b]inpacket = device.Read(500); [color="Green"]// return true if the write was successful[/color] [b]return [/b](inpacket != null && inpacket.Status == HidDeviceData.ReadStatus.Success); } From here you need to just enumerate everything and push that info up to some sort of UI. It's important that you not try to talk to the same device using multiple threads, that breaks things, so either stick to single threading, single threading per USB device, or use locks to ensure that the write-read sequence is always unbroken. Apologies for the messy code. The code is very "C" like and should be easy enough to port to Linux (unless mono can be made to work), but that's beyond my realm of expertise...
  8. For the people who are still interested... H80/H100/Cooling Node Register set: [TABLE=head]Register|Hex|Length|Read|Description Device ID|00|2|RO|H80 0x37; Cooling node 0x38; H100 0x3A Firmware Version|01|2|RO|#.#.## that is 0x1011 is version 1.0.17 System Status|02|2|RO|Status 0 okay 0xFF bad Status Led|04|2||Each bit = led; 0 = Extreme, 1 = Perf, 2 = Quiet, 3 = CLink Dude Temps|07|8|RW|Current temp for four channels (signed fixed point) RPM Current|0B|10|RO|Current fan speed RPM Max|10|10|RO|Measured fan maximum Input Temp|1A|10|RW|Temperature when in one of the curve modes Fan Control|20 30 40 50 60|2|RW|Bit 7 Fan detected; Bit 6~4 Temp Input; Bit 3~1 Fan Mode; Bit 0 Fan Pins (1 is 4-Pin) |(each channel)|||Temp Input: 0 Coolant Temp 1 Temp 0 2 Temp 1 3 Temp 2 4 MCU Temp 7 ManualFan ||||Mode: 0 Off 1 PWM 2 RPM 3 Set Point 4 Quiet 5 Performance 6 Extreme 7 User Curve Fan Target PWM|21 31 41 51 61|2|RW|Current fan speed as a percentage * 2.55 (e.g. 255 == 100%) Fan Target RPM|22 32 42 52 62|2|RW|Current RPM target Fan User Curve RPM|23 33 43 53 63|10|RW|User curve profile Fan User Curve Temp|28 38 48 58 68|10|RW|User curve profile [/TABLE] Note: Fields which only expect a single byte (e.g., PWM) must be zero-padded. LED Node Register set: [TABLE=head]Register|Hex|Length*|Read|Description Device ID|00|1|RO|0x3A for LED Node Firmware Version|01|2|RO|BCD 0x0910 is version 1.0.9 SystemStatus|03|1|RO|Good is 0; bad is 0xFF User Mode|04|2x1|RW|0x10 Pulse Mode; 0x01 through 0x0F Built-In Colours; 0x00 C-Link Mode RGB Out|06|2x3|RO|Current calculated LED output Temps|0C|2x2|RW|Input temperatures Cycle Mode|10 30|1|RW|Bit 6~7: 00 - Static; 01 - Two Cycle; 10 - Four Cycle; 11 - Temperature ||||Bit 0~2 Cycle Speed in powers of 2 x 1/8th Second Cold Warm Hot|11 31|3x5|RW|Temp (two bytes) and RGB (three bytes) for each threshold Cycle Colours|20 40|4x4|RW|RGB (with one byte padding) for each cycle step [/TABLE] Note: Lengths may be shown as number of sets x length of each set. For example, there are three sets (one for cold, warm and hot respectively) with five bytes of data for temperature settings. The hex address will be offset byt the BYTE position being written. LED Nodes, Cooling Nodes and the original H80 and H100 all have seuqential registers, meaning that writing more data than the first register will hold will "spill over" into the next register, allowing large block reads and writes.
  9. Hey CFSworks; great job so far.... :cool: Some additional information to keep this thread moving in the positive direction. Corsair Link protocol overview As mentioned, the USB device uses a simple 64-byte HID "raw" report; the rawhid source code can be a useful starting point to establish low-level communication if a device isn't "just available" (e.g., on Windows). http://www.pjrc.com/teensy/rawhid.html The ReportID is always zero and is length-prefixed with a number of SMBus-like packets within the HID packet. Each packet has an index (which should be from 20~255), SMBus sub-packet type followed by any write data, if the sub-packet is writing data. Block types also have a length, and it appears the "quick" and "byte" modes are unsupported (only CmdByte, CmdWord and CmdBlock appear to be used). H80i/H100i Register set: [TABLE=head]__Number__|R/W|__Length__| Description 00 | R | 1 byte | Device ID - (H80 0x37, Cooling node 0x38, Lighting node 0x39, H100 0x3A, 80i 0x3B, 100i 0x3c -- this field as well as the version are common on all C-Link devices; but the rest aren't 01 | R | 2 bytes | Firmware Version in BCD (for example 1.0.5 is 0x1005, or 0x05, 0x10 in little endianess) 02 | R | 8 bytes | Product name, zero-terminated - only present on the H80i and H100i 03 | R | 1 | Status, 0 okay, 0xFF bad 04 | RW | 1 byte | Select current LED 05 | R | 1 byte | Number of LEDs 06 | RW | 1 byte | LED mode - 0x00 for static color, 0x40 for 2-color cycle, 0x80 for 4-color, 0xC0 for temperature mode; low nibble defines cycle speed or the temperature channel to use (0 internal sensor, 7 manual) 07 | R | 3 bytes | LED current color, RGB color of the selected LED 08 | RW | 2 bytes | In temperature controlled mode (0xC0) this defines the colour to use to with the below gradients 09 | RW | 6 bytes | LED temperature-mode temperatures: 3 temperatures; used when cycle mode is 0xc0 0a | RW | 9 bytes | LED temperature-mode colors: RGBx3 colors, corresponding to temperatures in register above 0b | RW | 12 bytes | LED cycle colors: RGBx4 colors (only first color used if cycle mode set to 00, first two if 4b, ignored if c0) 0c | RW | 1 byte | Select active temperature sensor 0d | R | 1 byte | Number of temperature sensors 0e | R | 2 bytes | Temperature as measured by selected sensor 0f | RW | 2 bytes | Temperature limit (when the temperature goes over this, status is set to 0xff) 10 | RW | 1 byte | Select current fan; for H100i, 0-3 are the fans, 4 is pump 11 | R | 1 byte | Number of fans 12 | RW | 1 byte | Fan mode; 02=fixed PWM, 04=fixed RPM, 06=default, 08=quiet, 0a=balanced, 0c=performance, 0e=custom; high bit is one when fan is detected, low bit is one when the fan is 4-pin, bits 6~4 define the temperautre channel to use in "curve" modes, 0 internal and 7 manual 13 | RW | 1 byte | Fan fixed PWM, 0-255, only used if fan mode is 02 14 | RW | 2 bytes | Fan fixed RPM; when fan mode is 04, controller will target this RPM 15 | RW | 2 bytes | Report external temperature to fan controller - used for controlling fans via external sensors 16 | R | 2 bytes | Current fan RPM 17 | R | 2 bytes | Maximum RPM recorded since power-on 18 | RW | 2 bytes | Fan under speed threshold 19 | RW | 10 bytes | Fan RPM table, for custom (0e) mode: array of 5 RPMs 1a | RW | 10 bytes | Fan temp table, for custom (0e) mode: array of 5 temperatures [/TABLE] Note: All data is little-endian. Temperatures are reported in units of 1/256th of a degree Celsius.
  • Create New...