0% encontró este documento útil (0 votos)
23 vistas

Módulos Arduino CIES

Notas de sensores y módulos Arduino
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
23 vistas

Módulos Arduino CIES

Notas de sensores y módulos Arduino
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 42

NOTAS

Módulos_Arduino_CIES

Ing. Mauricio Hernández


Contenido

Ir al índice

Índice general

Índice de figuras iii


Índice de tablas iii
1. Introducción 1
2. Módulos disponibles 1
3. Sensor de medición inercial MPU6050 2
3.1. Calibración ········································································································ 4
3.2. Valores de inclinación del sensor ·········································································· 11
3.3. Valores de rotación con el giroscopio ···································································· 12
3.3.1. Código en Arduino ······················································································ 12
3.4. Sistema de control de inclinación (posición angular) ················································· 16
3.4.1. Sistema implementado y resultados ······························································· 19

4. Sensor de distancia por ultrasonido AJ-SR04M 20


5. Sensor de distancia por ultrasonido HC-SR04 y similares 21
6. Temporizador digital programable HCW-M421 23
7. Otros módulos y dispositivos 24
7.1. Módulos de relés ······························································································· 24
7.2. Servo motores ·································································································· 25
7.3. Motor de paso ·································································································· 26
7.3.1. Motores de paso unipolares ·········································································· 28
7.3.2. Controlando motores unipolares ···································································· 30

8. Anexos 32
8.1. Preparación del software Arduino ········································································· 32
8.1.1. Windows ··································································································· 32
8.1.2. Linux ········································································································ 33
8.1.2.1. Error en puerto serie ············································································· 34

i
Contenido

8.2. Pinout Arduino ·································································································· 34

ii
Contenido

Índice de figuras
Figura 1: Conexión del sensor MPU a la placa controladora ······························································ 4
Figura 2: Vector de aceleración de gravedad y cálculo de la inclinación del sensor, los ejes x, y y z son fijos
respecto al propio sensor. ········································································································· 11
Figura 3: Un mismo modo de rotación de un objeto entorno a su eje z, con dos inclinaciones diferentes del
mismo respecto a tierra. ··········································································································· 12
Figura 4: Salida del monitor al rotar el sensor MPU6050 en su eje X unos 90 grados ···························· 15
Figura 5: Salida del monitor al rotar el sensor MPU6050 en su eje Y unos 90 grados ···························· 16
Figura 6: Modo 1 de funcionamiento del sensor AJ-SR04M, activación por disparo y medida dada como
función de la duración del pulso en señal “echo” ············································································ 20
Figura 7: Modo 4 de funcionamiento del sensor AJ-SR04M, activación por señal en trigger y medida dada
como valor serial ····················································································································· 21
Figura 8: Temporizador digital HCW-M421 ···················································································· 24
Figura 9: Módulo de 8 relés electromecánicos del Instituto FIIIDT ······················································ 25
Figura 10: Modulo de 4 relés de estado sólido, se disparan con nivel bajo en la entrada ························ 25
Figura 11: Diagrama del bobinado simplificado del motor de paso unipolar de 6 cables, las flechas
representan 3 pasos de giro de la tabla 7.1 ·················································································· 29
Figura 12: Fecha y versión del controlador del chip CH340G y similares (Windows) ······························ 32
Figura 13: Opciones del menú herramientas en Arduino IDE 2.3.2 (Windows) ······································ 33
Figura 14: Mapa de pines de Arduino Uno ···················································································· 35

Índice de tablas
Tabla 2.1: Módulos sensores disponibles para Arduino o similares – Octubre 2024 ································· 1
Tabla 3.1: Rangos por defecto del MPU6050 por cada eje ································································· 2
Tabla 3.2: Rangos máximos del MPU6050 por cada eje ···································································· 2
Tabla 7.1: Tabla de pasos en modo half-step de motor de paso unipolar ············································· 29

iii
Contenido

iv
2. Módulos disponibles

1. Introducción
En el presente documento se pretende exponer información referente a módulos compatibles
con Arduino disponibles en CIES – FIIIDT para investigación y desarrollo de proyectos, tanto
internos como externos. Esto puede funcionar como un método de recopilación de datos sobre los
dispositivos disponibles en el instituto.

2. Módulos disponibles
En el laboratorio 235 se tiene un set de componentes y módulos para Arduino de uso general,
los cuales se listan en la tabla siguiente:

Tabla 2.1: Módulos sensores disponibles para Arduino o similares – Octubre 2024

Concepto Código/Modelo Cantidad Notas

Brújula digital o
magnetómetro soldado en
1 placa HMC5883L 2 Interfaz I2C

Sensor de humedad y
temperatura RH/T soldado en
2 placa HTU21/SHT21 Interfaz I2C

Sensor de medición inercial,


3 acelerómetro-giroscopio ITG/MPU (MPU6050) Interfaz I2C

Conversor analógico digital, 4


entradas 0-VDD. VDD max:
4 5V ADS1115

Detección de 20 cm a
Sensor de distancia por 800 cm. Alimentación
5 ultrasonido AJ-SR04M AJ-SR04M 1 3 a 5,5V

Compatible con
diversos perfiles y
protocolos de la pila
Módulo receptor de audio HW de protocolos de
6 770 HW-770 V0.2 2 Bluetooth

Unidad de medición inercial,


acelerómetro-giroscopio,
7 magnetómetro MPU-9250/6500/9255

Ir al índice 1
Tema 3: Sensor de medición inercial MPU6050

Para el sensor MPU6050, se empezará probando la librería MPU6050 v1.4.1 (de Electronic
Cats), descargable desde el Arduino IDE. Esta librería está basada en la librería jrowberg/i2cdevlib
(visite i2cdevlib/Arduino/MPU6050 at master · jrowberg/i2cdevlib · GitHub para detalles).

Continúa...

3. Sensor de medición inercial MPU6050


Los rangos por defecto del sensor se muestran en la tabla siguiente:

Tabla 3.1: Rangos por defecto del MPU6050 por cada eje

Variable Valor mínimo Valor central Valor máximo

Lectura del MPU -32768 0 +32767

Aceleración lineal -2g m/s2 0 m/s2 2g m/s2

Velocidad angular -250° /s 0° /s 250° /s

Tabla 3.2: Rangos máximos del MPU6050 por cada eje

Variable Valor mínimo Valor central Valor máximo

Lectura del MPU -32768 0 +32767

Aceleración lineal -16g m/s2 0 m/s2 16g m/s2

Velocidad angular -2000° /s 0° /s 2000° /s

El rango de aceleración por defecto está comprendido entre -2g y 2g, siendo g la gravedad de
la tierra cerca del nivel del mar. El MPU permite trabajar hasta con 16g m/s2 para el acelerómetro y
2000 grados /s para el giroscopio por cada eje.

A continuación se muestra un ejemplo de código en el cual se usa la librería mencionada para


manejar el sensor MPU1:

// Librerias I2C para controlar el mpu6050


// la libreria MPU6050.h necesita I2Cdev.h, I2Cdev.h necesita Wire.h
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

// La dirección del MPU6050 puede ser 0x68 o 0x69, dependiendo

1 Referencias: Tutorial MPU6050, Acelerómetro y Giroscopio, https://howtomechatronics.com/tutorials/arduino/arduino-and-mpu6050-accelerometer-


and-gyroscope-tutorial/, Calibrating & Optimising the MPU6050 – chillibasket

2 Ir al índice
3. Sensor de medición inercial MPU6050
// del estado de pin AD0. Si no se especifica, 0x68 estará implícito (voltaje bajo por
pull down interno)
MPU6050 sensor; //objeto de clase

// MPU6050 sensor(0x69); // el constructor puede recibir la direccción I2C 0x69 o 0x68


explicitamente

//las mediciones obtenidas del sensor directamente son: aceleración lineal (por eje) y
velocidad angular (por eje)

// Valores RAW (sin procesar) del acelerometro y giroscopio en los ejes x,y,z
int ax, ay, az; //
int gx, gy, gz;

void setup() {
Serial.begin(9600); //Iniciando puerto serial, altas velocidades pueden impedir una
transmisión correcta
Wire.begin(); //Iniciando I2C
sensor.initialize(); //Iniciando el sensor

if (sensor.testConnection()) Serial.println("Sensor iniciado correctamente");


else Serial.println("Error al iniciar el sensor");
}

void loop() {
// Leer las aceleraciones y velocidades angulares cada 100 ms
sensor.getAcceleration(&ax, &ay, &az);
sensor.getRotation(&gx, &gy, &gz);

//Mostrar las lecturas separadas


Serial.print("\n a[x y z] \n");
Serial.print(ax); Serial.print(" ");
Serial.print(ay); Serial.print(" ");
Serial.print(az); Serial.print("\n \n ");

Serial.print("g[x y z] \n");
Serial.print(gx); Serial.print(" ");
Serial.print(gy); Serial.print(" ");
Serial.println(gz);

delay(100);
}

El código anterior fue grabado en la placa tipo Arduino con el sensor conectado y ha
funcionado sin errores, sin embargo los valores de medida mostrados hacen referencia a un offset
que no ha sido ajustado. Para resolver esto se requiere calibrar el sensor MPU.

Para obtener la lectura real de aceleración (o velocidad angular) se debe transformar el valor
devuelto por getAcceleration o getRotation (por defecto un entero entre -32768 y 32767), en base
al código previo:

accx=ax*(2*9.8) / (32767); // valor en m/s2, regla de 3

El valor 2*9.8 (2g) corresponde al escalado por defecto (tabla 3.1), este valor puede ser
cambiado sobreescribiendo uno de los registros de configuración del MPU vía I2C (pueden usarse
las funciones de la clase Wire, que son funciones de bajo nivel). Los valores posibles son 2g, 4g,

Ir al índice 3
Tema 3: Sensor de medición inercial MPU6050

8g, y 16g. Ver hoja de datos para más detalles de este y otros registros de configuración.

Figura 1: Conexión del sensor MPU a la placa controladora

3.1. Calibración
Para realizar una calibración se usan algoritmos como se muestra en esta sección. La
calibración se realiza en parte debido a que cada módulo es único en lo que se refiere al error del
cero que viene de fábrica. El error de desplazamiento del cero (offset) se refiere a que el sensor
mide pequeñas magnitudes distintas de cero incluso si está completamente nivelado, cuando en
teoría debería medir valores cero (velocidad angular, ángulos y aceleración angular por ejes).

Existen funciones de la clase MPU6050 tanto para establecer como para obtener el offset de
aceleración por cada eje y velocidad angular del sensor. El código usa estas funciones para ajustar
el offset, como se verá a continuación. Las funciones de offset de aceleración son:

ax_o=sensor.getXAccelOffset(); // lee el offset actual para la aceleración en X


sensor.setXAccelOffset(ax_o); // establece ax_o como nuevo offset para aceleración en X

Para la velocidad angular se tiene:

gx_o=sensor.getXGyroOffset(); // retorna offset de velocidad de rotación en el eje x del


sensor
// el valor devuelto se debe escalar
sensor.setXGyroOffset(gx_o);

Sustituyendo X por Y o Z en las funciones previas se trabaja con los ejes restantes.

A continuación se muestra un código de calibración, la posición actual del sensor será la


nueva posición de referencia para mediciones futuras. La calibración se realiza antes de usar el
sensor.

4 Ir al índice
3.1. Calibración
// Codigo de calibracion del MPU6050, este codigo solo calibra el sensor
// I2Cdev and MPU6050 deben estar instaladas como librerías
// La libreria MPU6050.h necesita I2Cdev.h, I2Cdev.h necesita Wire.h
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

// MPU6050 sensor(0x69); // el constructor puede recibir la direccción I2C 0x69 o 0x68


explicitamente

//las mediciones obtenidas del sensor directamente son: aceleración lineal (por eje) y
velocidad angular (por eje)

MPU6050 sensor(0x68); // crea el objeto sensor, si conecta 2 sensores MPU, estos deberán
tener direcciones I2C diferentes

// Valores RAW (sin procesar) del acelerometro y giroscopio en los ejes x,y,z
int ax, ay, az;
int gx, gy, gz;

//Variables usadas por el filtro pasa bajos


long f_ax,f_ay, f_az;
int p_ax, p_ay, p_az;
long f_gx,f_gy, f_gz;
int p_gx, p_gy, p_gz;
int counter=0;

// Valor de los offsets


int ax_o,ay_o,az_o;
int gx_o,gy_o,gz_o;

void setup() {
Serial.begin(9600); //Iniciando puerto serial
Wire.begin(); //Iniciando I2C
sensor.initialize(); //Iniciando el sensor

if (sensor.testConnection()) Serial.println("Sensor iniciado correctamente");

// Leer los offset los offsets anteriores


ax_o=sensor.getXAccelOffset();
ay_o=sensor.getYAccelOffset();
az_o=sensor.getZAccelOffset();
gx_o=sensor.getXGyroOffset();
gy_o=sensor.getYGyroOffset();
gz_o=sensor.getZGyroOffset();

Serial.println("Offsets:");
Serial.print(ax_o); Serial.print("\t");
Serial.print(ay_o); Serial.print("\t");
Serial.print(az_o); Serial.print("\t");
Serial.print(gx_o); Serial.print("\t");
Serial.print(gy_o); Serial.print("\t");
Serial.print(gz_o); Serial.print("\t");
Serial.println("nnEnvie cualquier caracter para empezar la calibracion");
// Espera un caracter para empezar a calibrar
while (true){if (Serial.available()) break;}
Serial.println("Calibrando, no mover IMU");

} // fin de funcion setup

void loop() {
// Leer las aceleraciones y velocidades angulares sin escalar
sensor.getAcceleration(&ax, &ay, &az);
sensor.getRotation(&gx, &gy, &gz);

Ir al índice 5
Tema 3: Sensor de medición inercial MPU6050

// se van sumando y filtrando las medidas


f_ax = f_ax-(f_ax>>5)+ax;
p_ax = f_ax>>5; // (el factor 5 es escogido en funcion del ruido en la medida)

f_ay = f_ay-(f_ay>>5)+ay;
p_ay = f_ay>>5;

f_az = f_az-(f_az>>5)+az;
p_az = f_az>>5;

f_gx = f_gx-(f_gx>>3)+gx;
p_gx = f_gx>>3;

f_gy = f_gy-(f_gy>>3)+gy;
p_gy = f_gy>>3;

f_gz = f_gz-(f_gz>>3)+gz;
p_gz = f_gz>>3;

//Cada 100 lecturas corregir el offset


if (counter==100){
//Mostrar las lecturas separadas por un [tab]
Serial.print("promedio:"); Serial.print("t");
Serial.print(p_ax); Serial.print("\t");
Serial.print(p_ay); Serial.print("\t");
Serial.print(p_az); Serial.print("\t");
Serial.print(p_gx); Serial.print("\t");
Serial.print(p_gy); Serial.print("\t");
Serial.println(p_gz);

//Calibrar el acelerometro a 1g en el eje z (ajustar el offset)


if (p_ax>0) ax_o--;
else {ax_o++;}
if (p_ay>0) ay_o--;
else {ay_o++;}
if (p_az-16384>0) az_o--;
else {az_o++;}

sensor.setXAccelOffset(ax_o);
sensor.setYAccelOffset(ay_o);
sensor.setZAccelOffset(az_o);

//Calibrar el giroscopio a 0º/s en todos los ejes (ajustar el offset)


if (p_gx>0) gx_o--;
else {gx_o++;}
if (p_gy>0) gy_o--;
else {gy_o++;}
if (p_gz>0) gz_o--;
else {gz_o++;}

sensor.setXGyroOffset(gx_o);
sensor.setYGyroOffset(gy_o);
sensor.setZGyroOffset(gz_o);

counter=0;
} // fin de if
counter++;
} // fin de la funcion loop

La función Serial.available() obtiene y retorna la cantidad de bytes disponibles para leer en el

6 Ir al índice
3.1. Calibración

puerto serial, pero no los lee en sí. Esto se refiere a los datos que ya han llegado al bufer
previamente. Si el valor devuelto es cero, no hay datos para leer en ese instante. Después de esta
función se suele ejecutar dato=Serial.read(), que retorna el primer byte disponible de la pila del
bufer como un entero, o un -1 si no hay datos para leer. A medida que se leen bytes, el bufer se va
liberando, por ende el valor devuelto por Serial.available() será cada vez menor hasta ser igual a
cero.

Es posible cambiar el tamaño del buffer de lectura, es decir la cantidad de datos que el
Arduino leerá en una sola secuencia, pero si el tamaño se hace muy grande, el código tardará más
en ejecutar cada ciclo loop.

En el código se usa desplazamiento de bits para corregir las mediciones. La expresión


f_ax>>5 desplaza 5 bits de f_ax a la derecha rellenando con ceros a la izquierda. Desplazar cinco
posiciones a la derecha los bits de una cantidad equivale a dividirla entre 32 (2 5) en decimal. Por
tanto, f_ax-(f_ax>>5) equivale a 0,96f_ax. Similarmente, desplazar 3 bits equivale a dividir entre 2 3
(8).

Si los valores medidos no cambian de signo, los valores filtrados p se alejan del cero a medida
que se suman las mediciones de cada magnitud. Los valores de aceleración a x y ay y velocidades
angulares deben ser cercanos a cero, dejando el sensor inmóvil y nivelado. Por cada 100
iteraciones, todos los offset se corrigen sumando o restando una unidad, y esto afecta las
mediciones en el próximo ciclo.

Cuando los valores de p convergen a los valores deseados de offset, se procede a


desconectar o reiniciar nuestro sistema.

Al usar el codigo anterior tiene algunos inconvenientes, el tiempo para la convergencia es


considerable. Es necesario instalar el sensor en un soporte fijo para no tener fluctuaciones en las
medidas. Una vista parcial de la salida obtenida se muestra abajo. Los valores de p iniciales están
muy lejos del cero, y debido a ello su aproximación al valor deseado tarda un tiempo considerable.

Promedio valores offset ( ax_o ay_o az_o gx_o gy_o gz_o ): -944 -1507 1954 12 32 120

Promedio valores p: 337 7592 15650 -6 -8 2

Promedio valores offset ( ax_o ay_o az_o gx_o gy_o gz_o ): -945 -1508 1955 13 33 119

Promedio valores p: 308 7603 15642 1 1 -2

Promedio valores offset ( ax_o ay_o az_o gx_o gy_o gz_o ): -946 -1509 1956 12 32 120

Promedio valores p: 296 7573 15661 -7 -4 5

Promedio valores offset ( ax_o ay_o az_o gx_o gy_o gz_o ): -947 -1510 1957 13 33 119

Promedio valores p: 284 7568 15673 5 -1 -4

Promedio valores offset ( ax_o ay_o az_o gx_o gy_o gz_o ): -948 -1511 1958 12 34 120

Ir al índice 7
Tema 3: Sensor de medición inercial MPU6050

A continuación se muestra un código2 de calibración adicional, que funciona calculando el


promedio de varias mediciones sucesivas, mientras el sensor está fijo y nivelado:

// Algoritmo para calibrar el sensor MPU6050 (método 2)


#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"
// El buffer y la zona muerta puede ajustarse para cambiar la precisión
int buffersize=1000; // cantidad de lecturas a realizar para promediar
int acel_deadzone=8; // valor por defecto
int giro_deadzone=1; // valor por defecto
// (valores bajos pueden aumentar la precisión, pero la calibración puede no converger)

MPU6050 accelgyro(0x68); // objeto de clase para manejar el MPU

// Estados iniciales de las variables:


int16_t ax, ay, az, gx, gy, gz; // valores enteros sin procesar

int mean_ax,mean_ay,mean_az,mean_gx,mean_gy,mean_gz,state=0;
int ax_offset,ay_offset,az_offset,gx_offset,gy_offset,gz_offset;

/////////////////////////////////// SETUP ////////////////////////////////////


void setup() {
// join I2C bus (I2Cdev library doesn't do this automatically)
Wire.begin();

// Comunicación serial con el PC


Serial.begin(9600);

// Inicializa el MPU
accelgyro.initialize();

// espera de lectura
while (Serial.available() && Serial.read()); // este while libera el bufer
while (!Serial.available()){ // generalmente sera true, hasta pulsar una tecla
Serial.println(F("Presione una tecla para iniciar rutina de calibracion \n"));
delay(1500);
}
while (Serial.available() && Serial.read()); // este while tambien libera el bufer de
los datos provenientes del teclado

// Inicia mensaje
Serial.println("\nMPU6050 Calibration Sketch");
delay(2000);
Serial.println("\nEl sensor debe estar en posición horizontal, no tocarlo hasta que se
termine la rutina.\n");
delay(3000);
// verify connection
Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050
connection failed");
delay(1000);
// reinicia offsets:
accelgyro.setXAccelOffset(0);
accelgyro.setYAccelOffset(0);
accelgyro.setZAccelOffset(0);
accelgyro.setXGyroOffset(0);
accelgyro.setYGyroOffset(0);
accelgyro.setZGyroOffset(0);
} // fin de setup

2 Otro código de calibración se puede encontrar en los códigos de ejemplo de Arduino IDE, que se denomina IMU_ZERO.ino. Dicho ejemplo viene
incluido con la librería MPU6050, descrita en la sección 2.

8 Ir al índice
3.1. Calibración
/////////////////////////////////// Lazo ////////////////////////////////////
void loop() {
if (state==0){
Serial.println("\nLeer sensores por primera vez...");
meansensors(); // suma mediciones y promedia
state++;
delay(1000);
}

if (state==1) {
Serial.println("\nEstablece los nuevos offsets...");
calibration(); // solo terminara si los offset son inferiores que las zonas muertas
state++;
delay(1000);
}

if (state==2) { // Solo se ejecuta si la calibracion previa es exitosa


meansensors();
Serial.println("\nFINISHED!");
Serial.print("\nSe han establecido los offsets:\t");
Serial.print(mean_ax); // mean son los valores promedio
Serial.print("\t");
Serial.print(mean_ay);
Serial.print("\t");
Serial.print(mean_az);
Serial.print("\t");
Serial.print(mean_gx);
Serial.print("\t");
Serial.print(mean_gy);
Serial.print("\t");
Serial.println(mean_gz);
Serial.print("Sus offsets son:\t");
Serial.print(ax_offset);
Serial.print("\t");
Serial.print(ay_offset);
Serial.print("\t");
Serial.print(az_offset);
Serial.print("\t");
Serial.print(gx_offset);
Serial.print("\t");
Serial.print(gy_offset);
Serial.print("\t");
Serial.println(gz_offset);
Serial.println("\nSe imprime como: acelX acelY acelZ giroX giroY giroZ");
Serial.println("Chequear que las lecturas son cercanas a: 0 0 16384 0 0 0");
while (1);
}
}

/////////////////////////////////// FUNCTIONS ////////////////////////////////////


void meansensors(){
long i=0,buff_ax=0,buff_ay=0,buff_az=0,buff_gx=0,buff_gy=0,buff_gz=0;

while (i<(buffersize+101)){
// read raw accel/gyro measurements from device
accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); // lee sensores y almacena
// (se descartan las primeras 100 lecturas)

if (i>100 && i<=(buffersize+100)){ // de 101 hasta 1100, suma 1000 mediciones


buff_ax=buff_ax+ax;
buff_ay=buff_ay+ay;
buff_az=buff_az+az;
buff_gx=buff_gx+gx;
buff_gy=buff_gy+gy;

Ir al índice 9
Tema 3: Sensor de medición inercial MPU6050
buff_gz=buff_gz+gz;
}
if (i==(buffersize+100)){ // iteracion i = 1100, se promedian mediciones aquí
mean_ax=buff_ax/buffersize;
mean_ay=buff_ay/buffersize;
mean_az=buff_az/buffersize;
mean_gx=buff_gx/buffersize;
mean_gy=buff_gy/buffersize;
mean_gz=buff_gz/buffersize;
}
i++;
delay(2); // para dar tiempo entre mediciones
}
}

void calibration(){ // calcula los offsets y llama a meansensors() para probar


ax_offset=-mean_ax/8;
ay_offset=-mean_ay/8;
az_offset=(16384-mean_az)/8;

gx_offset=-mean_gx/4;
gy_offset=-mean_gy/4;
gz_offset=-mean_gz/4;
// (el acelerometro generalmente tiene mas ruido que el giroscopio)
while (1){
int ready=0;
accelgyro.setXAccelOffset(ax_offset);
accelgyro.setYAccelOffset(ay_offset);
accelgyro.setZAccelOffset(az_offset);

accelgyro.setXGyroOffset(gx_offset);
accelgyro.setYGyroOffset(gy_offset);
accelgyro.setZGyroOffset(gz_offset);

meansensors(); // se vuelve a medir y promediar, con offset nuevos en cada ciclo


Serial.println("...");

if (abs(mean_ax)<=acel_deadzone) ready++;
else ax_offset=ax_offset-mean_ax/acel_deadzone; // reajuste de offset
// se obtiene un promedio suficientemente pequeño, o en lugar de ello se corrige el
offset para volver a sumar y promediar

if (abs(mean_ay)<=acel_deadzone) ready++;
else ay_offset=ay_offset-mean_ay/acel_deadzone;

if (abs(16384-mean_az)<=acel_deadzone) ready++;
else az_offset=az_offset+(16384-mean_az)/acel_deadzone;

if (abs(mean_gx)<=giro_deadzone) ready++;
else gx_offset=gx_offset-mean_gx/(giro_deadzone+1);

if (abs(mean_gy)<=giro_deadzone) ready++;
else gy_offset=gy_offset-mean_gy/(giro_deadzone+1);

if (abs(mean_gz)<=giro_deadzone) ready++;
else gz_offset=gz_offset-mean_gz/(giro_deadzone+1);

if (ready==6) break;
}
}

La última parte de la función calibración (bloque while(1)...) se asegura de que la calibración

10 Ir al índice
3.2. Valores de inclinación del sensor

termine solo si los valores promedio calculados (mean_ax, mean_ay, ...) son inferiores al valor
establecido de zona muerta.

Todos los valores de promedio y offset que se obtienen en las funciones son valores no
escalados. En realidad no hace falta convertirlos a las unidades físicas asociadas.

3.2. Valores de inclinación del sensor


En esta sección se muestra un código para calcular los ángulos que forma el vector de
aceleración de gravedad (referencia) respecto a los planos de los ejes del sensor 3, esto permite
determinar la inclinación del mismo, obviamente se debe determinar la inclinación de una
aceleración conocida y fija, como lo es la aceleración de gravedad. Los ejes del sensor x, y y z son
fijos respecto al sensor.

El ángulo x es el ángulo que forma el vector de gravedad g con el plano yz del sensor, el
ángulo Y es el ángulo que forma el vector g con el plano xz. Por triángulos rectángulos, se
expresan estos ángulos en función de arcotangentes. Los catetos son básicamente proyecciones
del vector g, como se ve en la figura 2. Se asume el vector g positivo apuntando hacia “arriba” por
simplicidad.

ap2=Accx2 + Accz2
ap tan(y)=tan(roll)=|Accy| / |ap|
tan(y)=|Accy| / sqrt(Accx2 + Accz2)

Figura 2: Vector de aceleración de gravedad y cálculo de la inclinación del sensor, los ejes x, y y z
son fijos respecto al propio sensor.

El vector de gravedad g, que se muestra de color blanco en la figura 2, forma un ángulo y


(roll) respecto al plano xz del sensor, la proyección del vector g en el plano xz es el vector a p, que
se muestra de color verde. Hay que recordar que los planos que forman los ejes rotan con el
sensor, mientras el vector de gravedad g queda fijo. La gravedad es la única aceleración presente
para el análisis, siempre que el sensor permanezca inmovil.

