4 – Arduino Nano ESP32

Controlling the RGB LED on the Arduino Nano ESP32

There are many stories circulating about problems controlling the RGB-LED’s on the Nano ESP32 with used software program. Many solutions are proposed on internet forums, but unfortunately, none of them worked on my (official) Nano Espressif board. For example, there’s discussion about a WS2812/NeoPixel chip being present on the board, but that’s not true! so I find out. Further the GPIO ports listed on the schematic drawing of the board are also unusable! One of the ports even interferes with the software upload process! All in all, a nightmare to be caught in. That’s why I started investigating why that is and how to get out of this misery and how the RGB-LED’s get back to work through software control.

What has been discovered regarding the Arduino NanoESP32:
1. The onboard RGB LED is not a NeoPixel.
– Not a WS2812/NeoPixel on pin 46, but a regular common-anode RGB LED and than three of these.
– The three colors are on the symbolic pins namely  LED_RED, LED_GREEN, and LED_BLUE (active-low).
2. Active-low is controlled with PWM.
– Use the ESP32 PWM driver (ledcSetup/ledcAttachPin + ledcWrite).
– ledcWrite(channel, 255–value) → 0 = full on, 255 = off.
3. Different control patterns
– Blinking with digital (or PWM) writes in blinkColor()
– Fading with small steps in fadeColor()
– Color loops and combinations work reliably.
4. Flash remnants can affect behavior
– Old firmware in OTA and/or partitions sometimes remains. Therefore if this happen erase before upload program.
– Fully erase via:
– – Arduino IDE 2.2.x+: Tools → Erase Flash → All Flash Contents
– – esptool.py: erase_flash command-line.
5. IDE/Core options & RGB.h
– RGB.h exists as a variant feature in the Arduino patches on Espressif-core 2.0.18-arduino5 and version 3.x.x, but in practice it proved unusable for onboard LED’s.

Therefore, in my case I used native PWM control instead of RGB.h. Please note that when ever you want to use this library it does NOT need to be #included as it is part of the Arduino firmware of the Nano ESP32 itself.

Caution!
The Arduino Nano ESP32 uses its own proprietary firmware! Therefore, you must explicitly select the Arduino Nano ESP32 variant when choosing your board. If you choose the ESP32 variant, everything will go wrong. If you installed the “ESP32 core 2.0.18-arduino5” board, you’re using the wrong one so I find out.
In my IDE version 2.3.5, I wasn’t able to install the correct board core. I eventually switched to the cloud version of Arduino.cc. That worked fine. Apparently, the IDE is no longer properly maintained by the Arduino group?

Why so confusing?
Most of the confusion arises because the version number “2.0.18” that was available for download resembles Espressif’s version, but the “-arduino5” indicates that this is a modified version with Nano ESP32 support. This is because Arduino shares the Core with Espressif and only adds patches, without releasing its own Core with a new version number. Therefore, board selection is crucial.

For your interest here you find the official-Arduino Nano ESP32-datasheet  and the drawing of the board.

Two programs are shown below:
The first program is complete and flashes and fades the LEDs. The second program is designed as a subroutine and can therefore easily be incorporated into your own program.

Here is the first complete working program

/* A working program that makes the RGB LED on the Arduino Nano ESP32 
board blink red, green, blue and does a fade loop red → green → blue → 
white → off and then turns the RGB LED off.*/

#include <Arduino.h>

// Active‑low common‑anode RGB LED‑pins
const uint8_t PIN_R = LED_RED;
const uint8_t PIN_G = LED_GREEN;
const uint8_t PIN_B = LED_BLUE;

// PWM‑CHANNELS
const uint8_t CH_R = 0;
const uint8_t CH_G = 1;
const uint8_t CH_B = 2;
const uint32_t PWM_FREQ = 5000;
const uint8_t PWM_RES = 8;

void setup() {
// PWM‑setup
ledcSetup(CH_R, PWM_FREQ, PWM_RES);
ledcSetup(CH_G, PWM_FREQ, PWM_RES);
ledcSetup(CH_B, PWM_FREQ, PWM_RES);
ledcAttachPin(PIN_R, CH_R);
ledcAttachPin(PIN_G, CH_G);
ledcAttachPin(PIN_B, CH_B);

// 0) Swiths LED's off
setColor(0, 0, 0);
delay(500);

// 1) Blinking: rood, groen, blauw
blinkColor(255, 0, 0, 200);
blinkColor(0, 255, 0, 200);
blinkColor(0, 0, 255, 200);

// 2) Fade‑loop: rood → groen → blauw → wit → uit
fadeColor(255, 0, 0);
fadeColor(0, 255, 0);
fadeColor(0, 0, 255);
fadeColor(255, 255, 255);
fadeColor(0, 0, 0);

// 3) Flashing red one more time
blinkColor(255, 0, 0, 500);

// 4) Switch LED's off
setColor(0, 0, 0);

// 5) infinite loop
while (true) {
  delay(1000);
  }
}

