Arduino UNO R4 WiFi Bench Reference

Everything on the board, on the pins, and in the language — pin multiplexing taken from the Renesas core's own variant.cpp and pinmux.inc, and net names read out of the ABX00087 schematic, so the tables say what the silicon actually does.

SKU
ABX00087
MCU
R7FA4M1AB3CFM#AA0
Radio
ESP32-S3-MINI-1-N8
FQBN
arduino:renesas_uno:unor4wifi
Core
ArduinoCore-renesas
01

The board

UNO form factor, 5 V logic, two processors. The RA4M1 owns every header pin; the ESP32-S3 is a peripheral that speaks AT-style commands over an internal UART and doubles as the USB-serial bridge and the SWD programmer.

D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 GND AREF SDA SCL BOOT IOREF RESET 3V3 5V GND GND VIN A0 A1 A2 A3 A4 A5 ~ ~ ~ ~ ~ ~ USB-C · J1 DC jack · J5 · 6–24 V Qwiic · J2 3V3 · Wire1 OFF GND VRTC ICSP · J3 ESP hdr · J6 RESET · PB1 RA4M1U1 · 48 MHz M4 ESP32-S3M1 · MINI-1-N8 antenna 12 × 8 LED matrix · charlieplexed L TX RX ON U3 buck U5 3V3 U4 lvl-xlat U2/U6 mux
digital / analog I/Opower railgroundcontrol (AREF, RESET, BOOT, OFF, VRTC)LED matrix~ = PWM at boot

Schematic top view, USB-C at the left. Header pin order and spacing are to scale (2.54 mm pitch); component outlines are indicative. Board outline 68.85 × 53.34 mm, four M3 mounting holes, ~25 g.

Silicon U1 · M1

RA4M1R7FA4M1AB3CFM#AA0 — Arm Cortex-M4F @ 48 MHz, 5 V, LQFP64
Flash256 kB program · 8 kB data flash used as EEPROM
SRAM32 kB
BlocksFPU, MPU, 4× DMAC + DTC, RTC, 14-bit ADC, 12-bit DAC, OPAMP, CTSU touch, CAN, USBFS
ESP32-S3MINI-1-N8 — Xtensa LX7 dual-core, 3.3 V, 384 kB ROM, 512 kB SRAM, 8 MB flash
RadioWi-Fi 4 802.11 b/g/n 2.4 GHz ≤150 Mbps · Bluetooth 5 / BLE ≤2 Mbps · PCB trace antenna
BridgeTXB0108DQSR (U4) translates 5 V ↔ 3.3 V between the two MCUs
One antenna, one radio at a time. Wi-Fi and Bluetooth share the trace antenna — you cannot run WiFiS3 and ArduinoBLE concurrently.

Reference designators silkscreen

U1R7FA4M1AB3CFM#AA0 main MCU
M1ESP32-S3-MINI-1-N8 radio module
U2, U6NLASB3157DFT2G analog muxes — route USB D+/D− to ESP32 or RA4M1
U3ISL854102FRZ-T buck converter, VIN → 5 V
U4TXB0108DQSR 8-bit level translator
U5SGM2205-3.3 LDO, 5 V → 3.3 V
D1, D2PMEG6020AELRX Schottky (reverse-polarity / over-voltage)
D3PRTR5V0U2X ESD protection on USB
PB1RESET button
DL1–DL4TX, RX, power (green), SCK indicator LEDs
J1 / J5CX90B-16P USB-C · DC barrel jack
J2SM04B-SRSS-TB Qwiic / STEMMA QT, 3.3 V, on Wire1
J3 / J6ICSP (SPI) 2×3 · ESP32 debug/boot header 2×3
SJ1solder jumper "RA4M1 USB" — permanently hands USB to the RA4M1

Dedicated connectors

ICSP (J3) — SPI, viewed with pin 1 top-left
1 CIPOD12 / P410
2 +5V
3 SCKD13 / P102
4 COPID11 / P411
5 RESET
6 GND
ESP header (J6)
1 ESP_IO42MTMS (JTAG)
2 ESP_IO41MTDI (JTAG)
3 ESP_TXD0ESP32 UART0 TX
4 ESP_DOWNLOADpull to GND while resetting → ESP32 bootloader
5 ESP_RXD0ESP32 UART0 RX
6 GND
Qwiic (J2), JST SH 1 mm
1 GND
2 3V33.3 V only — never 5 V
3 SDAP401 → Wire1
4 SCLP400 → Wire1

Compatible with SparkFun Qwiic, Adafruit STEMMA QT and Arduino Modulino nodes.

02

Pin map

Every alternate function the core can actually mux onto a pin, read from variants/UNOWIFIR4/variant.cpp and pinmux.inc. Header positions come from the ABX00087 netlist. Bold = the role the board is wired for.

PinidxRA4M1HeaderADCPWM outIRQBus functionNotes
D00P301DIG-1GTIOC4BIRQ6SCI2 RXDSerial1 RX
D11P302DIG-2GTIOC4AIRQ5SCI2 TXDSerial1 TX
D22P104DIG-3GTIOC1BIRQ1SCI0 RXclassic INT1 slot
D33P105DIG-4GTIOC1AIRQ0PWM at boot · classic INT0 slot
D44P106DIG-5GTIOC0Bplain GPIO
D55P107DIG-6GTIOC0APWM at boot
D66P111DIG-7GTIOC3AIRQ4SCI2/SCI9 SCK · SPI1 SCKPWM at boot
D77P112DIG-8GTIOC3BSCI1 SCK · SCI2 TXplain GPIO
D88P304DIG-9GTIOC7AIRQ9plain GPIO
D99P303DIG-10GTIOC7BPWM at boot
D1010P103DIG-11AN19GTIOC2ASPI0 SSL · CAN0 TX · SCI0 CTSSPI CS · PWM at boot
D1111P411DIG-12GTIOC6AIRQ4SPI0 MOSI · SCI0 TXSPI COPI · PWM at boot
D1212P410DIG-13GTIOC6BIRQ5SPI0 MISO · SCI0 RXSPI CIPO
D1313P102DIG-14AN20GTIOC2BSPI0 RSPCK · CAN0 RXSPI SCK · LED_BUILTIN
A014P014ANA-9AN9DAC0 outtrue analog out, 8/12-bit
A115P000ANA-10AN0IRQ6OPAMP0 +op-amp non-inverting in
A216P001ANA-11AN1IRQ7OPAMP0 −op-amp inverting in
A317P002ANA-12AN2IRQ2OPAMP0 OUTop-amp output
A4 / SDA18P101ANA-13 = DIG-17AN21GTIOC5AIRQ1IIC1 SDA · SCI0 TXWire — same net as the SDA header pin
A5 / SCL19P100ANA-14 = DIG-18AN22GTIOC5BIRQ2IIC1 SCL · SCI0 RXWire — same net as the SCL header pin
ADC channel numbers are the RA4M1 AN inputs. D10 (AN19) and D13 (AN20) are ADC-capable in silicon but are not exposed as A-pins by the core. PWM out names the GPT timer channel and output (GTIOCnA/B); only D3, D5, D6, D9, D10 and D11 are configured as PWM at boot.

Off-header pins idx 20–38

The core maps 39 pins. Indices 20–38 are not on any header but are addressable with digitalWrite(21, HIGH) and friends.

idxportADCPWMIRQRole
20P500AN16GTIOC2Asupply-voltage sense, ×8.33 divider
21P408GTIOC5BIRQ7USB mux select — HIGH hands USB to the RA4M1
22P109GTIOC1ASerial TX → level translator → ESP32 USB bridge
23P110GTIOC1BIRQ3Serial RX ← ESP32 USB bridge
24P501AN17GTIOC2BIRQ11Serial2 TX → ESP32 Wi-Fi modem
25P502AN18GTIOC3BIRQ12Serial2 RX ← ESP32 Wi-Fi modem
26P400GTIOC6AIRQ0Wire1 SCL — Qwiic
27P401GTIOC6BIRQ5Wire1 SDA — Qwiic
28–38P003, P004, P011, P012, P013, P015, P204, P205, P206, P212, P213AN3,4,6,7,8,10severalseveralthe 11 charlieplex drive lines of the 12×8 LED matrix

