Investigating ASUS WMI sensors from PowerShell

2020/03/25

Tags: tags

ASUS Ryzen and Threadripper motherboards with ITE superIO chips have a WMI interface in their BIOS that allows reading sensor values. This was originally implemented as an interface for monitoring software to use, as multiple applications accessing the SuperIO IC directly could cause lockups.

In this post I will explore how to query the WMI sensors interface from PowerShell.

First open a PowerShell console with administrator privileges:

image

There are two ways of querying WMI, the WMI commands, and the more recent and general CIM commands.I’m using the WMI methods as the syntax of the older commands is easier.

Now we can get a list of all WMI objects with ASUS in the name:

Get-CimClass -namespace root/WMI | Where-Object CimClassName -Match ".*ASUS.*"
   NameSpace: ROOT/WMI

CimClassName                        CimClassMethods      CimClassProperties
------------                        ---------------      ------------------
AsusWpbtWmi                         {get_wpbt_status}    {Active, InstanceName}
ASUSHW                              {read_smbus_byte,... {Active, InstanceName}
ASUSManagement                      {initialize, set_... {Active, InstanceName}

The ASUSHW object looks interesting, lets see what methods are available on it

Get-CimClass -namespace root/WMI -ClassName ASUSHW | %{$_.CimClassMethods}
Name                      ReturnType Parameters                            Qualifiers
----                      ---------- ----------                            ----------
read_smbus_byte              Boolean {cmd, slave, data, Result}            {Implemented, WmiMethodId}
write_smbus_byte             Boolean {cmd, data, slave, Result}            {Implemented, WmiMethodId}
read_smbus_word              Boolean {cmd, slave, data, Result}            {Implemented, WmiMethodId}
write_smbus_word             Boolean {cmd, data, slave, Result}            {Implemented, WmiMethodId}
read_smbus_block             Boolean {cmd, slave, data, Result}            {Implemented, WmiMethodId}
write_smbus_block            Boolean {cmd, count, data, slave...}          {Implemented, WmiMethodId}
asio_hw_fun07                Boolean {wPort, bData}                        {Implemented, WmiMethodId}
asio_hw_fun08                Boolean {bData, wPort}                        {Implemented, WmiMethodId}
asio_hw_fun42                Boolean {wPort, wData}                        {Implemented, WmiMethodId}
asio_hw_fun43                Boolean {wData, wPort}                        {Implemented, WmiMethodId}
asio_hw_fun44                Boolean {wPort, dwData}                       {Implemented, WmiMethodId}
asio_hw_fun45                Boolean {dwData, wPort}                       {Implemented, WmiMethodId}
asio_hw_fun09                Boolean {dwAddr, Width, dwData}               {Implemented, WmiMethodId}
asio_hw_fun10                Boolean {dwAddr, dwData, Width}               {Implemented, WmiMethodId}
asio_hw_fun11                Boolean {Bus, Dev, Func, Reg...}              {Implemented, WmiMethodId}
asio_hw_fun12                Boolean {Bus, Dev, dwData, Func...}           {Implemented, WmiMethodId}
asio_hw_fun13                Boolean {Index, wIndexPort, Data}             {Implemented, WmiMethodId}
asio_hw_fun14                Boolean {Data, Index, wIndexPort}             {Implemented, WmiMethodId}
asio_hw_fun15                Boolean {Index, DataHi, DataLo}               {Implemented, WmiMethodId}
asio_hw_fun16                Boolean {DataHi, DataLo, Index}               {Implemented, WmiMethodId}
asio_hw_fun17                Boolean {Index, Eax, Ebx, Ecx...}             {Implemented, WmiMethodId}
asio_hw_fun28                Boolean {Index, Data}                         {Implemented, WmiMethodId}
asio_hw_fun29                Boolean {Data, Index}                         {Implemented, WmiMethodId}
asio_hw_fun19                Boolean {Index, LDN, Data}                    {Implemented, WmiMethodId}
asio_hw_fun20                Boolean {Data, Index, LDN}                    {Implemented, WmiMethodId}
asio_hw_fun21                Boolean {Bank, Index, Data}                   {Implemented, WmiMethodId}
asio_hw_fun22                Boolean {Bank, Data, Index}                   {Implemented, WmiMethodId}
asio_hw_fun46                Boolean {LdnIndexArray, DataArray}            {Implemented, WmiMethodId}
asio_hw_fun47                Boolean {LdnIndexDataArray}                   {Implemented, WmiMethodId}
asio_hw_fun23                Boolean {BankIndexArray, DataArray}           {Implemented, WmiMethodId}
asio_hw_fun24                Boolean {BankIndexDataArray}                  {Implemented, WmiMethodId}
asio_hw_fun25                Boolean {BankIndexArray, DataArray}           {Implemented, WmiMethodId}
asio_hw_fun26                Boolean {BankIndexDataArray}                  {Implemented, WmiMethodId}
sensor_get_value             Boolean {Index, Data}                         {Implemented, WmiMethodId}
sensor_update_buffer         Boolean {Source, Data}                        {Implemented, WmiMethodId}
sensor_get_info              Boolean {Index, Data_Type, Location, Name...} {Implemented, WmiMethodId}
sensor_get_number            Boolean {Data}                                {Implemented, WmiMethodId}
sensor_get_buffer_address    Boolean {Address}                             {Implemented, WmiMethodId}
sensor_get_version           Boolean {Data}                                {Implemented, WmiMethodId}

Sensor_get_version looks interesting, let’s call that:

$asushw = Get-CimInstance -Namespace root/wmi -ClassName ASUSHW
Invoke-CimMethod $asushw -MethodName sensor_get_version
Data ReturnValue PSComputerName
---- ----------- --------------
   3        True

OK great, now let’s see how many sensors we have:

Invoke-CimMethod $asushw -MethodName sensor_get_number
Data ReturnValue PSComputerName
---- ----------- --------------
  32        True

Awesome, 32 sensors!

Right so now lets see what the info about each of the sensors:

0..31 | %{Invoke-CimMethod $asushw -MethodName sensor_get_info -Arguments @{Index=$_ } } | Format-Table
Data_Type Location Name                    Source Type ReturnValue PSComputerName
--------- -------- ----                    ------ ---- ----------- --------------
        3        0 CPU Core Voltage             1    0        True
        3        1 CPU SOC Voltage              1    0        True
        3        2 DRAM Voltage                 1    0        True
        3        3 VDDP Voltage                 1    0        True
        3        3 1.8V PLL Voltage             1    0        True
        3        3 +12V Voltage                 1    0        True
        3        3 +5V Voltage                  1    0        True
        3        3 3VSB Voltage                 1    0        True
        3        3 VBAT Voltage                 1    0        True
        3        3 AVCC3 Voltage                1    0        True
        0        0 CPU Temperature              1    1        True
        0        0 CPU Socket Temperature       1    1        True
        0        3 Motherboard Temperature      1    1        True
        1        3 CPU Fan                      1    2        True
        1        3 Chassis Fan 1                1    2        True
        1        3 Chassis Fan 2                1    2        True
        1        3 Chassis Fan 3                1    2        True
        1        3 HAMP Fan                     1    2        True
        1        3 Water Pump                   1    2        True
        3        3 SB 1.05V Voltage             2    0        True
        0        4 Chipset Temperature          2    1        True
        0        5 Tsensor 1 Temperature        2    1        True
        0        6 CPU VRM Temperature          2    1        True
        1        6 CPU VRM Output Current       2    3        True
        1        3 CPU OPT                      2    2        True
        0        7 Water In                     2    1        True
        0        7 Water Out                    2    1        True
        1        7 Water Flow                   2    4        True
        1        3 AIO Pump                     2    2        True
        3        0 CPU Core Voltage             2    0        True
        3        1 CPU SOC Voltage              2    0        True
        3        2 DRAM Voltage                 2    0        True

Okay, cool.

Let’s get the value for the CPU VRM temperature. That has a source of 2, so first let’s update buffer 2:

%{Invoke-CimMethod $asushw -MethodName sensor_update_buffer -Arguments @{Source=2 } }
Data ReturnValue PSComputerName
---- ----------- --------------
   0        True

Now we can read the value for sensor 22

%{Invoke-CimMethod $asushw -MethodName sensor_get_value -Arguments @{Index=22 } }
Data ReturnValue PSComputerName
---- ----------- --------------
  56        True

The same effect could also be achieved using the older WMI commands, which also make it easier to pass parameters to the methods:

get-WmiObject -Namespace "root\WMI" -List | Where-Object Name -Match ".*ASUS.*"
get-WmiObject -class ASUSHW -Namespace "root\WMI" |  Get-Member -MemberType method
$asushw = get-WmiObject -class ASUSHW -Namespace root\WMI -Impersonation Impersonate
$asushw.sensor_get_version()
$asushw.sensor_update_buffer(2)
$asushw.sensor_get_value(22)

Finally, all these commands can be combined together into a program to report on all the sensor values

# Asus WMI test sensor program for Windows
# Tested on the Asus Crosshair VII
# Run as administrator

function getUnits($type) {
    return $(switch ($type) {
            0 { "V" }
            1 { "C" }
            2 { "RPM" }
            3 { "A" }
            4 { "L/h" }
        })
}

function getScaledValue($val, $Data_Type) {
    return $(If ($Data_Type -eq 3) { $val / 1000000 } else { $val });
}

$asushw = get-WmiObject -class ASUSHW -Namespace root\WMI -Impersonation Impersonate

if ($asushw.sensor_get_version().Data -le 2) {
    Write-Output "Only supports ASUSHW >= v2"
    return
}

0..($asushw.sensor_get_number().Data - 1) | ForEach-Object {
    $sensor = $asushw.sensor_get_info($_)
    if ($currentSource -ne $sensor.Source) {
        $asushw.sensor_update_buffer($sensor.Source) > $null
        $currentSource = $sensor.Source
    }
    return New-Object -TypeName psobject -Property @{
        Value = "$(getScaledValue -val $asushw.sensor_get_value($_).Data -Data_Type $sensor.Data_Type) $(getUnits -type $sensor.Type)"
        Name  = $sensor.Name
    }
} | Format-Table
PS C:\WINDOWS\system32> C:\Users\ebwib\asus-sensors.ps1

Name                    Value
----                    -----
CPU Core Voltage        1.3734 V
CPU SOC Voltage         1.1227 V
DRAM Voltage            1.3298 V
VDDP Voltage            0.6213 V
1.8V PLL Voltage        1.853 V
+12V Voltage            11.663 V
+5V Voltage             4.98675 V
3VSB Voltage            3.3354 V
VBAT Voltage            3.2046 V
AVCC3 Voltage           3.379 V
CPU Temperature         72 C
CPU Socket Temperature  51 C
Motherboard Temperature 34 C
CPU Fan                 1371 RPM
Chassis Fan 1           0 RPM
Chassis Fan 2           1153 RPM
Chassis Fan 3           1119 RPM
HAMP Fan                1138 RPM
Water Pump              0 RPM
SB 1.05V Voltage        1.071875 V
Chipset Temperature     48 C
Tsensor 1 Temperature   216 C
CPU VRM Temperature     56 C
CPU VRM Output Current  58 A
CPU OPT                 1378 RPM
Water In                216 C
Water Out               35 C
Water Flow              0 L/h
AIO Pump                1358 RPM
CPU Core Voltage        1.386 V
CPU SOC Voltage         1.13 V
DRAM Voltage            1.36 V