void loop() {
// never reached
}

// ----------------------------------------------------------------------------
// Convert RGB-LED to (r,g,b). Active‑low: 255 = uit, 0 = max.
// ----------------------------------------------------------------------------
void setColor(uint8_t r, uint8_t g, uint8_t b) {
  ledcWrite(CH_R, 255 - r);
  ledcWrite(CH_G, 255 - g);
  ledcWrite(CH_B, 255 - b);
}

// ----------------------------------------------------------------------------
// Flash one color: 4× time on/off with interval ms.
// ----------------------------------------------------------------------------
void blinkColor(uint8_t r, uint8_t g, uint8_t b, uint16_t interval) {
  for (int i = 0; i < 4; i++) {
    setColor(r, g, b);
    delay(interval);
    setColor(0, 0, 0);
    delay(interval);
  }
}

// ----------------------------------------------------------------------------
// Fade in → fade out true (r,g,b). (with int-counter in stead off uint8_t)
// ----------------------------------------------------------------------------
void fadeColor(uint8_t r, uint8_t g, uint8_t b) {
// fade in
  for (int v = 0; v <= 255; v += 5) { setColor((r * v) / 255, (g * v) / 255, (b * v) / 255); delay(20); } delay(300); // fade out for (int v = 255; v >= 0; v -= 5) {
    setColor((r * v) / 255,
(g * v) / 255,
(b * v) / 255);
    delay(20);
  }
  delay(200);
}

 

Now as a subroutine where the RGB LED color settings take place in the setup() or loop().

#include <Arduino.h>

// Active‑low common‑anode RGB LED‑pins
const uint8_t PIN_R = LED_RED;
const uint8_t PIN_G = LED_GREEN;
const uint8_t PIN_B = LED_BLUE;

// PWM‑CHANNELS
const uint8_t   CH_R = 0;
const uint8_t   CH_G = 1;
const uint8_t   CH_B = 2;
const uint32_t PWM_FREQ = 5000;
const uint8_t   PWM_RES = 8;

// ───────────────────────────────────────────
// choose a color for this in setup() or loop():
// For example in setup(): #define START_COLOR R_GREEN
// or in loop(): #define LOOP_COLOR R_BLUE
// ───────────────────────────────────────────
#define R_RED 1
#define R_GREEN 2
#define R_BLUE 3
#define R_WHITE 4
#define R_OFF 0

// ──────────────────────────────────────────────────────────────
// Subroutine‑prototypes
void initLed();
void setLedColor(uint8_t colorMacro);
void ledOff();

// Helper function that converts and controls the (r,g,b) values
void writeColor(uint8_t r, uint8_t g, uint8_t b);

// ──────────────────────────────────────────────────────────────
void setup() {
  Serial.begin(115200);
  initLed();

  // Example: turn on green directly in setup
  #define START_COLOR R_GREEN
  setLedColor(START_COLOR);

  delay(3000);
    ledOff();
}

void loop() {
// You could change colors here:
  #define LOOP_COLOR R_BLUE
  setLedColor(LOOP_COLOR);

  delay(2000);
  ledOff();

  delay(2000);
}

// ──────────────────────────────────────────────────────────────
// Initializes PWM channels and turns off LED
void initLed() {
  ledcSetup(CH_R, PWM_FREQ, PWM_RES);
  ledcSetup(CH_G, PWM_FREQ, PWM_RES);
  ledcSetup(CH_B, PWM_FREQ, PWM_RES);
  ledcAttachPin(PIN_R, CH_R);
  ledcAttachPin(PIN_G, CH_G);
  ledcAttachPin(PIN_B, CH_B);
  ledOff();
}

// Set the LED to one of the predefined colors
void setLedColor(uint8_t colorMacro) {
  switch (colorMacro) {
    case R_RED: writeColor(255, 0, 0); break;
    case R_GREEN: writeColor(0, 255, 0); break;
    case R_BLUE: writeColor(0, 0, 255); break;
    case R_WHITE: writeColor(255, 255, 255); break;
    default: ledOff(); break;
  }
}

// Swithc off RGB‑LED
void ledOff() {
  writeColor(0, 0, 0);
}

// Underlying control (active‑low common‑anode)
void writeColor(uint8_t r, uint8_t g, uint8_t b) {
  // Inverteren while common‑anode active‑low
  ledcWrite(CH_R, 255 - r);
  ledcWrite(CH_G, 255 - g);
  ledcWrite(CH_B, 255 - b);
}