Designing a Low‑Cost Sensor Node with the Arduino Nano’s 10‑bit ADC
This guide shows how to wire, configure, and program the Nano’s 10‑bit ADC for reliable analog sampling in battery‑powered sensor nodes, covering hardware, firmware, validation, and fault recovery.
03 Sept 2026, 14:58 UTC

Problem Statement
In many battery‑powered sensor nodes the only source of analog data is a low‑cost microcontroller. The Arduino Nano (ATmega328P) offers a single 10‑bit ADC that can read voltages from 0 V to the supply voltage (≈5 V). To build a robust node you must:
- Wire the sensor with minimal components.
- Configure the ADC for the desired resolution and reference.
- Buffer readings to avoid blocking the main loop.
- Validate data and recover from faults.
Requirements
- Hardware: Nano board, 5 V regulated supply, low‑pass RC filter (optional), series resistor & zener for over‑voltage protection.
- Software: Arduino IDE 1.8+ or PlatformIO, C/C++ firmware, watchdog timer enabled.
- Testing: Multimeter for voltage, oscilloscope for noise, Serial Monitor for debug.
Minimal Hardware Wiring
For a simple temperature sensor that outputs 0–5 V:
- Connect sensor output to
A0. - Place a 10 kΩ pull‑down to ground if the sensor can float.
- Add a 0.1 µF decoupling capacitor from
A0to GND to filter high‑frequency noise. - Use a 1 kΩ series resistor followed by a 4.7 V zener to clamp the input if the sensor might exceed 5 V.
ADC Configuration – Smallest Suitable Design
Use the internal 1.1 V reference for higher resolution on low‑range signals, otherwise Vcc is simpler.
// Enable 1.1V reference and set channel A0
ADMUX = (1 << REFS0) | (0 << MUX0); // REFS0=1 selects 1.1V, MUX=0 selects ADC0
// Disable ADC to save current between conversions
ADCSRA &= ~(1 << ADEN);
Configure the ADC clock prescaler to 128 for maximum accuracy (ADC clock 125 kHz at 16 MHz). The ATmega328P datasheet recommends this setting for most applications.
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 128 prescaler
Ring Buffer for Non‑Blocking Sampling
A 16‑sample circular buffer keeps the main loop free for other tasks. The following struct shows the implementation:
#define BUF_SIZE 16
volatile uint16_t adcBuf[BUF_SIZE];
volatile uint8_t bufWrite = 0;
volatile uint8_t bufRead = 0;
void adcISR() {
uint16_t val = ADC; // read result
adcBuf[bufWrite] = val;
bufWrite = (bufWrite + 1) % BUF_SIZE;
}
Enable the ADC interrupt:
ADCSRA |= (1 << ADIE) | (1 << ADEN);
sei();
In loop() read from the buffer without blocking.
Operational Checks and Validation
- Reference stability: Measure Vcc with
analogRead(AREF)and compare against expected 5 V. If the value deviates >5 % log an error and suspend sampling. - Over‑voltage detection: If the input exceeds 4.8 V (or 1.1 V when using the internal ref), set a flag and skip the sample. The zener in the wiring ensures the pin never sees more than 5 V.
- Noise rejection: If successive samples differ by >200 counts on a stable sensor, trigger a debounce routine that discards outliers.
- Watchdog recovery: Enable the watchdog timer (WDT) with a 2 s timeout. In
loop()reset the WDT after each ADC cycle. If the firmware hangs, the MCU resets automatically.
Failure Modes and Design Changes
| Condition | Impact | Design Adjustment |
|---|---|---|
| Supply voltage drops below 4.5 V | ADC reference shifts, measurement error | Use an external 1.2 V reference or add a low‑dropout regulator to keep Vcc stable. |
| High‑speed sampling (>5 kHz) | ADC shares clock with UART, causing frame errors | Disable UART during sampling or use a separate timer for data logging. |
| Sensor output exceeds 5 V | Pin damage, measurement saturation | Replace series resistor with a proper voltage divider or use an op‑amp buffer. |
| Battery‑driven node runs <200 mA | ADC current (~1–2 mA) becomes significant | Power‑down ADC between conversions as shown in the configuration section. |
| Noise from nearby motors | Spurious ADC spikes | Add a low‑pass RC filter (e.g., 10 kΩ & 0.1 µF) and increase series resistor to 100 Ω. |
Practical Verification Steps
- Connect a calibrated 0–5 V divider to
A0and open Serial Monitor. Verify that the printed ADC values match the expected counts (e.g., 0 V → 0, 2.5 V → 512). - Switch to the internal 1.1 V reference, set the divider to output 1.1 V, and confirm the ADC reads close to 1023.
- Measure current draw with a multimeter:
ADC OFF≈ 0.8 mA,ADC ON≈ 1.9 mA. The difference validates the power‑down strategy. - Run the firmware for 60 s with the watchdog enabled. If the node never resets, the watchdog is functioning.
Limitations
- The ADC reference is tied to Vcc; any supply ripple directly corrupts readings.
- Noise on the analog pin can produce outliers; a simple RC filter mitigates but does not eliminate high‑frequency interference.
- The ADC shares the main clock; enabling other high‑speed peripherals may require additional timing adjustments.
- Only one analog channel can be sampled at a time; multiplexing is necessary for multi‑sensor nodes.
Conclusion
By wiring a minimal sensor interface, configuring the ADC with the internal reference, buffering samples, and validating data, you can build a low‑cost, battery‑efficient sensor node on the Arduino Nano. The design remains flexible: if higher resolution, faster sampling, or stricter power budgets are required, the architecture can be upgraded with an external ADC, a dedicated reference, or a more sophisticated power‑management strategy.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.