Shared IRQ channels collision list

The RA4M1 has 16 external-interrupt channels and several header pins land on the same one. Two pins that share a channel cannot both hold an attachInterrupt().

IRQ0D3 · (Qwiic SCL)
IRQ1D2 · A4/SDA
IRQ2A3 · A5/SCL
IRQ4D6 · D11
IRQ5D1 · D12 · (Qwiic SDA)
IRQ6D0 · A1
IRQ7A2 · (pin 21)
IRQ9D8

No IRQ at all: D4, D5, D7, D9, D10, D13, A0.

Pin electrical limits

Logic level5 V (VDD = the 5 V rail); IOREF is tied to 5 V
I/O current8 mA max per GPIO — well under the AVR UNO's 20 mA
ModesINPUT, INPUT_PULLUP, OUTPUT, OUTPUT_OPENDRAIN; no internal pull-down
ESP32 pins3.3 V — never let them touch a 5 V RA4M1 net
Qwiic rail3.3 V only
I2C pull-upsnot fitted on the main bus; footprints exist on the PCB
AREFexternal ADC reference input, selected with analogReference(AR_EXTERNAL)
BOOTthe RA4M1 MD pin, brought out on the power header; also driven by the ESP32
Servos and LED strips do not run off a GPIO. At 8 mA a single pin can drive an LED and a resistor, and not much else. Use a transistor or an external supply.

Handy pin aliases

LED_BUILTIN13
A0 … A514 … 19
D0 … D150 … 15 (also defined as constants)
SDA, SCL18, 19
MOSI, MISO, SCK11, 12, 13
SS / CS10
DACA0
NUM_DIGITAL_PINS20
NUM_ANALOG_INPUTS6
PINS_COUNT39 (runtime, via PINCOUNT_fn())
Direct register access
// the classic AVR port macros work on the RA4M1
volatile uint32_t *out =
    portOutputRegister(digitalPinToPort(13));
uint32_t mask = digitalPinToBitMask(13);
*out |= mask;                // D13 high, no function call

// or straight at the peripheral
R_PORT1->PODR |= (1 << 2);   // P102 = D13
03

Basics

The five things every sketch does — drive a pin, read a pin, read a voltage, print a line, and keep time without stopping. Plain Arduino that works on any board; where the R4 differs from the classic UNO, it says so.

Digital output turning a pin on and off

pinMode(pin, OUTPUT)once, in setup() — every pin is an input until you say otherwise
digitalWrite(pin, HIGH)drive it to 5 V
digitalWrite(pin, LOW)drive it to 0 V
HIGH / LOW1 and 0 — a bool passes straight through
digitalRead(pin)legal on an output too: it reads back what you drove
LED_BUILTIND13 — an LED already fitted to the board, nothing to wire
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);      // D13
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // on
  delay(500);                        // milliseconds
  digitalWrite(LED_BUILTIN, LOW);    // off
  delay(500);
}
Toggle, and blink without blocking
digitalWrite(13, !digitalRead(13));      // flip it, no state variable

// the same blink, leaving the rest of loop() free to run
static uint32_t last = 0;
if (millis() - last >= 500) {
  last += 500;
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
8 mA per pin on the R4, down from the AVR UNO's 20 mA. An LED and its resistor is fine. A relay, a motor, a servo or an LED strip is not — switch those with a transistor or a driver, and give them their own supply.

Digital input buttons and switches

INPUT_PULLUPthe mode to reach for — wire the button from the pin to GND, no resistor needed
Pressed reads LOWthe pull-up holds it HIGH until the button shorts it to ground
INPUThigh-impedance; floats and reads noise unless you fit your own resistor
No pull-downsthe RA4M1 has none — tie low externally if you want active-high
OUTPUT_OPENDRAINdrive low or let go; for shared or level-shifted lines
const int BUTTON = 2;

void setup() {
  pinMode(BUTTON, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  bool pressed = !digitalRead(BUTTON);   // LOW == pressed
  digitalWrite(LED_BUILTIN, pressed);
}
Debounce, and act on the edge
static bool prev = HIGH;
static uint32_t at = 0;
bool now = digitalRead(BUTTON);

// 25 ms of quiet before believing it
if (now != prev && millis() - at > 25) {
  at = millis();
  prev = now;
  if (now == LOW) { /* pressed, once */ }
}

A bare mechanical contact bounces for a few milliseconds. Without the timer you get five presses out of one push.

Analog in and out

analogRead(pin)A0–A5 only — 0–1023 by default, measured against the 5 V rail
analogWrite(pin, 0–255)PWM on the pins marked ~: D3, D5, D6, D9, D10, D11
analogWrite(A0, …)a genuine DAC on this board, not PWM — see Analog
No pinModeneeded for either one
map(x, a, b, c, d)rescale one range onto another — integer maths, it truncates
constrain(x, lo, hi)clamp, because map() happily returns out-of-range values
void loop() {
  int pot  = analogRead(A0);             // 0 … 1023
  int duty = map(pot, 0, 1023, 0, 255);
  analogWrite(9, duty);            // dim an LED on D9

  float volts = pot * (5.0f / 1023.0f);
}
Fade, with nothing wired to the input
for (int v = 0; v <= 255; v++) {   // up
  analogWrite(9, v); delay(4);
}
for (int v = 255; v >= 0; v--) {   // and back down
  analogWrite(9, v); delay(4);
}

PWM is a 490 Hz square wave, not a voltage. It dims an LED and drives a motor; it will not feed an analog input without a filter. The Analog section covers depth, frequency and the real DAC.

Serial talking to the computer

Serial.begin(115200)in setup(); the Serial Monitor must be set to the same baud
Serial.print(x)no newline
Serial.println(x)newline
Serial.print(x, fmt)DEC HEX OCT BIN, or a digit count for floats
Serial.available()bytes waiting — how you test for input without blocking
Serial.read()next byte, or −1 if there is none
Serial.readStringUntil('\n')a whole line, subject to setTimeout()
void setup() {
  Serial.begin(115200);
  Serial.println("ready");
}

void loop() {
  while (Serial.available()) {
    char c = Serial.read();
    Serial.print("got: ");
    Serial.println(c);
  }
}
Formatting
Serial.println(3.14159, 2);            // 3.14
Serial.println(0xC0FFEE, HEX);         // C0FFEE
Serial.printf("up %lu ms\n", millis());  // available on this core

Serial.print(analogRead(A0));          // tab-separated columns feed
Serial.print('\t');                     // the IDE's Serial Plotter
Serial.println(analogRead(A1));
No while (!Serial) needed here. On the UNO R4 WiFi Serial is a UART bridged by the ESP32, not native USB, so it is ready the moment you call begin() and it survives a reset without re-enumerating. The wait loop is harmless but does nothing.

Timing without stopping the sketch

millis()unsigned long since reset; wraps at ~49.7 days
micros()µs since reset; wraps at ~71.6 minutes, resolution 4 µs
delay(ms)blocks — nothing else in loop() runs, though interrupts still fire
delayMicroseconds(us)tight busy-wait for short waits
// two jobs at two rates, neither blocking the other
uint32_t tLed = 0, tLog = 0;

void loop() {
  uint32_t now = millis();

  if (now - tLed >= 250) {
    tLed = now;
    digitalWrite(13, !digitalRead(13));
  }
  if (now - tLog >= 1000) {
    tLog = now;
    Serial.println(analogRead(A0));
  }
}
Always compare by subtraction. now - last >= interval stays correct across the rollover; now >= last + interval does not, and will hang your sketch for 49 days once every 49 days.

Servo install from Library Manager

attach(pin)any digital pin — the library generates its own pulse, no ~ needed
write(deg)0–180; on a continuous-rotation servo 90 is stop
writeMicroseconds(us)the raw pulse, ~1000–2000 — finer than degrees
read()the last angle you commanded, not a measurement
attached() / detach()release the pin and stop pulsing
#include <Servo.h>
Servo arm;

void setup() { arm.attach(9); }

void loop() {
  arm.write(0);              delay(600);
  arm.write(180);            delay(600);
  arm.writeMicroseconds(1500);   // centre
}
Never power a servo from a 5 V pin. Even a small one pulls hundreds of milliamps on stall against the board's 8 mA per-pin limit. Separate supply, grounds tied together, signal wire to the Arduino.

Sound tone()

tone(pin, hz)50 % square wave on any digital pin; runs until stopped
tone(pin, hz, ms)fixed duration, returns immediately
noTone(pin)stop it
One at a timea second tone() on another pin replaces the first
const int SPK = 8;
int notes[] = {262, 294, 330, 349};   // C4 D4 E4 F4

for (int n : notes) {
  tone(SPK, n, 200);
  delay(250);                        // note + a gap
}
noTone(SPK);

A piezo disc works straight off a pin. A loudspeaker does not — it needs a series resistor at the very least, and really wants an amplifier.

04

Power

Two inlets, one buck converter, one LDO. The OFF pin gates the buck; VRTC keeps only the clock alive.

Recommended operating conditions

SymbolSourceMinTypMax
VINVIN pad / DC jack6 V7.0 V24 V
VUSBUSB-C4.8 V5.0 V5.5 V
TOPambient−40 °C25 °C85 °C
5 V rail≈1.2 A available when fed from VIN through the buck; ≈2 A when fed from USB (the regulator is bypassed)
3V3 railSGM2205 LDO from the 5 V rail — feeds the ESP32-S3 and the Qwiic connector
USB dropUSB power reaches the RA4M1 at ≈4.7 V after the Schottky
Barrel jackcentre-positive, wired straight to the VIN pin
ProtectionPMEG6020 Schottkys give reverse-polarity and over-voltage protection on both inlets

Power tree

DC jack (6–24 V) ─┐
VIN pin           ├─▶ D1 ─┐
USB-C VBUS (5 V) ─┴─▶ D2 ─┴─▶ ISL854102 buck (U3) ─▶ 5 V rail
                                        ▲                │
                                 OFF pin ┘ (to GND = off) │
                                                          ▼
                                        SGM2205 LDO (U5) ─▶ 3.3 V rail
                                                          ├─▶ ESP32-S3 (M1)
                                                          ├─▶ Qwiic (J2)
                                                          └─▶ TXB0108 A-side (U4)
5 V rail ─▶ RA4M1 (U1) ─▶ every header pin, IOREF, AREF domain
VRTC pin (1.6–3.3 V) ─▶ RTC domain only, survives loss of the main supply

OFF and VRTC JOFF header

Two pins UNO boards never had before, on the small header beside the barrel jack.

OFFshort to GND to disable the buck converter — the board powers down
GND
VRTCapply 1.6–3.3 V (coin cell) to keep the RTC counting with the board off
OFF only works on VIN power. When the board runs from USB the 5 V comes straight off the cable, so there is no regulator to switch off and the OFF pin does nothing.
Cold-start pattern with a VRTC cell
#include "RTC.h"
RTC.begin();
RTCTime fallback(6, Month::NOVEMBER, 2023, 18, 12, 0,
   DayOfWeek::MONDAY, SaveLight::SAVING_TIME_ACTIVE);
RTCTime saved;
RTC.getTime(saved);
// woke up "as new"?
if (!RTC.isRunning())
  RTC.setTime(saved.getYear() == 2000 ? fallback : saved);
05

Buses & connectivity

Three UARTs, one SPI, two I2C, one CAN controller, one native-USB device controller — and a mux that decides which processor the USB-C port is talking to.

Serial ports the part that surprises people

The core builds this variant with -DNO_USB, so the Serial object is not native USB — it is a hardware UART wired to the ESP32, which bridges it to the USB-C port.

ObjectSCIPinsGoes to
SerialSCI9P109/P110level translator → ESP32 → USB-C serial monitor
Serial1SCI2D1 TX / D0 RXthe header pins — your external UART device
Serial2SCI1P501/P502ESP32 AT modem — owned by WiFiS3, leave alone
SerialUSBUSBFSUSB D+/D−RA4M1 native USB CDC — needs the mux flipped
beginSerial1.begin(9600) or begin(baud, SERIAL_8N1)
Buffers512 bytes RX and TX per port
ConfigsSERIAL_5N1SERIAL_8O2 — data bits 5–8, parity N/E/O, 1 or 2 stop bits
serialEvent()unsupported on R4 — poll Serial.available() instead
Serial1.begin(115200);
while (Serial1.available()) {
  char c = Serial1.read();
  Serial.write(c);            // forward to the USB monitor
}

USB, the mux, and HID

USB D+/D− pass through two NLASB3157 muxes. By default they point at the ESP32, which acts as the serial bridge and resets the RA4M1 for uploads.

Pin 21 (P408)mux select — HIGH = RA4M1 owns USB, LOW = ESP32 (default)
SJ1"RA4M1 USB" solder pads on the back — bridge them to make it permanent
<HID.h>redefines Serial to SerialUSB and starts native USB, which flips the mux for you
VID:PID0x2341:0x1002 in the bootloader · 0x2341:0x006D running a sketch
#include <Keyboard.h>   // pulls in HID.h
Keyboard.begin();
Keyboard.press('W');   Keyboard.releaseAll();
Mouse.move(x, y);       Mouse.click(MOUSE_LEFT);

// manual switch, without HID
pinMode(21, OUTPUT);  digitalWrite(21, HIGH);
Extra methods once Serial is SerialUSB
Serial.baud()host-requested baud rate
Serial.numbits()data bits
Serial.stopbits()stop bits
Serial.paritytype()parity
Serial.dtr()DTR line state
Serial.rts()RTS line state
Under HID the board enumerates as a different USB device, so the port name changes. Double-tap RESET to get back to the bootloader and reselect the port.

SPI SPI0 · D10–D13 + ICSP

COPI / MOSID11 · P411
CIPO / MISOD12 · P410
SCKD13 · P102 — also drives the SCK indicator LED
CS / SSD10 · P103 — software-controlled, you toggle it yourself
Instancesone (SPI); the ICSP header carries the same bus
#include <SPI.h>
const int CS = 10;
pinMode(CS, OUTPUT);  digitalWrite(CS, HIGH);
SPI.begin();

SPI.beginTransaction(
  SPISettings(8000000, MSBFIRST, SPI_MODE0));
digitalWrite(CS, LOW);
uint8_t in = SPI.transfer(0x9F);
SPI.transfer(buf, len);   // in-place block transfer
digitalWrite(CS, HIGH);
SPI.endTransaction();

I2C two independent buses

WireIIC1 — A4 (SDA, P101) and A5 (SCL, P100); the SDA/SCL header pins are the same nets
Wire1IIC0 — the Qwiic connector (P401 SDA, P400 SCL), 3.3 V
Pull-upsnot fitted on Wire; the Qwiic side is pulled up on the module you plug in
Addressing7-bit, so 0x08–0x77 is the usable range
#include <Wire.h>
Wire.begin();            // controller on A4/A5
Wire1.begin();           // controller on Qwiic
Wire.setClock(400000);   // 100k / 400k

Wire.beginTransmission(0x3C);
Wire.write(0x00);  Wire.write(value);
uint8_t err = Wire.endTransmission();   // 0 = ok

Wire.requestFrom(0x3C, 2);
while (Wire.available()) uint8_t b = Wire.read();

// peripheral mode
Wire.begin(0x42);
Wire.onReceive(handler);  Wire.onRequest(handler);
A4/A5 double-booked. They are the only main-bus I2C pins and ADC channels AN21/AN22. Don't analogRead() them while the bus is running.

CAN CAN0 · 2.0A / 2.0B

CAN TXD10 · P103
CAN RXD13 · P102
Transceivernot on the board — add an MCP2551, TJA1050, SN65HVD230 …
Bit ratesBR_125k BR_250k BR_500k BR_1000k, or a raw value
Mailboxes8 standard + 8 extended filter slots
#include <Arduino_CAN.h>
CAN.begin(CanBitRate::BR_250k);

uint8_t data[] = {0xCA,0xFE,0,0,0,0,0,0};
CanMsg msg(CanStandardId(0x20), sizeof(data), data);
CAN.write(msg);

if (CAN.available()) {
  CanMsg rx = CAN.read();
  Serial.println(rx.id, HEX);
}
CAN.setFilterMask_Standard(0x7FF);
CAN.setFilterId_Standard(0, 0x20);
CAN.enableInternalLoopback();  // bench test, no bus
The datasheet contradicts itself. Its feature list says CAN is on D4/D5; the pin table, the user manual and the core all say D10 = TX, D13 = RX. The core is right — D4/D5 have no CAN mux at all.
06

Analog

A 14-bit ADC, a real 12-bit DAC on A0, and an on-chip operational amplifier wired to A1–A3. This is where the R4 leaves the AVR UNO furthest behind.

ADC analogRead

InputsA0–A5 on the header; D10 and D13 are AN19/AN20 in silicon too
Hardwareruns at its maximum resolution always; analogReadResolution() scales the returned value
Resolutions8, 10 default, 12, 14, 16 — anything else silently breaks the scaling
Default range0–1023 against the 5 V rail
Reference selection
AR_DEFAULTAVCC ≈ 5 V
AR_INTERNALon-chip band-gap; the core's scaling constant is 1.43 V (Arduino's docs round it to 1.5 V)
AR_INTERNAL_1_5Vsame band-gap, named explicitly
AR_INTERNAL_2_0V / 2_5Venumerated by the core for other Renesas parts — not available on the RA4M1
AR_EXTERNALvoltage you apply to the AREF pin
void setup() {
  analogReadResolution(14);        // 0 – 16383
  analogReference(AR_DEFAULT);
}
int raw = analogRead(A0);
float volts = raw * (5.0f / 16383.0f);
Free-running scan mode
analogAddPinToGroup(A0);
analogAddPinToGroup(A1);
attachScanEndIrq(onScanDone);      // void f(uint8_t unit)
// DMA-fed, no blocking reads
analogStartScan();

DAC A0 · 12-bit

A0 is a genuine analog output, not PWM — no filtering needed, no carrier ripple.

PinA0 only (P014, DAC0)
Default8-bit — write 0–255
Max12-bit — write 0–4095 after analogWriteResolution(12)
Range0 V to just under the 5 V rail
CautionanalogWriteResolution() is global — it changes PWM depth on the other pins too
analogWriteResolution(12);
analogWrite(A0, 2048);           // ≈ mid-rail
Waveform generator — the analogWave library
#include "analogWave.h"
analogWave wave(DAC);            // DAC == A0

wave.sine(440);         // also square() and saw()
wave.amplitude(0.5);             // 0.0 – 1.0
wave.freq(880);
wave.offset(12);
wave.stop();  wave.start();

// or your own sample table, cycled out by DTC
uint16_t table[256];
analogWave custom(DAC, table, 256, 0);
custom.begin(1000);              // whole table per period

OPAMP on-chip, channel 0

A1non-inverting input (+)
A2inverting input (−)
A3output
#include <OPAMP.h>
// or OPAMP_SPEED_LOWSPEED
OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
if (OPAMP.isRunning(0)) { /* … */ }
OPAMP.end();

Once started, the three pins belong to the amplifier — build your gain network externally between A3 and A2 exactly as you would with a discrete op-amp. The DAC on A0 makes a convenient signal source.

Also on-chip, no Arduino API
CTSUcapacitive touch sensing unit — reachable through the Renesas FSP headers
ComparatorsACMPLP low-power analog comparators, FSP only
07

Timers, PWM & interrupts

Two AGT timers and eight GPT channels. The core spends one AGT on millis() and claims six GPT outputs for PWM at boot; everything left is yours through FspTimer.

Timer inventory

AGT ×2asynchronous general-purpose timers; AGT0 backs millis(), micros(), delay()
GPT ×8general PWM timers — 2 × 32-bit (GPT0, GPT1) and 6 × 16-bit
Outputseach GPT has an A and a B output: GTIOCnA / GTIOCnB
Claimed at bootthe channels behind D3, D5, D6, D9, D10, D11
WDTindependent watchdog, see below
RTCseparate 32 kHz domain with its own VRTC supply
#include "FspTimer.h"
FspTimer t;

void tick(timer_callback_args_t *a) { /* ISR */ }

uint8_t type;
int8_t ch = FspTimer::get_available_timer(type);
if (ch < 0) {                       // nothing spare?
  FspTimer::force_use_of_pwm_reserved_timer();
  ch = FspTimer::get_available_timer(type, true);
}
t.begin(TIMER_MODE_PERIODIC, type, ch,
        1000.0f, 0.0f, tick, nullptr);
t.setup_overflow_irq();
t.open();
t.start();
// .stop()  .close()  .set_frequency()

Timer type constants are GPT_TIMER and AGT_TIMER.

PWM

PinsD3, D5, D6, D9, D10, D11 — the ones marked ~
Frequency490 Hz by default
Depth8-bit default; analogWriteResolution(12) for 0–4095
Unofficialevery other digital pin has a GPT output too, but claiming it collides with SPI, UART or I2C
analogWrite(9, 128);              // 50 % at 490 Hz
analogWriteResolution(12);
analogWrite(9, 2048);
Arbitrary frequency and duty — PwmOut
#include "pwm.h"
PwmOut pwm(9);
// 25 kHz at 50 % — silent fan drive
pwm.begin(25000.0f, 50.0f);
pwm.pulse_perc(30.0f);
pwm.period_us(40);  pwm.pulseWidth_us(12);
pwm.suspend();  pwm.resume();  pwm.end();
Tone
tone(pin, hz)square wave on any digital pin, uses a spare timer
tone(pin, hz, ms)fixed duration
noTone(pin)stop

External interrupts

attachInterrupt(pin, isr, mode)pin number is used directly — digitalPinToInterrupt(p) returns p
ModesRISING, FALLING, CHANGE, LOW
detachInterrupt(pin)release the channel
noInterrupts() / interrupts()global mask around a critical section
Capable pinsD0, D1, D2, D3, D6, D8, D11, D12, A1, A2, A3, A4, A5
Not capableD4, D5, D7, D9, D10, D13, A0
Several pins share one IRQ channel — see the collision list in the pin map. Attaching to the second pin of a pair quietly steals the channel from the first.
volatile bool pressed = false;
void isr() { pressed = true; }

pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), isr, FALLING);

Watchdog WDT

#include <WDT.h>
// timeout in ms, rounded to a legal prescaler
WDT.begin(2000);
void loop() {
  WDT.refresh();          // kick it, or the board resets
}
uint32_t t = WDT.getTimeout();
uint32_t c = WDT.getCounter();

Prescalers 1–8192 and reload values 128–16384 constrain the achievable timeouts; the library picks the closest pair and getTimeout() tells you what you actually got.

Real-time clock

#include "RTC.h"
RTC.begin();
RTCTime t(30, Month::JUNE, 2023, 13, 37, 0,
   DayOfWeek::WEDNESDAY, SaveLight::SAVING_TIME_ACTIVE);
RTC.setTime(t);
RTC.setTimeIfNotRunning(t);

RTCTime now;  RTC.getTime(now);
now.getDayOfMonth();  now.getMonth();   now.getYear();
now.getHour();        now.getMinutes(); now.getSeconds();
now.getDayOfWeek();   now.getUnixTime();
Month2int(now.getMonth());  DayOfWeek2int(dow, true);
Periodic and alarm callbacks
RTC.setPeriodicCallback(tick, Period::ONCE_EVERY_1_SEC);

AlarmMatch m;
m.addMatchHour();    m.addMatchMinute();
m.addMatchSecond();
RTC.setAlarmCallback(ring, alarmTime, m);
RTC.isRunning();
PeriodONCE_EVERY_2_SEC, ONCE_EVERY_1_SEC, N2_N128_TIMES_EVERY_SEC
Match fieldssecond, minute, hour, day, month, year, day-of-week — add or remove each
Network timeWiFi.getTime() returns a Unix epoch you can feed to RTCTime

