Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Componentes y suministros

Componente Cantidad
Arduino UNO 1
Micro-servo SG90 2

Sobre el proyecto

Este proyecto contiene código genérico y eficiente para leer un receptor RC (o cualquier señal PWM) en cualquier pin de entrada Arduino, y aplicar fail-safe ante pérdida de señal del transmisor.

El video muestra un Arduino Uno actuando como mezclador de servos usando el código PWMread_RCfailsafe.ino (disponible al final).

Las funciones en PWMread_RCfailsafe.ino gestionan los registros de interrupción automáticamente y se mueven fácilmente entre proyectos con distintos pines.

En el ejemplo del video:

Contenido

¿Cómo se controlan servos y variadores por PWM?

Se asume conocimiento de señales PWM para servos/variadores. Buen video explicativo.

Conocimientos previos necesarios:

Ejemplos de uso de Arduino en modelos RC/robots

Solo tu imaginación te limita:

Resumen del código: Decodificar PWM de receptor RC con fail-safe

El código mide señales PWM usando interrupciones por cambio de pin (PCI). Las funciones automatizan la configuración de interrupciones y extracción de datos de cualquier pin digital/analógico (excepto A6, A7) en Arduino Uno, Nano o Pro Mini. Fácil para principiantes.

Objetivo: crear un "módulo" genérico de receptor RC con fail-safe portable entre proyectos. El código de ejemplo en "cómo usar" es un medio para ese fin.

Nota: No funciona con SoftwareSerial u otras librerías que usen PCI.

Funcionamiento interno (para interesados):

Video de Joop Brokking explica este método para conectar receptor RC a Arduino (primeros 8 min).

Todo este detalle lo gestiona PWMread_RCfailsafe.ino (descarga al final).

Info útil sobre manipulación de puertos: tronixstuff.com

Además del manejo de PCI, función dedicada RC_decode() convierte ancho de pulso (1000-2000µs) en señal de control ±100%. Fail-safe verifica señal válida del transmisor (tolerancias 10-330Hz, 500-2500µs). Si se pierde señal, RC_decode() devuelve valor fail-safe predefinido.

Valores de calibración por transmisor y posiciones fail-safe por canal se configuran en PWMread_RCfailsafe.ino.

Cómo usar PWMread_RCfailsafe.ino

Paso 1: Hardware ejemplo con Arduino Uno

Si sigues el ejemplo con Arduino Uno, conecta tu receptor así (si usas tu propio proyecto, ve al Paso 2):

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Paso 2: Copiar PWMread_RCfailsafe.ino a carpeta del sketch

Sketch ejemplo RC_Read_Example incluido para descargar. Úsalo como sketch principal.

Copia y pega el archivo PWMread_RCfailsafe.ino en la carpeta de tu sketch principal. Al abrir en IDE, aparecerá segunda pestaña con el código.

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Paso 3: Especificar pines de entrada

Abre/reaabre sketch principal en IDE. Pestaña PWMread_RCfailsafe, ve a "USER DEFINED VARIABLES" e introduce pines en array pwmPIN[].

Nota: Cualquier número de pines, cualquier orden. Más entradas = más tiempo en rutina de interrupción. Pines A6 y A7 son solo analógicos, no válidos. Arduino MEGA no soportado actualmente (fácil de añadir si hay demanda).

El primer elemento de pwmPIN[] es canal 1, segundo canal 2… Si usas todos los canales del receptor, asegura que canal 1 del receptor corresponda a canal 1 en pwmPIN[].

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Paso 4: Revisar funciones disponibles

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Paso 5: Imprimir datos ancho de pulso a serial

Sube RC_Read_Example, enciende transmisor e imprime datos PWM en bruto a serial.

Usa RC_avail() para comprobar nuevos datos en todos los canales, luego print_RCpwm() para enviar anchos de pulso a serial.

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Paso 6: Calibrar transmisor

Usa datos PWM en bruto de print_RCpwm() para modificar manualmente arrays RC_min[], RC_mid[], RC_max[] y calibrar cada canal a rango ±100%.

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Paso 7: Imprimir canales calibrados a serial

