iMac 2009 / 2010 / 2011 GPU upgrade — Fixing LED/LCD PWM brightness with an ESP32

iMac 2009–2011 GPU upgrade PWM backlight mod — regain brightness control after upgrading to an unsupported non-Apple GPU.

Fixing Things
9 min readMay 15, 2020

Here’s how to get back adjustable LCD brightness after installing a better GPU in your 2009–2011 iMac.

Warning!

It goes without saying that modifying your iMac is dangerous and may destroy your iMac. I am describing how I modified my iMac and if you follow these steps do so at your own risk. I can’t guarantee that these modifications will work for you and you are quite likely to destroy your iMac or hurt yourself in the process if you don’t know what you are doing.

!! Always unplug your power cord when you work inside the iMac, or else sparks! Ask me how I know ;) !!

Quick Video of it working

https://youtu.be/aiL-gU4nH2M

Ok, that’s not impressive and shoddy camera work but the brightness control works and almost behaves like native brightness controls.

Why?

The 2009–2011 iMacs are nicely upgradeable, thanks to many MacRumors members (especially Nick [D]vB) it is possible to even run rather recent graphics cards on these old iMacs.

The problem is that iMacs run custom GPU vBIOS (video BIOS) software from Apple that, among other stuff, enables backlight control. So when you upgrade your graphics card, you lose backlight control, even with Nick [D]vB’s custom vBIOS for many cards. You can regain backlight by running OpenCore but just doing it in hardware seemed like a safer longer term bet.

The Solution

MacRumors member wlagarde reverse engineered how these iMacs control the LED brightness. The LCD controller receives a pulse-width-modulated (PWM) signal from the logic board/GPU and correspondingly adjusts brightness. Since upgraded unofficial GPUs do not send a proper signal, the display always runs at full brightness. That leads to potential heat buildup issues and shorter lifespan as well as problems in low ambient light.

The PWM signal has a 3.25V amplitude and a frequency of 13kHz, the length (or duty cycle) of these pulses determines the backlight intensity.

MacRumors posters proposed a couple solutions: using a Dying Light module originally intended for failing MacBook Pro backlights (currently unavailable), a stand-alone potentiometer-configured PWM module, serial PWM modules or even a Raspberry Pi running a Node Red env.

Fortunately, the ESP32 microcontroller supports PWM frequencies of 13kHz and higher (unfortunately the cheaper ESP-8266 only does 1kHz), is easy to obtain for < $10, easy to program and use, and supports Wifi/BT but more importantly Serial comms via USB.

And MacRumors user passatgt also wrote software to dim Mac displays by adjusting the screen’s gamma or using a Raspberry Pi as a PWM source — I have adapted the software to work with an ESP32 hooked up internally and communicate via serial. The Raspi seemed to rely on Wifi/networking and seemed a bit overkill (running a small, real computer running Linux inside your large computer running MacOS! mindburst).

Quick Summary

But don’t just copy&paste this, read below and adjust these steps as needed to your own system.

  1. Install/download the necessary software
  1. Flash and program the ESP32
  • Install SiLabs drivers and connect ESP32s
  • Flash micropython: /usr/local/bin/esptool.py --port /dev/cu.SLAB_USBtoUART erase_flash then immediately on the ESP32-D1 dev kit, hold down “BOOT” first, then briefly press “EN”, then /usr/local/bin/esptool.py --chip esp32 --port /dev/cu.SLAB_USBtoUART --baud 460800 write_flash -z 0x1000 ~/Downloads/esp32-idf4-20191220-v1.12.bin and repeat BOOT + EN as before
  • run ./micropy/install_mac.sh from the GitHub repo local clone to put the files on the ESP32, reboot the ESP32 and check that the blue LED is on
  1. Wire up the 6-pin PCI-e extension cable to the LCD controller board, cut the PWM wire and route the PWM wire to your ESP32 pin D23/GPIO23.
  2. Wire up your ESP32 to the iMac’s internal IR USB port or run a cable to the back of the iMacs normal USB ports.
ESP32 running Micropython and the PWM/LED code.
Cut the PWM wire in your PCI-e extension cable/harness
Route the cables and tuck away, connect PWM wire to ESP32 — hook up the ESP32 to internal or ext. USB ports.

Parts

  • ESP-32 microcontroller (important, has to be an ESP-32 for PWM freq, not ESP-8266) — I used the most common “D1 dev kit” board from eBay with headers, no headers may be preferable.
  • PCI-E 6-pin male/female extension harness or splitter
  • Extra iMac Infrared sensor cable (if you don’t want to destroy your own)
  • 1 or 2 Micro USB cables (one to program the ESP-32, and a sacrificial one)
  • Soldering iron, solder and wires

