Documentation

ESP32-WROVER 4MB PSRAM WiFi & Bluetooth Module | ShillehTek Product Manual
Documentation / ESP32-WROVER 4MB PSRAM WiFi & Bluetooth Module | ShillehTek Product Manual

ESP32-WROVER 4MB PSRAM WiFi & Bluetooth Module | ShillehTek Product Manual

manualshillehtek

Overview

The ESP32-WROVER is Espressif's classic dual-core WiFi + Bluetooth module with a superpower the plain WROOM doesn't have: 4MB of external PSRAM alongside its 4MB of flash. That extra RAM is what makes camera streaming, large JSON/TLS buffers, audio processing, JPEG handling, and MicroPython with room to breathe practical on an ESP32. If a project has ever thrown "out of memory" at you on a WROOM, the WROVER is the fix.

This is the raw solder-down module — castellated pads, onboard PCB antenna, RF shield — not a dev board. That's exactly what you want when a breadboard prototype graduates to a custom PCB: you control the regulator, the USB (or lack of it), and the pin breakout. It runs from a single 3.3V supply, and programming happens over TXD0/RXD0 with a USB-UART adapter using the standard two-signal auto-reset arrangement (EN + IO0).

Everything the ESP32 ecosystem offers applies: Arduino IDE, ESP-IDF, MicroPython, PlatformIO. A few pins carry rules worth knowing before you route a board — IO34/IO35 and SENSOR_VP/VN are input-only, the SD0-SD3/CLK/CMD pads belong to the internal flash and must stay unconnected, and because PSRAM claims IO16/IO17 internally, those pins aren't available on WROVER designs. The pinout diagram below is the map.

At a Glance

SoC
ESP32 dual-core @ 240 MHz
Memory
4MB flash + 4MB PSRAM
Wireless
WiFi b/g/n + BT 4.2/BLE
Supply
3.3V, 500 mA capable
Format
Castellated SMD module
Programming
UART (TXD0/RXD0 + EN/IO0)

Specifications

Parameter Value
Module ESP32-WROVER (B revision), PCB antenna
CPU Xtensa LX6 dual-core, up to 240 MHz
SRAM / PSRAM 520 KB internal + 4 MB external PSRAM
Flash 4 MB
WiFi 802.11 b/g/n, 2.4 GHz
Bluetooth v4.2 BR/EDR + BLE
Supply Voltage 3.0 - 3.6V (3.3V nominal)
Peak Current ~500 mA during WiFi TX bursts
Input-Only Pins IO34, IO35, SENSOR_VP (IO36), SENSOR_VN (IO39)
Reserved Pads SD0-SD3, CLK, CMD (internal flash) — leave unconnected; IO16/IO17 used by PSRAM
Strapping Pins IO0 (boot mode), IO2, IO12 (flash voltage), IO15
Operating Temperature -40°C to +85°C

Pinout Diagram

Viewed from above with the antenna at the top: the left column runs GND, IO23, IO22, TXD0, RXD0, IO21 down through the SD pads; the right column runs GND, 3V3, EN, the sensor/input-only pins, then the general-purpose IOs. TXD0/RXD0 are the flashing UART, EN is reset (active low), and IO0 held low at reset selects download mode.

ESP32-WROVER-B module pinout diagram showing all castellated pads including GND, 3V3, EN, IO pins, TXD0 RXD0 and reserved SD flash pads

Wiring Guide

Minimal Boot Circuit

The bare minimum for the module to run your firmware:

Module Pad Connect To Details
3V3 3.3V supply + 10uF and 100nF close to the pad
GND (all three) Ground plane Tie every GND pad
EN 10k to 3.3V + 1uF to GND Power-on reset RC
IO0 10k to 3.3V Boots from flash by default
Warning: Leave SD0-SD3, CLK, and CMD completely unconnected — they are the internal flash bus. Also skip IO16/IO17 in your design: the WROVER's PSRAM owns them.

Flashing With a USB-UART Adapter

Module Pad USB-UART Pin Details
TXD0 RXD Crossed over
RXD0 TXD Crossed over
GND GND
IO0 Hold LOW during reset Enters download mode
EN Pulse LOW Reset
Tip: Use a 3.3V-logic adapter and power the module from a proper 3.3V regulator, not the adapter's 3.3V pin — most can't feed WiFi's 500 mA bursts. Manual flashing: hold IO0 low, tap EN low, release both, then run esptool/IDE upload.

