Mostrando entradas con la etiqueta PWM. Mostrar todas las entradas
Mostrando entradas con la etiqueta PWM. Mostrar todas las entradas

viernes, 31 de julio de 2026

Ejemplos Arduino: LEDC (LED Control) en el ESP32

Muy buenas a todos y todas a la vuelta al canal!!

El periférico LEDC está diseñado principalmente para controlar la intensidad de LEDs, aunque también pero también es muy útil para generar señales PWM para otros propósitos tales como controlar motores DC.

El ESP32-C3 cuenta con 6 canales que pueden generar formas de onda independientes, ideales para controlar LEDs o dispositivos que necesiten PWM tan solo cambiando algunos parámetros de la configuración como el temporizador,la confirmación del canal y el cambio de la señal PWM.


¿Cuando se implementó ledc?

La API de LEDC para Arduino-ESP32 está documentada y disponible como parte del núcleo (core) de ESP32 en Arduino.
La versión actual (a partir de la 3.0.0) incluye cambios importantes en la sintaxis, como la sustitución de:


ledcAttachPin() // V < 3.0.0

por


ledcAttach() // Versión > 3.0.0

Para la versión 3.0.0 del core de ESP32 para Arduino, ya no se usa la función ledcSetup() ni la función ledcAttachPin(), sino que se recomienda utilizar la función ledcAttach(pin, freq, resolution)
ESP32 C3 Mini 0.42 Pinout
ESP32 C3 mini 0.42" pinout

Funciones y definiciones relacionadas

1. ledcAttach()

Función:


bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);

La función ledcAttach configura un pin para PWM con la frecuencia (uint32_t freq) y resolución (uint8_t resolution)indicadas.
El canal LEDC se selecciona automáticamente.

Devuelve true si la configuración es exitosa.


2. ledcAttachChannel()

Función:


bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);

Con ledcAttachChannel configuramos un pin para PWM con la frecuencia, resolución y canal específicos.

Devuelve true si la configuración es exitosa.


3. ledcWrite()

Función:


void ledcWrite(uint8_t pin, uint32_t duty);

Establece el ciclo de trabajo (duty cycle) para el pin LEDC.

El valor de duty depende de la resolución: para 10 bits, el rango es de 0 a 1023.


4. ledcRead()

Función:


uint32_t ledcRead(uint8_t pin);

Lee el ciclo de trabajo configurado para el pin.


5. ledcReadFreq()

Función:


uint32_t ledcReadFreq(uint8_t pin);

Con ledcReadFreq lee la frecuencia configurada para el pin que hemos seleccionado.


Relación entre frecuencia y resolución


6. ledcDetach()

Función:


bool ledcDetach(uint8_t pin);

Desvincula el pin del periférico LEDC para usarlo como GPIO digital.


7. ledcChangeFrequency()

Función:


uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution);

Cambia la frecuencia y resolución de un pin LEDC ya configurado.


8. ledcFade()

Función:


bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);

Configura e inicia un desvanecimiento (fade) automático por hardware desde un valor inicial hasta un valor objetivo.

Devuelve true si se inicia correctamente.


9. ledcWriteTone()

Función:


uint32_t ledcWriteTone(uint8_t pin, uint32_t freq);

Genera un tono PWM al 50% en la frecuencia especificada.

10. ledcWriteNote()

Función:


uint32_t ledcWriteNote(uint8_t pin, note_t note, uint8_t octave);

Por último tenemos ledcWriteNote que genera un tono a partir de una nota musical y una octava.


LEDC ¿es mejor o peor que analogWrite?

En ESP32, analogWrite() es una capa de abstracción sobre LEDC.
La principal diferencia es que analogWrite() simplifica el uso, pero limita el control.


Característica analogWrite() LEDC nativo
Resolución Fija a 8 bits (0-255) Configurable (1-14 bits en ESP32-C3)
Frecuencia Fija (~5 kHz) Totalmente configurable
Gestión de canales Automática, puede causar conflictos Manual, 6 canales independientes
Funciones avanzadas No disponible Fade por hardware, interrupciones
Complejidad de código Muy baja Media

Conclusión: analogWrite() es perfecto para prototipos rápidos y proyectos simples.
LEDC es necesario cuando se necesita precisión, frecuencias personalizadas o múltiples canales PWM sin interferencias .


🛠️ Componentes necesarios
ESP32-C3 con OLED de 0.42, Antena Cerámica, Wifi, Bluetooth
ESP32-C3OLED de 0.42" Placa de Desarrollo ESP32-C3 con OLED de 0.42", Antena Cerámica, Wifi, Bluetooth.

Relación entre frecuencia y resolución

En el ESP32-C3, la frecuencia y la resolución están interrelacionadas. A mayor frecuencia, menor resolución máxima disponible, y viceversa.

Ejemplos prácticos:

  • 5 kHz con 13 bits: 8192 niveles de intensidad.
  • 40 MHz con 1 bit: Solo 50% fijo.

Si se intenta configurar una combinación no soportada, la API mostrará un error:


E (196) ledc: requested frequency and duty resolution cannot be achieved, try reducing freq_hz or duty_resolution. div_param=128 


Ejemplos para ESP32-C3 con pantalla OLED 0.42"

La placa "ESP32-C3 OLED 0.42" tiene la pantalla conectada por I2C: en el pin GPIO5 corresponde a SDA y el pin GPIO6 para SCL.


Ejemplo 1: Uso de analogWrite (simplificado)

Primero veamos un ejemplo clásico programa con analogWrite()



#include <Wire.h> 
#include <Adafruit_SSD1306.h>

#define OLED_SDA 5
#define OLED_SCL 6
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define LED_PIN 8

void setup() {
  Wire.begin(OLED_SDA, OLED_SCL);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);

  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  for (int brightness = 0; brightness <= 255; brightness += 5) {
    analogWrite(LED_PIN, brightness);

    display.clearDisplay();
    display.setCursor(30, 30);
    display.print("AnalogWrite: ");
    display.println(brightness);

    int porcentaje = map(brightness, 0, 255, 0, 100);
    display.setCursor(30, 40);
    display.print("Inten.: ");
    display.print(porcentaje);
    display.println("%");

    display.display();
    delay(30);
  }
  for (int brightness = 255; brightness >= 0; brightness -= 5) {
    analogWrite(LED_PIN, brightness);

    display.clearDisplay();
    display.setCursor(30, 30);
    display.print("AnalogWrite: ");
    display.println(brightness);

    int porcentaje = map(brightness, 0, 255, 0, 100);
    display.setCursor(30, 40);
	display.print("Inten.: ");
    display.print(porcentaje);
    display.println("%");

    display.display();
    delay(30);
  }
}


Ejemplo 2: Ledc PWM en LED y mostrar valor en OLED (I2C)

En este ejemplo tendremos la salida del valor Ledc en la pantalla OLED 0.42". El pin 8 del LED builtin el estado de Ledc.

NOTA: el encendido del LED builtin esta invertido, es decir cuando la señal Ledc esta en 0 el LED builtin estará a su máxima intensidad y apagado en su valor más alto.



 

#include <Wire.h> 
#include <Adafruit_SSD1306.h>

#define OLED_SDA 5
#define OLED_SCL 6
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define LED_PIN 8
#define PWM_FREQ 5000
#define PWM_RES 10
#define MAX_DUTY ((1 << PWM_RES) - 1) // 1023 para 10 bits

int duty = 0;
int step = 5;

void setup() {
  Wire.begin(OLED_SDA, OLED_SCL);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);

  ledcAttach(LED_PIN, PWM_FREQ, PWM_RES);
  ledcWrite(LED_PIN, 0);
}

