ESPLL: Raw PLL operations on the ESP32-WROOM-32!
Posted: Fri Jul 24, 2026 2:52 pm
Hello! I found a way to modify the RF PLL clock on the ESP32-WROOM-32 (tested this only on the v1 silicon). I won't go in depth on how to utilize this (you figure it out on your own!) because my custom transmission path is not suitable for releasing, legal issues for both me, you, and Espressif if I share my transmit path, but it should be fine only sharing PLL.
Warning: Modifying the PLL clock outside its intended range is outside the spec sheet. It may cause harmonics. Do not use the PCB antenna for non-compliant frequencies. It will overheat and you will have a bad day, Use a U.FL to pigtail adapter for testing. This is NOT a officially supported method.
PLL divider values for 5GHz bands (ESP32-WROOM-32UE required)
Format: rfpll_set_freq(divider, 0, 0, buf)
5180 MHz (Ch 36): 0x1CC
5200 MHz (Ch 40): 0x1CD
5220 MHz (Ch 44): 0x1CE
5240 MHz (Ch 48): 0x1CF
2412 MHz (Ch 1): 0x0C8 // reference
I didn't provide the offset since the offset can be found by mapping the phy_get_romfuncs() vtable in Ghidra or IDA. The ROM is identical across v1 silicon so your offset will match anyone else's findings (I don't know if it is identical for newer versions like v3).
Warning: Modifying the PLL clock outside its intended range is outside the spec sheet. It may cause harmonics. Do not use the PCB antenna for non-compliant frequencies. It will overheat and you will have a bad day, Use a U.FL to pigtail adapter for testing. This is NOT a officially supported method.
PLL divider values for 5GHz bands (ESP32-WROOM-32UE required)
Format: rfpll_set_freq(divider, 0, 0, buf)
5180 MHz (Ch 36): 0x1CC
5200 MHz (Ch 40): 0x1CD
5220 MHz (Ch 44): 0x1CE
5240 MHz (Ch 48): 0x1CF
2412 MHz (Ch 1): 0x0C8 // reference
Code: Select all
extern void* phy_get_romfuncs(void);
typedef void (*rfpll_fn)(uint32_t, uint8_t, int16_t, uint8_t*);
void espll_set_freq(uint32_t divider) {
void **phy = (void**)phy_get_romfuncs();
rfpll_fn rfpll = (rfpll_fn)phy[/* offset */];
uint8_t buf[3];
rfpll(divider, 0, 0, buf);
}
// espll_set_freq(0x1CC); // 5180MHz
// espll_set_freq(0x1CD); // 5200MHz
// espll_set_freq(0x1CE); // 5220MHz
// espll_set_freq(0x1CF); // 5240MHz