Arduino data types define how much memory a variable consumes and how the compiler interprets its bits in RAM. The most critical rule for modern embedded development is that data type sizes are not universal across all Arduino boards. A standard int is 16 bits (2 bytes) on an 8-bit AVR board like the Arduino Uno R3, but it is 32 bits (4 bytes) on a 32-bit ARM board like the Arduino Uno R4 Minima or the ESP32. Choosing the wrong type leads to silent integer overflow, SRAM exhaustion, and math errors that only appear after hours of runtime.

This guide breaks down the exact memory footprints, builds a high-precision 16-bit ADC logger to demonstrate safe type casting, and provides exact compiler error fixes for the most common data type bugs.

The Architecture Trap: 8-Bit vs 32-Bit Memory Sizes

When you migrate code from a classic Arduino Uno R3 (ATmega328P) to an Arduino Uno R4 Minima (Renesas RA4M1) or an ESP32 DevKit V1, the C++ compiler changes how it allocates memory. If your code relies on an int overflowing at 32,767 to trigger a specific behavior, that logic will silently fail on a 32-bit board where int maxes out at 2,147,483,647.

Bench Anecdote: I once spent three hours debugging a solar charge controller that hard-reset every 9 hours. The developer used a signed 16-bit int to store a millis() delta on an Uno R3. At 32,767 milliseconds (32.7 seconds), it didn't reset, but when they multiplied it by a scaling factor, it exceeded the 16-bit limit and corrupted the stack. Always use explicitly sized types like uint32_t for time and int16_t for sensor registers.
Data Type Size (AVR 8-bit / Uno R3) Size (ARM/ESP32 32-bit / R4) Minimum Value Maximum Value Best Use Case
boolean / bool 1 byte (8 bits) 1 byte (8 bits) 0 (false) 1 (true) State flags, digital pin status
byte / uint8_t 1 byte (8 bits) 1 byte (8 bits) 0 255 I2C/SPI registers, raw sensor bytes
int 2 bytes (16 bits) 4 bytes (32 bits) -32,768 (AVR) / -2,147,483,648 (ARM) 32,767 (AVR) / 2,147,483,647 (ARM) Avoid for portability; use int16_t/int32_t
int16_t 2 bytes (16 bits) 2 bytes (16 bits) -32,768 32,767 16-bit ADC raw readings, motor encoder counts
uint32_t 4 bytes (32 bits) 4 bytes (32 bits) 0 4,294,967,295 millis() timestamps, large counters
float 4 bytes (32 bits) 4 bytes (32 bits) -3.4028235E+38 3.4028235E+38 Voltage calculations, PID control loops
double 4 bytes (32 bits)* 8 bytes (64 bits) Same as float (AVR) 1.7976931348623157E+308 (ARM) High-precision GPS coordinates (ESP32 only)

*Note: On 8-bit AVR boards, double is just an alias for float and offers no extra precision. True 64-bit double precision requires a 32-bit board.

Project Build: High-Precision 16-Bit ADC Logger

To see data types in action, we will build a precision voltage logger using an Arduino Uno R4 Minima and an external 16-bit ADC. The internal 10-bit ADC of the Arduino is insufficient for measuring millivolt-level sensor drops, and reading a 16-bit I2C register into a standard 32-bit int without proper casting can cause sign-extension bugs if the MSB (Most Significant Bit) is handled incorrectly.

Parts List

  • Microcontroller: Arduino Uno R4 Minima (32-bit ARM Cortex-M4)
  • ADC Module: Adafruit ADS1115 16-Bit ADC Breakout (Product ID: 1085)
  • Passive Components: 10kΩ precision resistor (0.1% tolerance) for test voltage division
  • Hardware: Half-size solderless breadboard, silicone jumper wires (22 AWG)

Pin Mapping Table

ADS1115 Pin Arduino Uno R4 Minima Pin Notes & I2C Requirements
VDD 5V (or 3.3V) ADS1115 is 3.3V/5V tolerant. Match to your logic level.
GND GND Must share a common ground with the measured circuit.
SCL A5 (or dedicated I2C header SCL) I2C Clock. Breakout includes 10kΩ pull-ups.
SDA A4 (or dedicated I2C header SDA) I2C Data. If adding multiple I2C devices, add 4.7kΩ external pull-ups.
A0 Test Point (0-4.096V) Analog Input Channel 0. Max voltage dictated by PGA gain.
ADDR GND Ties I2C address to 0x48. Tie to VDD for 0x49.

Complete Code with Safe Type Casting

This code targets the Arduino Uno R4 Minima. It explicitly uses int16_t for the raw ADC register read to prevent the compiler from misinterpreting the sign bit when promoting the value to a 32-bit integer, and uses uint32_t for the timestamp to prevent the 49-day rollover bug associated with signed 32-bit integers.

#include <Wire.h>
#include <Adafruit_ADS1X15.h>

// PIN DEFINITIONS (Arduino Uno R4 Minima)
// The R4 Minima routes I2C to A4/A5 and the dedicated header
const uint8_t I2C_SDA_PIN = A4;
const uint8_t I2C_SCL_PIN = A5;
const uint8_t LED_STATUS_PIN = 13;

// Initialize the ADS1115 object
Adafruit_ADS1115 ads;

