The Short Answer: What Code Does Arduino Use?

When makers and engineers ask, "what code does Arduino use?", the standard textbook answer is C++. However, from a systems engineering perspective, that answer is incomplete. Arduino does not use standard desktop C++; it uses a specialized, hardware-abstracted dialect of C++ (currently tracking C++14 and C++17 standards depending on the core) built on top of the GCC/Clang toolchains. More importantly, the "Arduino language" is actually a collection of C/C++ libraries wrapped around a simplified execution loop.

In this deep dive, we bypass the basic setup() and loop() tutorials to dissect the actual architecture of Arduino code. We will examine the hidden main() function, reverse-engineer the hardware abstraction layer (HAL), and perform a forensic deep dive into two of the most critical libraries in the ecosystem: the core Wire.h (I2C) library and the third-party standard ArduinoJson.

Under the Hood: The Hidden main() and C++ Toolchain

To understand what code Arduino uses, you must understand how the Arduino IDE (versions 2.3 and newer) processes your .ino sketch files. The IDE does not compile your sketch directly. Instead, it performs three automated preprocessing steps:

  1. Concatenation: All .ino tabs are merged into a single C++ file.
  2. Prototype Generation: The IDE scans for custom functions and automatically generates C++ forward declarations (prototypes) at the top of the file.
  3. Main Injection: Your code is injected into a hidden main.cpp file provided by the board's core library.

The underlying C++ code that actually executes on the microcontroller looks like this:

#include <Arduino.h>

int main(void) {
    init();          // Configures timers, ADC, and UART hardware
    setup();         // Calls your user-defined setup()
    for (;;) {       // Infinite while loop
        loop();      // Calls your user-defined loop()
        if (serialEventRun) serialEventRun();
    }
    return 0;
}

According to the official Arduino Language Reference, the init() function is part of the core library (e.g., avr-libc for the Uno R3, or the Renesas FSP for the Uno R4 Minima). This means "Arduino code" is fundamentally just standard C++ calling hardware-specific C libraries.

Core Library Anatomy: The digitalWrite() Overhead Problem

The most common function in Arduino code is digitalWrite(pin, value). While beginner-friendly, a deep dive into the AVR core library reveals significant execution overhead. When you call digitalWrite(13, HIGH), the library must:

  • Verify if the pin is used for PWM and disable the timer if necessary.
  • Translate the Arduino logical pin number (13) to the physical microcontroller port (PORTB) and bit (PB5) using a lookup table stored in Flash memory.
  • Execute the bitwise operation to set the pin high.

On a 16 MHz ATmega328P (Arduino Uno R3), this abstraction takes approximately 50 clock cycles (3.2 µs). If you are bit-banging a protocol or driving high-speed shift registers, this overhead is catastrophic. The professional alternative is Direct Port Manipulation, which bypasses the Arduino library entirely and uses native AVR C:

// Native AVR C: Takes exactly 2 clock cycles (0.125 µs)
PORTB |= (1 << PB5);  // Set Pin 13 HIGH
PORTB &= ~(1 << PB5); // Set Pin 13 LOW

Understanding this distinction is crucial when optimizing Arduino code for real-time constraints.

Deep Dive: The Wire.h I2C Library Architecture

The Wire.h library is the backbone of Arduino sensor communication. However, treating it as a simple serial pipe leads to severe edge-case failures. Let us dissect the TwoWire class architecture.

Buffer Limits and Truncation Failures

A common failure mode in Arduino I2C communication is silent data truncation. The Wire library relies on fixed-size RAM buffers. According to the AVR Libc documentation and core source files, the default buffer sizes vary wildly by architecture:

Microcontroller / Board Architecture I2C TX/RX Buffer Size Failure Mode if Exceeded
ATmega328P (Uno R3) AVR 8-bit 32 Bytes Silent truncation; Wire.endTransmission() returns 0 (success) but data is lost.
ESP32-S3 (DevKitC) Xtensa 32-bit 128 Bytes Truncation or I2C bus lockup if FIFO overflows.
Renesas RA4M1 (Uno R4) ARM Cortex-M4 256 Bytes Queue overflow; returns error code 1.

Clock Stretching and SMBus Timeouts

When communicating with complex sensors (like the BNO055 IMU), the sensor may hold the SCL line low to "stretch" the clock while it processes data. The standard AVR Wire library has no timeout mechanism for clock stretching; if the sensor hangs, the ATmega328P will freeze indefinitely in an I2C interrupt service routine (ISR). To prevent this, advanced Arduino code must implement custom I2C wrappers with hardware watchdog timers or use the ESP32 core, which natively supports I2C timeout configurations via Wire.setTimeOut(100) (measured in milliseconds).

Third-Party Powerhouse: ArduinoJson Memory Management

No analysis of what code Arduino uses is complete without addressing data serialization. ArduinoJson, developed by Benoît Blanchon, is the undisputed industry standard for parsing JSON on microcontrollers. With the release of version 7 (the standard for 2026 development), the library underwent a massive architectural shift regarding memory allocation.

Static vs. Dynamic Allocation in v7

In older versions (v5 and v6), developers had to choose between StaticJsonDocument (stack-allocated, safe but limited by SRAM) and DynamicJsonDocument (heap-allocated, prone to fragmentation). Version 7 unified this into a single JsonDocument class.

The v7 JsonDocument starts by allocating memory on the stack. If the JSON payload exceeds the stack capacity, it seamlessly falls back to heap allocation. While convenient, this introduces a critical edge case on 8-bit AVR boards with only 2KB of SRAM: Heap Fragmentation.

Expert Troubleshooting Tip: If your Arduino Uno randomly reboots while parsing a 500-byte JSON payload, you are likely experiencing heap fragmentation. The String class and dynamic JSON allocations chop the 2KB SRAM into unusable slivers. Always pre-allocate a global JsonDocument or use fixed-size C-arrays (char buffer[512]) on 8-bit architectures to prevent malloc() failures.

While the MIT-licensed version of ArduinoJson is free, professional engineering teams often purchase the commercial Pro edition (priced around $65 per developer seat) to access the advanced deserialization debugging tools and custom allocator templates required for strict automotive and industrial IoT memory budgets.

Architecture Comparison Matrix

Because "Arduino code" is just a hardware abstraction layer, the underlying C++ implementation changes depending on the board package you install via the Boards Manager. Here is how the core codebases compare in 2026:

Board Core Package Underlying C/C++ Framework Standard Library Support RTOS Integration
Arduino AVR Boards avr-libc (C99 / C++14) Partial (No <thread>, limited <vector>) None (Bare Metal)
ESP32 Arduino Core (v3.x) ESP-IDF (C++17 / C++20) Full STL support FreeRTOS (Pre-emptive multitasking)
Arduino Mbed OS Core (Nano 33 BLE) ARM Mbed OS 6 (C++14) Full STL support Mbed RTOS

Conclusion: Writing Professional Arduino Code

Ultimately, what code does Arduino use? It uses C++ constrained by the physical realities of embedded systems. Writing robust Arduino code in 2026 requires looking past the simplified IDE facade. By understanding the hidden main() loop, respecting the strict buffer limits of the Wire.h HAL, and managing SRAM fragmentation in libraries like ArduinoJson, you transition from a hobbyist sketching in the IDE to an embedded systems engineer deploying production-grade firmware.