跳到主要内容

ADC Peripheral Guide

Read analog voltages from sensors, potentiometers, and other analog sources using the TuyaOpen TKL ADC interface.

Prerequisites

How ADC Works in TuyaOpen

TuyaOpen's ADC uses a port + channel bitmask model:

  • Port selects the ADC unit: TUYA_ADC_NUM_0 = ADC1, TUYA_ADC_NUM_1 = ADC2
  • Channel bitmask (cfg->ch_list.data) enables specific channels: bit N enables ADC_CHANNEL_N
  • Attenuation is fixed at ADC_ATTEN_DB_12 (0-3.3 V full range)
  • Reference voltage is 3300 mV (tkl_adc_ref_voltage_get())

Basic ADC Read

#include "tkl_adc.h"

#define ADC_PORT TUYA_ADC_NUM_0
#define ADC_CHANNEL 4

static OPERATE_RET read_adc(uint32_t *raw_value)
{
TUYA_ADC_BASE_CFG_T cfg = {
.ch_list.data = (1 << ADC_CHANNEL),
.ch_nums = 1,
.width = 12,
.type = TUYA_ADC_INNER_SAMPLE_VOL,
};

OPERATE_RET rt = tkl_adc_init(ADC_PORT, &cfg);
if (rt != OPRT_OK) {
return rt;
}

int32_t value;
rt = tkl_adc_read_single_channel(ADC_PORT, ADC_CHANNEL, &value);
tkl_adc_deinit(ADC_PORT);

*raw_value = (uint32_t)value;
return rt;
}

Converting Raw to Voltage

uint32_t raw;
read_adc(&raw);

uint32_t ref_mv;
tkl_adc_ref_voltage_get(ADC_PORT, &ref_mv);

float voltage = (float)raw * ref_mv / ((1 << 12) - 1);
TAL_PR_INFO("voltage: %.2f mV", voltage);

Multi-Channel Reading

Enable multiple channels simultaneously with the bitmask:

TUYA_ADC_BASE_CFG_T cfg = {
.ch_list.data = (1 << 0) | (1 << 1) | (1 << 4),
.ch_nums = 3,
.width = 12,
};
tkl_adc_init(TUYA_ADC_NUM_0, &cfg);

int32_t ch0_val, ch1_val, ch4_val;
tkl_adc_read_single_channel(TUYA_ADC_NUM_0, 0, &ch0_val);
tkl_adc_read_single_channel(TUYA_ADC_NUM_0, 1, &ch1_val);
tkl_adc_read_single_channel(TUYA_ADC_NUM_0, 4, &ch4_val);

tkl_adc_deinit(TUYA_ADC_NUM_0);

ADC Channel-to-GPIO Mapping

Channel-to-GPIO mapping depends on the chip. See the per-chip pinmux docs for complete tables:

PlatformADC1 PortADC2 PortPinmux Doc
ESP32TUYA_ADC_NUM_0 (GPIO 32-39)TUYA_ADC_NUM_1 (GPIO 0-27)ESP32 Classic Pinmux
ESP32-S3TUYA_ADC_NUM_0 (GPIO 1-10)TUYA_ADC_NUM_1 (GPIO 11-20)ESP32-S3 Pinmux
ESP32-C3TUYA_ADC_NUM_0 (GPIO 0-4)TUYA_ADC_NUM_1 (GPIO 5)ESP32-C3 Pinmux
ESP32-C6TUYA_ADC_NUM_0 (GPIO 0-6)N/AESP32-C6 Pinmux
T5AITUYA_ADC_NUM_0 (P0-P28)N/AT5AI Peripheral Mapping

ESP32 ADC2 and Wi-Fi Conflict

注意

On classic ESP32, ADC2 cannot be used while Wi-Fi is active. ADC2 channels share hardware with the Wi-Fi radio. If your application uses Wi-Fi (most do), only ADC1 channels are available.

This limitation does not apply to ESP32-S3, ESP32-C3, or ESP32-C6.

Bit Width Options

WidthResolutionMax Raw Value
9512 levels511
101024 levels1023
112048 levels2047
12 (default)4096 levels4095
138192 levels8191

Not all widths are available on all chips. Use 12 for best compatibility.

Temperature Sensor

tkl_adc_temperature_get() returns OPRT_NOT_SUPPORTED on ESP32. The chip's internal temperature sensor uses a separate ESP-IDF driver (temperature_sensor_*), not the ADC path.

Practical Example: Battery Voltage Monitor

Read a battery voltage through a resistor divider (2:1 ratio):

#define BATTERY_ADC_PORT TUYA_ADC_NUM_0
#define BATTERY_ADC_CHANNEL 5
#define DIVIDER_RATIO 2.0f

float get_battery_voltage_mv(void)
{
TUYA_ADC_BASE_CFG_T cfg = {
.ch_list.data = (1 << BATTERY_ADC_CHANNEL),
.ch_nums = 1,
.width = 12,
};
tkl_adc_init(BATTERY_ADC_PORT, &cfg);

int32_t raw;
tkl_adc_read_single_channel(BATTERY_ADC_PORT, BATTERY_ADC_CHANNEL, &raw);
tkl_adc_deinit(BATTERY_ADC_PORT);

uint32_t ref_mv;
tkl_adc_ref_voltage_get(BATTERY_ADC_PORT, &ref_mv);

float measured_mv = (float)raw * ref_mv / 4095.0f;
return measured_mv * DIVIDER_RATIO;
}

References