Time functions

millis()unsigned long ms since boot; wraps after ≈49.7 days
micros()µs since boot; wraps after ≈71.6 minutes
delay(ms)blocking; interrupts still run
delayMicroseconds(us)busy-wait, no interrupts disabled
yield()called inside delay(); override it to keep background work alive
// non-blocking cadence — reach for this by default
static uint32_t last = 0;
if (millis() - last >= 500) { last += 500; /* … */ }
08

LED matrix

96 red LEDs in 12 columns × 8 rows, charlieplexed across 11 RA4M1 pins. A timer ISR lights exactly one LED at a time at ~10 kHz, so the whole display costs you eleven pins you never see and one spare GPT channel.

Getting a picture up

#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;

void setup() { matrix.begin(); }
Two ways to describe a frame
// 1. readable — a byte per pixel, edit at runtime
uint8_t frame[8][12] = {
  {0,0,1,1,0,0,0,1,1,0,0,0},
  {0,1,0,0,1,0,1,0,0,1,0,0},
  {0,1,0,0,0,1,0,0,0,1,0,0},
  {0,0,1,0,0,0,0,0,1,0,0,0},
  {0,0,0,1,0,0,0,1,0,0,0,0},
  {0,0,0,0,1,0,1,0,0,0,0,0},
  {0,0,0,0,0,1,0,0,0,0,0,0},
  {0,0,0,0,0,0,0,0,0,0,0,0}};
frame[2][1] = 1;
matrix.renderBitmap(frame, 8, 12);

// 2. compact — 96 bits packed into three uint32, row-major
const uint32_t heart[] = {0x3184a444, 0x44042081, 0x100a0040};
matrix.loadFrame(heart);

Read the packed form by writing all three words out in binary, concatenating them and slicing into groups of twelve — each group is one row, MSB on the left.

Animation API

begin()claim a timer and start refreshing
loadFrame(uint32[3])show one still frame
renderBitmap(arr, rows, cols)show a byte-per-pixel bitmap
loadSequence(uint32[][4])load an animation: 3 words of pixels + a duration in ms
play(loop)start the loaded sequence; true repeats forever
next()advance one frame by hand
renderFrame(n)jump to frame n of the sequence
autoscroll(ms)override the per-frame duration
sequenceDone()true once, when a non-looping sequence finishes
setCallback(fn)fires at the end of a sequence — from the ISR, keep it tiny
on(i) / off(i)poke a single LED, 0–95
clear()blank the display
const uint32_t blink[][4] = {
  {0x0, 0x0, 0xc00c0000, 150},   // pixels ×3, then ms
  {0x0, 0x1e01, 0x201201e0, 150}};
matrix.loadSequence(blink);
matrix.play(true);

Text with ArduinoGraphics

Install the ArduinoGraphics library and include it before the matrix header — the matrix class inherits from it only if it is already visible.

#include "ArduinoGraphics.h"   // must come first
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;

matrix.beginDraw();
matrix.stroke(0xFFFFFFFF);
matrix.textScrollSpeed(50);
matrix.textFont(Font_5x7);        // or Font_4x6
matrix.beginText(0, 1, 0xFFFFFF);
matrix.println("    Hello World!    ");
// SCROLL_RIGHT, or omit it for static text
matrix.endText(SCROLL_LEFT);
matrix.endDraw();

The colour argument exists because ArduinoGraphics also drives RGB hardware; any non-zero value lights the LED. Twelve columns fits about two 5×7 characters, so scroll anything longer.

Built-in gallery

Still frames — matrix.loadFrame(NAME)

LEDMATRIX_BLUETOOTH · LEDMATRIX_BOOTLOADER_ON · LEDMATRIX_CHIP · LEDMATRIX_CLOUD_WIFI · LEDMATRIX_DANGER · LEDMATRIX_EMOJI_BASIC · LEDMATRIX_EMOJI_HAPPY · LEDMATRIX_EMOJI_SAD · LEDMATRIX_HEART_BIG · LEDMATRIX_HEART_SMALL · LEDMATRIX_LIKE · LEDMATRIX_MUSIC_NOTE · LEDMATRIX_RESISTOR · LEDMATRIX_UNO

Animations — matrix.loadSequence(NAME)

LEDMATRIX_ANIMATION_ ⟨ ARROWS_COMPASS · ATMEGA · AUDIO_WAVEFORM · BATTERY · BOUNCING_BALL · BUG · CHECK · CLOUD · DOWNLOAD · DVD · HEARTBEAT · HEARTBEAT_LINE · HOURGLASS · INFINITY_LOOP_LOADER · LED_BLINK_HORIZONTAL · LED_BLINK_VERTICAL · LOAD · LOAD_CLOCK · LOCK · NOTIFICATION · OPENSOURCE · SPINNING_COIN · STARTUP · TETRIS · TETRIS_INTRO · WIFI_SEARCH ⟩

Drawing tool

The browser-based editor at ledmatrix-editor.arduino.cc exports both frame formats. Its live-preview mode wants a sketch that reads 12 bytes at a time off Serial and calls loadFrame()File ▸ Examples ▸ LED_Matrix ▸ LivePreview.

What it costs you

PinsP003, P004, P011, P012, P013, P015, P204, P205, P206, P212, P213 — core indices 28–38
Timerone GPT or AGT channel, taken by matrix.begin()
RAM12 bytes of frame buffer plus whatever your sequence table costs in flash
ISR loada short handler at ~10 kHz — visible if you write long interrupt code elsewhere

None of the eleven pins reach a header, so the matrix never conflicts with your wiring — only with your timer budget.

09

Wi-Fi & Bluetooth

The ESP32-S3 ships with Arduino's own firmware and behaves as an AT-style modem on Serial2. You talk to it through WiFiS3, which mimics the classic WiFiNINA API.

Connecting

#include "WiFiS3.h"

if (WiFi.status() == WL_NO_MODULE) { /* radio not answering */ }
Serial.println(WiFi.firmwareVersion());

while (WiFi.begin(ssid, pass) != WL_CONNECTED) delay(5000);

Serial.println(WiFi.localIP());
Serial.println(WiFi.RSSI());
Serial.println(WiFi.SSID());
Station
begin(ssid[, pass])join a network, returns a status code
disconnect() / end()drop the link / shut the radio down
status()current connection state
reasonCode()why the last attempt failed
config(ip[, dns[, gw[, mask]]])static addressing — call before begin()
setDNS(d1[, d2])override resolvers
setHostname(name)DHCP client name
localIP() subnetMask() gatewayIP() dnsIP(n)address details
macAddress(buf) BSSID(buf)6-byte identifiers
RSSI() encryptionType()signal and security of the current link
hostByName(host, ip)DNS lookup
getTime()Unix epoch from the module — feed it straight to the RTC
ping(ip|host[, ttl[, count]])round-trip test
setTimeout(ms)modem command timeout
Scanning
scanNetworks()count of networks found
SSID(i) RSSI(i) channel(i)per-network details
encryptionType(i) BSSID(i, buf)security and MAC
Access point
beginAP(ssid[, pass][, ch])become an AP
softAPIP() softAPSSID()AP identity
Status constants

WL_IDLE_STATUS · WL_NO_SSID_AVAIL · WL_SCAN_COMPLETED · WL_CONNECTED · WL_CONNECT_FAILED · WL_CONNECTION_LOST · WL_DISCONNECTED · WL_AP_LISTENING · WL_AP_CONNECTED · WL_AP_FAILED · WL_NO_MODULE

Sockets