void loop() {
  duty += step;
  if (duty >= MAX_DUTY || duty <= 0) step = -step;

  ledcWrite(LED_PIN, duty);

  display.clearDisplay();
  display.setCursor(30, 30);
  display.print("Duty: ");
  display.setCursor(30, 40);
  display.print(duty);
  display.print(" / ");
  display.println(MAX_DUTY);

  int porcentaje = map(duty, 0, MAX_DUTY, 0, 100);
  display.setCursor(30, 50);
  display.print("Intensidad: ");
  display.print(porcentaje);
  display.println("%");

  display.display();
  delay(20);
}


Ejemplo 3: Ledc fade automático por hardware y mostrar estado


 
#include <Wire.h> 
#include <Adafruit_SSD1306.h>

#define OLED_SDA 5
#define OLED_SCL 6
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define LED_PIN 8
#define PWM_CHANNEL 0
#define PWM_FREQ 5000
#define PWM_RES 10
#define MAX_DUTY ((1 << PWM_RES) - 1)

void setup() {
  Wire.begin(OLED_SDA, OLED_SCL);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);

  ledcAttachChannel(LED_PIN, PWM_FREQ, PWM_RES, PWM_CHANNEL);
 ledcWrite(LED_PIN, 0);

  display.display();
  delay(1000);
}

void loop() {
  display.clearDisplay();
  display.setCursor(30, 30);
  display.println("Fade IN");
  display.display();

  ledcFade(LED_PIN, 0, MAX_DUTY, 2000);
  delay(2500);

  display.clearDisplay();
  display.setCursor(30, 30);
  display.println("Fade OUT");
  display.display();

  ledcFade(LED_PIN, MAX_DUTY, 0, 2000);
  delay(2500);
}


Ejemplo 4: Ledc PWM para ESC de motor brushed

Para controlar un ESC, se necesita una señal PWM de 50 Hz con pulsos entre 1000 µs y 2000 µs.



#define ESC_PIN 8 
#define PWM_FREQ 50 
#define PWM_RES 14 
#define MAX_DUTY ((1 << PWM_RES) - 1)

// Para 14 bits: 16384 = 100% (20 ms)
// 1000 µs = 1 ms → (1/20)16384 = 819
// 2000 µs = 2 ms → (2/20)16384 = 1638

void setup() {
  ledcAttach(ESC_PIN, PWM_FREQ, PWM_RES);
  ledcWrite(ESC_PIN, 819); // Neutro
  delay(3000);
}

void loop() {
  ledcWrite(ESC_PIN, 819); // Neutro
  delay(2000);
  ledcWrite(ESC_PIN, 1638); // Máximo
  delay(2000);
}


También puedes revisar el repositorio supermini_esp32c3_sketches para ejemplos muy variados y completos.


Conclusión sobre LEDC en el ESP32-C3

El periférico LEDC del ESP32-C3 es una herramienta extremadamente versátil y potente para generar señales PWM, muy superior a la función analogWrite() que muchos conocen de Arduino.

Mientras que analogWrite() está limitada a 8 bits de resolución y frecuencias fijas, LEDC ofrece un control total con resoluciones de hasta 14 bits y frecuencias completamente configurables.
Esto hace que Ledc sea ideal para aplicaciones que requieren precisión, como el control de motores DC, servomotores, atenuación de LEDs profesionales o incluso generación de tonos y notas musicales.

Lo bueno:

El ESP32-C3 dispone de 6 canales independientes, lo que permite manejar múltiples dispositivos PWM simultáneamente sin interferencias.
Una de sus características más destacadas es la capacidad de realizar desvanecimientos (fade) automáticos por hardware, lo que descarga al procesador de esta tarea y permite transiciones suaves sin necesidad de interrupciones constantes.


Lo malo:

Sin embargo, hay que tener en cuenta algunas limitaciones importantes. El ESP32-C3 solo funciona en modo "low speed", a diferencia de otros ESP32 que también tienen modo "high speed".
Además, existe una relación de compromiso entre frecuencia y resolución: a mayor frecuencia, menor resolución disponible, y viceversa. Por ejemplo, a 5 kHz se puede alcanzar 13 bits de resolución, pero a 40 MHz apenas se obtiene 1 bit.