Comenta print_RCpwm(). Usa RC_decode(canal) para calibrar cada canal a rango ±1. Imprime cada canal calibrado con decimal2percentage() seguido de Serial.println("").

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Paso 8: Configurar fail-safe

Ajusta posiciones fail-safe en array RC_failsafe[] por canal (rango ±1). Enciende/apaga transmisor para verificar fail-safe.

La entrada RC ya puede usarse en tu sketch.

Nota: Puede que necesites desactivar fail-safe en el receptor, sino Arduino no detectará pérdida de señal.

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Mostrar tasa de frames y frecuencia del receptor

Período y frecuencia de repetición del receptor se pueden imprimir a serial. Comprueba nuevos datos en canal elegido con PWM_read(número_canal), luego usa PWM_period() y PWM_freq() para extraer datos. Código ejemplo en RC_FrameRate.ino.

Mejor usar canal 1 (primer pulso de cada frame). PWM_read() usa mismos flags que RC_decode(CH); llama a PWM_read() primero.

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

El período del receptor indica cuánto tiempo tiene el código antes del siguiente dato. Si RC_avail() no detecta nuevos datos RC tras tiempo predeterminado (ej. 21ms), ejecuta RC_decode() para activar fail-safe y/o continuar programa (ej. controlador PID) a frecuencia estable.

Logrado en RC_Read_Example.ino con:

now = millis();
if(RC_avail() || now - rc_update > 21) {
    rc_update = now;
    // actualizar datos RC con RC_decode()
    // ejecutar controlador PID
    // aplicar mezcla servos
    // posicionar servos
}

Ejemplo mezcla de servos

Incluido RC_ServoMixer_Example.ino mezclando 2 canales receptores (canales 2 y 3: elevador y alerón). Muestra método para dirección, tasa y sub-trim de servos. Librería Servo controla servos por pines 9 y 10.

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Mezcla: sumar/restar canales y limitar salida a -1..+1.

mix1 = canal2 - canal3 (elev - ail)
mix2 = canal2 + canal3 (elev + ail)

Antes de posicionar servos, convierte señal ±100% (±1) a ancho de pulso equivalente en µs. En ejemplo uso función calc_uS() al final del sketch.

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Dirección, tasa y sub-trim por servo calculan ancho de pulso apropiado.

Pulso neutro estándar: 1500µs, rango normal ±500µs → mínimo 1000µs (-100%), máximo 2000µs (+100%).

pulso, µs = 1500 + (pos_servo_% * tasas * dirección + sub_trim) * 500

Dirección, tasa y sub-trim pueden ser estáticos o modificados dinámicamente por el sketch según otra entrada del receptor u otro medio.

Razonamiento del enfoque

Se puede leer receptor RC con pulseIn(PIN, HIGH), pero pulseIn() bloquea loop() esperando inicio y fin de pulso, desperdiciando tiempo de procesamiento. Con más de una entrada, también se pierden datos.

Para velocidad, mejor usar interrupción por cambio de pin (PCI) del Arduino con manipulación directa de puertos, permitiendo que loop() corra con mínima demora. Pero es más complejo y lento que simplemente llamar pulseIn().

Por eso quise lo mejor de ambos mundos: código genérico portable entre proyectos. Solo copiar/pegar archivo .ino (funciones y rutinas ISR) a carpeta sketch principal, especificar pines de entrada, y usar funciones en el sketch.

Limitaciones

Función micros()

Temporización en microsegundos usa micros(), que cuenta en pasos de 4µs. Precisión de 4µs al medir pulsos 1000-2000µs; más que adecuado en práctica.

Si se desea, se puede mejorar a 0.5µs usando interrupciones de timer: instructables.com

Eficiencia de PWMread_RCfailsafe.ino

Leyendo receptor 6-9 canales: 1.4-2.0% tiempo procesador en rutinas PCI, aceptable.

Tiempos ISR según canales de entrada:

Canales Tiempo ISR
1< 8µs
2< 12µs
3< 16µs
4< 20µs
5< 20µs
6< 24µs

Más canales = ISR más larga (bucle FOR recorre cada canal cada llamada ISR). Esta ineficiencia extra es insignificante midiendo señales RC baja frecuencia (50Hz).

Además, ~4µs para entrar/salir ISR. Por pulso, ISR corre 2 veces (flanco subida y bajada).

Tiempo medir 1 pulso con 6 entradas RC: 2 × (4µs entrar + 24µs ejecutar) = 56µs (ancho mínimo medible).

Leer 6 canales: 336µs (6 × 56µs).

Período repetición receptor 20ms → interrupción activa 1.68% (0.000336/0.02). Muy superior a pulseIn() que bloquearía hasta 20ms por pin.

FYI: con 2 entradas RC, ISR activo solo 0.16% tiempo.

Frecuencia práctica máxima (Hz)

Para otros usos, frecuencia práctica máx ~2.5kHz (100 pasos resolución micros() ±0.025kHz).

Con 1 pin a esa frecuencia, 3% tiempo en interrupción → duty mínimo medible 0.03 = pulso mínimo 12µs.

Para frecuencias mayores, reescribe ISR para tu aplicación.

Código

PWMread_RCfailsafe Arduino

Archivo .ino con funciones y rutinas ISR para decodificar receptor RC y aplicar fail-safe si se pierde señal transmisor. Copia en misma carpeta que sketch principal (aparecerá como segunda pestaña en IDE). Sigue instrucciones en el archivo.

/* Kelvin Nelson 24/07/2019
 * Decodificación PWM de Receptor RC con fail-safe
 * Funciones fáciles para medir señales cuadradas en pines Arduino Uno/Pro Mini/Nano (excluyendo A6,A7).
 * Pensado para receptores RC, pero usable en otras apps medición PWM como reemplazo directo de pulseIn(PIN, HIGH).
 * (No testado >1kHz ni en Arduino Mega)
 * Convierte pulso RC (1000-2000µs) en salida -+100% (-+1.0) para uso en sketch.
 * Calibración conversión + fail-safe por canal (tolerancias fail-safe 10-330Hz y 500-2500µs).
 * Extrae datos brutos por pin: tiempo pulso, ancho, longitud frame, duty, frecuencia.
 * Setup rápido. Organización:
 * - Resumen código
 * - Lista funciones
 * - Cómo usar, sketches ejemplo
 * - Variables usuario → pines entrada, calibración transmisor, fail-safe
 * - Variables globales y funciones
 */
// LISTA FUNCIONES:
// void setup_pwmRead()           // inicializa medición PWM con PCI
// boolean RC_avail()             // HIGH cuando nuevos datos RC disponibles
// float RC_decode(canal)         // decodifica canal RC a ±100% con fail-safe
// void print_RCpwm()             // imprime datos brutos RC a serial (calibración)
// boolean PWM_read(canal)        // HIGH cuando nuevo pulso detectado en canal
// unsigned long PWM_time()       // tiempo inicio pulso
// float PWM()                    // ancho pulso
// float PWM_period()             // período entre pulsos
// float PWM_freq()               // frecuencia
// float PWM_duty()               // duty cycle
// NOTA: PWM_read(CH) y RC_decode(CH) usan mismos flags → datos perdidos si ambos usados mismo canal simultáneamente.
// SUGERENCIA: si usas PWM_read(CH) para frame rate de canal RC, llámalo antes que RC_decode(CH).
// CÓMO USAR:
// En "USER DEFINED VARIABLES":
// Paso 1: pines entrada en array pwmPIN[] = {}.
 // - Cualquier número pines en pwmPIN[] (disponibles 0-13, A0-A5)
 // - Orden no importa, ej: pwmPIN[] = {A0,5,6,10,8} (5 canales) o {A0,5} (2 canales)
 // - Primer elemento = pin "canal 1", segundo = "canal 2"...
 // - Pines conectados a receptor RC al inicio del array. Ej: primeros 2 canales RC, 3º pin sensor ultrasónico.
// Paso 2: si receptor RC conectado a todas entradas, RC_inputs = 0; si no, nº canales conectados ej: RC_inputs = 2.
// Paso 3: calibra transmisor subiendo sketch simple con este .ino en carpeta, imprime PWM bruto a serial (o copia funciones necesarias al sketch).
 //   Modifica manualmente RC_min[], RC_mid[], RC_max[] según tu transmisor (usa tasas máximas para mejor resolución).
 //   Sketch ejemplo imprimir PWM RC a serial:
