Skip to content

Instantly share code, notes, and snippets.

@waydabber
Last active October 7, 2022 09:22
Show Gist options
  • Save waydabber/ca5c4f52183598fe95cd8045adfc7623 to your computer and use it in GitHub Desktop.
Save waydabber/ca5c4f52183598fe95cd8045adfc7623 to your computer and use it in GitHub Desktop.
This should change the brightness level of a single external display connected to an M1 MBP or MBA relative to the current setting using DDC/CI. Reportedly does not work on M1 Mini.
@import Darwin;
@import Foundation;
@import IOKit;
/*******
This should change the brightness level of a single external display connected to an M1 MBP or MBA relative to the current setting using DDC/CI. Reportedly does not work on M1 Mini.
Credits to @tao-j and @alin23
Compile:
clang -fmodules -o chbrt chbrt.m
To increase brightness by 10% enter:
./chbrt 10
To decrease brightness by 10% enter:
./chbrt -10
*******/
typedef CFTypeRef IOAVServiceRef;
extern IOAVServiceRef IOAVServiceCreate(CFAllocatorRef allocator);
extern IOReturn IOAVServiceCopyEDID(IOAVServiceRef service, CFDataRef* x2);
extern IOReturn IOAVServiceReadI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t offset, void* outputBuffer,
uint32_t outputBufferSize);
extern IOReturn IOAVServiceWriteI2C(IOAVServiceRef service, uint32_t chipAddress, uint32_t dataAddress, void* inputBuffer,
uint32_t inputBufferSize);
#define BRIGHTNESS 0x10
#define CONTRAST 0x12
#define AUDIO_SPEAKER_VOLUME 0x62
#define AUDIO_MUTE 0x8D
#define SLEEPTIME 5000
struct DDCWriteCommand {
UInt8 control_id;
UInt8 new_value;
};
int main(int argc, char** argv) {
IOAVServiceRef avService = IOAVServiceCreate(kCFAllocatorDefault);
if (!avService) {
NSLog(@"This is a tool to change the brightness of a single external display connected to an M1 Mac via DDC/CI. Do you have an external monitor? Are you on M1? Are you root?");
return 1;
}
IOReturn err;
UInt8 data[256];
memset(data, 0, sizeof(data));
data[0] = 0x82;
data[1] = 0x01;
data[2] = BRIGHTNESS;
data[3] = 0x6e ^ data[0] ^ data[1] ^ data[2] ^ data[3];
char i2cBytes[12];
memset(i2cBytes, 0, sizeof(i2cBytes));
NSData *sendingData = [NSData dataWithBytes:(const void *)data length:(NSUInteger)4];
usleep(SLEEPTIME);
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 4);
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
return 1;
}
usleep(SLEEPTIME);
err = IOAVServiceReadI2C(avService, 0x37, 0x51, i2cBytes, 12);
NSRange maxValueRange = {7, 1};
NSRange currentValueRange = {9, 1};
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
} else {
NSData *readData = [NSData dataWithBytes:(const void *)i2cBytes length:(NSUInteger)11];
char maxValue, currentValue;
[[readData subdataWithRange:maxValueRange] getBytes:&maxValue length:sizeof(1)];
[[readData subdataWithRange:currentValueRange] getBytes:&currentValue length:sizeof(1)];
NSLog(@"Current brightness is %i", currentValue);
if (argc == 2) {
int nextValue;
nextValue = currentValue+atoi(argv[1]);
if (nextValue<0) nextValue=0;
if (nextValue>maxValue) nextValue=maxValue;
struct DDCWriteCommand command;
command.control_id = BRIGHTNESS;
NSLog(@"Setting brightness to %i", nextValue);
command.new_value = nextValue;
data[0] = 0x84;
data[1] = 0x03;
data[2] = command.control_id;
data[3] = (command.new_value) >> 8;
data[4] = command.new_value & 255;
data[5] = 0x6E ^ 0x51 ^ data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4];
for (int i = 0; i < 3; ++i)
{
err = IOAVServiceWriteI2C(avService, 0x37, 0x51, data, 6);
if (err) {
NSLog(@"i2c error: %s", mach_error_string(err));
}
usleep(SLEEPTIME);
}
NSData *nsdata = [NSData dataWithBytes:(const void *)data length:(NSUInteger)6];
} else {
NSLog(@"Please specify a value between -100 and 100 as an argument to increase/decrease brightness by the desired level.");
}
}
return 0;
}
@waydabber
Copy link
Author

Here is a much more improved version to control the display:

https://github.com/waydabber/m1ddc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment