Velocidad del viento: Anemómetro

Referencia: https://es.wikipedia.org/wiki/Anem%C3%B3metro

Con el objetivo de medir la velocidad del viento, se dispone de un motor eléctrico al que se le añadieron 3 aspas. Ante la exposición al viento, las aspas hacen girar el motor generando un voltaje entre los terminales.

La relación velocidad de viento y voltaje debe obtenerse experimentalmente por algún medio propuesto y así obtener la tabla que relaciona ambos valores.

 

 Velocidad de viento Voltage generado por el motor

Con los valores obtenidos, realice la función que describa la relación entre los valores. Usar algun método de interpolación para usar un polinomio que describa la realción entre los valores. Revisar unidad05 Interpolación de MATG1013

http://blog.espol.edu.ec/matg1013/

Luego usando la función es posible usar un arduino con una pantalla para mostrar los valores de voltaje transformados a su equivalente de velocidad.

Se usa un pin de sensor analógico para realizar la lectura de voltajes.

Recuerde revisar el voltaje máximo obtenido del motor en la parte experimental, para que en caso que se exceda la capacidad del sensor de 5V, se utilice un divisor de voltaje con resistencias.

Instrucciones Arduino

/* Lecturas del anemometro a Km/h
 *  http://blog.espol.edu.ec/edelros/velocidad-del-viento/
 *  Referencia: ReadAnalogVoltage
 *  http://www.arduino.cc/en/Tutorial/ReadAnalogVoltage
*/

#include <LiquidCrystal_I2C.h> // LCD por I2C
#include <Wire.h> //comunicación I2C

// Pantalla LCD. npxmp
const byte mp = 16;
const byte np = 2;
LiquidCrystal_I2C lcd(0x27,mp,np); 

int SensorPin = 0; // Analógico

void setup() {
  // inicializa monitor serial
  Serial.begin(9600);
  
  // Inicializa Pantalla
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Anemometro Voltaje a Kmh");
  lcd.setCursor(0,1);
  lcd.print("/matg1013");
  delay (2000);
  lcd.clear();
}

void loop() {
  double valorsensor = analogRead(SensorPin);
  float voltaje = valorsensor * (5.0 / 1023.0);
  float kmh = convertirV_Kmh(voltaje);

  // SALIDA A MONITOR SERIAL
  Serial.print("volts: ");
  Serial.println(voltaje);
  Serial.print(" Km/h: ");
  Serial.print(kmh);
  
  // SALIDA A PANTALLA LCD
  lcd.clear();
  lcd.setCursor(0,0);
  // muestra decimales = 3
  lcd.print("Votaje: ");
  lcd.print(voltaje,3);
  lcd.setCursor(0,1);
  lcd.print("Km/h: ");
  lcd.print(kmh,3);

  delay (500);
}

float convertirV_Kmh(float voltaje){
    float resultado = 0;
    
    // usar polinomio del experimento
    resultado = 2.0+10.0*voltaje;

    return resultado;
}

Arreglo de capacitores y resistencias

Para observar varias curvas de carga y descarga del capacitor, se propone usar un arreglo de capacitores y resistencias.

Los capacitores se encuentran en paralelo para realizar la variación de capacitancia total. Si se desconecta uno de ellos la capacitancia total es la suma de solo los que quedan conectados.

En el caso de las resistencias, se propone variar la resistencia de descarga, Se presentan resistencias en serie para disponer puntos de valores intermedios. otras estan en paralelo para tomar los valores de solo una de ellas al ser conectadas.

Con ésto se logrará observar diferentes datos de descarga, en números y visualización de curvas.

Esquematico

Protoboard

 

Pulso Cardiaco

Se obtienen los datos para describir el comportamiento de un pulso cardiaco a partir de un "Sensor cardiaco" para arduino, luego pueden ser procesados con los conceptos teóricos del caso, por ejemplo: MATG1052 Métodos Numéricos.

Un ejemplo de los datos capturados "en vivo" se muestran a continuación.

Los datos del sensor se transmiten por medio del puerto USB al computador, donde son capturados con el programa Python. Los datos se agregan a un arreglo o vector y se pueden procesar con los algoritmos desarrollados en el curso.

1. Piezas y Partes

Para el ejemplo se requieren los siguiente elementos:

  • Arduino Uno
  • Sensor de pulso cardiaco
  • cables de conexión

En las figuras se muestra el tipo de  sensor que se aproxima a un dedo índice del sujeto  bajo pruebas.

El sensor dispone de tres pines: Señal (S), Vcc(+) y GND (-)

2. Conexión Arduino

La señal obtenida es de tipo analógica, por lo que se debe conectar a pines analógicos del Arduino, en el ejemplo etiquetado A0. El arduino cuantifica el valor y lo envía al puerto serial para procesar los datos.

El diagrama básico de conexión se muestra en el esquema.

Para evitar interferencias en las lecturas, se recomienda cubrir el sensor durante el uso con material obscuro. De ésta forma el sensor recibirá solo la luz emitida por el LED (foquito).

La lectura se debe realizar en reposo para mantener consistencia de los datos entre los pulsos y evitar lecturas con errores por movimientos del sujeto bajo pruebas.

Los datos obtenidos se envían al puerto serial para ser procesados.

3. Instrucciones Arduino

Las instrucciones en arduino son sencillas, requieren identificar el pin de conexión, tiempo entre muestras o "retraso" y la lectura del valor.

Cargue las siguientes instrucciones en al arduino para obtener las lecturas.

// Monitor de pulso cardiaco
//  El sensor en PIN Analógico A0

int SensorPin = 0;
int retraso = 100;
void setup (){
  Serial.begin (9600);
  }
void loop (){
    double valor = analogRead (SensorPin);
    Serial.println (valor); 
    delay (retraso);
  }

Las lecturas se pueden observar en el IDE Arduino, menú de Herramientas/monitor Serie.
Sin embargo, para la captura de datos con Python, no se debe tener abierta la ventana del monitor serie del IDE Arduino, pues el puerto com puede ser usado por único un programa al mismo tiempo.

4. Captura y Gráfico de datos en Python

Las siguientes instrucciones de Python permiten la captura de datos desde el puerto serial, conexión USB, del arduino.

Para obtener el número del puerto "com#" se observa el valor en el IDE Arduino que tiene en el menú Herramientas/puerto, con lo que se actualiza la variable "puerto" en las instrucciones siguientes.

Para graficar los datos se conservan una ventana de 50 valores en el vector yi.
Si observa los valores de yi, se encuentran en el rango definido por los valores a y b, modifique si su sensor esta calibrado en otro rango.

# Grafica "en vivo" para actualizar datos
# recibidos por puerto serial
# Plantilla para proyecto, datos de prueba aleatorios
# http://blog.espol.edu.ec/edelros/pulso-cardiaco/
# Tarea: Actualizar los datos en la función una trama para yi
#        Con los obtenidos desde el puerto Serial

import numpy as np
import serial, time
import matplotlib.pyplot as plt
import matplotlib.animation as animation

# Datos puerto Serial
puerto  = 'com10' # revisar en arduino
baudios = 9600

# PARAMETROS DE LA GRAFICA
retraso = 5
ventana = 50
# rango de lectura
a = 495
b = 535

# Datos a graficar
yi = [500]*ventana

# PROCEDIMIENTO

# Inicializa comunicación con arduino
arduino = serial.Serial(puerto, baudios)
arduino.setDTR(False)  
time.sleep(0.3)  
# limpia buffer de datos anteriores
arduino.flushInput()
arduino.setDTR()  
time.sleep(0.3)
print('\nEstado del puerto: ',arduino.isOpen())
print('Nombre del dispositivo conectado: ', arduino.name)
print('Dump de la configuración:\n ',arduino)
print('\n###############################################\n')


# GRAFICA figura
figura = plt.figure()
grafica = figura.add_subplot(111)
grafica.set_ylim(a,b)
grafica.set_title('Pulso Cardiaco')

# Linea de pulso y ventana de datos a graficar
tamano = ventana
pulso  = yi[-tamano:]
lineaPulso, = grafica.plot(pulso, 'y')

# linea del pulso y puntoreferencia:
puntoPulso, = grafica.plot(len(yi)-1,yi[-1],'ro')

# Nueva Trama
def unatrama(i,yi,ventana):

    while (arduino.inWaiting()==0):
        pass #esperar hasta recibir un dato
    linea   = arduino.readline() # lee puerto serial
    lectura = linea.decode().strip() # elimina /r/n
    undato  = float(lectura)

    # actualiza datos xi, yi
    # xi.append(angulo) 
    yi.append(undato)# numero del dato ejemplo

    # ventana de datos a graficar
    Pulso = yi[-ventana:]
    lineaPulso.set_ydata(Pulso)
    
    # Linea y punto del Pulso
    puntoPulso.set_ydata(Pulso[-1])  

    # Presenta valores últimos valores en pantalla
    print(Pulso)

    # Si los datos son más de 1000
    # Elimina el más antiguo del historial
    if len(yi)>ventana:
        # xi.pop(0)
        yi.pop(0)
    
    return()

# Animación
ani = animation.FuncAnimation(figura,
                              unatrama,
                              fargs=(yi,ventana),
                              interval=retraso,
                              blit=True)

plt.show()

Referencias: Sensor https://pulsesensor.com/

1. Sensor Ultrasonico HC-SR04

El sensor de ultrasonido funciona como un sonar, emite un pulso de ultrasonido por un tiempo de disparo, y luego mide el tiempo de retorno del eco.

El diagrama muestra el concepto a ser aplicado:

Conversión de tiempor dt_Echo a distancias

La velocidad del sonido en el aire es de 343.2 m/s a 20°C y 50% de Humedad.

Referenciahttps://es.wikipedia.org/wiki/Velocidad_del_sonido

Usando la fórmula básica de velocidad se tiene que:

\text{velocidad del sonido} = \frac{\delta x}{\delta t} = 343.2\text{ }m/s \delta x = 343.2\text{ }\delta t

Para convertir los tiempos a distancias, considere que el recorrido del pulso es de ida y vuelta, por lo que la distancia del dispositivo al objeto será la mitad.
Considere además que la fórmula está dada en convertirla a segundos y los datos estan en μs

\delta t = \frac{Echo}{2} \frac{1\text{ }s}{1000000\text{ }\mu s}

aplicando lo anterior, y convirtiendo a cm

\delta x = \Big[ 343.2 \frac{m}{s} \frac{100\text{ }cm}{1\text{ }m} \Big] \Big[ \frac{Echo}{2} \frac{1\text{ }s}{1000000\text{ }\mu s} \Big]

se obtiene la fórmula a usar en cm:

distancia = 0,01716 \text{ } Echo

Desarrollo del concepto

Se desarrolla el concepto en dos partes:

  • En arduino se realizan las lecturas de echo del sensor de ultrasonido, los datos se envian al computador por puerto serial (USB)
  • Los dados se procesa en la computadora con Python, fórmulas, conversión de unidades, gráficas, etc.

 Lecturas de datos en Arduino

En la parte de Arduino se usan los siguientes componentes:

  • Arduino UNO con cable USB
  • Sensor de ultrasonido HC-SR04
  • Cables de Conexión (4 hembra macho)

El arduino envía al computador los tiempos de recorrido del PULSO en μs.

La recepción de datos en la computadora en forma Serial se detallan las secciones con Python:

Serial – Envia y Recibe texto
http://blog.espol.edu.ec/edelros/serial-envia-y-recibe-texto/
Serial – Lectura datos
http://blog.espol.edu.ec/edelros/serial-lectura-datos/

Sensor  HC-SR04

El dispositivo HC-SR04 se opera como un dispositivo que requiere alimentación (Vcc y GND), una señal de disparo del pulso (Trigger) y una señal para la lectura del eco (Echo).

La duración del pulso se controla con tpulsoON, y el tiempo de apagado con tpulsoOFF, se usa un tiempo adicional dt_apaga, para asegurarse que no exista interferencia entre el apagado del pulso y la lectula del retorno del eco.

La conexión del dispositivo con el Arduino como controlador, se realiza por medio de los pines indicados en el script.

/* Sensor de ultrasonido HC-SR04
 *  Válido con objetos distantes entre 2 y 400 cm (aproximado)
usado para medir tiempo de returno de un pulso
            __________ 
____________| PULSO  |___
tpulsoOFF   tpulsoON  dt_apaga

dt_Echo e el tiempo de retorno del PULSO
los tiempos son en microsegundos
Los datos se envian por Serial para procesar en Python
 * http://blog.espol.edu.ec/edelros/sensor-ultrasonido-hc-sr04/
 */

// Disparo del PULSO, Trigger
int TriggerPin = 12;
int tpulsoON = 15;
int tpulsoOFF = 2000;

// Sensor del Retorno del PULSO, Echo
int EchoPin = 11;
int dt_apaga = 10;

// tiempo de retorno Echo
float dt_Echo;

void setup() {
  Serial.begin(9600);
  pinMode(TriggerPin, OUTPUT);
  pinMode(EchoPin, INPUT);
}
 
void loop(){
    // tiempo entre PULSOs: tpulsoOFF
    digitalWrite(TriggerPin, LOW);
    delayMicroseconds(tpulsoOFF);
    // Dispara PULSO de duración: tpulsoON  
    digitalWrite(TriggerPin, HIGH);
    delayMicroseconds(tpulsoON); 
    digitalWrite(TriggerPin, LOW); 
    delayMicroseconds(dt_apaga);  
    
    // Lectura sensor pulso: tiempo de echo 
    dt_Echo = pulseIn(EchoPin, HIGH);  
    Serial.println(dt_Echo);
    delay(10);
}

Referencia

https://www.arduino.cc/en/tutorial/ping

https://create.arduino.cc/projecthub/FunguyPro/how-to-use-an-hc-sr04-ultrasonic-sensor-with-arduino-8d646f


Procesamiento de datos en Python

El arduino envía al computador los tiempos de recorrido del PULSO en μs, se usa la fórmula para encontrar la distancia en cm:

distancia = 0,01716 \text{ } dtEcho

La lectura y aplicación de fórmula en Python tiene como resultado por ejemplo de 12 cm:

12.149280000000001
11.72028
12.13212
11.806080000000001
12.13212
11.703120000000002
11.703120000000002
12.217920000000001
11.788920000000001
11.685960000000001
11.668800000000001

Nota: La medición se realizó sosteniendo con la mano el sensor apuntando al una superficie plana, por lo que se muestran variaciones en la lectura. Realizar sus propias mediciones al respecto en superficies fijas, y con el sensor fijo a un soporte.

En caso de realizar una gráfica de los datos recibidos, se crea un vector datos, con tamaño "ventana" para graficar los puntos.

# Datos desde puerto Serial
# generados desde arduino

import serial, time

# INGRESO
puerto = 'com10'
baudios = 9600
ventana = 20 # tamaño ventana datos
datos=[] # vector para graficar

# PROCEDIMIENTO
arduino = serial.Serial(puerto, baudios)
arduino.setDTR(False)
time.sleep(0.3)