WiFiClientTCP client — connect(), print(), available(), read(), stop()
WiFiSSLClientTLS client, certificate bundle lives in the module firmware
WiFiServerTCP listener — begin(), available()
WiFiUDPdatagrams — beginPacket(), write(), endPacket(), parsePacket()
WiFiServer server(80);
server.begin();

WiFiClient c = server.available();
if (c) {
  while (c.connected()) {
    if (c.available()) { char ch = c.read(); /* … */ }
  }
  c.stop();
}
Over-the-air sketch update
#include <OTAUpdate.h>
OTAUpdate ota;
ota.setCACert(root_ca);
ota.begin();
// or startDownload + downloadProgress
ota.download(url);
ota.verify();
ota.update();        // reboots into the new sketch

Bluetooth LE

#include <ArduinoBLE.h>
if (!BLE.begin()) while (1);
BLE.scan();
BLEDevice p = BLE.available();
if (p && p.localName() == "Sensor") {
  BLE.stopScan();
  p.connect();
  p.discoverAttributes();
  BLECharacteristic c = p.characteristic(0);
  if (c.canRead()) c.read();
}
Not at the same time as Wi-Fi. One trace antenna, one radio path — pick a protocol per sketch.

Living with the ESP32

Default jobUSB-serial bridge, RA4M1 reset controller, and the Wi-Fi/BLE modem
It programs the RA4M1the level translator carries MD (boot), SWDIO, SWCLK and RESET from the ESP32 to the main MCU
Modem linkUART on Serial2 at 115200 by default
Its bootloaderpull ESP_DOWNLOAD (J6 pin 4) to GND while resetting
Its UARTJ6 pins 3/5, or the pads next to the module, or the pads on the underside
Reflashing the ESP32 breaks the board's normal behaviour — the serial bridge, uploads and WiFiS3 all stop working until you restore Arduino's firmware with espflash. Keep the stock binary before you experiment.
Firmware version check
Serial.println(WiFi.firmwareVersion());
// mismatch with the core's expectation → update via
// IDE ▸ Tools ▸ Firmware Updater, or arduino-cli
10

The language

An Arduino sketch is C++17 compiled by arm-none-eabi-g++ with a generated main() that calls setup() once and loop() forever. On this 32-bit target several familiar type sizes change.

Sketch skeleton

// everything above setup() is global scope
#include <Wire.h>
const int BUTTON = 2;
volatile bool flag = false;

void setup() {          // runs once after reset
  Serial.begin(115200);
  pinMode(BUTTON, INPUT_PULLUP);
}

void loop() {           // runs forever
}

The core supplies main(): it calls initVariant(), then setup(), then loops on loop() — no while(1) of your own needed. Sketch files (.ino) are concatenated alphabetically after the primary tab, and function prototypes are auto-generated, which is why you can call a function defined further down the file.

Predefined macros worth testing
ARDUINO_UNOR4_WIFIset by the board definition — the friendly identifier
ARDUINO_UNOWIFIR4the variant name; libraries branch on this one
ARDUINO_ARCH_RENESASarchitecture family, also matches the Portenta C33
ARDUINO_ARCH_RENESAS_UNOthe UNO R4 sub-family (Minima + WiFi)
NO_USBdefined for this board — why Serial is a UART, not USB CDC
F_CPU48000000
ARDUINOcore API version, e.g. 10607

Types and their sizes here 32-bit ARM

The single biggest source of ported-sketch bugs: int is four bytes, and double is a real double.

bool1 byte · true / false
char1 byte, signed, −128…127
unsigned char / byte / uint8_t1 byte, 0…255
short / int16_t2 bytes, −32,768…32,767
int / int32_t4 bytes on the R4, −2,147,483,648…2,147,483,647 (2 bytes on the AVR UNO)
unsigned int / word / uint32_t4 bytes, 0…4,294,967,295
long / int32_t4 bytes
unsigned long4 bytes — the type of millis() and micros()
long long / int64_t8 bytes
float4 bytes, ~6–7 significant digits, hardware FPU
double8 bytes, ~15 digits — genuinely double precision, unlike the AVR where it aliases float
size_t / pointers4 bytes
voidno value — return type of setup() and loop()
Stringthe Arduino heap-allocated string class
arrayint a[5] = {1,2,3,4,5}; — no bounds checking, ever
Literals
123decimal int
0b1011binary
0x2Fhexadecimal
017octal — a leading zero, easy to write by accident
12U 12L 12UL 12ULLunsigned / long / unsigned long / unsigned long long suffixes
2.5f 2.5e-3float and scientific notation
'A'character — one byte
"text"null-terminated char array
R"(raw \ string)"raw string literal, no escapes
Qualifiers
constread-only; prefer it over #define for typed constants
constexprcompile-time constant, cheaper than const in headers
staticinside a function: keeps its value between calls; at file scope: private to the file
volatilere-read from memory every time — mandatory for anything an ISR touches
PROGMEM / F()no-op the RA4M1 maps flash into the normal address space, so string constants already live in flash. Harmless if inherited from AVR code
__attribute__((weak))let a sketch override a core symbol
externdeclared elsewhere

Operators

Arithmetic and assignment
= + - * /assignment and the four operations
%remainder — integer operands only
++ --increment / decrement, prefix or postfix
+= -= *= /= %=compound assignment
Comparison and logic
== !=equal / not equal — = in an if is the classic silent bug
< > <= >=ordering
&& || !logical and / or / not, short-circuiting
Bitwise
& | ^ ~and, or, xor, not
<< >>shift left / right — shifting a signed negative value is undefined
&= |= ^= <<= >>=compound forms
Pointers and members
&xaddress of
*pdereference
p->fieldmember through a pointer
obj.fieldmember of an object
a[i]subscript — sugar for *(a + i)
Other
(type)x / static_cast<T>(x)conversion
sizeof(x)bytes occupied — sizeof(arr)/sizeof(arr[0]) is the element count
cond ? a : bternary select
a, bcomma — evaluates both, yields the second

Control flow

if / else if / elseconditional branch
switch (x) { case k: … default: }integer dispatch; fall-through unless you break
for (init; test; step)counted loop
for (auto v : container)range-based loop, C++11
while (test)test first
do { } while (test);body runs at least once
breakleave the innermost loop or switch
continueskip to the next iteration
return [value]leave the function
goto label;legal, rarely defensible
Functions
int average(int a, int b) { return (a + b) / 2; }

// default arguments and overloads are fine — this is C++
void blink(int pin, int times = 1);

// pass an array by pointer plus a length; arrays decay
void sum(const int *data, size_t n);

// lambdas work, and convert to a plain function pointer
attachInterrupt(2, []() { flag = true; }, FALLING);

Structs, enums and classes

A sketch is C++17, so the aggregate types are all available and cost nothing at run time. Grouping related state beats a row of parallel arrays.

struct Reading { uint32_t at; float v; };
Reading r = { millis(), 3.3f };
r.v = 5.0f;               // dot; arrow via a pointer

enum class Mode : uint8_t { Idle, Arming, Run };
Mode m = Mode::Idle;                // scoped — no name collisions

enum { LEFT, RIGHT, BOTH };         // plain, converts to int
class Blinker {
  int pin; uint32_t last = 0; bool on = false;
public:
  Blinker(int p) : pin(p) {}
  void begin() { pinMode(pin, OUTPUT); }
  void tick(uint32_t ms) {
    if (millis() - last < ms) return;
    last = millis(); on = !on;
    digitalWrite(pin, on);
  }
};

Blinker led(LED_BUILTIN);      // a global object

// hardware gets touched here, never in the constructor
void setup() { led.begin(); }
Do not call pinMode(), Serial or millis() from the constructor of a global object. Static constructors run before the core has initialised the clocks and the pin tables. Give the class a begin() and call it from setup() — which is exactly why every Arduino library works that way.

Preprocessor

