Last active
November 2, 2021 20:05
-
-
Save outermeasure/36d77d0ee8c45b96993b1c0d4950d544 to your computer and use it in GitHub Desktop.
Sensors for Apple M1 MacBooks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This code is is an adaptation of | |
// https://github.com/freedomtan/sensors/blob/master/sensors/sensors.m that | |
// prints the temperatures for Apple M1 MacBooks to the standard output. | |
// WARNING: ONLY USE WITH APPLE M1 MACBOOKS | |
// To compile run: | |
// `clang -Wall -O3 -v sensors.m -framework IOKit -framework Foundation -o sensors` | |
// Example ouput: | |
// > ./sensors | |
// CPU performance core: +36.8°C | |
// CPU efficiency core: +35.5°C | |
// GPU core: +30.0°C | |
// Neural engine: +30.0°C | |
// Battery: +32.3°C | |
// Disk: +36.0°C | |
// Here is the original code's license | |
// BSD 3-Clause License | |
// Copyright (c) 2016-2018, "freedom" Koan-Sin Tan | |
// All rights reserved. | |
// Redistribution and use in source and binary forms, with or without | |
// modification, are permitted provided that the following conditions are met: | |
// * Redistributions of source code must retain the above copyright notice, this | |
// list of conditions and the following disclaimer. | |
// * Redistributions in binary form must reproduce the above copyright notice, | |
// this list of conditions and the following disclaimer in the documentation | |
// and/or other materials provided with the distribution. | |
// * Neither the name of the copyright holder nor the names of its | |
// contributors may be used to endorse or promote products derived from | |
// this software without specific prior written permission. | |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
// POSSIBILITY OF SUCH DAMAGE. | |
#include <Foundation/Foundation.h> | |
#include <IOKit/hidsystem/IOHIDEventSystemClient.h> | |
#include <stdio.h> | |
typedef struct __IOHIDEvent *IOHIDEventRef; | |
typedef struct __IOHIDServiceClient *IOHIDServiceClientRef; | |
#ifdef __LP64__ | |
typedef double IOHIDFloat; | |
#else | |
typedef float IOHIDFloat; | |
#endif | |
struct Sensor_t { | |
char const *keyPrefix; | |
char const *name; | |
double maxTemperature; | |
uint8_t hasReading; | |
}; | |
typedef struct Sensor_t *Sensor; | |
struct Sensor_t M1_SPEC[] = { | |
{.keyPrefix = "pACC MTR Temp Sensor", | |
.name = "CPU performance core:", | |
.maxTemperature = 0.0, | |
.hasReading = 0.0}, | |
{.keyPrefix = "eACC MTR Temp Sensor", | |
.name = "CPU efficiency core:", | |
.maxTemperature = 0.0, | |
.hasReading = 0.0}, | |
{.keyPrefix = "GPU MTR Temp Sensor", | |
.name = "GPU core:", | |
.maxTemperature = 0.0, | |
.hasReading = 0.0}, | |
{.keyPrefix = "ANE MTR Temp Sensor", | |
.name = "Neural engine:", | |
.maxTemperature = 0.0, | |
.hasReading = 0.0}, | |
{.keyPrefix = "gas gauge battery", | |
.name = "Battery:", | |
.maxTemperature = 0.0, | |
.hasReading = 0.0}, | |
{.keyPrefix = "NAND CH", | |
.name = "Disk:", | |
.maxTemperature = 0.0, | |
.hasReading = 0.0}, | |
}; | |
uint8_t testString(CFStringRef cfstring, char const *prefix) { | |
char buffer[1024]; | |
if (!cfstring) { | |
return 0; | |
} | |
CFStringGetCString(cfstring, buffer, 1023, kCFStringEncodingUTF8); | |
return strncmp(prefix, buffer, strlen(prefix)) == 0; | |
} | |
IOHIDEventSystemClientRef | |
IOHIDEventSystemClientCreate(CFAllocatorRef allocator); | |
int IOHIDEventSystemClientSetMatching(IOHIDEventSystemClientRef client, | |
CFDictionaryRef match); | |
IOHIDEventRef IOHIDServiceClientCopyEvent(IOHIDServiceClientRef, int64_t, | |
int32_t, int64_t); | |
CFStringRef IOHIDServiceClientCopyProperty(IOHIDServiceClientRef service, | |
CFStringRef property); | |
IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef event, int32_t field); | |
#define kIOHIDEventTypeTemperature 15 | |
#define IOHIDEventFieldBase(type) (type << 16) | |
void computeSupportedCapabilities(CFDictionaryRef sensors) { | |
int32_t j, c; | |
c = sizeof(M1_SPEC) / sizeof(M1_SPEC[0]); | |
IOHIDEventSystemClientRef system = | |
IOHIDEventSystemClientCreate(kCFAllocatorDefault); | |
IOHIDEventSystemClientSetMatching(system, sensors); | |
CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices( | |
system); // matchingsrvs = matching services | |
long count = CFArrayGetCount(matchingsrvs); | |
for (int i = 0; i < count; i++) { | |
IOHIDServiceClientRef sc = | |
(IOHIDServiceClientRef)CFArrayGetValueAtIndex(matchingsrvs, i); | |
CFStringRef name = IOHIDServiceClientCopyProperty(sc, CFSTR("Product")); | |
if (!name) { | |
continue; | |
} | |
IOHIDEventRef event = IOHIDServiceClientCopyEvent( | |
sc, kIOHIDEventTypeTemperature, 0, 0); // here we use ...CopyEvent | |
if (!event) { | |
CFRelease(name); | |
continue; | |
} | |
for (j = 0; j < c; j++) { | |
if (testString(name, M1_SPEC[j].keyPrefix)) { | |
M1_SPEC[j].hasReading = 1; | |
double temp = IOHIDEventGetFloatValue( | |
event, IOHIDEventFieldBase(kIOHIDEventTypeTemperature)); | |
if (temp > M1_SPEC[j].maxTemperature) { | |
M1_SPEC[j].maxTemperature = temp; | |
} | |
break; | |
} | |
} | |
CFRelease(event); | |
CFRelease(name); | |
} | |
CFRelease(matchingsrvs); | |
CFRelease(system); | |
} | |
// create a dict ref, like for temperature sensor {"PrimaryUsagePage":0xff00, | |
// "PrimaryUsage":0x5} | |
CFDictionaryRef matching(int page, int usage) { | |
CFNumberRef nums[2]; | |
CFStringRef keys[2]; | |
keys[0] = CFStringCreateWithCString(0, "PrimaryUsagePage", 0); | |
keys[1] = CFStringCreateWithCString(0, "PrimaryUsage", 0); | |
nums[0] = CFNumberCreate(0, kCFNumberSInt32Type, &page); | |
nums[1] = CFNumberCreate(0, kCFNumberSInt32Type, &usage); | |
CFDictionaryRef dict = CFDictionaryCreate( | |
0, (const void **)keys, (const void **)nums, 2, | |
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
return dict; | |
} | |
void dump() { | |
int32_t i, c; | |
c = sizeof(M1_SPEC) / sizeof(M1_SPEC[0]); | |
for (i = 0; i < c; i++) { | |
if (!M1_SPEC[i].hasReading) { | |
continue; | |
} | |
if (!M1_SPEC[i].maxTemperature) { | |
continue; | |
} | |
printf("%-32s+%0.1lf°C\n", M1_SPEC[i].name, M1_SPEC[i].maxTemperature); | |
} | |
} | |
int main() { | |
CFDictionaryRef thermalSensors = matching(0xff00, 5); // 65280_10 = FF00_16 | |
// thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of | |
// 0xff05 can be checked by ioreg -lfx | |
computeSupportedCapabilities(thermalSensors); | |
CFRelease(thermalSensors); | |
dump(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment