The Short Answer: C++ and the Wiring API

When makers and engineers ask, what programming language does Arduino use, the most accurate answer is C++, specifically tailored through the Wiring framework. There is no standalone language called "Arduino C" or "Arduino C++". Instead, the Arduino IDE takes your .ino sketch files, injects necessary headers, generates function prototypes, and passes the code to a standard GNU Compiler Collection (GCC) toolchain.

Understanding this distinction is critical for embedded systems development. Because you are writing C++, you have full access to object-oriented programming (OOP) features, pointers, memory management, and templates. The "Arduino" part is simply an abstraction layer—a set of C++ libraries and a hidden main() function that simplifies hardware interaction.

The Core Architecture: Wiring vs. Standard C++

The Wiring framework was originally created by Hernando Barragán in 2003 as a master's thesis project, later evolving into the foundation of the Arduino API. It provides hardware-agnostic functions like digitalWrite(), analogRead(), and Serial.println().

However, relying solely on the Wiring API can obscure what is actually happening at the silicon level. Let us compare the Wiring abstraction against direct C++ register manipulation.

Execution Speed: Wiring API vs. Direct Port Manipulation

Consider the act of turning on an LED connected to Pin 13 (which maps to Port B, Bit 5 on the ATmega328P).

  • Wiring API: digitalWrite(13, HIGH);
    This function performs safety checks, looks up the pin-to-port mapping in an array, and disables interrupts. On a 16 MHz Arduino Uno R3, this takes roughly 50 clock cycles (3.2 microseconds).
  • Direct C++ Register: PORTB |= (1 << PB5);
    This directly modifies the hardware register using standard C++ bitwise operations. It takes exactly 2 clock cycles (125 nanoseconds).

For basic prototyping, the 3-microsecond delay of the Wiring API is negligible. But for high-frequency communication protocols (like bit-banging WS2812B addressable LEDs or software-based SPI), bypassing the Wiring API and using pure C++ register manipulation is mandatory.

The Compilation Pipeline: How .ino Becomes Machine Code

To truly understand what programming language Arduino uses, you must understand how the IDE preprocesses your code. When you click "Upload," the following 4-step pipeline occurs:

  1. Concatenation & Prototype Generation: All .ino tabs are merged into a single file. The IDE scans your code and automatically generates C++ function prototypes, placing them at the top of the file. (This is why you do not need to declare functions before using them in Arduino, unlike standard C++).
  2. Header Injection: The IDE automatically inserts #include <Arduino.h> at the very top. This master header pulls in the Wiring API, standard math libraries, and hardware-specific definitions.
  3. GCC Compilation: The file is renamed to .cpp and passed to the architecture-specific GCC compiler (e.g., avr-gcc for classic boards, arm-none-eabi-gcc for ARM boards).
  4. Linking & Uploading: The compiled object files are linked with the Arduino core libraries and the bootloader, generating a .hex file that is flashed to the microcontroller via tools like avrdude or bossac.

C++ Standards Across Arduino Architectures (2026 Overview)

The specific flavor of C++ you are using depends entirely on the microcontroller's architecture and the core toolchain version. As of 2026, the ecosystem has largely moved past legacy C++98 standards.

Board Model Microcontroller Compiler Toolchain C++ Standard Supported
Arduino Uno R3 ATmega328P (AVR 8-bit) avr-gcc 7.3.0 C++11
Arduino Uno R4 Minima Renesas RA4M1 (ARM Cortex-M4) arm-none-eabi-gcc 12.x C++17
Arduino Nano ESP32 ESP32-S3 (Xtensa LX7) xtensa-esp32-elf-gcc 12.x C++17 / C++20
Arduino Giga R1 WiFi STM32H747 (ARM Cortex-M7) arm-none-eabi-gcc 12.x C++17

Note: While modern ARM boards support C++17 and C++20 features like constexpr, structured bindings, and concepts, the AVR-based boards (Uno R3, Nano, Mega) are largely restricted to C++11 due to the legacy avr-gcc 7.3.0 toolchain maintained for backward compatibility.

Expert Memory Management: The Harvard Architecture Trap

A common failure mode for developers transitioning from desktop C++ to Arduino C++ is SRAM exhaustion. The classic ATmega328P utilizes a Harvard Architecture, meaning Flash memory (where code lives) and SRAM (where variables live) are on separate buses.

The Uno R3 has 32KB of Flash but only 2KB of SRAM. In standard C++, string literals are automatically copied from Flash into SRAM at boot. If you write a sketch with heavy serial debugging:

Serial.println("Initializing the WiFi module and checking for firmware updates...");

That single string consumes 68 bytes of your precious 2KB SRAM. If SRAM overflows into the heap/stack, the microcontroller will experience random reboots or silent data corruption.

The Solution: PROGMEM and the F() Macro

To force the C++ compiler to leave strings in Flash memory and read them directly during execution, Arduino provides the F() macro, which leverages the PROGMEM attribute.

Serial.println(F("Initializing the WiFi module and checking for firmware updates..."));

This simple wrapper keeps your SRAM free for dynamic variables and buffers. For advanced users storing large lookup tables or audio samples, utilizing the PROGMEM directive directly via avr-libc is required.

Troubleshooting Common Language-Related Compilation Errors

Because the Arduino IDE hides the underlying C++ preprocessing, error messages can sometimes appear cryptic. Here are three common compiler errors and their exact fixes:

1. "expected constructor, destructor, or type conversion before ';' token"

  • The Cause: This almost always occurs when you attempt to execute a function or use an Arduino macro (like digitalWrite) in the global scope, outside of setup() or loop(). C++ only allows variable declarations and initializations in the global scope.
  • The Fix: Move the executable code inside setup().

2. "section '.text' will not fit in region 'flash'"

  • The Cause: Your compiled C++ binary exceeds the physical Flash memory limit of the chip (minus the ~500 bytes reserved for the Optiboot bootloader).
  • The Fix: Audit your libraries. The standard Servo.h and SoftwareSerial.h libraries consume significant Flash. Switch to hardware UART pins, use the F() macro for strings, or upgrade to an ARM-based board like the Nano ESP32.

3. "undefined reference to 'vtable for..."

  • The Cause: A pure C++ object-oriented error. You have declared a virtual function in a class header but forgot to implement it in the .cpp file, or you forgot to define the class destructor.
  • The Fix: Ensure every virtual method declared in your class has a corresponding implementation, even if it is an empty {} block.

Beyond C++: Modern Alternatives in the Arduino Ecosystem

While C++ remains the undisputed king of the Arduino environment, the definition of "Arduino programming" has expanded. As of 2026, the Arduino AVR Core repository and broader ecosystem support alternative languages for specific use cases:

  • MicroPython: Heavily utilized on RP2040 and ESP32-based Arduino boards. It sacrifices raw execution speed for rapid prototyping and interactive REPL debugging.
  • Embedded Rust: Through crates like avr-hal and the embassy framework, Rust is gaining traction among professional engineers who require memory safety guarantees that C++ cannot provide without strict discipline.

Summary

So, what programming language does Arduino use? It uses standard C++, abstracted by the Wiring API, and compiled via architecture-specific GCC toolchains. By understanding the C++ foundation, leveraging direct register manipulation for speed, and respecting the physical memory limits of the silicon, you transition from a casual sketcher to an embedded systems engineer.

Authoritative Sources