void setup() {
  Serial.begin(115200);
  pinMode(LED_STATUS_PIN, OUTPUT);

  // Explicitly route Wire to the correct pins for R4 Minima
  Wire.setSDA(I2C_SDA_PIN);
  Wire.setSCL(I2C_SCL_PIN);
  Wire.begin();

  // Error handling: Verify I2C communication
  // 0x48 is the default address when ADDR is tied to GND
  if (!ads.begin(0x48, &Wire)) {
    Serial.println("FATAL: Failed to initialize ADS1115.");
    Serial.println("Check I2C wiring, pull-up resistors, and ADDR pin state.");
    while (1) {
      // Blink LED rapidly to indicate hardware fault
      digitalWrite(LED_STATUS_PIN, HIGH);
      delay(100);
      digitalWrite(LED_STATUS_PIN, LOW);
      delay(100);
    }
  }
  
  // Set gain to +/- 4.096V range (1 bit = 0.125mV)
  ads.setGain(GAIN_ONE); 
  Serial.println("ADS1115 Initialized. Logging...");
}

void loop() {
  // CRITICAL TYPE CASTING:
  // The ADS1115 returns a 16-bit signed integer.
  // If we use a standard 'int' on the R4 (which is 32-bit), 
  // bitwise operations or negative voltage readings can corrupt.
  int16_t adc_raw = ads.readADC_SingleEnded(0);

  // Calculate voltage using float for fractional precision
  // 0.000125 is the multiplier for GAIN_ONE
  float voltage = adc_raw * 0.000125;

  // Use uint32_t for millis() to safely handle the 49.7-day rollover
  uint32_t timestamp_ms = millis();

  // Output formatted data
  Serial.print("Time(ms): ");
  Serial.print(timestamp_ms);
  Serial.print(" | Raw 16-bit: ");
  Serial.print(adc_raw);
  Serial.print(" | Voltage: ");
  Serial.println(voltage, 4); // 4 decimal places

  // Status LED heartbeat
  digitalWrite(LED_STATUS_PIN, HIGH);
  delay(50);
  digitalWrite(LED_STATUS_PIN, LOW);
  
  delay(450); // Total loop time ~500ms
}

Debugging Data Type Errors

When working with mixed architectures or porting old sketches, the GCC compiler used by the Arduino IDE will throw specific warnings or errors. Here is how to decode them.

Error 1: Implicit Constant Overflow

Exact Error String: warning: overflow in implicit constant conversion [-Woverflow]

Ranked Causes:

  1. Un-suffixed Math: You wrote uint32_t micro = 60 * 60 * 1000000;. The compiler evaluates 60 * 60 * 1000000 as a 16-bit int on AVR boards before assigning it to the 32-bit variable. It overflows at 32,767 during the calculation.
  2. Fix: Append the UL (Unsigned Long) suffix to the first number to force 32-bit evaluation: uint32_t micro = 60UL * 60 * 1000000;

Error 2: Float to Int Conversion

Exact Error String: error: conversion from 'float' to 'int' may alter its value [-Werror=float-conversion]

Ranked Causes:

  1. Strict Compiler Flags: You are assigning a float sensor reading directly to an int array or PWM function without explicitly telling the compiler you accept the data loss.
  2. Fix: Use an explicit cast int myVal = (int)myFloat; or, better yet, use rounding: int myVal = round(myFloat); to prevent truncation errors (e.g., 3.99 becoming 3).
The First 3 Things to Check When I2C Sensors Return Garbage Data:
  1. Pull-up Resistor Impedance: The ADS1115 breakout has 10kΩ pull-ups. If you daisy-chain more than two I2C devices, the bus capacitance rises, dulling the SCL clock edges. Drop to 4.7kΩ or 2.2kΩ pull-ups to maintain 400kHz Fast Mode integrity.
  2. Sign Extension on 32-bit Boards: If your negative voltage readings show up as massive positive numbers (e.g., 4,294,967,200), you stored a 16-bit signed ADC result in an unsigned 32-bit variable. The compiler padded the sign bit with zeros instead of ones. Always use int16_t for raw ADC registers.
  3. Board Selection in IDE: If your math works on an Uno R3 but fails on an R4/ESP32, check your Tools > Board menu. The IDE changes the GCC target architecture, which changes the size of int and double.

Extending and Simplifying the Build

How to Simplify (Lower Resolution, Less Code)

If you do not need 16-bit precision and want to eliminate the I2C dependency, you can simplify this build by using the Arduino Uno R4 Minima's internal 14-bit DAC/ADC capabilities. The R4 features a true 14-bit internal ADC (unlike the R3's 10-bit). You can strip out the Wire and Adafruit_ADS1X15 libraries entirely and simply use analogRead(A0). Change the data type from int16_t to uint16_t, as the internal ADC will not return negative values without hardware biasing.

How to Extend (Multi-Channel Logging & SD Storage)

To scale this into a multi-channel data logger:

  • Hardware: Add a MicroSD SPI breakout (like the Adafruit MicroSD breakout, Product ID: 254). Wire MISO, MOSI, SCK, and CS to the R4's dedicated SPI header.
  • Memory Management: When logging to SD, avoid using the String class. The String class causes heap fragmentation on microcontrollers, leading to unpredictable reboots after a few hours of logging. Instead, use character arrays (char buffer[64];) and snprintf() to format your CSV rows before writing to the file.
  • Data Types for Storage: If you are logging millions of rows, storing float voltage values takes 4 bytes per reading. Multiply the voltage by 10,000 and store it as a uint16_t (if max voltage is under 6.5V) or uint32_t. This cuts your SD card write times in half and saves significant storage space, which is critical when writing to FAT32 formatted cards at high speeds.

For more on standardizing your variable sizes across different microcontroller families, refer to the official Arduino Language Reference for Data Types and the Adafruit ADS1115 Breakout Documentation for specific I2C register mappings.