De forma similar al ángulo y, se determina x, que es el ángulo de proyección de g sobre el
plano yz del sensor, entonces:

tan(x)=Accx / sqrt(Accy2 + Accz2)

Siendo sqrt la función para sacar la raiz cuadrada. En nuestro código se usa directamente la

3 Referencias: https://youtu.be/7VW_XVbtu9k?si=fuFyVvICLSS6YjMB

Ir al índice 11
Tema 3: Sensor de medición inercial MPU6050

función arcotangente atan():

// ...
sensor.getAcceleration(&ax, &ay, &az); // valores sin procesar (escalar)
//Calcular los angulos de inclinacion:
float accel_ang_x=atan(ax/sqrt(pow(ay,2) + pow(az,2)))*(180.0/3.14); // radianes
float accel_ang_y=atan(ay/sqrt(pow(ax,2) + pow(az,2)))*(180.0/3.14);
// ...

Para calcular los ángulos de inclinación no hace falta escalar los valores leídos con
getAcceleration(…), ya que la tangente es adimensional.

3.3. Valores de rotación con el giroscopio


El giroscopio permite obtener el ángulo instantáneo de rotación de cada eje del sensor por
separado. Se trata de los mismos ejes usados de la sección previa, en los que se proyectan a ccx,
accy y accz. La rotación es relativa, es decir se mide grado de rotación de un eje respecto de un
estado de rotación previo de ese eje. Cada ángulo actual, pasa a ser el angulo previo.

No se debe confundir los ángulos de rotación de ejes, con los ángulos de inclinación del
sensor, aunque pueden coincidir como casos particulares.

Hay que tener presente que un objeto puede rotar respecto a un eje del mismo, con
inclinaciones diferentes, esto se visualiza mejor en la figura 3. A esto se refiere la diferencia entre
inclinación del sensor y la rotación entorno a un eje.

Eje
perpendicular al
suelo z

Figura 3: Un mismo modo de rotación de un objeto entorno a su eje z, con dos inclinaciones
diferentes del mismo respecto a tierra.

3.3.1. Código en Arduino


Es posible medir el ángulo de rotación respecto a cada eje del sensor, usando la velocidad
angular calculada por el giroscopio y el tiempo transcurrido t entre dos ejecuciones del código

12 Ir al índice
3.3. Valores de rotación con el giroscopio

principal, de la siguiente manera:

actual = previo + t*w

Donde w es la velocidad medida por el giroscopio en rad/s. Se puede asumir un ángulo inicial
para  = 0 antes de entrar en el bucle. Esta fórmula se aplica por cada eje del sensor (x, y, z).
Según el sentido de giro, w y  serán positivo o negativo.

// Código para obtener los ángulos de rotación de los ejes del sensor
// Librerias I2C para controlar el mpu6050
// la libreria MPU6050.h necesita I2Cdev.h, I2Cdev.h necesita Wire.h
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

// La dirección del MPU6050 puede ser 0x68 o 0x69, dependiendo


// del estado de AD0. Si no se especifica, 0x68 estará implicito
MPU6050 sensor(0x68);

// Valores RAW (sin procesar) del giroscopio


int gx, gy, gz; // valores en cero

float dt; // precision sencilla


double tiempo_pre;
float ang_x, ang_y;
float ang_x_prev, ang_y_prev; // valores previos

void setup() {
Serial.begin(9600); //Iniciando puerto serial
Wire.begin(); //Iniciando I2C
sensor.initialize(); //Iniciando el sensor

if (sensor.testConnection()) Serial.println("Sensor iniciado correctamente");


else Serial.println("Error al iniciar el sensor");
tiempo_prev=millis(); // primer instante en miliseg, angulos en cero
} // fin de setup()

void loop() {
// Leer las velocidades
sensor.getRotation(&gx, &gy, &gz); // sin procesar

dt = (millis()-tiempo_prev)/1000.0; // tiempo llevado a seg


tiempo_prev=millis(); // guardar el instante actual como previo

// Angulo actual por eje:


ang_x = (gx/131.0)*dt/1.0 + ang_x_prev; // grados centigrados
ang_y = (gy/131.0)*dt/1.0 + ang_y_prev; // grados centigrados
// g/131 es el escalado a grado/s según escala por defecto

ang_x_prev=ang_x; // guardar angulo actual como previo


ang_y_prev=ang_y;

// Mostrar los angulos separadas por un [tab]


Serial.print("Rotacion en X: ");
Serial.print(ang_x);
Serial.print("tRotacion en Y: ");
Serial.println(ang_y);

delay(50); // todos los delays incluidos en el loop influyen en el dt


// usar el plotter de arduino IDE para graficar valores impresos
}

Ir al índice 13
Tema 3: Sensor de medición inercial MPU6050

Con este código por lo general se produce un error acumulativo en la medición llamado “drift”,
el cual se debe eliminar o reducir agregando un filtro complementario en el código. Este error se
debe principalmente al ruido en la medición de la velocidad.

El ángulo de rotación del eje X se puede expresar en función del ángulo de proyección del
vector g sobre el plano xz. Cada ángulo de rotación se complementa como en el siguiente código.

// Obteniendo angulos de rotacion de ejes usando filtro complementario de ángulos, se usa


lecturas del giroscopio y del acelerómetro:
// Librerias I2C para controlar el mpu6050
// la libreria MPU6050.h necesita I2Cdev.h, I2Cdev.h necesita Wire.h
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

// La dirección del MPU6050 puede ser 0x68 o 0x69, dependiendo


// del estado de AD0. Si no se especifica, 0x68 estará implicito
MPU6050 sensor(0x68);

// Valores RAW (sin procesar) del acelerometro y giroscopio en los ejes x,y,z
int ax, ay, az;
int gx, gy, gz;

double tiempo_prev;
float dt;
float ang_x, ang_y; // valores previos punto flotante
float ang_x_prev, ang_y_prev;

void setup() {
Serial.begin(9600); //Iniciando puerto serial
Wire.begin(); //Iniciando I2C
sensor.initialize(); //Iniciando el sensor

if (sensor.testConnection()) Serial.println("Sensor iniciado correctamente");


else Serial.println("Error al iniciar el sensor");

} // fin de setup

void loop() {
// Leer las aceleraciones y velocidades angulares
sensor.getAcceleration(&ax, &ay, &az);
sensor.getRotation(&gx, &gy, &gz);

dt = (millis()-tiempo_prev)/1000.0; // tiempo en segundos


tiempo_prev=millis(); // guardar el instante actual como previo

// Calcular los ángulos proyectados por la aceleracion lineal:


float accel_ang_x=atan(ay/sqrt(pow(ax,2) + pow(az,2)))*(180.0/3.14); // grados
float accel_ang_y=atan(-ax/sqrt(pow(ay,2) + pow(az,2)))*(180.0/3.14); // grados

// Calcular angulo de rotación con giroscopio y filtro:


ang_x = 0.90*(ang_x_prev+(gx/131.0)*dt) + 0.1*accel_ang_x; // Las constantes numericas
suman 1
ang_y = 0.90*(ang_y_prev+(gy/131.0)*dt) + 0.1*accel_ang_y; // grados

// ( g/131 es el escalado a grado/s según escala por defecto )

ang_x_prev=ang_x; // angulo previo = angulo de rotacion actual


ang_y_prev=ang_y;

14 Ir al índice
3.3. Valores de rotación con el giroscopio
//Mostrar los angulos separadas por un [tab]

Serial.print("\n\n\n Rotacion en X: ");


Serial.print(ang_x);
Serial.print("\n\n Rotacion en Y: ");
Serial.println(ang_y);

delay(50);
}

Las mediciones con ruido del acelerómetro son filtradas por las mediciones del giroscopio en
el eje respectivo. La medición del acelerometro contribuye solo con un 10% del ángulo de giro por
eje. Estas constantes numéricas se pueden cambiar para fines de prueba.

Usando el código anterior, al rotar el sensor un ángulo aproximado de 90 grados positivos en


el eje x se muestran los valores parciales siguientes:

Figura 4: Salida del monitor al rotar el sensor MPU6050 en su eje X unos 90 grados

Hay que tomar en cuenta que el sensor fue colocado sobre una mesa para esta prueba, aún
así puede estar inclinado, ya que la mesa y el edificio donde se ubica pueden tener ángulos de
inclinación pequeños (respecto a tierra) que contribuyen con el desnivel absoluto del sensor.

Una rotación pura en el eje Y de unos 90 grados produjo la salida siguiente en la consola del
IDE Arduino:

Ir al índice 15
Tema 3: Sensor de medición inercial MPU6050

Figura 5: Salida del monitor al rotar el sensor MPU6050 en su eje Y unos 90 grados

Los resultados previos indican que el sensor puede funcionar para determinar su inclinación
aproximada respecto al suelo siempre que se haya calibrado y fijado adecuadamente en un
soporte.

Es posible usar el MPU6050 para realizar sistemas de control en drones o plataformas que
requieran nivelación o control de inclinación. Cabe destacar que este sensor no es de los más
precisos de su tipo, en caso de requerir mediciones de mayor precisión y menos ruido se pueden
adquirir otros modelos de sensores disponibles comercialmente, cuyo modo de funcionamiento es
muy similar al del MPU6050.

3.4. Sistema de control de inclinación (posición angular)


La necesidad de controlar niveles o inclinación de plataformas móviles es bastante común en
proyectos de ingeniería y robótica. Se puede diseñar un sistema de control de nivel sencillo de
laboratorio para demostrar el funcionamiento del sensor MPU6050. El sensor permite medir
constantemente la inclinación respecto al suelo terrestre de los ejes de la plataforma (conjunto
plataforma-sensor), luego el motor se encarga de nivelar la plataforma al punto de referencia (set-
point) en grados (o radianes), en función del error o diferencia entre el valor actual y el valor
deseado. Un valor deseado de cero significa que el sistema intentará mantener la plataforma
horizontalmente, aunque puede elegirse otro ángulo. La plataforma gira acoplada al eje de salida
del motor, pero el cajetin de motor en sí es independiente respecto a la plataforma, y puede formar
parte de otro sistema móvil externo, como un vehículo de ruedas.

Un posible algoritmo de control de inclinación de plataforma en el eje X se muestra a


continuación. Se recomienda calibrar el sensor MPU antes de ejecutar el código de control.

// Código para controlar ángulo de inclinación de plataforma mediante servomotor

16 Ir al índice
3.4. Sistema de control de inclinación (posición angular)
// última revisión: 13-dic-2024
// Librerias I2C para controlar el mpu6050 y el servo
// la libreria MPU6050.h necesita I2Cdev.h, I2Cdev.h necesita Wire.h
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"
#include <Servo.h>

// Declaramos la variable para controlar el servo


Servo servoMotor;

// La dirección del MPU6050 puede ser 0x68 o 0x69, dependiendo


// del estado de AD0. Si no se especifica, 0x68 estará implicito
MPU6050 sensor(0x68); // se recomienda calibrar previamente el MPU

// Valores RAW (sin procesar) del giroscopio y acelerometro


int gx, gy, gz=0; // valores en cero
int ax, ay, az=0; //

float accel_ang_x;
float accel_ang_y;
float dt=0;
double tiempo_prev=0;
float ang_x, ang_y=0; // angulos de ejes del sensor (grados)
float ang_x_prev, ang_y_prev; // valores previos
int refX=45; // angulo arbitrario de la plataforma (entre -90 y 90) respecto a la tierra
int refM=90; // angulo del eje del motor respecto al motor (0 - 180)

int ek,ek_prev=0; // error en x actual y previo (grados)


int i=0; // cuenta ciclos de loop() antes de cambiar pwm del servo, al cambiar pwm se
pone a 0.
int flag=0; // bandera que indica con 1 que se cambió pwm, se pone a 0 junto con i

//*************************************************************

void setup() {
// SENSOR Y COM SERIAL:
Serial.begin(9600); //Iniciando puerto serial
Wire.begin(); //Iniciando I2C
sensor.initialize(); //Iniciando el sensor

if (sensor.testConnection()) Serial.println("Sensor iniciado correctamente");


else Serial.println("Error al iniciar el sensor");
tiempo_prev=millis(); // primer instante en miliseg, angulos en cero

// MOTOR SERVO:
// Iniciamos el servo para que empiece a trabajar con el pin 9
servoMotor.attach(9); // pin digital 9, o D9
delay(1000);
// Inicializamos al ángulo 0 el servomotor
servoMotor.write(90); // 0 a 180, al inicio: 0=plataforma hacia abajo, 90=plataforma
horizontal, 180=plataforma hacia arriba
delay(1000);

do{ // se ejecuta hasta que se nivele la plataforma en X (acoplada al eje del motor)
// Leer las velocidades angulares y aceleraciones
sensor.getRotation(&gx, &gy, &gz); // sin procesar
sensor.getAcceleration(&ax, &ay, &az); // se necesita para corregir error drift

// Calcular ángulo de proyección de la aceleracion lineal neta:


accel_ang_x=atan(ay/sqrt(pow(ax,2) + pow(az,2)))*(180.0/3.14); // grados

dt = (millis()-tiempo_prev)/1000.0; // en segundos
tiempo_prev=millis(); // guardar el instante actual como previo

Ir al índice 17
Tema 3: Sensor de medición inercial MPU6050

// Angulo actual por eje compensado mediante lectura acelerometro:


ang_x = 0.90*(ang_x_prev+(gx/131.0)*dt) + 0.1*accel_ang_x;
//ang_y = 0.90*(ang_y_prev+(gy/131.0)*dt) + 0.1*accel_ang_y;
// g/131 es el escalado a grado/s según escala por defecto

ang_x_prev=ang_x; // guardar angulo actual como previo


//ang_y_prev=ang_y;
delay(30);
}
while (ang_x>5 || ang_x<-5);

tiempo_prev=millis();
delay(30);
} // fin de setup

//*************************************************************

void loop() {
// Leer las velocidades angulares y aceleraciones
sensor.getRotation(&gx, &gy, &gz); // sin procesar
sensor.getAcceleration(&ax, &ay, &az); // se necesita para corregir error drift

// Calcular ángulo proyectado por la aceleracion lineal neta:


accel_ang_x=atan(ay/sqrt(pow(ax,2) + pow(az,2)))*(180.0/3.14); // grados

dt = (millis()-tiempo_prev)/1000.0; // en segundos
tiempo_prev=millis(); // guardar el instante actual como previo

// Angulo actual por eje compensado mediante lectura acelerometro:


ang_x = 0.90*(ang_x_prev+(gx/131.0)*dt) + 0.1*accel_ang_x;
//ang_y = 0.90*(ang_y_prev+(gy/131.0)*dt) + 0.1*accel_ang_y;
// g/131 es el escalado a grado/s según escala por defecto

ang_x_prev=ang_x; // guardar angulo actual como previo


//ang_y_prev=ang_y;

// Mostrar los angulos separados por un [tab]


Serial.print("\n\n\n Rotacion en X: ");
Serial.print(ang_x);
// Serial.print("\n\n Rotacion en Y: ");
// Serial.println(ang_y);
Serial.print("\n\n\n Valores de ek, refM: ");
Serial.print(ek);
Serial.print(" , ");
Serial.print(refM);

// error en X:
ek=refX-ang_x; // error en grados

if(ek>=2 && flag==0) // error positivo


{
if (refM+ek < 0)
{
servoMotor.write(0);
refM=0;
flag=1;
}
else {

if(refM+ek > 180){


servoMotor.write(180); // valor al que quedará rotado el eje hasta la proxima
correccion del error

18 Ir al índice
3.4. Sistema de control de inclinación (posición angular)
flag=1;
refM=180; // nuevo setpoint del eje del motor respecto a caja del motor

}
else{ // cuando está entre 0 y 180 grados entonces...
servoMotor.write(refM+ek); // valor al que quedará rotado el eje hasta la proxima
correccion del error ek
flag=1;
refM=refM+ek; // nuevo setpoint del eje del motor respecto al motor
}
} // else
} // FIN DE IF

if(ek<=-2 && flag==0) // error negativo


{
if (refM+ek < 0)
{
servoMotor.write(0);
refM=0;
flag=1;
}
else {

if(refM+ek > 180){


servoMotor.write(180); // valor al que quedará rotado el eje hasta la proxima
correccion del error
flag=1;
refM=180; // nuevo setpoint del eje del motor respecto al motor

}
else{ // cuando está entre 0 y 180 grados...
servoMotor.write(refM+ek);
flag=1;
refM=refM+ek;
} // else
} // else
} // FIN DE IF
delay(3); // para generar un breve retardo
i=i+1;
ek_prev=ek; // error previo (en caso de usarse para algun otro metodo de control)
if(i==5){ // cada n ciclos loop se volverá a escribir pwm
i=0;
flag=0;
} // fin de if
} // fin de loop

Puede observar que no en todos los ciclos de loop se corrige la señal enviada al motor, el
error ek es el incremento que se agrega al ángulo del motor; en este sistema no se puede
implementar un control con parámetros (como PI, PID, …) ya que el servo motor tiene su propio
control de velocidad y posición incorporado. En caso de usar otro mecanismo que conecte la
plataforma con el eje del motor, se debe determinar el error de sensor e k, el ángulo inicial y final del
sensor αk, y con estos datos se determina el incremento requerido en el angulo del eje del motor
(ángulo respecto al cajetin del motor).

3.4.1. Sistema implementado y resultados


Se ha implementado a modo experimental el sistema de control de inclinación simple con

Ir al índice 19
Tema 4: Sensor de distancia por ultrasonido AJ-SR04M

servo motor usando el código de la sección anterior, este ha funcionado sin inconvenientes.
Primero el sensor MPU fue calibrado según lo descrito en la sección de calibración, antes de
ejecutar el código de control.

Ajustando el límite de conteo de la variable i, se ajusta la frecuencia con la que se modifica la


onda pwm, de tal forma que el sistema no corriga excesivamente el movimiento del motor. El valor
de 2 grados centigrados en los condicionales if(ek>=2 && flag==0)... o if(ek<=-2 && flag==0)... es
una zona muerta de prueba, se puede colocar incluso 1 grado para una mejor precisión, pero esto
puede incrementar la cantidad de movimientos correctivos que realiza el motor.

4. Sensor de distancia por ultrasonido AJ-SR04M


Este es un módulo completo compatible con placas de control para medir distancia por
ultrasonido. Tiene 5 modos de funcionamiento, básicamente la diferencia entre ellos radica en la
forma en que se trasmite el valor medido. Puede funcionar mediante disparo, es decir, cada vez
que queremos obtener la medición se envía un pulso al módulo y este responderá enviando la
medición como una señal en el pin “echo”. Mientras no se envíe un pulso el módulo queda en
estado de “espera”.

Los otros modos básicamente transmiten un dato serial codificado por el pin echo hacia el
exterior. El módulo puede encargarse internamente de disparar el sensor y enviar periódicamente
la medida, o en su lugar se pueden enviar pulsos al pin trigger del sensor, cada vez que se quiere
obtener una medición (el resto del tiempo el módulo permanecerá en reposo).

Figura 6: Modo 1 de funcionamiento del sensor AJ-SR04M, activación por disparo y medida
dada como función de la duración del pulso en señal “echo”

20 Ir al índice
5. Sensor de distancia por ultrasonido HC-SR04 y similares

Figura 7: Modo 4 de funcionamiento del sensor AJ-SR04M, activación por señal en trigger y
medida dada como valor serial

5. Sensor de distancia por ultrasonido HC-SR04 y similares


Este es un módulo sensor sencillo de utilizar con Arduino, para medir distancias cortas de
objetos, alejados hasta 4 metros del sensor. Funciona mediante una onda ultrasónica que emite el
dispositivo, la cual rebota en el objeto del que se quiere calcular su distancia y luego regresa al
sensor. En base al tiempo de ida y vuelta se calcula la distancia del sensor al objeto. Se explican
más detalles en el ejemplo de código a continuación. Este fue probado sin problemas, en este
caso enciende un led indicador en función de la distancia medida.

// Rutina para probar el sensor HC-SR04, en modo GPIO (usando Trigger y Echo).

// Mauricio Hernandez

#define TRIG 8
#define ECHO 9
#define LED 10
#define TIMEOUT 200000 // timeout, tiempo de espera en microsegundos
// el sensor está diseñado para medir cada al menos 50ms o 60ms
// el ancho del pulso Echo durará unos 36 ms si no se detecta objeto (cuando la onda no
retorna)
// se debe permitir al menos 10ms entre el final del pulso Echo y el siguiente flanco de
subida en Trigger

unsigned long tp = 0; // mide duración de pulso echo, microsegundos


double distancia = 0; // distancia en cm
double v = 100*340.0/1000000; // m/s a cm/us, velocidad del sonido en aire a 25ºC

void setup() {
Serial.begin(9600);
pinMode(TRIG,OUTPUT);
pinMode(ECHO,INPUT);
pinMode(LED,OUTPUT);
digitalWrite(TRIG,LOW); // se coloca a cero

Ir al índice 21
Tema 5: Sensor de distancia por ultrasonido HC-SR04 y similares

void loop() {

tp=medirTiempo(); // tp en microsegundos
//calculo de distancia en cm:
if (tp<=30000) {
distancia=v*tp/2;
Serial.print("Distancia en cm: ");
Serial.println(distancia);
Serial.println("\n");
}
else {
Serial.print("Ausencia de objeto u objeto muy alejado");
Serial.println("\n");
distancia=0;
}

delay(50); // tiempo de al menos 50ms para el siguiente trigger

if(distancia<10) { // estado de distancia corta a objeto, led ON


digitalWrite(LED,HIGH);
if(distancia==0){ // estado de ausencia de objeto o problema con señal Echo, led
intermitente
delay(200);
digitalWrite(LED,LOW);
delay(200);
digitalWrite(LED,HIGH);
delay(200);
digitalWrite(LED,LOW);
delay(150);
} // fin de if distancia cero
}
else
digitalWrite(LED,LOW); // estado de distancia media de objeto, led OFF

} // fin de loop

unsigned long medirTiempo(){


digitalWrite(TRIG,LOW); // se coloca a cero
delayMicroseconds(10);
digitalWrite(TRIG,HIGH); // se coloca a cero
delayMicroseconds(11);
digitalWrite(TRIG,LOW);
unsigned long tx = pulseIn(ECHO,HIGH,TIMEOUT);
return tx; // queda en espera a que ECHO se haga ALTO, luego mide el tiempo que permanece
en alto, cuando ECHO se hace bajo termina la función
// pulseIn(pin, value, timeout) // con timeout opcional, la función esperará solo el
tiempo dado, si no se especifica, se usa un valor predeterminado

El objeto debe tener suficiente superficie perpendicular a la emisión de la onda ultrasónica


para ser detectado. Esta superficie varía con la distancia, ya que hay rangos de ángulo en los
cuales la onda tiene más efectividad 4. La función clave de este código es la función de proposito
general pulseIn(…), esta calcula y retorna la duración de un nivel alto o bajo de cualquier pin
digital, se puede usar en muchos contextos, no solo con el sensor acá expuesto.

4 Máa detalles en las hojas de datos del sensor

22 Ir al índice
6. Temporizador digital programable HCW-M421

6. Temporizador digital programable HCW-M421


Este dispositivo permite activar o desactivar un relé electromecánico de forma temporizada
con varios modos de funcionamiento. Permite controlar sistemas simples en residencias o
industrias, activando o desactivando cargas de potencia en función de tiempos preestablecidos5.

El panel frontal de botones permite configurar, activar y pausar el módulo. Puede ser
alimentado a traves de las borneras de alimentación (6 – 30 V), o alternativamente por puerto
USB. Tiene una entrada digital Trigger, que al recibir un pulso inicia el funcionamiento en el modo
seleccionado.

En modo de reposo OFF, haga una pulsación corta del botón SET para ver un resumen del
modo actual y los valores de parámetros en el display (OP, CL, LOOP). Los modos disponibles
son:

P1.1: Activa de inmediato el relé durante el tiempo OP al recibir un flanco de subida en


Trigger. Si durante el transcurso de OP ocurre otro flanco, se ignorará.

P1.2: Es igual que P1.1, expecto que un nuevo flanco durante el transcurso de OP reinicia la
cuenta OP, quedando el relé activado, hasta que termine el tiempo OP normalmente.

P1.3: En este caso, el inicio funciona como los dos anteriores. Un flanco en el transcurso de
OP finaliza la cuenta OP y el relé se apaga, quedando listo para un nuevo flanco.

P-2: El relé se activa después de transcurrido el tiempo CL, cada vez que se recibe un flanco
de subida en Trigger. El relé se mantiene activo durante el tiempo OP. Un flanco durante el
transcurso de OP es ignorado.

P3.1: Activa el relé durante el tiempo OP y se desactiva durante el tiempo CL de forma cíclica,
el número de veces fijado en LOOP. Si ocurre un flanco durante el conteo, el relé se apaga y se
detiene el ciclo.

P3.2: Igual que P3.1, pero funciona sin necesidad de flanco en Trigger, se activa al energizar
el módulo.

P-4: Similar a un retardo a la desactivación. El relé se activa con el flanco de subida en


Trigger. Al pasar el nivel de Trigger a cero, se inicia la cuenta de OP. Al transcurrir OP se apaga el
relé, quedando en el estado inicial. Un flanco durante el conteo reinicia la cuenta OP tal como en la
primera vez, el relé permanecerá activo hasta que transcurre OP.

Mantenga una pulsación larga en SET para ingresar al listado de modos. Puede navegar por
los modos pulsando UP/DOWN.

Para elegir uno de los modos haga una pulsación corta del botón SET. Al hacer esto se
muestra durante unos segundos en pantalla alguno de los parámetros correspondientes (CL, OP,
LOOP) seguido de su valor, pulse UP/DOWN para cambiar el valor. Cuando este listo pase al
siguiente parámetro pulsando SET, y así de forma cíclica. Cuando termine con todos los
parámetros haga una pulsación larga en SET para volver al modo de reposo inicial.

5 Detalles adicionales en: https://www.makerhero.com/blog/como-funciona-um-rele-temporizador-digital/

Ir al índice 23
Tema 7: Otros módulos y dispositivos

Finalmente pulse STOP (pulso corto) para pasar a modo activo (ON en pantalla). El
dispositivo queda en espera de un flanco de subida en Trigger.

Figura 8: Temporizador digital HCW-M421

7. Otros módulos y dispositivos

7.1. Módulos de relés


Se tiene disponible 2 módulos de 8 relés electromecánicos para control de cargas, estos son
adecuados para usar con microcontroladores como Arduino, internamente incluye desacople óptico
y protección. Este puede funcionar con 5V DC. Cada entrada se encuentra a un voltaje lógico alto
cuando no está conectada. Cuando se alimenta Vcc con 5 V el voltaje en cada entrada es de unos
3.3 V respecto a neutro. Al colocar una entrada a tierra se activa el relé correspondiente.

Estos relés manejan hasta 10 A, a voltajes de 30VDC, o de 120V/220 VAC.

24 Ir al índice
7.2. Servo motores

Figura 9: Módulo de 8 relés electromecánicos del Instituto FIIIDT

Otros módulos disponibles son 2 módulos de 4 relés de estado sólido cada uno, el cual se
muestra en la figura 10. El modelo del relé es el G3MB-202P, este tipo de relé maneja corriente
alterna. La tensión de operación es de 5 V para el control. Cada relé tiene una capacidad de 2A,
hasta 240VAC. La corriente de trabajo es de unos 12mA por relé.

Figura 10: Modulo de 4 relés de estado sólido, se disparan con nivel bajo en la entrada

Estos relés están especificados como “disparador por nivel bajo” (Low level trigger), lo cual
significa que se activan con un nivel de voltaje lógico bajo. Se pueden conectar al Arduino
directamente.

7.2. Servo motores


En este caso se hace referencia a servo motores pequeños de 5 V disponibles, con torque

Ir al índice 25
Tema 7: Otros módulos y dispositivos

cercano a 2 Kg . cm al voltaje Vcc nominal. La gran mayoría de servo motores de 5 V comerciales


trabajan con señal PWM de 50 Hz, aunque pueden probarse con señales de hasta 200 Hz.

Se debe tomar en cuenta el peso de la carga de tal manera que no supere el peso límite que
puede mover un servo motor (generalmente dado como torque en Kg.cm). Como regla general, en
un servo con un par-motor de 2 Kg.cm, el producto del radio en el que cuelga la carga y su peso P
(o en su defecto la componente del peso perpedicular al radio), no debe superar el valor de 2 * g.
Por ejemplo, a un radio r = 2 cm del eje del motor, solo se podría colocar 1 kilos de carga (2/r)
máximo (carga con centro de masa lo mas cerca posible del punto de radio r = 2 cm).

En función de la marca y modelo de servo, con un mismo código se puede obtener un


comportamiento diferente de cada motor. Cada motor puede tener su propia velocidad y diferentes
rangos de ángulo, así como el mapeo de valores de ancho de pulso (ms) a posición angular
(grados). También se debe verificar el estado de cada motor, sobre todo si se han usado de forma
prolongada.

Por lo general los servomotores tienen una caja reductora de las revoluciones (RPM) del eje
del motor, para trabajar en la zona de operación de mayor torque de salida.

7.3. Motor de paso


Se dispone en el instituto de un motor de paso de 4 pines NEMA 14 (presumiblemente de 1.4
Kg.cm), el cual se usa para movimientos de presición. Estos motores pueden funcionar de forma
muy similar a un servo motor, pero requieren de un driver aparte para conectarse a un
microcontrolador como Arduino.

El motor de 4 pines consta de 2 bobinas independientes en el estator. Generalmente se


manejan en modo bipolar, es decir las bobinas se energizan de forma que se pueda cambiar el
sentido de la corriente en cada bobina. Típicamente cada bobina es conectada a un puente H,
como el típico L298N. El driver L298N tiene dos puentes internos en el chip.

Por lo general se usa una secuencia de 4 pasos (4 estados de energización) para hacer girar
el rotor, en cada paso se energizan las dos bobinas, de un paso al siguiente se invierte el sentido
de corriente de una bobina, en el próximo paso se invierte la corriente de la otra bobina, y así en lo
sucesivo. Esto se ve en el ejemplo de código siguiente el cual fue probado con motores de paso
bipolares.

Moviendo un motor paso a paso 4 cables NEMA en ambos sentidos con Arduino, sin usar
librería

// Mauricio Hernández (ene-2025)


--------------------------------------------------------------------
*/
#define IN1 12
#define IN2 11
#define IN3 10
#define IN4 9
#define STEPS 64 // 64 pasos
// Como no se dispone de derivación central en las bobinas del motor, se debe usar
cambios de sentido de corriente en las mismas

26 Ir al índice
7.3. Motor de paso
// Siempre estan encendidas ambas bobinas en cada paso, en el paso siguiente se invierte
la corriente de una bobina, y luego en la otra bobina, y así en lo sucesivo.
// Cada bobina se ha conectado a un puente H (L298N).

int steps_left=STEPS; // cantidad de pasos totales.


bool Direction = true;
int Steps = 0; // para indexar la secuencia, 4 en total
int tr = 40; // controla la velocidad de pasos por retardo, duración entre un paso y el
siguiente
// se envían los pasos al motor sin librería, escribiendo directamente en los 4 pines
según la secuencia
// la velocidad de giro depende del retardo de todas las instrucciones del bloque loop
int Paso [ 4 ][ 4 ] =
{ {1, 0, 0, 1},
{1, 0, 1, 0},
{0, 1, 1, 0},
{0, 1, 0, 1}
}; // arreglo del modo NORMAL, paso completo

void setup()
{
// Serial.begin(115200);
pinMode(IN1, OUTPUT); // los pines 1 y 2 son los terminales de una bobina, los pines 3 y
4 son para la otra bobina
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
}

void loop()
{ while(steps_left>0)
{
stepper() ; // Avanza un paso, no hay retardos internos
steps_left-- ; // Un paso menos
delay(tr); // esto controla la frecuencia de los pasos, colocar 10 como
mínimo (10 es aprox 100Hz)
//(con tr de 40ms, la medición manual de tiempo de vuelta es cercana 2.6 seg)
}
delay(1000); // tiempo entre giros del motor
Direction=!Direction; // inversión de giro condicional y reset del total de pasos
restantes.
steps_left=STEPS;
}

void stepper() // Avanza un paso, esta funcion no tiene retardos


{
digitalWrite( IN1, Paso[Steps][ 0] );
digitalWrite( IN2, Paso[Steps][ 1] );
digitalWrite( IN3, Paso[Steps][ 2] );
digitalWrite( IN4, Paso[Steps][ 3] );

SetDirection();
}

void SetDirection() // verifica si hay que cambiar el sentido de giro


{
if(Direction)
Steps++;
else
Steps--;
// Uso de operador residuo
Steps = ( Steps + 4 ) % 4 ; // al ingresar Steps = 4 (o -4), se devuelve Steps = 0
//al ingresar Steps = -1, se devuelve Steps = 3, Step = -2 devuelve Step = 2, y así
sucesivamente

Ir al índice 27
Tema 7: Otros módulos y dispositivos

En el ejemplo se usa el retardo propio del lazo loop añadiendo además retardos explícitos con
delay(tr) para establecer la velocidad de giro del motor. Los motores de paso típicamente manejan
frecuencias cercanas a 60 Hz (pasos por segundo). La variable Steps indexa el paso que se va a
usar en cada iteración, el operador residuo se encarga de “reiniciar” el valor de Steps a cero
cuando llega a 4 (o -4), como se explica en el código. La función stepper() es la encargada de
realizar cada paso estableciendo los voltajes en los 4 pines respectivos, y en general no produce
retardo apreciable. El sentido de giro del motor lo define el modo de cambio de Steps
(descendente o ascendente), que está en función del valor de Direction (1 o 0).

Cuando se requiera detener y posicionar el rotor en un ángulo fijo solo hay que detener el
avance de pasos dejando el motor en uno de los pasos de la secuencia (por ejemplo {1,0,1,0}), sin
desenergizar las bobinas, ya que al hacerlo el rotor queda libre de girar y el motor no aplica par.
Cada paso es un ángulo fijo de rotación que depende de la fabricación de motor (número de
polos).

Los pasos por vuelta (PPR por siglas) del motor dependen de su construcción, en específico
de la cantidad de pares de polos norte-sur en la periferia interior del estator. Para más detalles del
funcionamiento interno del motor de paso, puede consultar información disponible en Internet.
Valores típicos de PPR son 64 y 49 pasos, entre otros. En realidad se suele hablar de medios
pasos (half step), cuando un polo del rotor se alinea justo al frente de un polo opuesto del estator, y
además puede colocarse en el medio entre dos polos adyacentes del estator.

7.3.1. Motores de paso unipolares


Adicionalmente, se puede disponer de motores de paso de 5 y 6 cables, estos son llamados
motores unipolares, lo cual se explica en breve. Generalmente constan de dos bobinas que, a
diferencia de los motores de 4 cables, tienen una toma central. Es decir, en la mitad de cada
bobina hay un punto de conexión. En el motor de 5 cables las tomas centrales de las dos bobinas
estan conectadas internamente. En el motor de 6 cables las tomas centrales no están unidas.

El motor de 6 cables se suele manejar como uno de 5 cables conectando las tomas centrales.
Las tomas centrales se conectan a neutro y se alimentan las bobinas con un voltaje positivo en sus
extremos, en una cierta secuencia. Se explica el modo de 8 pasos con ayuda de la figura 11 y la
tabla 7.1. Cada bobina A y B se divide en dos mitades (fases + y -) por la toma central, se muestra
la secuencia de encendido de las fases en el tiempo, dando un total de 8 pasos que se repiten
cíclicamente. Un valor 1 en la tabla 7.1 indica que se energiza esa fase con un voltaje alto en el
extremo no común. Un cero indica voltaje cero.

28 Ir al índice
7.3. Motor de paso

Fase -

Bobina B

Fase + Fase -
Paso 5
GND

Paso 6
Paso 7

Bobina A
Fase +

GND

Figura 11: Diagrama del bobinado simplificado del motor de paso unipolar de 6 cables, las
flechas representan 3 pasos de giro de la tabla 7.1

Tabla 7.1: Tabla de pasos en modo half-step de motor de paso unipolar

Bobina A Bobina B

Paso Fase + Fase - Fase + Fase -

1 1 0 0 0

2 1 0 1 0

3 0 0 1 0

4 0 1 1 0

5 0 1 0 0

6 0 1 0 1

7 0 0 0 1

8 1 0 0 1

Ir al índice 29
Tema 7: Otros módulos y dispositivos

Observe que en ningún paso de la tabla 7.1 se alimentan dos fases de una misma bobina,
siempre de bobinas diferentes, esto se debe tomar en cuenta al momento de conectar
correctamente el motor al controlador.

El diagrama de la figura 11 es una simplificación de las bobinas del motor, que realiza una
vuelta completa con 8 pasos. La mayoría de motores realiza muchos más pasos en una vuelta,
debido a la distribución de las bobinas en la periferia del estator.

Los motores de paso también se clasifican por la construcción del rotor, existen los de imán
permanente, de reluctancia variable (rotor sin polos magnéticos) y los híbridos, estos últimos
tienen un rotor magnetizado con polos norte y sur alineados con el propio eje del rotor.

7.3.2. Controlando motores unipolares


En este ejemplo de código, se muestra el control de un motor unipolar general, el cual es
manejado por una secuencia de 8 pasos como se describió previamente. Se usa un puente dual
como el L298N. Cada puente se conecta a una de las bobinas del estator.

/* ----------------------------------------------------------------

Moviendo un motor paso a paso de 6 cables (o de 5 cables) en ambos sentidos con Arduino,
sin usar librería

// Mauricio Hernández (Ene-2025)


--------------------------------------------------------------------
*/
#define IN1 12 // bobina A
#define IN2 11 // bobina B
#define IN3 10 // bobina A
#define IN4 9 // bobina B
#define STEPS 50 // // 65, 50
// El motor de 6 cables, generalmente se conecta como uno de 5 cables, uniendo los 2
terminales centrales, estos serán conectados a GND
// Al usar el modo half-step, no se energizan 2 devanados de una misma bobina, siempre de
bobinas distintas
// Es decir, si se tienen bobinas aisladas A y B, se activa una mitad de la bobina A y
una mitad de la bobina B a la vez
//

int steps_left=STEPS; // cantidad de pasos totales.


bool Direction = true;
int Steps = 0;
int tr = 20; // controla la velocidad de pasos por retardo, duración entre un paso y el
siguiente
// se envian los pasos al motor sin librería, escribiendo directamente en los 4 pines
según la secuencia
// la velocidad de giro depende del retardo del programa principal loop
int Paso [ 8 ][ 4 ] =
{ {1, 0, 0, 0},
{1, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 1},

30 Ir al índice
7.3. Motor de paso
{1, 0, 0, 1}
}; // arreglo del modo half, medios pasos

void setup()
{
// Serial.begin(115200);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
}

void loop()
{ while(steps_left>0)
{
stepper() ; // Avanza un paso, no hay retardos internos
steps_left-- ; // Un paso menos
delay(tr); // esto controla la frecuencia de los pasos, colocar 10 como
mínimo (10 es aprox 100Hz)
//
}
delay(1000);
Direction=!Direction; // inversión de giro y reset del total de pasos restantes.
steps_left=STEPS;
}

void stepper() // Avanza un paso, esta funcion no tiene retardos


{
digitalWrite( IN1, Paso[Steps][ 0] );
digitalWrite( IN2, Paso[Steps][ 1] );
digitalWrite( IN3, Paso[Steps][ 2] );
digitalWrite( IN4, Paso[Steps][ 3] );

SetDirection(); // incrementa o decrementa la cuenta de pasos (de 0 a 7), Step sirve


para indexar la secuencia a usar
}

void SetDirection()
{
if(Direction)
Steps++;
else
Steps--;
// Uso de operador residuo
Steps = ( Steps + 8 ) % 8 ; // al ingresar step = 8 (o -8), se devuelve step = 0
//al ingresar Step = -1, se devuelve Step = 7, Step = -2 devuelve Step = 6, y así
sucesivamente
}

El código anterior muestra cómo controlar de forma sencilla un motor de paso unipolar
mediante la secuencia de 8 pasos 6. La función Stepper es la encargada de establecer cada paso,
escribiendo en los pines de salida. La función SetDirection() va cambiando el valor de Steps entre
0 y 7 para indexar cada paso en el arreglo Paso.

6 Detalles adicionales en: https://robots-argentina.com.ar/didactica/arduino-motor-paso-a-paso-28byj-48-y-modulo-uln2003/

Ir al índice 31
Tema 8: Anexos

8. Anexos

8.1. Preparación del software Arduino

8.1.1. Windows
Se puede hacer la instalación del Arduino IDE en Windows, versión legacy (1.8.19), además
del driver del chip CH340G para la comunicación UART del IDE con placas clon de Arduino. Puede
usarse también una versión más reciente del IDE Arduino (2.3.2 o posterior).

Figura 12: Fecha y versión del controlador del chip CH340G y similares (Windows)

Una vez abierto el IDE, seleccione en el menú Herramientas la placa Arduino a utilizar. El
programador seleccionado7 para las placas Nano clon usualmente es el AVRISP mkii, pero puede
probar con otro. Se selecciona generalmente el procesador objetivo ATMega328p con
bootloader8 antiguo para usarlo con la placa Nano. Estas opciones están en el menú Herramientas
del IDE de Arduino.

7 En Arduino IDE se hace clic en Herramientas > Programador y luego en el programador que tenga conectado con el microcontrolador objetivo.

8 El bootloader es un microcódigo del controlador ATMega que contiene la configuración necesaria para que inicie ya sea como lo hace en una placa
Arduino, o bien como otro microsistema que no sea Arduino.

32 Ir al índice
8.1. Preparación del software Arduino

Figura 13: Opciones del menú herramientas en Arduino IDE 2.3.2 (Windows)

Las librerías se colocan en la carpeta Library de la instalación de Arduino IDE 1.8.x. Se debe
colocar la directiva “include” en el código para incluir una librería, por ejemplo así: “include pwm.h”.
Las librerías constan de ficheros h y cpp.

La versión de Arduino IDE 2.3.2 (o superior) por defecto instala las librerías en la carpeta
Documentos/Arduino/libraries del usuario de Windows.

8.1.2. Linux
En el caso de Linux hay algunos pasos adicionales para realizar correctamente la instalación
del IDE, se requiere tener permisos para acceder al puerto serie desde la aplicación Arduino IDE.
Puede descargar el instalador del IDE comprimido disponible en el sitio web, se sugiere
descomprimir en la carpeta “Home” u otra conocida. Este tiene unos archivos de shell llamados
Arduino-setup.sh e install.sh o similar. Ejecute el archivo Arduino-setup en primer lugar, luego el
archivo install:

./Arduino-setup.sh $USER

./install.sh

O tiene la opción de ejecutar solo el archivo install como superusuario:

sudo ./install.sh

Ir al índice 33
Tema 8: Anexos

8.1.2.1. Error en puerto serie


Usualmente en Linux se obtiene un error de denegación de acceso al puerto serie, al intentar
subir el código compilado a la placa Arduino, ya que por lo general el usuario de la sesión no tiene
privilegios para ello.

Para tener permisos de usar el puerto serie se puede añadir el usuario Linux al grupo
propietario del fichero de puerto serie, dicho grupo debe tener los permisos de lectura y escritura
sobre el fichero de puerto serie. Abra el Arduino IDE, conecte la placa y ejecute lo siguiente en
terminal Linux para desplegar detalles de los grupos del archivo de puerto serie:

ls -l /dev/ttyUSB0

ttyUSB0 es el fichero de puerto serie (puede tener otro nombre en su sistema). El nombre del
fichero se puede ver en la ventana principal de Arduino IDE, en el puerto al que se conecta la
placa. Se debe mostrar a la salida algo como:

crw-rw---- 1 root dialout 188, 0 nov 13 01:46 /dev/ttyUSB0

En el ejemplo el grupo propietario se llama dialout y tiene permisos de escritura y lectura (rw)
sobre el fichero. Se agrega el usuario Linux al grupo dialout en este caso:

sudo usermod -a -G dialout <username>

username es el nombre de usuario de la sesión Linux que se está usando. Se debe tener
cuidado de no omitir la opción -a, esta opción activa la modalidad de añadir nuevo grupo, en lugar
de sustituir los grupos actuales a los que pertenece username.

8.2. Pinout Arduino


Los pines del Arduino Uno y sus funciones se muestran en la figura 149.

9 Fuente de la imagen: Wikimedia en https://commons.wikimedia.org/wiki/File:Pinout_of_ARDUINO_Board_and_ATMega328PU.svg

34 Ir al índice
8.2. Pinout Arduino

Figura 14: Mapa de pines de Arduino Uno

Los pines para PWM están designados del mismo modo en Arduino Nano y Uno: PD3, PD5,
PD6, PB1, PB2, PB3. En el código se identifican respectivamente como 3, 5, 6, 9, 10, 11. En el IDE
se usa A0, A1, … para hacer referencia a los pines de entrada analógica.

Un ejemplo de uso de la designación de pines se muestra a continuación:

int potpin = A0; // entrada analógica


void setup() {
myservo.attach(9); // asocia el pin 9 a las funciones del servo
}

void loop() {
val = analogRead(potpin); // en el pin son valores de 0 a 5 voltios
val = map(val, 0, 1023, 0, 180); // pasa el valor leido (0-1023) a grados (0-180)
myservo.write(val); // val es un valor en grados
delay(15); // tiempo de espera
}

Ir al índice 35

También podría gustarte