# ADC Wrapper Module

## Overview

The ADC wrapper module provides a C++ object-oriented interface for ESP-IDF ADC functionality. This module encapsulates the ESP-IDF ADC oneshot driver functions and provides a clean, easy-to-use API for analog-to-digital conversion with automatic calibration support.

## Features

- **Multi-Unit Support**: Support for ADC1 and ADC2 units
- **Multi-Channel Support**: Support for up to 10 channels per unit
- **Automatic Calibration**: Built-in calibration for accurate voltage readings
- **Multiple Resolutions**: Support for 9-13 bit ADC resolution
- **Attenuation Control**: Configurable input voltage ranges
- **Averaging**: Built-in sample averaging for noise reduction
- **Error Handling**: Comprehensive error checking and logging

## Architecture

### Class Diagram

```
┌─────────────────────────────────────┐
│              Adc                    │
├─────────────────────────────────────┤
│ - m_adcHandle_[2]: handle_t         │
│ - m_caliHandle_[2][10]: handle_t    │
│ - m_unitInitialized_[2]: bool       │
│ - m_channelConfigured_[2][10]: bool │
│ - m_unitBitwidth_[2]: AdcBitwidth   │
├─────────────────────────────────────┤
│ + Adc()                             │
│ + ~Adc()                            │
│ + initializeUnit(unit, width): bool │
│ + deinitializeUnit(unit): bool      │
│ + configureChannel(config): bool    │
│ + readVoltage(unit, ch): int32_t    │
│ + readRaw(unit, ch): int32_t        │
│ + readVoltageAverage(...): int32_t  │
│ + readRawAverage(...): int32_t      │
│ + isUnitInitialized(unit): bool     │
│ + isChannelConfigured(...): bool    │
│ + getMaxRawValue(width): uint32_t   │
│ + getDefaultChannelConfig(): Config │
│ - convertUnit(unit): adc_unit_t     │
│ - convertChannel(ch): adc_channel_t │
│ - convertAttenuation(...): adc_att  │
│ - convertBitwidth(...): adc_bit_t   │
│ - initializeCalibration(...): bool  │
│ - deinitializeCalibration(...): bool│
│ - getUnitIndex(unit): uint8_t       │
│ - getChannelIndex(ch): uint8_t      │
└─────────────────────────────────────┘
```

### Enumerations

#### AdcUnit
- `UNIT_1`: ADC unit 1
- `UNIT_2`: ADC unit 2

#### AdcChannel
- `CHANNEL_0` to `CHANNEL_9`: ADC channels 0-9

#### AdcAttenuation
- `ATTEN_0dB`: 0dB attenuation (0-950mV range)
- `ATTEN_2_5dB`: 2.5dB attenuation (0-1250mV range)
- `ATTEN_6dB`: 6dB attenuation (0-1750mV range)
- `ATTEN_11dB`: 11dB attenuation (0-3100mV range)

#### AdcBitwidth
- `WIDTH_9BIT`: 9-bit resolution (0-511)
- `WIDTH_10BIT`: 10-bit resolution (0-1023)
- `WIDTH_11BIT`: 11-bit resolution (0-2047)
- `WIDTH_12BIT`: 12-bit resolution (0-4095)
- `WIDTH_13BIT`: 13-bit resolution (0-8191)

### Configuration Structure

```cpp
struct AdcChannelConfig {
    AdcUnit unit;              // ADC unit
    AdcChannel channel;        // ADC channel
    AdcAttenuation atten;      // Attenuation level
    AdcBitwidth bitwidth;      // ADC resolution
};
```

## Usage Examples

### Basic ADC Reading

```cpp
#include "adc.hpp"

// Create ADC instance
Adc adc;

// Initialize ADC unit 1 with 12-bit resolution
adc.initializeUnit(AdcUnit::UNIT_1, AdcBitwidth::WIDTH_12BIT);

// Configure channel 0
AdcChannelConfig config = Adc::getDefaultChannelConfig();
config.unit = AdcUnit::UNIT_1;
config.channel = AdcChannel::CHANNEL_0;
config.atten = AdcAttenuation::ATTEN_11dB;  // 0-3.1V range
config.bitwidth = AdcBitwidth::WIDTH_12BIT;

adc.configureChannel(config);

// Read voltage in millivolts
int32_t voltage = adc.readVoltage(AdcUnit::UNIT_1, AdcChannel::CHANNEL_0);
printf("Voltage: %ld mV\n", voltage);

// Read raw ADC value
int32_t raw = adc.readRaw(AdcUnit::UNIT_1, AdcChannel::CHANNEL_0);
printf("Raw value: %ld\n", raw);
```

### Multiple Channels

```cpp
// Configure multiple channels
AdcChannelConfig configs[] = {
    {AdcUnit::UNIT_1, AdcChannel::CHANNEL_0, AdcAttenuation::ATTEN_11dB, AdcBitwidth::WIDTH_12BIT},
    {AdcUnit::UNIT_1, AdcChannel::CHANNEL_1, AdcAttenuation::ATTEN_6dB, AdcBitwidth::WIDTH_12BIT},
    {AdcUnit::UNIT_2, AdcChannel::CHANNEL_0, AdcAttenuation::ATTEN_11dB, AdcBitwidth::WIDTH_12BIT}
};

// Initialize units
adc.initializeUnit(AdcUnit::UNIT_1, AdcBitwidth::WIDTH_12BIT);
adc.initializeUnit(AdcUnit::UNIT_2, AdcBitwidth::WIDTH_12BIT);

// Configure all channels
for (const auto& config : configs) {
    adc.configureChannel(config);
}

// Read from all channels
for (const auto& config : configs) {
    int32_t voltage = adc.readVoltage(config.unit, config.channel);
    printf("Unit %d Channel %d: %ld mV\n", 
           static_cast<int>(config.unit), 
           static_cast<int>(config.channel), 
           voltage);
}
```

### Noise Reduction with Averaging

```cpp
// Read 10 samples and return average
int32_t avgVoltage = adc.readVoltageAverage(AdcUnit::UNIT_1, AdcChannel::CHANNEL_0, 10);
printf("Average voltage: %ld mV\n", avgVoltage);

// Read 50 raw samples and return average
int32_t avgRaw = adc.readRawAverage(AdcUnit::UNIT_1, AdcChannel::CHANNEL_0, 50);
printf("Average raw: %ld\n", avgRaw);
```

### Different Voltage Ranges

```cpp
// Low voltage sensor (0-950mV)
AdcChannelConfig lowVoltageConfig = {};
lowVoltageConfig.unit = AdcUnit::UNIT_1;
lowVoltageConfig.channel = AdcChannel::CHANNEL_0;
lowVoltageConfig.atten = AdcAttenuation::ATTEN_0dB;  // 0-950mV
lowVoltageConfig.bitwidth = AdcBitwidth::WIDTH_12BIT;

// Battery voltage (0-3.1V)
AdcChannelConfig batteryConfig = {};
batteryConfig.unit = AdcUnit::UNIT_1;
batteryConfig.channel = AdcChannel::CHANNEL_1;
batteryConfig.atten = AdcAttenuation::ATTEN_11dB;  // 0-3100mV
batteryConfig.bitwidth = AdcBitwidth::WIDTH_12BIT;

adc.configureChannel(lowVoltageConfig);
adc.configureChannel(batteryConfig);
```

## API Reference

### Constructor/Destructor

- **Adc()**: Initialize ADC wrapper instance
- **~Adc()**: Clean up resources and deinitialize all units

### Unit Management

- **initializeUnit(unit, bitwidth)**: Initialize ADC unit with resolution
- **deinitializeUnit(unit)**: Deinitialize ADC unit
- **isUnitInitialized(unit)**: Check if unit is initialized

### Channel Configuration

- **configureChannel(config)**: Configure ADC channel
- **isChannelConfigured(unit, channel)**: Check if channel is configured

### Reading Methods

- **readVoltage(unit, channel)**: Read calibrated voltage in mV
- **readRaw(unit, channel)**: Read raw ADC value
- **readVoltageAverage(unit, channel, samples)**: Read averaged voltage
- **readRawAverage(unit, channel, samples)**: Read averaged raw value

### Utility Methods

- **getMaxRawValue(bitwidth)**: Get maximum raw value for resolution
- **getDefaultChannelConfig()**: Get default configuration

## Voltage Ranges by Attenuation

| Attenuation | Voltage Range | Typical Use Case |
|-------------|---------------|------------------|
| 0dB         | 0 - 950mV     | Low voltage sensors |
| 2.5dB       | 0 - 1250mV    | 1.2V references |
| 6dB         | 0 - 1750mV    | 1.8V logic levels |
| 11dB        | 0 - 3100mV    | 3.3V logic, battery voltage |

## ADC Resolution and Range

| Bitwidth | Resolution | Max Raw Value | LSB (at 3.1V) |
|----------|------------|---------------|----------------|
| 9-bit    | 512 steps  | 511           | ~6.1 mV        |
| 10-bit   | 1024 steps | 1023          | ~3.0 mV        |
| 11-bit   | 2048 steps | 2047          | ~1.5 mV        |
| 12-bit   | 4096 steps | 4095          | ~0.76 mV       |
| 13-bit   | 8192 steps | 8191          | ~0.38 mV       |

## Error Handling

The module provides comprehensive error handling:
- Unit and channel validation
- Initialization status checks
- ESP-IDF error codes are caught and logged
- Return values indicate success/failure for all operations
- Graceful fallback when calibration is unavailable

## Dependencies

- ESP-IDF ADC driver (`esp_adc/adc_oneshot.h`)
- ESP-IDF ADC calibration (`esp_adc/adc_cali.h`)
- ESP-IDF error handling (`esp_err.h`)
- ESP-IDF logging (`esp_log.h`)

## Thread Safety

The ADC wrapper uses ESP-IDF's thread-safe ADC driver. Multiple tasks can safely read from different channels simultaneously.

## Memory Usage

- Fixed memory footprint per instance
- Calibration handles stored per channel
- No dynamic memory allocation in wrapper layer

## Performance Considerations

- Direct ESP-IDF function calls for optimal performance
- Calibration lookup tables for fast voltage conversion
- Averaging reduces noise but increases read time
- Higher resolution increases conversion time

## Calibration

The module automatically initializes calibration for each configured channel:
- Uses ESP-IDF's curve fitting calibration scheme
- Provides accurate voltage readings across temperature range
- Falls back to linear estimation if calibration fails
- Calibration data stored in eFuse (factory calibrated)

## Limitations

- ADC2 cannot be used when WiFi is active
- Some channels may not be available on all ESP32 variants
- Maximum sampling rate depends on resolution and calibration
- Input impedance affects accuracy for high-impedance sources