adc-lectura-analogica-nanoframework

ADC en nanoFramework: leer sensores analógicos

  • 5 min

Un ADC es un conversor que transforma una tensión analógica en un valor numérico que nuestro programa puede usar.

Vivimos en un mundo analógico. La temperatura no cambia de 20ºC a 21ºC de golpe; pasa por infinitos valores intermedios. La luz del sol varía suavemente, y el volumen de tu voz es una onda continua.

Los microcontroladores, sin embargo, son seres digitales. Solo entienden ceros y unos. Para salvar esta brecha, necesitamos un traductor: el ADC (Analog-to-Digital Converter).

En esta entrada aprenderemos a usar la librería System.Device.Adc para leer voltajes variables, permitiéndonos usar potenciómetros, joysticks, sensores de luz (LDR) y mucho más.

Para este tutorial necesitas instalar el paquete NuGet: nanoFramework.System.Device.Adc

¿Cómo funciona un ADC?

El ADC toma un voltaje de entrada (por ejemplo, entre 0V y 3.3V) y lo convierte en un número entero.

La calidad de esta conversión depende de la Resolución, medida en bits.

  • En Arduino Uno (10-bit): El rango es de 0 a 1023.
  • En un ADC de 12 bits: el rango nominal es de 0 a 4095.

Un ADC de 12 bits ofrece cuatro veces más niveles de cuantificación que uno de 10 bits. La precisión real también depende del ruido, la referencia y la linealidad del conversor.

  • 0V -> Valor 0
  • 1.65V -> Valor ~2048
  • 3.3V -> Valor 4095

La clase AdcController

Siguiendo la filosofía de nanoFramework, todo empieza por un controlador. El flujo es idéntico al de GPIO o PWM:

  1. Instanciamos el AdcController.
  2. Abrimos un canal (AdcChannel).
  3. Leemos el valor.

Conexión del potenciómetro

Para nuestro primer ejemplo, vamos a leer un Potenciómetro. Conecta el potenciómetro así:

  • Pata 1: GND
  • Pata 3: 3.3V
  • Pata Central (Wiper): GPIO 34 (En ESP32, los pines 34, 35, 36 y 39 son excelentes para analógico porque son solo de entrada).

Leer el valor crudo

Vamos a escribir un programa sencillo que muestre el valor numérico en la consola.

using System;
using System.Device.Adc;
using System.Threading;
using System.Diagnostics;

namespace AdcEjemplo
{
    public class Program
    {
        public static void Main()
        {
            // 1. Instanciamos el controlador
            AdcController adc = new AdcController();

            // 2. Abrimos el canal. 
            // OJO: En ESP32 el mapeo de Canal a GPIO no siempre es directo.
            // Generalmente, para GPIO 34 el canal es 6 (ADC1_CHANNEL_6).
            // Sin embargo, nanoFramework suele abstraer esto y permitir usar el número de pin
            // dependiendo de la implementación de tu placa. 
            // Si te da error, consulta el esquema de tu placa "ADC Channel Mapping".
            
            // Probamos abriendo el canal asociado al pin 34.
            AdcChannel pot = adc.OpenChannel(34); 

            Debug.WriteLine($"Resolución máxima: {adc.ResolutionInBits} bits");
            Debug.WriteLine($"Valor máximo posible: {adc.MaxValue}");

            while (true)
            {
                // 3. Leemos el valor (0 a 4095)
                int valorRaw = pot.ReadValue();
                
                // Calculamos el porcentaje
                double porcentaje = (double)valorRaw / adc.MaxValue * 100;

                Debug.WriteLine($"Raw: {valorRaw}  -  {porcentaje:F1}%");

                Thread.Sleep(500);
            }
        }
    }
}
Copied!

Nota sobre ESP32 y Canales: Si usas un ESP32, asegúrate de no usar el ADC2 (GPIOs 0, 2, 4, 12-15, 25-27) si estás usando Wi-Fi. El Wi-Fi interfiere con el ADC2. Usa siempre pines del ADC1 (GPIOs 32-39) para lecturas estables.

Convertir la lectura en tensión

Tener un número “3200” no nos dice mucho. Queremos saber el voltaje real. Para eso aplicamos una regla de tres simple, teniendo en cuenta el Voltaje de Referencia (normalmente 3.3V).

Vamos a crear una clase o método de utilidad para esto, ya que es una práctica muy común.

// ... dentro del bucle
int valorRaw = pot.ReadValue();

// Convertir a Voltaje
// OJO: Los ESP32 no son perfectamente lineales y la referencia puede variar.
// 3.3V es el ideal teórico.
double voltaje = (valorRaw / (double)adc.MaxValue) * 3.3;

Debug.WriteLine($"Voltaje: {voltaje:F2} V");
Copied!

Ejemplo práctico: sensor de luz LDR

El potenciómetro está bien para jugar, pero vamos a leer un sensor real. Un LDR (Fotorresistencia) cambia su resistencia según la luz.

Como el ADC solo lee voltaje (no resistencia), necesitamos crear un Divisor de Tensión.

  1. Conecta una resistencia de 10kΩ desde 3.3V al pin del ADC.
  2. Conecta el LDR desde el pin del ADC a GND.

Ahora, cuando haya mucha luz, la resistencia del LDR bajará, tirando el voltaje hacia 0V. Cuando esté oscuro, la resistencia subirá, y leeremos cerca de 3.3V.

// Lógica para interruptor crepuscular
int lecturaLuz = ldrChannel.ReadValue();

// Umbral empírico (ajustar según tu habitación)
if (lecturaLuz > 3000) 
{
    Debug.WriteLine("Está oscureciendo. Encender luces.");
    // Aquí podrías activar un relé o un LED PWM
}
Copied!

El problema de la linealidad del ESP32

Si eres un usuario avanzado, notarás algo raro en el ESP32: cerca de 0V y cerca de 3.3V, el sensor “miente”. El ADC del ESP32 no es famoso por su calidad; tiene una curva de respuesta no lineal y mucho ruido.

Para aplicaciones profesionales de precisión con ESP32, solemos recomendar:

  1. Hacer un multisampling: Leer 10 veces seguidas y hacer la media para eliminar ruido.
  2. Usar ADCs externos por I2C (como el ADS1115) si necesitas precisión real.

Para saber si es de día o de noche, o leer un potenciómetro, el ADC interno es más que suficiente.

Con el ADC, nuestro microcontrolador ya tiene “sentidos”. Puede “ver” la luz, “sentir” la temperatura (termistores) o saber la posición de un brazo robótico.