Strapping Pins — What Not To Load at Boot

Pin Role at Reset Rule of Thumb
IO0 Boot mode select Pull up; low at reset = download mode
IO2 Must be low/floating to flash Fine as an output (LED) after boot
IO12 Flash voltage select Keep LOW/floating at reset — high bricks boot on 3.3V flash
IO15 Boot log silencing Pull low to mute ROM messages if desired
IO34/IO35, VP/VN Input-only Perfect for ADC and buttons; no pull-ups inside

Power Supply Design

Requirement Recommendation
Regulator 3.3V LDO rated 600 mA+ (AMS1117-3.3, ME6211, or buck)
Bulk capacitance 10-22 uF at the module's 3V3 pad
Decoupling 100 nF ceramic right at the pad
Brownout symptoms Reboots when WiFi starts = undersized supply
Antenna keep-out No copper/ground under the antenna end of the module

Code Examples

The first two sketches run in the Arduino IDE (board: "ESP32 Wrover Module", PSRAM: enabled). The MicroPython examples show flashing the interpreter and proving the PSRAM is alive.

Arduino IDE — PSRAM check + WiFi scan

wrover_psram_wifi.ino
// ESP32-WROVER - PSRAM check + WiFi scan
// Arduino IDE: Tools -> Board -> "ESP32 Wrover Module", PSRAM: "Enabled"

#include <WiFi.h>

void setup() {
  Serial.begin(115200);
  delay(500);

  Serial.printf("Chip: %s, %d cores @ %d MHz\n",
                ESP.getChipModel(), ESP.getChipCores(), ESP.getCpuFreqMHz());
  Serial.printf("Flash: %u bytes\n", ESP.getFlashChipSize());
  Serial.printf("PSRAM: %u bytes (%s)\n", ESP.getPsramSize(),
                psramFound() ? "FOUND" : "NOT FOUND");

  // Allocate 1MB in PSRAM to prove it's usable
  uint8_t *big = (uint8_t *)ps_malloc(1024 * 1024);
  Serial.printf("1MB PSRAM alloc: %s\n", big ? "OK" : "FAILED");
  if (big) free(big);

  WiFi.mode(WIFI_STA);
  Serial.println("\nScanning WiFi...");
  int n = WiFi.scanNetworks();
  for (int i = 0; i < n; i++) {
    Serial.printf("%2d: %-24s %d dBm\n",
                  i + 1, WiFi.SSID(i).c_str(), WiFi.RSSI(i));
  }
}

void loop() {}

Arduino IDE — Big buffer web fetch (PSRAM in action)

wrover_big_buffer.ino
// ESP32-WROVER - download into a PSRAM buffer
// Fill in your WiFi credentials before uploading.

#include <WiFi.h>
#include <HTTPClient.h>

const char *SSID = "YOUR_WIFI";
const char *PASS = "YOUR_PASSWORD";

void setup() {
  Serial.begin(115200);
  WiFi.begin(SSID, PASS);
  while (WiFi.status() != WL_CONNECTED) { delay(300); Serial.print("."); }
  Serial.printf("\nConnected: %s\n", WiFi.localIP().toString().c_str());

  // 2MB buffer - impossible on a WROOM, trivial on a WROVER
  size_t cap = 2 * 1024 * 1024;
  uint8_t *buf = (uint8_t *)ps_malloc(cap);
  Serial.printf("PSRAM buffer: %s\n", buf ? "allocated" : "failed");

  HTTPClient http;
  http.begin("http://example.com/");
  int code = http.GET();
  if (code == 200) {
    String body = http.getString();
    size_t n = min((size_t)body.length(), cap);
    memcpy(buf, body.c_str(), n);
    Serial.printf("Fetched %u bytes into PSRAM. First line:\n", n);
    Serial.println(body.substring(0, body.indexOf('\n')));
  } else {
    Serial.printf("HTTP error: %d\n", code);
  }
  http.end();
  free(buf);
}

void loop() {}

MicroPython — flash + PSRAM proof