# limpia buffer de datos anteriores
arduino.flushInput()  
arduino.setDTR()  
time.sleep(0.3)
print('\nEstado del puerto: ',arduino.isOpen())
print('Nombre del dispositivo conectado: ', arduino.name)
print('Dump de la configuración:\n ',arduino)
print('\n###############################################\n')

# Lectura de datos
while True:
    #esperar hasta recibir un dato
    while (arduino.inWaiting()==0):
        pass
    
    # leer linea desde puerto serial
    lectura = arduino.readline()
    # binario a texto, elimina /r/n
    texto = lectura.decode().strip()
    valor = float(texto)

    # Aplica fórmula
    valor = 0.01716 *valor

    datos.append(valor)
    
    # mantiene el tamaño "ventana" de datos 
    if (len(datos)>=ventana):
        datos.pop(0)
    print(valor)
    
# Cerrar el puerto serial.
serial.Serial.close

2. Servo - Barrido

El barrido del radar se realiza usando un motor servo controlando el ángulo de posición. Luego de un tiempo dt, se avanza un poco el ángulo entre un rango [a,b], al llegar a los límites se invierte el sentido del movimiento.

Los componentes para implementar el barrido son:

  • Arduino UNO con cable USB para PC
  • Servo
  • cables de conexión

Los dispositivos se pueden conectar para pruebas directamente al arduino, se puede usar un proto, o usar las conexiones de los pines de la parte central para alimentación del motor.

Para el caso de motores, servos, se prefiere alimentar al motor con una fuente externa. Se dió el caso que usando un arduino uno genérico, al usar juntos el ultrasonido y el servo, se obtenían lecturas erroneas del ultrasonido, al desconectar el motor, las lecturas volvían a la normalidad. En consecuencia, hacía falta energía para alimentar los dos dispositivos. Recuerde las limitaciones de corriente en el arduino.

Instrucciones Arduino

Las instrucciones en Arduino para controlar el servo propuestas son:

/* Servo - Barrido entre[a,b]
* Se usa un tiempo de espera entre cada ángulo
* http://blog.espol.edu.ec/edelros/servo-barrido/
* edelros@espol.edu.ec
 */

#include <Servo.h>

// Servo Pin y Objeto
int servoPin = 3; 
Servo Servo1; 

int avance = 20;
int angulo = avance;
int espera = 1000; //ms

int a = angulo;
int b = 180 - avance;

void setup(){
    Serial.begin(9600);
    Servo1.attach(servoPin); 
}
void loop(){
    Servo1.write(angulo);
    delay(espera);
    angulo = angulo + avance;
    
    // Sentido de rotación
    if (angulo>=b or angulo<=a){
        avance = -avance;
    }
    
    // Salida a Serial
    Serial.print(angulo);
    Serial.print(",");
    Serial.println(avance);
}

El siguiente paso es combinar el movimiento del servo con el sensor de ultrasonido.


Referencia

https://www.arduino.cc/en/Tutorial/Sweep

 

3. Radar - Integrando partes

Para que el radar realice el barrido de lo que se encuentra al frente, se integran  las partes del sensor de ultrasonido y el Servo-Barrido para el radar.

Integrando las partes (Hardware)

Un ejemplo de ensamble del hardware se muestra en las imágenes. Se requiere un poco de trabajo para la integración mecánica usando  envases plásticos por facilidad de corte y/o reemplazo.

Vista lateral

El esquema de conexión para las partes integradas es:


Integrando las Instrucciones Arduino

Para integrar las instrucciones de cada componente, por simplicidad se convierte una de ellas en una función. Por ejemplo, se realiza la lectura del sensor ultrasonido como una función, se mueve el servo, se envian los resultados por puerto serial.

Para revisar la declaración de una función se adjunta la referencia al final del documento.

/* Radar Ultrasonido
 *  integrando: Servo - Barrido
 *  y Sensor Ultrasonido HC-SR04
 *  http://blog.espol.edu.ec/edelros/category/arduino/radar-ultrasonido/
 *  La lectura del ultrasonido se convierte en una función UnPulso()
 *  Los datos de las lecturas se envian por Serial
 *  Tarea: Desarrollar el procesamiento de los datos en Python
 */

#include <Servo.h> 

// Servo Pin y Objeto
int servoPin = 3; 
Servo Servo1; 

int avance = 20;
int angulo = avance;
int espera = 1000; //ms

int a = angulo;
int b = 180 - avance;

// Disparo del PULSO, Sensor del Retorno de pulso
int TriggerPin = 12;
int EchoPin = 11;

// tiempos: pulso, sin pulso, eco
int tpulsoON = 15;
int tpulsoOFF = 2000;
int dt_apaga = 10;
float dt_Echo;

void setup(){
    Serial.begin(9600);

    Servo1.attach(servoPin); 

    pinMode(TriggerPin, OUTPUT);
    pinMode(EchoPin, INPUT);
}
void loop(){
    Servo1.write(angulo);
    dt_Echo = UnPulso();
    
    delay(espera);
    angulo = angulo + avance;
    
    // Sentido de rotación
    if (angulo>=b or angulo<=a){
        avance = -avance;
    }
    
    // Salida a Serial
    Serial.print(angulo);
    Serial.print(",");
    Serial.println(dt_Echo);
}
 
float UnPulso(){
    // tiempo entre PULSOs: tpulsoOFF
    digitalWrite(TriggerPin, LOW);
    delayMicroseconds(tpulsoOFF);
    // Dispara PULSO de duración: tpulsoON  
    digitalWrite(TriggerPin, HIGH);
    delayMicroseconds(tpulsoON); 
    digitalWrite(TriggerPin, LOW); 
    delayMicroseconds(dt_apaga);  
    
    // Lectura sensor pulso: tiempo de echo 
    dt_Echo = pulseIn(EchoPin, HIGH);
    return dt_Echo;
}

Tarea: Continuar con el procesamiento de los datos en Python, usando como base la Gráfica de Radar

Referencia

https://www.arduino.cc/en/Reference/FunctionDeclaration

4. Grafica radar - Python

Terminados los pasos anteriores, se usan los datos enviados por el arduino al puerto serial para realizar el grafico.

La gráfica se presenta en formato de coordenadas polares, que en la librería matplotlib se requieren en radianes. Antes de graficar se realiza una conversión.

Del ejemplo se  muestra que se crean tres elementos:

  • Pulso Punto, que marca el punto de la lectura "actual", la última recibida.
  • Pulso Linea, que traza la distancia entre el punto de origen y lectura "actual"
  • barrido, que grafica una linea de barrido del radar usando un grupo de puntos recibidos y almacenados en un vector.

Dado que los datos llegan "en vivo", la gráfica es animada, y se actualizan los datos de los tres elementos generando una nueva imagen en la función unatrama() de la animación. En la animación, se usa la opción 'blit' en la para actualizar solo los elementos del gráfico, manteniendo los ejes, títulos, etc sin cambios.

En las instrucciones en Python se usan números aleatorios para realizar la presentación.

Tarea

Integrar con la entrega de datos del experimento arduino. Observe que se debe sincronizar los tiempos de lectura del sensor, servo, datos Serial, gráfico, junto a los rangos de distancia del gráfico.

Use como referencia, Grafica animada:

http://blog.espol.edu.ec/edelros/grafica-animada-datos-serial/

Intrucciones en Python

# Grafica "en vivo" para actualizar datos
# recibidos por puerto serial
# Plantilla para proyecto, datos de prueba aleatorios
# http://blog.espol.edu.ec/edelros/category/arduino/radar-ultrasonido/
# Tarea: Actualizar los datos en la función una trama para ri,yi
#        Con los obtenidos desde el puerto Serial

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
import random as rnd

# PARAMETROS DE LA GRAFICA
# angulo
avance = 20
angulo = avance
a = avance
b = 180-avance
# alcance del radar
alcance = 50

# Datos a graficar
xi = [avance]
yi = [0]

# GRAFICA figura
# tiempo entre tramas
retraso = 1000
figura = plt.figure()
grafica = figura.add_subplot(111, projection='polar')
grafica.set_xlim(0,np.pi)
grafica.set_ylim(0,alcance)
grafica.set_title('Radar de Ultradonido')