A couple Notes

  • Code blocks in Medium (backtick) are commands to be executed in your Terminal App, e.g.ls /dev/cu.*` lists serial devices in the Terminal.
  • I am assuming that homebrew installs all applications in “/usr/loca/bin”, adjust if necessary.
  • Quotes indicate code or a literal, if you enter these literals always omit the quotes.

Steps

0. Upgrade your GPU and flash appropriate Nick [D]vB bios. Honestly, it is quite risky and not advised if you don’t know what you are doing.

1. Download the software from GitHub, install homebrew, install SiLabs USB VCP drivers.

2. Program the ESP32

  • Make sure you have the SiLabs USB VCP drivers installed!
  • Connect the ESP32 via micro-USB cable to a working Mac
  • Check if you can see any USB serial devices (High Sierra/Catalina: ls /dev/cu.*), I see 2–3 devices depending on computer. Make sure you have a “/dev/cu.SLAB_USBtoUART” device. If not, either you didn’t properly install the SiLabs driver, or the ESP32 or your cable are not working.
  • Run /usr/local/bin/esptool.py flash_id to check the connection to the ESP with its tools and note the serial port => referred to as <port> below, e.g. “/dev/cu.SLAB_USBtoUART”.
  • Erase and flash /usr/local/bin/esptool.py --port <port> erase_flash on the ESP32-D1 dev kit, hold down “BOOT” first, then briefly press “EN”, esptool should now see your ESP32 and erase it. /usr/local/bin/esptool.py --chip esp32 --port <port> --baud 460800 write_flash -z 0x1000 ~/Downloads/esp32-idf4-20191220-v1.12.bin
  • Restart the ESP32 again (push “EN” button) and connect via picocom: /usr/local/bin/picocom /dev/cu.SLAB_USBtoUART -b115200 — hit enter and you should see the Python REPL “>>>”. Play around, then exit with CTRL+A, CTRL+Q.
  • Try running an LED test if you want: /usr/local/bin/ampy — port /dev/cu.SLAB_USBtoUART run test.py.
  • There’s an ./micropy/install_mac.sh in the GitHub repo, run that script to copy all files from “./micropy/src” to the ESP32.
  • Reboot the ESP32 and you should see the blue and red LED both on.
  • If you have a different ESP type or want to use a pin other than D23 / GPIO23 edit “./micropy/src/pwm.py” and replace “pin=23” in the “init” with your desired pin. Run “install_mac.sh” again then.
  • Connect again via picocom: /usr/local/bin/picocom /dev/cu.SLAB_USBtoUART -b115200, the script “./micropy/src/shell.py” has hijacked the stdin from the REPL and accepts brightness commands only, type “repl” if you ever want to exit back to the Python REPL. Try to enter e.g. “10” (no quotes) and hit return, the LED should dim to about 10%, then try other numbers. Exit picocom with CTRL+A, CTRL+Q.
  • If the LED dims properly when you type in your numbers you’ve got the ESP32 up and running and you can proceed to modding the iMac.
Test the ESP32, the blue LED should mirror the set PWM brightness. Try to send some cmds with picocom.

3. Open up your iMac (carefully!) and unplug the IR module towards the bottom of the screen behind the front Apple logo (see iFixit e.g.) and connect the ESP32 to the IR module’s USB port.

!! Warning: look at schematics to make sure this applies to your vintage of iMac, mine is Late 2009 with i7, the schematic I saw online didn’t work for me and had D+/D- crossed !!

  • Remember which way the IR connector is facing when plugged into the logic board, I will count pins left-to-right as it is when plugged in.
  • Cut your sacrificial micro-USB cable, strip the wires.
  • Cut the wires on the IR module’s logic board connector.
  • Look at schematics, verify which pins on the connector are 5V and ground! If you mess these up you may fry the ESP32 or unlikely even your logic board.
  • For iMac 11,1: Connect IR left-most pin (pin 1) to USB GND/black, pin 2 to USB 5V/red, pin 3 to USB D-/green, pin 4 USB D+/white.
  • According to this post the data pins may be switched on newer iMacs (or I got something else mixed up).

My ESP32 wasn’t showing up as USB device via IR port when I first wired it up, if I read the online schematics correctly pins 3/4 were D+/D- but I had to rewire to D-/D+ with my 2009 iMac and it worked.

!!Incorrect!! wiring for my iMac 27" Late 2009 i7.
Corrected wiring!
ESP32 running off the IR port USB connector.

4. Remove the LCD driver board and disconnect the 6-pin PCI-e cable at the top.

  • On your extension cable (not the original if you want this hack to be reversible!), when looking at the female connector from the back, cut the bottom right cable and extend it so that you can solder/connect it to pin D23 on your ESP32 when it’s plugged into the logic board IR connector and stowed away.
  • Solder/connect the cable from the PCI-e cable to your ESP32 pin D23/GPIO23, plug the PCI-e extension cable in to the LCD driver board and reinstall the LCD driver board.
  • Connect the extension cable to the original PCI-e LCD driver board cable and tuck the cables away — they need to be neatly tucked away for the LCD to fit back on.
  • Don’t reinstall the LCD panel yet, briefly connect the power cable and turn on the iMac. Both the blue and red LED on the ESP32 should turn on. Turn off the iMac before it boots.
  • Unplug the iMac, reinstall the LCD panel, plug back in and boot into MacOS.
Route the cables, connect PWM.
Tuck it all away for proper LCD fit.

5. All Done?

Yay, it’s working!

If everything went well, your screen should run at about 70% brightness at boot now. Boot into MacOS and run the “imac_esp32_pwm_brightness” app, MacOS will ask for permissions you will need to grant “Accessibility permissions” in System Prefs if you want to use the brightness keys to adjust brightness. Otherwise you can use a slider in MacOS menu bar (little brightness icon).

The first time the app runs, it shows the preferences dialog and asks for serial port and baud rate, you probably want “115200” baud and “/dev/cu.SLAB_USBtoUART”. Click “Done”. If you want to quit the app, click the brightness icon on the right side menu bar, click the preferences logo and “Quit” in the dialog.

Thanks to everyone who contributed to the GPU mod thread on MacRumors! Special thanks Nick [D]vB for making all this even possible with his vBIOS, wlagarde for reverse engineering the iMac PWM protocol and passatgt for open-sourcing “imac-pwm-control” which I based the Mac software on.

And BTW: It’s been 10+ years since I’ve done Mac/iOS development, never in Swift, and I haven’t had time to clean up the code yet — so don’t judge ;)

--

--

Fixing Things

A collection of guides on how to take apart, hopefully fix and reassemble things like vintage cameras, lenses or bicycle related things.