/* void setup() { setup_pwmRead(); Serial.begin(9600); }
void loop() { if(RC_avail()) print_RCpwm(); } */
// Paso 4: elige posición fail-safe por canal (-1.0 a +1.0) en array RC_failsafe[] = {}
 // Nota: para que Arduino responda a pérdida señal transmisor, puede que necesites desactivar fail-safe en receptor.
// Sketch ejemplo comprobar fail-safe e imprimir canales calibrados:
/* unsigned long now, rc_update;
const int channels = 6;
float RC_in[channels];
void setup() { setup_pwmRead(); Serial.begin(9600); }
void loop() {
  now = millis();
  if(RC_avail() || now - rc_update > 25){
    rc_update = now;
    //print_RCpwm();
    for(int i=0;i=0 && pwmPIN[i]<=7) pwmPIN_port[i]=2; // PCINT2_vect (PORTD)
    else if(pwmPIN[i]>=8 && pwmPIN[i]<=13) pwmPIN_port[i]=0; // PCINT0_vect (PORTB)
    // convierte nº pin (ej. pin 11 o A0) a posición en registro puerto
    if(pwmPIN[i]==0 || pwmPIN[i]==A0 || pwmPIN[i]==8) pwmPIN_reg[i]=0b00000001;
    else if(pwmPIN[i]==1 || pwmPIN[i]==A1 || pwmPIN[i]==9) pwmPIN_reg[i]=0b00000010;
    else if(pwmPIN[i]==2 || pwmPIN[i]==A2 || pwmPIN[i]==10) pwmPIN_reg[i]=0b00000100;
    else if(pwmPIN[i]==3 || pwmPIN[i]==A3 || pwmPIN[i]==11) pwmPIN_reg[i]=0b00001000;
    else if(pwmPIN[i]==4 || pwmPIN[i]==A4 || pwmPIN[i]==12) pwmPIN_reg[i]=0b00010000;
    else if(pwmPIN[i]==5 || pwmPIN[i]==A5 || pwmPIN[i]==13) pwmPIN_reg[i]=0b00100000;
    else if(pwmPIN[i]==6) pwmPIN_reg[i]=0b01000000;
    else if(pwmPIN[i]==7) pwmPIN_reg[i]=0b10000000;
  }
}
// SETUP INTERRUPCIONES CAMBIO PIN
void setup_pwmRead(){
  for(int i=0;inum_ch) RC_inputs=num_ch;
}
// ISR PINES D8-D13 (PORTB)
ISR(PCINT0_vect){
  pciTime=micros();
  for(int i=0;iRC_inputs) return 0;
  int i=CH-1;
  int Min=(CH<=size_RC_min)?RC_min[CH-1]:1000;
  int Mid=(CH<=size_RC_mid)?RC_mid[CH-1]:1500;
  int Max=(CH<=size_RC_max)?RC_max[CH-1]:2000;
  float CH_output;
  if(FAILSAFE(CH)==HIGH){
    CH_output=(CH>size_RC_failsafe)?0:RC_failsafe[i];
  } else {
    CH_output=calibrate(PW[i],Min,Mid,Max);
  }
  return CH_output; // rango -1..+1 (0=neutro, 1=desplazamiento máx una dirección, -1=otra)
}
float calibrate(float Rx,int Min,int Mid,int Max){
  float calibrated;
  if(Rx>=Mid) calibrated=map(Rx,Mid,Max,0,1000);
  else if(Rx==0) calibrated=0;
  else calibrated=map(Rx,Min,Mid,-1000,0);
  return calibrated*0.001;
}
boolean FAILSAFE(int CH){
  int i=CH-1;
  boolean failsafe_flag=LOW;
  if(pwmFlag[i]==1){
    pwmFlag[i]=0;
    if(pwmPeriod[i]>100000) failsafe_flag=HIGH; // <10Hz
    else if(pwmPeriod[i]<3000) failsafe_flag=HIGH; // >330Hz
    if(PW[i]<500 || PW[i]>2500) failsafe_flag=HIGH;
  } else if(micros()-pwmTimer[i]>100000) failsafe_flag=HIGH; // sin pulso >100ms (10Hz)
  return failsafe_flag;
}
void print_RCpwm(){
  for(int i=0;i=0) Serial.print(" ");
  if(abs(pc)<100) Serial.print(" ");
  if(abs(pc)<10) Serial.print(" ");
  Serial.print(" ");Serial.print(pc);Serial.print("% ");
}
/* FUNCIONES PWM GENÉRICAS */
unsigned long pin_time;
float pin_pwm;
float pin_period;
boolean PWM_read(int CH){
  if(CH<1 && CH>num_ch) return false;
  int i=CH-1;
  boolean avail=pwmFlag[i];
  if(avail==HIGH){
    pwmFlag[i]=LOW;
    noInterrupts();
    pin_time=pwmTimer[i];
    pin_pwm=PW[i];
    pin_period=pwmPeriod[i];
    interrupts();
  }
  return avail;
}
unsigned long PWM_time(){return pin_time;}
float PWM_period(){return pin_period;}
float PWM(){return pin_pwm;}
float PWM_freq(){return 1000000/pin_period;}
float PWM_duty(){return pin_pwm/pin_period;}
RC_Read_Example Arduino