# Linea de barrido y ventana de datos a graficar
tamano = (180//avance)//2
# El gráfico usa radianes
ri = np.array(xi[-tamano:])/180*np.pi
di = yi[-tamano:]
barrido, = grafica.plot(ri, di, 'y')

# linea del pulso y puntoreferencia:
pulsox = [0,ri[-1]]
pulsoy = [0,di[-1]]
PulsoLinea, = grafica.plot(pulsox,pulsoy,'g')
PulsoPunto, = grafica.plot(ri[-1],yi[-1],'go')

# Nueva Trama
def unatrama(i, xi, yi,angulo,avance):

    # ---DATOS EJEMPLO|INICIO

    # Posición en ángulo
    if len(xi)>0:
        angulo = xi[-1]
    else:
        angulo = 0
    # Dirección de barrido
    direccion = 1
    if (len(xi)>=2):
        sentido = xi[-1]-xi[-2]
        direccion = np.sign(sentido)
        if angulo>=(180-avance) and sentido>0:
            direccion = -1
        if angulo<=avance and sentido<0:
            direccion = 1

    angulo = angulo + direccion*avance
   
    # alcance del radar
    alcance = 50
    # Recibe un dato nuevo| ejemplo usa aleatorio
    undato = int(rnd.random()*alcance)+1
    # ---DATOS EJEMPLO|INICIO

    # actualiza datos xi, yi
    xi.append(angulo) 
    yi.append(undato)# numero del dato ejemplo

    # ventana de datos a graficar
    tamano = (180//avance)//2

    # Linea de radar, el gráfico usa radianes
    ri = np.array(xi[-tamano:])/180*np.pi
    di = yi[-tamano:]
    barrido.set_xdata(ri)
    barrido.set_ydata(di)
    
    # Linea y punto del Pulso
    pulsox = [0,ri[-1]]
    pulsoy = [0,yi[-1]]
    
    PulsoLinea.set_xdata(pulsox)
    PulsoLinea.set_ydata(pulsoy)

    PulsoPunto.set_xdata(pulsox[1])
    PulsoPunto.set_ydata(pulsoy[1])  

    # Presenta valores últimos valores en pantalla
    print(xi[-1],yi[-1])

    # Si los datos son más de 1000
    # Elimina el más antiguo del historial
    if len(xi)>1000:
        xi.pop(0)
        yi.pop(0)
    
    return()

# Animación
ani = animation.FuncAnimation(figura,
                              unatrama,
                              fargs=(xi, yi,angulo,avance),
                              interval=retraso,
                              blit=True)

plt.show()

Referencias

https://matplotlib.org/examples/pylab_examples/polar_demo.html

https://learn.sparkfun.com/tutorials/graph-sensor-data-with-python-and-matplotlib/speeding-up-the-plot-animation

Calculadora con funciones

Se asigna una tecla para calcular el valor de una función, en éste caso se sustituye la tecla '*' por una función factorial como ejemplo:

Al presionar la tecla "!" se obtiene el valor del factorial del número.

5 !
120.000

Instrucciones Arduino

El archivo de intrucciones modificado a partir de calculadora básica y la función de la tecla escrita al final se muestra a continuación:

// Calculadora tecla factorial 2018.06.24
// blog.espol.edu.ec/edelros
// edelros@espol.edu.ec 

#include <LiquidCrystal_I2C.h> // LCD por I2C 
#include <Wire.h> //comunicación I2C 
#include <Keypad.h> //Teclado

// Pantalla LCD. npxmp FilasxColumnas
const byte mp = 20;
const byte np = 4;
LiquidCrystal_I2C lcd(0x27,mp,np); 

// Teclado ntxmt FilasxColumnas
const byte nt = 4; 
const byte mt = 4;
char tabla[nt][mt]={
        {'1', '2', '3', '+'},
        {'4', '5', '6', '-'},
        {'7', '8', '9', '!'},
        {'C', '0', '.', '='}};  
byte PinF[nt] = {7,6,5,4}; 
byte PinC[mt] = {3,2,1,0}; 
Keypad teclado = Keypad(makeKeymap(tabla),
                        PinF,PinC,nt,mt); 

// variables de calculadora
float numA = 0;
float numB = 0;
float resultado = 0; 
String num1, num2;
char operador  = ' ';
int tamano = 0;
byte cualnum = 0;
boolean haypunto = 0;

void setup(){
  // inicializa pantalla
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Calculadora-Basica");
  lcd.setCursor(0,1);
  lcd.print("FCNM-ESPOL");
  delay (2000);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("blog.espol.edu.ec");
  lcd.setCursor(0,1);
  lcd.print("/matg1052");
  delay (2000);
  lcd.clear();
}

void loop(){
  char tecla = teclado.getKey(); 

  // tecla presionada numero
  if (tecla != NO_KEY){
    if (tecla=='1' || tecla=='2' || tecla=='3' ||
        tecla=='4' || tecla=='5' || tecla=='6' || 
        tecla=='7' || tecla=='8' || tecla=='9' || 
        tecla=='0'){
      if (cualnum <= 1){
        num1 = num1 + tecla;
        cualnum = 1;
        }
      if (cualnum == 2){
        num2 = num2 + tecla;
        }
    }
    
    // tecla punto decimal
    if (tecla=='.' and haypunto==0){
      if (cualnum == 1 or cualnum == 0){
        tamano = num1.length();
        if (tamano == 0)
          {num1= "0.";}
        else
          {num1 = num1 + tecla;}
        }
      if (cualnum == 2){
        tamano = num2.length();
        if (tamano == 0)
          {num2= "0.";}
        else
          {num2 = num2 + tecla;}
        }
      haypunto = 1;
      }
  
    //tecla de Operacion
    if (cualnum >= 1 && (tecla == '+' || 
        tecla == '-' || tecla == '!' || 
        tecla == '/')){
      operador = tecla;
      cualnum = 2;
      haypunto = 0;
      }
  
    // obtener resultado
    if (tecla == '=' && cualnum == 2){
      obtenerresultado();
      }
    // tecla factorial
    if (tecla == '!'){
        resultado = factorial(numA);
        num1 = String(resultado);
        num2 = "";
        cualnum = 2;
        haypunto = 0;
        }
  
    //tecla Borrar C Clear
    if (tecla == 'C'){
      cualnum = 0;
      num1 = "";   num2 = "";
      operador = ' ';
      resultado = 0;
      haypunto = 0;
      }
    numA = num1.toFloat();
    numB = num2.toFloat();
  
    // SALIDA A PANTALLA LCD
    mostrarpantalla();
    }
  }

void obtenerresultado(){
  if (operador == '+') 
    {resultado = numA + numB;}
  if (operador == '-')
    {resultado = numA - numB;}
  if (operador == '*')
    {resultado = numA * numB;}
  if (operador == '/')
    {resultado = numA / numB;}
  num1 = String(resultado);
  num2 = "";
  cualnum = 2;
  haypunto = 0;
  }

void mostrarpantalla(){
    lcd.clear();
    lcd.setCursor(0,0);
    // muestra decimales = 3
    lcd.print(numA,3);
    if (numB!=0){
      lcd.setCursor(0,1);
      lcd.print(numB,3);    
      }
    lcd.setCursor(mp-2,0);
    lcd.print(operador);
  }


// ------ Funcion Factorial ----
float factorial(float n){
    float resultado = 1;
    int i = 2;
    for (i = 2; i<=n; i++){
        resultado = resultado*i;
    }
    return resultado;
}

Calculadora Básica-LCD y Teclado4x4

Como proyecto base se usa una calculadora básica para números reales (float).

Los componentes usados son los que tenía disponible, pueden usarse otros modelos de arduino, otros tamaños de pantalla.

La lista de componentes es:

Componentes
Descripción Unidades Observacion
Teclado 4x4 1 de membrana
Pantalla LCD 20×4 ó 16x2 1 o la que esté disponible
Módulo I2C para la pantalla 1 compatible con la pantalla LCD si no lo tiene integrado
Arduino Uno o equivalente y cable USB 1 o el modelo que esté disponible
cable Conector hembra - macho 4 para conectar el I2C al arduino
Espadines o conector macho-macho 8 pines para conectar el teclado al arduino

El módulo I2C facilita la conexión con menos cables: usando las señales SDA (Serial Data) y SCL (Serial Clock), y las de alimentación Vcc (5V) y GND.

El prototipo puede ser adaptado a otros modelos de pantallas, teclados y arduinos, debe cambiar las variables que controlan el display, el teclado o las entradas en los arduinos.


Librerías Arduino

Las librerías usadas para el manejo de los módulos del prototipo, en caso de no encontrarse disponibles en el Programa Arduino

Librería para pantalla LCD con I2C

Librería para teclado


Instrucciones en Arduino

Instrucciones básicas para operaciones de suma, resta y multiplicación, ver distribución del teclado.

// Calculadora Básica 2018.06.24
// blog.espol.edu.ec/edelros
// edelros@espol.edu.ec 

#include <LiquidCrystal_I2C.h> // LCD por I2C 
#include <Wire.h> //comunicación I2C 
#include <Keypad.h> //Teclado

// Pantalla LCD. npxmp FilasxColumnas
const byte mp = 20;
const byte np = 4;
LiquidCrystal_I2C lcd(0x27,mp,np); 

// Teclado ntxmt FilasxColumnas
const byte nt = 4; 
const byte mt = 4;
char tabla[nt][mt]={
        {'1', '2', '3', '+'},
        {'4', '5', '6', '-'},
        {'7', '8', '9', '*'},
        {'C', '0', '.', '='}};  
byte PinF[nt] = {7,6,5,4}; 
byte PinC[mt] = {3,2,1,0}; 
Keypad teclado = Keypad(makeKeymap(tabla),
                        PinF,PinC,nt,mt); 

// variables de calculadora
float numA = 0;
float numB = 0;
float resultado = 0; 
String num1, num2;
char operador  = ' ';
int tamano = 0;
byte cualnum = 0;
boolean haypunto = 0;


void setup(){
  // inicializa pantalla
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Calculadora-Basica");
  lcd.setCursor(0,1);
  lcd.print("FCNM-ESPOL");
  delay (2000);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("blog.espol.edu.ec");
  lcd.setCursor(0,1);
  lcd.print("/matg1052");
  delay (2000);
  lcd.clear();
}

void loop(){
  char tecla = teclado.getKey(); 

  // tecla presionada numero
  if (tecla != NO_KEY){
    if (tecla=='1' || tecla=='2' || tecla=='3' ||
        tecla=='4' || tecla=='5' || tecla=='6' || 
        tecla=='7' || tecla=='8' || tecla=='9' || 
        tecla=='0'){
      if (cualnum <= 1){
        num1 = num1 + tecla;
        cualnum = 1;
        }
      if (cualnum == 2){
        num2 = num2 + tecla;
        }
    }
    
    // tecla punto decimal
    if (tecla=='.' and haypunto==0){
      if (cualnum == 1 or cualnum == 0){
        tamano = num1.length();
        if (tamano == 0)
          {num1= "0.";}
        else
          {num1 = num1 + tecla;}
        }
      if (cualnum == 2){
        tamano = num2.length();
        if (tamano == 0)
          {num2= "0.";}
        else
          {num2 = num2 + tecla;}
        }
      haypunto = 1;
      }
  
    //tecla de Operacion
    if (cualnum >= 1 && (tecla == '+' || 
        tecla == '-' || tecla == '*' || 
        tecla == '/')){
      operador = tecla;
      cualnum = 2;
      haypunto = 0;
      }
  
    // obtener resultado
    if (tecla == '=' && cualnum == 2){
      obtenerresultado();
      }
  
    //tecla Borrar C Clear
    if (tecla == 'C'){
      cualnum = 0;
      num1 = "";   num2 = "";
      operador = ' ';
      resultado = 0;
      haypunto = 0;
      }
    numA = num1.toFloat();
    numB = num2.toFloat();
  
    // SALIDA A PANTALLA LCD
    mostrarpantalla();
    }
  }

void obtenerresultado(){
  if (operador == '+') 
    {resultado = numA + numB;}
  if (operador == '-')
    {resultado = numA - numB;}
  if (operador == '*')
    {resultado = numA * numB;}
  if (operador == '/')
    {resultado = numA / numB;}
  num1 = String(resultado);
  num2 = "";
  cualnum = 2;
  haypunto = 0;
  }


void mostrarpantalla(){
    lcd.clear();
    lcd.setCursor(0,0);
    // muestra decimales = 3
    lcd.print(numA,3);
    if (numB!=0){
      lcd.setCursor(0,1);
      lcd.print(numB,3);    
      }
    lcd.setCursor(mp-2,0);
    lcd.print(operador);
  }

Funciones personalizadas en Arduino, Entrada/salida serie

Componentes: Arduino, cable USB.

Una forma de usar funciones personalizadas para cálculo en arduino, consiste en serparar las instrucciones de cálculo como una función separada del bloque/lazo principal.

Por ejemplo, se puede usar la forma polinómica de la serie de Taylor para una función no disponible de cálculos con la precisión deseada.

La operatividad se muestra usando como ejemplo la función factorial(),   enviando y recibiendo los valores por medio de la conexión por USB y el "monitor serie". Ejemplo:

Observe que se encuentra habilitado el uso de "nueva línea" y "cambio de línea" (NL & CR) , para que al presionar "ENTER" se considera ingresado todos los dígitos del número.

Funciones para cálculos

Para el ejemplo, se realiza el cálculo del factorial:

n! = 1 x 2 x 3 x ... x n

float factorial(float n){
    float resultado = 1;
    int i = 2;
    for (i = 2; i<=n; i++){
        resultado = resultado*i;
    }
    return resultado;
}

En el resultado, la cantidad de dígitos  se extiende usando el tipo real (float). Como práctica,  cambie la variable resultado a tipo entero (int), actualice el programa en el arduino, y calcule para números mayores  a 7; observe y explique el resultado.

Envío y recepción de datos con "monitor Serie"

La prueba de la función se simplifica enviando y recibiendo datos por la conexión USB. La variable mensaje almacena un caracter enviado al arduino, validando que sea un dígito entre 0 y 9. Al presionar "ENTER" se cierra el mensaje, se cambia el valor a número a entero y se procede a usar la función factorial para mostrar el resultado.

// funciones personalizadas para cálculo
// ingreso y salida por 'monitor serial'
// el mensaje se procesa por dígitos hasta presional ENTER
//    y se convierte a número

String mensaje = "";
void setup() {
    Serial.begin(9600);
    // espera por conexion serial-USB
    while (!Serial) {}
    Serial.println("\n calcular funcion(): \n");
}
void loop() {
    int uncaracter = 0;
    float numero = 0;
    float resultado = 0.0;
    while (Serial.available() > 0) {
        uncaracter = Serial.read();
        if (isDigit(uncaracter)) {
            mensaje = mensaje + (char)uncaracter;
        }
        if (uncaracter == '.') {
            mensaje = mensaje + (char)uncaracter;
        }
        if (uncaracter == '\n') {
            numero = mensaje.toInt();
            Serial.print("mensaje: ");
            Serial.println(mensaje);
            Serial.print("numero es: ");
            Serial.println(numero);

            // bloque de función matematica
            resultado = factorial(numero);
            Serial.print("factorial(numero): ");
            Serial.println(resultado);
            mensaje = "";
        }
    }
}

float factorial(float n){
    float resultado = 1;
    int i = 2;
    for (i = 2; i<=n; i++){
        resultado = resultado*i;
    }
    return resultado;
}

Para probar otras funciones, modifique los mensajes siguiendo el contexto del problema a resolver.


Funciones con números reales

Siguiendo el ejemplo de Polinomio de Taylor para el coseno(), en este caso el número de ingreso y salida son es reales (float). El esquema de la función es muy semejante.

1.4 Taylor-polinomio Ejemplo01

// funciones personalizadas para cálculo
// ingreso y salida por 'monitor serial'
// el mensaje se procesa por dígitos número hasta presional ENTER
//    y se convierte a número, para usar la función

String mensaje = "";
void setup() {
    Serial.begin(9600);
    // espera por conexion serial-USB
    while (!Serial) {}
    Serial.println("\n calcular funcion(): \n");
}
void loop() {
    int uncaracter = 0;
    float numero = 0.0;
    double resultado = 0.0;
    while (Serial.available() > 0) {
        uncaracter = Serial.read();
        if (isDigit(uncaracter)) {
            mensaje = mensaje + (char)uncaracter;
        }
        if (uncaracter == '.') {
            mensaje = mensaje + (char)uncaracter;
        }
        if (uncaracter == '\n') {
            numero = mensaje.toFloat();
            Serial.print("mensaje: ");
            Serial.println(mensaje);
            Serial.print("numero es: ");
            Serial.println(numero);

            // bloque de función matematica
            resultado = poliTaylor(numero);
            Serial.print("poliTaylor(numero): ");
            Serial.println(resultado,4);
            Serial.println(cos(numero),4);
            mensaje = "";
        }
    }
}

float poliTaylor(float x){
    float resultado;
    float x0=0.0;
    resultado = 1 - 0.5*(x-x0)*(x-x0); 
    return resultado;
}

Referencias:

Arduino Serial read , Arduino lista de funciones básicas .