Skip to content

jwikiera/ZotacController

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ZotacController

This is a small C program to control a ZOTAC RTX 4070 Ti Trinity OC White Edition card.

The software talks to the GPU using nvapi.dll and allows to set almost all effects supported by the original program. The exceptions are effects that are actively controlled by the software (responding to sound), and are left as an exercise to the reader.

Usage:

Usage:

    -h, --help                show this help message and exit
    --set-activity=<str>      Set GPU state to 'idle' or 'active' and exit
    -b, --brightness=<int>    Brightness (0 - 100)
    -c, --color=<str>         Primary hex color (e.g. #FF0000 or FF0000)
    -C, --color2=<str>        Secondary hex color (optional)
    -e, --effect=<str>        Effect name: static, breathe, fade, wink, flash, random, slide, rainbow, marquee, drip, duet, path
    -s, --speed=<int>         Effect speed (0 - 100)
    -d, --direction=<str>     Effect direction: left or right
    -m, --mode=<str>          In whuch GPU mode should this effect be active: idle, active, or both
    --debug                   Enable debug info

The idle vs active state is just a stupid switch on the GPU that is either 1 or 0. The GPU does not know when it is being used, instead, the FireStorm utility (which must be running for this to happen) uses some other mechanisms (probably the NVML api) to detect GPU usage, and notifies the card to switch its profile accordingly. A tool that you can use instead is nvidia-smi.

This command gives a percentage usage of your GPU:

nvidia-smi.exe --query-gpu=utilization.gpu --format=csv,noheader,nounits --id=0

You can use the example zotac_state_manager.ps1 <poll_interval_seconds> <usage_threshold_percents> <zotac_controller_executable_location> script. You can also turn it into a Windows service with Non-Sucking Service Manager.

Writeup

Reason

My card is not supported by OpenRGB, and the only way to configure its RGB lights is a proprietary GUI app, Firestorm Utility. Besides wanting a CLI interface in order to config all of my system lights at once, the Firestorm Utility is awful. Its interface is laggy, and you can't even use values to set colors. My specific usecase is controlling the card from a Linux host, inside a Windows virtual machine, where it is passed through, via ssh.

Dead ends

  • Static/live analysis of the Firestorm Utility. Yeah no thanks.
  • First I tried adapting the following OpenRGB branch: https://gitlab.com/peterberendi/OpenRGB/-/tree/zotac_4070_ti_super_trinity_oc, but for some reason my card differs quite a bit from the other 4070's added in this branch. (I also still have not figured out how to build a standalone OpenRGB windows executable, the app complains about a missing entry point).

Solution

  • The Firestorm app talks to the GPU using an Nvidia API located at nvapi.dll.
  • The OpenRGB team has made a wrapper, NvAPISpy, for this dll, that logs i2c reads and writes made by it.
  • Using nvapi myself: I scoured Github for examples of how to use Nvapi. First comes the init + getting a GPU handle:
NvAPI_Initialize();
NvAPI_EnumPhysicalGPUs(gpuHandles, &gpuCount);

In my case I have only card so I can take gpuHandles[0]. Next, to read or write data to the card, you need to populate a NV_I2C_INFO_V3 struct and send it using NvAPI_I2CWrite. How to populate it? Use info from NvAPISpy. I modified it a little bit to print all fields in the NV_I2C_INFO_V3 struct. Sample output:

NvAPI_I2CWrite:  GPU handle: 00000600 Ver: 0x3002 DispMask: 0x0001 DDC: Y Dev: 0x4B RegSize: 0x00 Reg: Size: 0x02 Data: 0x28 0x00 Speed: 65535 SpeedKhz: 4 Port: 1 PortSet: Y

Those can be mapped quite easily to the NV_I2C_INFO_V3 struct. Beware, Dev, the identifier of the device (unique to each card model), is shifted for some reason. I had to put 0x96 to get 0x4B. To get the data values, I poked at the Firestorm app and looked at the logs. Firestorm sends data packets in chunks of two bytes. There are 17 writes sent each time some value is updated, which send over the whole config for a given mode (idle or active). Each write contains a two byte data pair. pair[0] seems to be always the same sequence, while pair[1]'s contain the interesting data. Here is what I reverse engineered so far:

Data: 0x20 MODE (idle: 0x0, active: 0x1)
Data: 0x21 0x01
Data: 0x22 EFFECT

// First color RGB (0 to 255)
Data: 0x23 r1
Data: 0x24 g1
Data: 0x25 b1

// Second color RGB (only used for the Duet effect)
Data: 0x26 r2
Data: 0x27 g2
Data: 0x28 b2

Data: 0x29 BRIGHTNESS (from 0 to 100)
Data: 0x2A SPEED (from 0 to 100)
Data: 0x2B DIRECTION (left: 0x0, right: 0x1)
Data: 0x2E 0x01
Data: 0x2C 0x08
Data: 0x2D 0x01
Data: 0x2F 0x01
Data: 0x17 0x01

There is also another packet which contains only one write and sets the card into idle or active mode:

Data: 0x16 MODE (idle: 0x0, active: 0x1)

I hope my code can be helpful to anyone wanting to do something similar.

Other notes

For some reason, when idle and active pairs are sent one right after the other, some active writes fail. I mitigated it with a Sleep call. All writes (instant or delayed) seem identical. This could be a bug somewhere deeper, or a hardware limitation.

About

Small C program to control my Zotac Nvidia card from the command line.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages