ADC API
The Analog-to-Digital Converter (ADC) API lets Lua code measure analog voltages using the ESP32’s built-in ADC hardware. This is the API you use when reading potentiometers, light sensors, simple analog outputs from other circuits, and similar low-bandwidth analog signals.
Xedge32 exposes two ADC usage models:
One-shot mode, which is easy to use and well suited for occasional polling.
Continuous mode, which samples in the background and delivers data through a callback.
For most applications, start with one-shot mode. Move to continuous mode only when you need steady high-rate sampling.
One-Shot Mode
Function signature:
adc, pinOrErr = esp32.adc(unit, channel [, cfg])
Parameters
unit: ADC unit number. ESP32 devices provide ADC unit1and2.channel: ADC channel number within the selected unit.cfg: Optional configuration table.
Supported configuration options:
attenuation: One of"0db","2.5db","6db", or"11db". The default is"11db".volt: Boolean. Whentrue,adc:read()returns both the raw ADC value and the value converted to millivolts.bitwidth: Number from9to13. The default is13.
Return Values
On success, esp32.adc() returns:
an ADC object, and
the GPIO pin number used by the selected channel.
On failure, the first return value is nil and the second value contains an
error code or diagnostic string.
ADC Object Methods
adc:read()Returns the current sample. If
volt=truewas set, the method returns both the raw value and the converted millivolt value.adc:close()Releases the ADC resources associated with the object.
One-Shot Example
The example below opens ADC unit 1, channel 0, and polls it once per second. The configuration requests both the raw reading and the converted millivolt value.
local adc, pin = esp32.adc(1, 0, {volt = true})
if adc then
trace("ADC is using GPIO pin", pin)
local function pollADC()
local raw, volt = adc:read()
trace(string.format("ADC raw data: %d, volt: %d", raw, volt))
return true
end
ba.timer(pollADC):set(1000)
else
trace("Failed to open ADC", pin)
end
Continuous Mode
Note
Continuous mode is currently experimental.
Continuous mode is designed for applications that need repeated sampling at a much higher rate than typical timer-based polling. The ESP32 samples in the background and Lua receives processed results through a callback.
Because Lua cannot handle a callback for every single hardware sample,
continuous mode uses a buffering and filtering layer. Internally, the ADC may
sample at rates as high as 48 kHz, but the Lua callback is limited to a much
lower rate that is practical for the runtime.
On ESP32 hardware, the effective sampling rate floor is around 20,000 Hz.
Keep that in mind when deciding whether continuous mode is really necessary for
your application.
Function signature:
adc, pinOrErr = esp32.adc(unit, channel, cfg)
Required continuous-mode options
When using continuous mode, set these options in cfg:
callback: Lua function called when a processed sample block is ready.fs: Sampling frequency in Hz. The runtime adjusts values outside the supported range.bl: Block length. This controls how many samples are collected before the callback is invoked. Together withfs, this determines the callback rate.filter: One of"data"or"mean".
Filters
"data"Returns the sampled block as a Lua string containing 16-bit little-endian PCM values.
"mean"Returns a single averaged value representing the current block of samples.
Callback Signatures
When using filter = "data":
function mycallback(data, err)
data: A PCM-encoded Lua string.err: An error string whendataisnil. Occasional"overflow"events can happen under load.
When using filter = "mean":
function mycallback(raw, voltOrErr)
raw: The averaged raw ADC value.voltOrErr: The converted millivolt value whenvolt=truewas enabled. Ifrawisnil, this argument contains the error.
Continuous Example: mean Filter
This example lets the runtime auto-adjust both fs and bl by setting
them to zero. On ESP32 hardware, the values typically settle at 20000 and
200 respectively, which results in a fast but still manageable callback
rate.
local cnt = 0
local function callback(raw, volt)
if cnt % 100 == 0 then
trace(raw, volt)
end
cnt = cnt + 1
end
local adc, pin = esp32.adc(1, 0, {
volt = true,
bl = 0,
callback = callback,
fs = 0,
filter = "mean"
})
Continuous Example: data Filter
This example asks for the highest sample rate and lets the block length be auto-adjusted. The callback prints the size of the returned binary block so you can verify that data is flowing.
local cnt = 0
local function callback(data, err)
if data then
if cnt % 100 == 0 then
trace(#data)
end
cnt = cnt + 1
else
trace(err)
end
end
local adc, pin = esp32.adc(1, 0, {
volt = true,
bl = 0,
callback = callback,
fs = 48000,
filter = "data"
})
Practical Guidance
Use one-shot mode when you only need occasional readings.
Use continuous mode when timing matters more than simplicity.
Prefer the
meanfilter when you want a stable interpreted value.Prefer the
datafilter when you want to process raw sampled blocks in Lua or pass them into another decoding step.