wrover_micropython.txt
# Flash MicroPython (SPIRAM build!) onto the WROVER over UART:
#   pip install esptool
#   esptool --port /dev/ttyUSB0 erase_flash
#   esptool --port /dev/ttyUSB0 --baud 460800 write_flash 0x1000 \
#       ESP32_GENERIC-SPIRAM-latest.bin
# (download the GENERIC-SPIRAM firmware from micropython.org)

# Then at the REPL:
import gc, esp, machine

print("CPU MHz:", machine.freq() // 1_000_000)
print("Flash size:", esp.flash_size())

gc.collect()
print("Free RAM:", gc.mem_free())   # ~4MB free = PSRAM active

# Allocate a 2MB bytearray - only possible with PSRAM
big = bytearray(2 * 1024 * 1024)
print("2MB bytearray OK, length:", len(big))

MicroPython — WiFi scan on the module

wrover_wifi_scan.py
# ESP32-WROVER - MicroPython WiFi scan
import network
import time

sta = network.WLAN(network.STA_IF)
sta.active(True)

print("Scanning...")
for ssid, bssid, ch, rssi, sec, hidden in sta.scan():
    print("{:24s} ch{:2d} {:4d} dBm".format(ssid.decode(), ch, rssi))

# Simple connect helper
def connect(ssid, password, timeout=15):
    sta.connect(ssid, password)
    t0 = time.time()
    while not sta.isconnected():
        if time.time() - t0 > timeout:
            raise RuntimeError("WiFi connect timeout")
        time.sleep(0.5)
    print("Connected:", sta.ifconfig()[0])

# connect("YOUR_WIFI", "YOUR_PASSWORD")

Frequently Asked Questions

WROVER vs WROOM — when is the PSRAM worth it?
Any time your working data outgrows the ESP32's ~300KB of usable internal RAM: camera frames (every ESP32-CAM-style design uses a WROVER for this), audio buffers, TLS connections with big certificates, large JSON documents, display framebuffers, and MicroPython projects that import heavy libraries. If you're blinking LEDs and posting sensor values, a WROOM is fine — the WROVER buys headroom.
psramFound() returns false — where did my 4MB go?
Almost always a build setting: in Arduino IDE choose board "ESP32 Wrover Module" and set PSRAM to "Enabled"; in PlatformIO add build flags -DBOARD_HAS_PSRAM and -mfix-esp32-psram-cache-issue; in MicroPython flash the SPIRAM firmware build. The hardware is there — the toolchain just has to be told to initialize it.
Why can't I use IO16 and IO17?
On WROVER modules those two GPIOs connect internally to the PSRAM chip (CS and clock). Driving them from outside crashes the module the moment PSRAM initializes. Treat them as nonexistent when porting WROOM designs — this is the single most common gotcha moving between the two modules.
The module resets randomly when WiFi transmits. Why?
Brownout: WiFi TX pulls current spikes near 500 mA, and a weak regulator or thin supply traces dip below 3.0V, tripping the brownout detector. Use a 600 mA+ regulator, put 10-22uF right at the 3V3 pad, and keep supply traces short and wide. Powering from a USB-UART adapter's 3.3V pin is the classic cause — those are usually good for only ~150 mA.
How do I get USB flashing like a dev board?
Add a USB-UART bridge (CP2102, CH340) wired to TXD0/RXD0, plus the standard auto-reset circuit: two transistors (or the bridge's DTR/RTS pins through 10k resistors) driving EN and IO0. That's literally all a dev board adds. For occasional flashing, the manual hold-IO0-tap-EN dance with a plain adapter works fine.
Can I hand-solder a castellated module?
Yes — castellations are the friendliest SMD format. Tin the PCB pads, tack two corner pads to align, then drag-solder each edge with flux; the half-hole vias wick solder beautifully. A breakout/adapter board also works for prototyping. Keep the antenna end hanging over the board edge or over a copper keep-out for full WiFi range.
Is PSRAM as fast as internal RAM?
No — it rides an 80 MHz SPI/QSPI bus with caching, so it's several times slower than internal SRAM and can't hold ISR code or DMA descriptors. The frameworks place stacks and hot data internally, and spill big allocations (ps_malloc, large MicroPython objects) to PSRAM automatically. For buffers and documents you'll never notice; for tight DSP loops, keep the hot path internal.

Related Tutorials