#include <lib.h>angle brackets: the library and core search paths
#include "local.h"quotes: the sketch folder first
#define NAME valuetextual substitution — no type, no scope, no trailing semicolon
#define f(x) ((x)*2)function-like; parenthesise every argument and the whole body
#undef NAMEforget it again
#ifdef / #ifndefcompile a block only if a name is or is not defined
#if defined(X)the form that composes with && and ||
#else / #elif / #endifthe rest of the conditional
#pragma onceheader guard, one line, supported by this compiler
#error / #warningstop or annotate the build with your own message
__FILE__ __LINE__current file and line, handy in a debug macro
#define LED_PIN 13            // untyped, invisible to the debugger
const int ledPin = 13;        // prefer this — typed and scoped

#if defined(ARDUINO_UNOR4_WIFI)
  #include "WiFiS3.h"
#elif defined(ARDUINO_AVR_UNO)
  #error "no radio on this board"
#endif

The board macros worth branching on are listed under Sketch skeleton. Reach for const or constexpr for values and leave the preprocessor for conditional compilation.

Interrupt-safe code

The RA4M1 has a nested vectored interrupt controller and the core is happy to hand you ISRs. The usual rules bite harder here because the CPU is fast enough to make races common.

volatileevery variable shared between an ISR and loop()
Keep it shortno delay(), no Serial.print(), no String, no malloc
millis() in an ISRfrozen — it is driven by another interrupt
Multi-byte readsa 64-bit or struct value read in loop() can tear; guard with noInterrupts()
ISR signaturevoid f() for attachInterrupt; void f(timer_callback_args_t*) for FspTimer
volatile uint32_t edges = 0;
void isr() { edges++; }

void loop() {
  noInterrupts();
  uint32_t snapshot = edges;
  interrupts();
  Serial.println(snapshot);
}

Memory 32 kB SRAM

Sketch limit262,144 bytes of flash
RAM limit32,768 bytes
Stackgrows down from the top of SRAM; deep recursion is the usual way to lose
Heapnew, malloc and String all use it — fragmentation is real in a long-running loop
EEPROM8 kB of data flash, emulated; see the library section
String vs char[]prefer fixed char buffers with snprintf() in code that runs for weeks
Where things live
0x00000000program flash, 256 kB — memory-mapped, so constants are read directly
0x20000000SRAM, 32 kB
0x40100000data flash, 8 kB, in 1 kB blocks — the EEPROM backing store
11

Function index

The whole callable surface: core API, Stream, String, and the bundled libraries. Use the filter box in the header bar to narrow every card at once.

Digital I/O core

pinMode(pin, mode)INPUT, OUTPUT, INPUT_PULLUP, OUTPUT_OPENDRAIN
digitalRead(pin)returns HIGH or LOW
digitalWrite(pin, value)drive an output; on an input pin it toggles the pull-up
digitalPinToPort(pin)RA4M1 port index for register-level access
digitalPinToBitMask(pin)bit within that port
portOutputRegister(port)pointer to PODR
portInputRegister(port)pointer to PIDR
portModeRegister(port)pointer to PDR
pinPeripheral(pin, fn)force a pin onto a specific peripheral function

Analog I/O core

analogRead(pin)ADC conversion, A0–A5
analogReadResolution(bits)8, 10, 12, 14 or 16 — scales the returned value
analogReference(ref)AR_DEFAULT, AR_INTERNAL, AR_INTERNAL_1_5V, AR_EXTERNAL
analogWrite(pin, value)PWM on D3/5/6/9/10/11, true DAC on A0
analogWriteResolution(bits)up to 12; affects PWM and DAC
analogAddPinToGroup(pin)add a channel to the scan group
analogStartScan()begin a multi-channel scan
attachScanEndIrq(cb)callback when a scan completes
analogAttachIrq(cb, type)ADC window-compare and scan-end interrupts

Advanced I/O core

tone(pin, freq[, ms])square wave on any digital pin
noTone(pin)stop it
pulseIn(pin, state[, timeout])measure a pulse in µs, blocking
pulseInLong(pin, state[, timeout])interrupt-based variant for long pulses
shiftOut(data, clk, order, val)bit-bang a byte out, MSBFIRST or LSBFIRST
shiftIn(data, clk, order)bit-bang a byte in

Time core

millis()unsigned long ms since reset, wraps at ~49.7 days
micros()µs since reset, wraps at ~71.6 minutes
delay(ms)block, interrupts still serviced
delayMicroseconds(us)tight busy-wait
yield()hook called from delay(); weak, override it freely

Math core

min(a, b) / max(a, b)smaller / larger
abs(x)magnitude
constrain(x, lo, hi)clamp
map(x, inLo, inHi, outLo, outHi)linear rescale, integer maths, truncates
pow(base, exp)power
sqrt(x) / sq(x)square root / square
round(x) / ceil(x) / floor(x)rounding
fabs(x) / fmod(a, b)float magnitude / remainder
exp(x) / log(x) / log10(x)exponential and logarithms
isnan(x) / isinf(x)float predicates
Trigonometry — radians
sin(r) cos(r) tan(r)the basics
asin() acos() atan() atan2(y, x)inverses; atan2 keeps the quadrant
radians(deg) / degrees(rad)conversion macros
PI, HALF_PI, TWO_PIconstants
DEG_TO_RAD, RAD_TO_DEGconversion factors
EULER2.718…

Random core

randomSeed(seed)seed the generator — feed it a floating analogRead() or micros()
random(max)0 … max−1
random(min, max)min … max−1

Bits and bytes core

lowByte(x) / highByte(x)byte 0 / byte 1
bitRead(x, n)read bit n
bitWrite(x, n, b)write bit n
bitSet(x, n) / bitClear(x, n)set / clear
bitToggle(x, n)flip
bit(n)1 << n
Character tests
isAlpha() isDigit() isAlphaNumeric()letters, digits, both
isSpace() isWhitespace()any whitespace / space or tab
isUpperCase() isLowerCase()case
isHexadecimalDigit() isOctalDigit()radix digits
isPunct() isPrintable() isGraph() isControl()character classes
isAscii()high bit clear

Interrupts core

attachInterrupt(pin, isr, mode)RISING, FALLING, CHANGE, LOW
detachInterrupt(pin)release the channel
digitalPinToInterrupt(pin)identity on this core, but keep using it for portability
interrupts() / noInterrupts()unmask / mask globally

Serial & Stream HardwareSerial

begin(baud[, config])start the port
end()release it
available()bytes waiting in the 512-byte RX buffer
availableForWrite()free space in the TX buffer
read()next byte, or −1
peek()look without consuming
write(b) / write(buf, len)raw bytes out
print(x[, fmt])formatted — DEC, HEX, OCT, BIN, or decimal places for floats
println(x)print plus CRLF
printf(fmt, …)available on this core
flush()wait for the TX buffer to drain
setTimeout(ms)timeout for the parsing calls below
find(s) / findUntil(s, t)scan the stream
readBytes(buf, n) / readBytesUntil(c, buf, n)bulk read
readString() / readStringUntil(c)read into a String
parseInt() / parseFloat()pull a number out of the stream
if (Serial)true once the port is ready
Objects
SerialUSB monitor, via the ESP32 bridge
Serial1D0 / D1 header pins
Serial2ESP32 Wi-Fi modem — do not open it yourself
SerialUSBRA4M1 native USB CDC
SoftwareSerialbundled, for an extra low-rate port

String class Arduino

String(value[, base])from a literal, number, or char
length()characters
charAt(i) / setCharAt(i, c)access by index
c_str()const char* view — no copy
substring(from[, to])slice
indexOf(x[, from]) / lastIndexOf(x)search, −1 if absent
startsWith(s) / endsWith(s)prefix / suffix test
equals(s) / equalsIgnoreCase(s)comparison
compareTo(s)ordering
concat(x) / operator+append
replace(a, b)substitute in place
remove(i[, n])delete characters
trim()strip surrounding whitespace
toUpperCase() / toLowerCase()case conversion, in place
toInt() / toFloat() / toDouble()parse
reserve(n)pre-allocate to avoid heap churn
getBytes(buf, n) / toCharArray(buf, n)copy out

