Skip to content

Instantly share code, notes, and snippets.

@outermeasure
Last active November 2, 2021 20:05
Show Gist options
  • Save outermeasure/36d77d0ee8c45b96993b1c0d4950d544 to your computer and use it in GitHub Desktop.
Save outermeasure/36d77d0ee8c45b96993b1c0d4950d544 to your computer and use it in GitHub Desktop.
Sensors for Apple M1 MacBooks
// 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