Aquí tienes otros enlaces de este blog relacionados con el microcontrolador ESP32 y Arduino:


lunes, 1 de julio de 2024

Raspberry Pi 3: Controla un ventilador PWM por temperatura (tutorial Python)

Muy buenas a todos y todas!!!

Como estamos a Julio y se vienen 'las calores', es hora de refrigerar la Raspberry pi 3. Esta es una Raspberry que adquirí de segunda mano y viene sin ningún tipo de refrigeración y eso no es muy recomendable.

Como le voy a dar varios usos (máquina arcade, probar SO etc) puede ser que tenerla sin un disipador no sea lo mejor.

Ya he encargado unos cuantos disipadores en una web chinesca, pero espero que sea valida para estos pequeños accesorios.

También se me ocurrió ponerle un pequeño ventilador conectado a uno de los puertos GPIO de las Raspberry pi. Como los ventiladores son de 5V DC pero no tienen linea PWM, tenemos que crear un pequeño driver con un transistor que controle el PWM.

Primero tenemos que saber cuales son las temperaturas aproximadas que alcanza el procesador. Investigando un poco he encontrado esta tabla, más o menos representativa:


Temperaturas: Sin disipador Con disipador Disipador activo
Sin carga: ~40ºC ~30ºC 25ºC - 30ºC
Carga media: 50ºC - 60ºC 40ºC - 50ºC 30ºC - 40ºC
Carga intensiva: 60ºC - 80ºC 50ºC - 70ºC 40ºC - 60ºC

Tomando esta tabla como referencia crearemos unos perfiles y por último un perfil personalizado.

También se puede añadir un sensor DHT11 para tener una referencia de la temperatura ambiente, pero eso para otro momento, Por el momento nos vamos a centrar en el PWM;

Programaremos en Python y usaremos las librerías RPi.GPIO para el control de los pines GPIO. La librería time para hacer una pausa antes de mostrar los datos y subprocess para capturar la salida de 'vcgencmd' donde nos mostrará el dato de measure_temp.

Con estos datos y con el perfil que tengamos seleccionado aplicará una velocidad u otra al ventilador.

El programa completo se puede descargar desde Github

En este punto ya tenemos la parte de programación. Ahora le toca el turno a la electrónica.

Como queremos mover un ventilador, aunque pequeño, su carga es mucho más que los 15mA que puede entregar el pin de la Raspberry Pi.

Para solucionar esto usaremos un transistor NPN como el BC548 una resistencia de 10Kohm y un diodo 1N4007.

Usaremos el siguiente diagrama:



Una vez tenemos todo conectado tenemos que habilitar los pines de la Raspberry Pi. Tenemos un diagrama de los pines si en el terminal usamos el comando 'pinout'



Este comando también nos dará información como el modelo, SoC, RAM, puertos USB y algunos parámetros más.

Si necesitas más información sobre la placa podemos usar el comando 'raspinfo'

Ahora solo falta que este programa se ejecute al iniciar la máquina. Esto es muy sencillo solo tenemos que añadir una linea en el 'crontab' ejecutando 'sudo crontab -e' que será la encargada de cargar el programa en el inicio del sistema. Añadiremos la siguiente linea:


@reboot sleep 15; /usr/bin/python3 /home/ruta_del_archivo/pwm_fan_rpi3.py > /dev/null


Lo podemos desglosar el comando de la siguiente manera:

  • @reboot sleep 15; -> Iniciara el script cuando se reinicie la maquina y hayan pasado 15 segundos
  • /usr/bin/python3 -> Ruta donde se encuentra Python3
  • home/.../pwm_fan_rpi3.py -> ruta donde se encuentra el script de Python
  • > /dev/null -> mandamos la salida del programa a null