Sketch ejemplo para mostrar datos brutos, calibrar receptor RC y configurar fail-safe. Requiere PWMread_RCfailsafe.ino en misma carpeta.

unsigned long now, rc_update;
const int channels = 6;
float RC_in[channels];
void setup() { setup_pwmRead(); Serial.begin(9600); }
void loop() {
  now = millis();
  if(RC_avail() || now - rc_update > 25){
    rc_update = now;
    print_RCpwm();
    for(int i=0;i
RC_FrameRate Arduino

Sketch que imprime tasa de frames y frecuencia receptor RC. Requiere PWMread_RCfailsafe.ino.

void setup() { setup_pwmRead(); Serial.begin(9600); }
void loop() {
  if(PWM_read(1)){
    Serial.print(PWM_period(),0);Serial.print("uS ");
    Serial.print(PWM_freq());Serial.println("Hz");
  }
}
RC_ServoMixer_Example Arduino

Ejemplo mezcla servos. 2 canales de receptor 6 canales mezclados y enviados a 2 servos con librería Servo. Requiere PWMread_RCfailsafe.ino.

#include 
Servo servo1; Servo servo2;
const int servo1_pin=9; const int servo2_pin=10;
boolean servo_dir[]={0,1};
float servo_rates[]={1,0.5};
float servo_subtrim[]={0.0,0.0};
boolean servo_mix_on=true;
unsigned long now, rc_update;
const int channels=6;
float RC_in[channels];
void setup(){
  servo1.attach(servo1_pin,500,2500);
  servo2.attach(servo2_pin,500,2500);
  setup_pwmRead(); Serial.begin(9600);
}
void loop(){
  now=millis();
  if(RC_avail() || now-rc_update>25){
    rc_update=now;
    print_RCpwm();
    for(int i=0;i

Esquemáticos

Receptor RC alimentado por 5V/GND pines ICSP, 6 salidas señal a pines 2-7.
Micro servo 1: 5V/GND, señal pin 9.
Micro servo 2: 3.3V/GND, señal pin 10.

Lector PWM Eficiente para Arduino y Decodificador Receptor RC con Fail-Safe Integrado

Proceso de manufactura

  1. Crea Animaciones y Juegos en LCD 16×2 con Arduino UNO
  2. Construye un Voltímetro Inteligente con Arduino y Smartphone – Proyecto DIY Fácil
  3. Registrador de Temperatura y Humedad con Arduino, DHT11 y Ethernet Shield
  4. Leer Temperatura y Humedad con DHT11 en Blynk – Tutorial Paso a Paso con Arduino
  5. Control Arduino UNO desde Python 3 – Envío de Comandos y Demo LED
  6. Autómatas Celulares en Tiempo Real con Arduino y Pantalla OLED 128x64
  7. Construye una Radio FM Compacta con Arduino Nano y RDA5807M
  8. Construye un Receptor FM Potente con Arduino y Módulo TEA5767
  9. Sistema de control de asistencia RFID en tiempo real con Arduino y Google Sheets
  10. Circuito de entrada analógica aislada para Arduino
  11. Dominando Entrada y Salida en programación C