The Direct Answer: C++ and the Wiring Framework

If you are asking what language is Arduino programming, the short answer is C and C++. However, to be technically precise, the Arduino IDE does not use a proprietary 'Arduino language.' Instead, it uses standard C++ (specifically C++11, C++14, or C++17, depending on your board's core) wrapped in an abstraction layer known as the Wiring API.

When you click 'Upload' in the Arduino IDE, the environment takes your .ino sketch, automatically generates function prototypes, appends the core Arduino libraries, and passes the resulting C++ file to a standard GCC (GNU Compiler Collection) toolchain. For classic AVR boards like the Uno R3, this is avr-gcc. For modern ARM or Xtensa boards, it uses arm-none-eabi-gcc or xtensa-esp32-elf-gcc. You can explore the foundational API structure directly via the Arduino Core API GitHub repository.

Walkthrough: Deconstructing the Hidden main() Function

In standard C or C++, every program must have a main() function as its entry point. Arduino sketches seemingly lack this, relying instead on setup() and loop(). This is an intentional abstraction designed to lower the barrier to entry for beginners.

Behind the scenes, the Arduino builder injects your code into a hidden main.cpp file. Here is what the actual C++ entry point looks like on an AVR microcontroller:

#include <Arduino.h>

// Hidden main function generated by the Arduino IDE
int main(void) {
    init();          // Configures hardware timers and ADC
    initVariant();   // Board-specific initialization
    
    #if defined(USBCON)
    USBDevice.attach();
    #endif
    
    setup();         // Calls your user-defined setup()
    
    for (;;) {       // Infinite loop
        loop();      // Calls your user-defined loop()
        if (serialEventRun) serialEventRun();
    }
    return 0;
}

Expert Insight: Because setup() and loop() are just standard C++ functions called from main(), you can actually bypass them entirely. By defining your own int main(void) in your .ino file and disabling the Arduino auto-prototype generation, you can write pure, bare-metal C++ while still utilizing the Arduino IDE's build system.

Memory Architecture: Flash vs. SRAM vs. EEPROM

Understanding what language Arduino programming relies on is only half the battle; understanding how that language interacts with microcontroller memory is where true expertise lies. Unlike a desktop PC with gigabytes of unified RAM, microcontrollers utilize a Harvard architecture with strictly separated memory spaces.

The F() Macro and Flash Memory

Take the classic ATmega328P (used in the Arduino Uno). It features 32KB of Flash memory (for storing code and constant strings) but only 2KB of SRAM (for runtime variables). In standard C++, a string literal like Serial.println("Hello World"); is copied from Flash into SRAM at boot, wasting precious RAM.

To prevent SRAM exhaustion, the Arduino C++ API provides the F() macro. This forces the compiler to keep the string in Flash and read it byte-by-byte during execution:

// BAD: Consumes 42 bytes of 2KB SRAM
Serial.println("Initializing BME280 sensor on I2C bus...");

// GOOD: Consumes 0 bytes of SRAM, reads directly from 32KB Flash
Serial.println(F("Initializing BME280 sensor on I2C bus..."));

For a deeper dive into how the underlying C library handles memory spaces, refer to the AVR Libc official documentation, which details the PROGMEM attribute that powers the F() macro.

Comparison Matrix: Arduino Cores and Compilers

The flavor of C++ and the underlying compiler toolchain change drastically depending on the hardware you select in the IDE. Here is a breakdown of the C++ standards and toolchains used across popular ecosystems as of modern core releases:

Microcontroller Board Architecture Compiler Toolchain C++ Standard Supported SRAM / Flash
Arduino Uno R3 / Nano 8-bit AVR avr-gcc (GCC 7.3+) C++17 (Partial) 2KB / 32KB
Arduino Nano 33 IoT 32-bit ARM Cortex-M0+ arm-none-eabi-gcc C++14 / C++17 32KB / 256KB
ESP32 DevKit V1 32-bit Xtensa LX6 xtensa-esp32-elf-gcc C++17 / C++20 520KB / 4MB+
Raspberry Pi Pico (RP2040) 32-bit ARM Cortex-M0+ arm-none-eabi-gcc C++17 264KB / 2MB+

Walkthrough: Implementing Object-Oriented C++ on an MCU

Because the language is C++, you are not restricted to procedural programming. You can (and should) use Object-Oriented Programming (OOP) to encapsulate hardware states, especially when managing non-blocking timing. The standard delay() function halts the CPU, which is unacceptable in production firmware.

Below is a walkthrough of a custom C++ class that blinks an LED using millis() without blocking the main loop.

#include <Arduino.h>

class NonBlockingBlinker {
private:
    uint8_t _pin;
    unsigned long _previousMillis;
    unsigned long _interval;
    bool _ledState;

public:
    // Constructor
    NonBlockingBlinker(uint8_t pin, unsigned long interval) 
        : _pin(pin), _interval(interval), _previousMillis(0), _ledState(false) {
        pinMode(_pin, OUTPUT);
    }

    // Update method to be called in loop()
    void update() {
        unsigned long currentMillis = millis();
        if (currentMillis - _previousMillis >= _interval) {
            _previousMillis = currentMillis;
            _ledState = !_ledState;
            digitalWrite(_pin, _ledState ? HIGH : LOW);
        }
    }
};

// Instantiate objects
NonBlockingBlinker fastLED(13, 250);  // Pin 13, 250ms interval
NonBlockingBlinker slowLED(12, 1000); // Pin 12, 1000ms interval

void setup() {
    // Initialization handled by constructors
}

void loop() {
    fastLED.update();
    slowLED.update();
    // CPU is free to handle I2C sensors, Serial commands, etc.
}

Advanced C++ Features Available in the Arduino IDE

Many developers mistakenly believe the Arduino IDE is limited to basic C-style functions. By leveraging modern C++ standards supported by the GCC toolchain, you can write safer, more optimized firmware.

1. Constexpr for Compile-Time Math

Use constexpr to force the compiler to calculate values during the build process rather than wasting CPU cycles at runtime. This is vital for calculating timer prescalers or filter coefficients.

constexpr float calculateCutoffFreq(float r, float c) {
    return 1.0f / (2.0f * 3.14159f * r * c);
}
const float freq = calculateCutoffFreq(10000.0f, 0.0000001f); // Evaluated at compile time

2. std::array over Raw C-Arrays

Raw arrays decay into pointers, losing size information and risking buffer overflows. By including the standard array library, you gain bounds-checking and size methods.

#include <array>
std::array<int, 5> sensorReadings = {0, 0, 0, 0, 0};
// sensorReadings.size() returns 5 safely

Common Pitfalls When Writing Standard C++ for Arduino

While the language is C++, the environment is heavily constrained. Desktop C++ habits will quickly crash a microcontroller.

  • The String Class Trap: The Arduino String object relies on dynamic memory allocation (malloc/free). On an ATmega328P with 2KB SRAM, frequent concatenation causes heap fragmentation, leading to random reboots after a few hours of operation. Always use fixed-size char arrays and snprintf().
  • Exceptions and RTTI: By default, the Arduino compiler passes the -fno-exceptions and -fno-rtti flags to save Flash space. try/catch blocks and dynamic_cast will result in compilation errors unless you manually edit the platform.txt build configuration.
  • Standard Library Bloat: Including heavy headers like <iostream> or <regex> can instantly exceed the 32KB Flash limit of an AVR chip. Stick to lightweight C-style equivalents like <stdio.h> or specialized embedded libraries.
Pro-Tip for Optimization: If you are pushing the limits of your microcontroller's Flash memory, open your IDE preferences and enable 'Show verbose output during compilation'. Look for the -Os flag (Optimize for Size). You can often reclaim 10-15% of Flash space by switching to the arduino-cli and applying Link-Time Optimization (LTO) via the -flto compiler flag, which strips out unused C++ class methods across different compilation units.

Summary

So, what language is Arduino programming? It is unequivocally C++, supercharged by the Wiring API and compiled via industry-standard GCC toolchains. By moving beyond basic procedural sketches and embracing C++ memory management, OOP, and compile-time optimizations, you transform the Arduino IDE from a beginner's toy into a professional embedded systems workbench. For the definitive list of built-in API functions and hardware abstractions, always keep the Official Arduino Language Reference bookmarked.