Tengo que añadir que al intentar ejecutar el programa en el sistema RetroPie que tengo instalado este comando no me ha funcionado correctamente, he tenido que cambiar la linea y poner esta:

  
@reboot sleep 15 && python3 /home/pi/Ruta_del_archivo/pwm_fan_rpi3.py > /home/Ruta_para_log/cron.log 2>&1

Por otro lado al cargar el programa me daba muchos errores de las librerías y la única solución que he encontrado es instalar las librerías con sudo. Haciendo esto todo ha funcionado perfectamente iniciando el ventilador al arrancar el sistema.

Y bueno hasta aquí la parte de programación y teoría. Montaremos los componentes y probaremos que tal va el programa.

Advertencia:

A partir de 80°C, la Raspberry Pi 3 comenzará a reducir su velocidad de reloj (thermal throttling) para protegerse del sobrecalentamiento. Esto puede afectar el rendimiento de la placa.

Superar los 85°C durante un período prolongado puede dañar la Raspberry Pi 3.

Fuente:

the-diy-life.com: Connecting a pwm fan to a Raspberry Pi


Otros articulos sobre la plataforma Raspberry Pi que te pueden interesar:

lunes, 13 de noviembre de 2017

Arduino Ejemplos: Control de tanque RC con L293, DHT11 y NRF2401 (Parte 1)

Muy buenas a todos y todas!!

Esto va sobre ruedas, y nunca mejor dicho, ya tengo la primera versión del mando XboxRC24 transmitter y para sus pruebas he utilizado un vehículo que construí cuando trabajaba con los microcontroladores 16f, que buenos recuerdos, siempre liado con los puertos COM del portátil...

Lo termine (todos sabemos que estos proyectos nunca terminan) y al poco tiempo empecé con Arduino.

Arduino nRF2401
Arduino nRF2401

Hace un tiempo lo actualice a esta plataforma utilizando el HC-06 encargado de la transmisiones de datos, ahora se encargará el nRF2401. Con un mayor alcance, un consumo casi de risa y siendo realmente estable es la mejor solución que he visto hasta ahora en el tema de radio control.


Oruga Cebek V2

La base de este proyecto es completamente artesanal, realizado en aluminio y plástico reciclado.

La estructura de aluminio son unos separadores de los que se utilizan en la obra en cocinas y baños.

La base sobre la que va montada la electrónica son de una caja registradora.

Oruga Cebek V2

Cuenta con una batería Ni-Cd de 9,6V y 900mAh, como ya he comentado y seguramente vuelva a comentar, el tema de las baterías tengo que estudiarlo más a fondo ya que es algo que a día de hoy esta muy avanzado. Es un diseño sencillo pero muy robusto, ideal para cacharrear con el.

Tenéis mas fotos de su construcción original en la entrada que escribí en su momento: Oruga cebek C-6083 V2

A partir de esas todo lo demás pertenece a la ultima actualización.

La electrónica:

Esquema de conexiones pata Tanque XboxRC24
Esquema de conexiones para Tanque XboxRC24

sábado, 28 de octubre de 2017

Arduino Ejemplos: Lectura analógica y control de LED con PWM

Logo arduino ADC PWM Muy buenas a todos!!!

Muchos de los sensores que utilizamos para Arduino son sensores analógicos como potenciómetros, fotoresistencias, etc.


Para esto Arduino utiliza unos pines específicos, que van desde el A0 al A5 en Arduino UNO y del A0 al A7 en el Arduino Nano.


El ADC (Convertidor Analógico-Digital):



Para poder leer correctamente los valores se utilizan 3 pines por sensor, pin de lectura, +5V y GND.

Por si mismo Arduino es incapaz de realizar lecturas analógicas y para esta función utiliza un conversor analógico digital ADC (Analog Digital Converter) que convierte la entrada analógica en una digital soportada por la lógica TTL (0 - 5V) de Arduino.

Para la lectura de las entradas analógicas Arduino UNO tiene una resolución de 10 bits, o lo que es lo mismo puede leer valores desde 0 hasta 1023. En cada paso aumentamos la lectura en 5 / 1024 = 0.00488V.