Every String operation touches the heap. In a sketch meant to run for months, build output with snprintf() into a fixed buffer instead.

EEPROM 8 kB data flash

EEPROM.read(addr)one byte
EEPROM.write(addr, val)one byte — always erases and rewrites the block
EEPROM.update(addr, val)writes only if the value differs — prefer this
EEPROM.get(addr, obj)read any type or struct
EEPROM.put(addr, obj)write any type or struct
EEPROM[addr]indexed access via EERef
EEPROM.length()8192
EEPROM.begin() / .end()iterators over the whole store

Data flash is rated for a finite number of erase cycles. Never call write() unconditionally inside loop().

Preferences — a friendlier key/value store
begin(name[, readOnly])open a namespace
putInt/putFloat/putString/putBytes(key, v)typed writes, also Char, Short, Long, Long64, Bool, Double
getInt/getFloat/getString/getBytes(key[, default])typed reads
isKey(key) / getType(key)inspect
remove(key) / clear()delete one / all
end()close the namespace
12

Bundled libraries

These ship inside the board package — no Library Manager needed. Everything else installs as usual.

LibraryIncludeWhat it gives you
Arduino_LED_Matrix"Arduino_LED_Matrix.h"the 12×8 display, frames, sequences, gallery
WiFiS3"WiFiS3.h"Wi-Fi station/AP, TCP, TLS, UDP, NTP time
Arduino_CAN<Arduino_CAN.h>CAN 2.0A/B controller on D10/D13
RTC"RTC.h"real-time clock, alarms, periodic callbacks
EEPROM<EEPROM.h>8 kB emulated EEPROM, AVR-compatible API
Preferences<Preferences.h>typed key/value store over the same flash
WDT<WDT.h>watchdog timer
OPAMP<OPAMP.h>on-chip operational amplifier on A1–A3
AnalogWave"analogWave.h"DMA-fed waveform generator on the DAC
SPI<SPI.h>SPI0 on D10–D13
Wire<Wire.h>both I2C buses, controller and peripheral
HID<HID.h>native-USB HID plumbing; remaps Serial to SerialUSB
SoftwareSerial<SoftwareSerial.h>bit-banged extra serial port
I2S<I2S.h>digital audio interface
OTAUpdate<OTAUpdate.h>download and apply a sketch over Wi-Fi
SSLClient<SSLClient.h>TLS layer over any Client
Ethernet · lwIpWrapper<Ethernet.h>wired networking stack (shield / Portenta family)
Storage · BlockDevices<Storage.h>block devices behind the filesystems
FATFilesystem · LittleFilesystem<FATFileSystem.h>FAT and LittleFS
UsbMsd · UsbHostMsd<UsbMsd.h>expose or mount USB mass storage
SDU · SFU<SDU.h>sketch update from SD card / flash
KVStore<KVStore.h>key/value backend used by Preferences
Arduino_FreeRTOS<Arduino_FreeRTOS.h>preemptive multitasking kernel
ESPhostlow-level ESP32 host protocol used by WiFiS3
Commonly paired installs: ArduinoGraphics (text on the matrix), ArduinoBLE (Bluetooth), Keyboard / Mouse (HID), Servo, ArduinoIoTCloud, Modulino (Qwiic nodes).
13

Build & flash

The IDE hides all of this, but knowing the identifiers makes CI, scripting and rescue work straightforward.

Identifiers

Package"Arduino UNO R4 Boards" in Boards Manager
FQBNarduino:renesas_uno:unor4wifi
VariantUNOWIFIR4
Corearduino (ArduinoCore-renesas), FSP from Renesas
Compilerarm-none-eabi-g++, -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
Uploaderbossac, SAM-BA protocol, 1200 bps touch to reset
Max sketch262,144 bytes
Max RAM32,768 bytes
DebugOpenOCD script R7FA4M1AB.cfg, SVD R7FA4M1AB.svd — needs an external SWD probe
arduino-cli core install arduino:renesas_uno
arduino-cli compile -b arduino:renesas_uno:unor4wifi sketch/
arduino-cli upload  -b arduino:renesas_uno:unor4wifi -p /dev/cu.usbmodem1101 sketch/
arduino-cli monitor -p /dev/cu.usbmodem1101 -c baudrate=115200

Reset, bootloader, recovery

Normal uploadthe ESP32 pulls RESET and drives the RA4M1 into its bootloader for you
Manual bootloaderdouble-tap RESET right after power-up; the matrix shows the bootloader glyph
USB identity0x2341:0x1002 in the bootloader · 0x2341:0x006D running a sketch
Port moved?a sketch that opens native USB or HID re-enumerates the board — reselect the port
Radio firmwareIDE ▸ Tools ▸ Firmware Updater, or espflash per Arduino's help-centre article
ESP32 bootloadertie ESP_DOWNLOAD to GND, then reset
A sketch that locks the CPU still can't brick the board. Double-tapping RESET gets you back to the bootloader every time, whatever the sketch was doing.

Coming from the UNO R3

int is 32-bitoverflow points, sizeof and printed widths all move
double is 64-bitmaths that silently ran at float precision now really is double
8 mA per pindown from 20 mA — recheck anything driven directly
Two serial portsUSB and D0/D1 are independent; the R3 shared one
PROGMEM / F()unnecessary — harmless, but delete it when you touch the code
No serialEvent()poll available() instead
Timer registersTCCR/OCR/TIMSK are gone — use FspTimer or PwmOut
ISR(vector) macroreplaced by attachInterrupt() and FSP callbacks
avr/*.h headersnot available; <avr/pgmspace.h> is shimmed away
Shield voltagestill 5 V logic, so classic shields keep working
14

Traps

The things that cost an evening. Every one of these is documented somewhere — just not where you looked.

Contradictions in the official docs

CAN pinsthe datasheet's feature list says D4/D5. The pin table, user manual and core all say D10 TX / D13 RX — and D4/D5 have no CAN mux in silicon
Internal Vrefdocs say 1.5 V; the core scales with 1.43 V
VRTC rangequoted as both 1.6–3.3 V and 1.6–3.6 V; stay at 3.3 V or below
Qwiic bus numbercalled both "IIC0" and "the secondary bus"; the object is always Wire1
Wire2appears in one doc snippet — there is no third I2C bus on this board

Wiring traps

SPI and CAN collideCAN needs D10 and D13, which are SPI's CS and SCK. You cannot run both on the header
A4/A5 are the I2C busand are the same nets as the SDA/SCL header pins — not two independent buses
Qwiic is 3.3 Vplugging a 5 V module into it puts 5 V on the ESP32's rail
D13 drives an LEDthe SCK indicator loads the pin — awkward for an SPI input or a sensitive input
Shared IRQ channelsD6/D11, D1/D12, D2/A4, D0/A1, A3/A5 each share one channel
No pull-downsthe RA4M1 offers pull-ups only; add a resistor for active-high buttons
No I2C pull-ups fittedon the main bus — most breakout boards supply their own, but check

Software traps

Serial is not USBit is a UART bridged by the ESP32. Native USB is SerialUSB, and including <HID.h> silently redefines Serial to it
Serial2 is the radioopening it yourself breaks Wi-Fi
analogWriteResolution is globalsetting 12 bits for the DAC also makes every PWM pin expect 0–4095
analogReadResolution(11)and any other unsupported value breaks the scaling silently — stick to 8/10/12/14/16
matrix.begin() takes a timerif FspTimer later reports nothing free, this is usually why
Matrix callbacks run in an ISRno Serial.print(), no delay()
ArduinoGraphics include orderit must come before Arduino_LED_Matrix.h or text methods vanish
EEPROM.write() in loop()burns the data flash; use update(), or write on a state change only
Wi-Fi and BLE togetherone antenna — the second one to start simply fails
String on a long-running boardheap fragmentation eventually bites; use fixed buffers
millis() rollovercompare with subtraction (now - last >= n), never with > on absolute values