Arduino, Rasperry y algo más

Carrera a número de vueltas y entrenamientos

Ya tiene pinta de lo que quiero lograr: cuenta vueltas, calcula tiempos de vuelta y vuelta rápida y permite elegir entre los dos tipos de carreras (me falta la de resistencia, pero supongo que no tardará mucho, pues básicamente es lo mismo pero con diferentes condicionantes). Una vez funcione la carrera de resistencia añadiré el control del segundo corredor, lo que conllevará un pequeño aumento del hardware, que publicaré en cuanto lo tenga disponible.

El código comentado que os dejo aquí permite elegir entre carreras a un número de vueltas determinado o entrenamientos (también permite la opción de resistencia pero solo saca el semáforo).

El código lo iré dividiendo en fragmentos para poder comentarlo y al final pondré el archivo de descarga.

Configuración de variables y pines

La mayoría de estos valores se pueden cambiar, sin ser necesario mas que, en algunos casos, cambiar también el cableado

/*Cuentavueltas y crono 0.3

Interrupciones:
La 0 (en el pin digital 2) CORREDOR 1
*/#include <LiquidCrystal.h> //Para controlar LCD
LiquidCrystal lcd(12, 11, 5, 4, 10, 6); //Pines del LCD
int sonido = 13; //Pin de sonido
int intervalo =  1000; //Intervalo de la secuencia
int control = 4; //Pin de control (Boton A)
int control2 = 5; //Pin ANALOGICO de control 2 (Boton B)

// En el 595: Rojos en Q0, Q1 y Q2 y verde en Q3
//Pin connected to ST_CP of 74HC595
//Pin connected to SH_CP of 74HC595
//Pin connected to DS of 74HC595
int dataPin = 7;
int latchPin = 8;
int clockPin = 9;

//Corredor 1
int paso1 = 2;
int vuelta = 1; //Contadores de vueltas
int vueltaB = 1;
unsigned long tiempoF = 0; // Tiempo de fin de vuelta
unsigned long tiempoS = 0; //Tiempo de salida de vuelta
unsigned long tiempoV = 0; //Tiempo de vuelta
unsigned long tiempoR = 4294967295; //Tiempo de vuelta rapida. Valor max de la variable
int vueltaR = 0; //Vuelta rápida

//Vueltas rápidas
int vueltaRt = 0;

//Variables para el tipo de carrera
int tp = 0;
int t = 0;
//Opciones del menu de t de carrera
char* menu[]={“N de vueltas”, “Resistencia”, “Entrenamiento”};

//Carrera a vueltas
int Nvueltas = 1; //Numero de vueltas

//Variables cronometro
int seg = 0;
int minut = 0;
int mil = 0;
unsigned long tiempocrono = 0;
unsigned long tiempocrono2 = 0;
int tipocrono = 0;
/*Con esta variable puedo usar la misma subrrutina “crono()”
para representar los distintos tipos de tiempos, condicionando
tiempo crono a tipocrono.
*/

//FIN Configuracion variables//

void setup()    {
//Configura los pines digitales

//Configura el pin asociado al altavoz
pinMode(sonido, OUTPUT);

//Configura pines 595
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);

// Configurar el numero de Columnas y filas del LCD
lcd.begin(16, 2);
lcd.clear();

//Configurar pines de control del 595
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);

//Configura los pines de paso de vuelta y las interrepciones
pinMode(paso1, INPUT);
attachInterrupt(0, pasoVuelta1, RISING);

//Apaga semaforo
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, 0);
digitalWrite(latchPin, HIGH);

//FIN Setup//
}

 

Función de la interrunción de paso de vuelta

Esta rutina, aunque pequeña, tiene su importancia: el paso por la meta de cada corredor (en este solo uno) es detectado por medio de cada una de las interrupciones externas del micro, con lo que me aseguro que no me voy a perder ningún paso de por meta, pero durante la ejecución de las rutinas asignadas a cada interrupción se detienen todas las demás interrupciones, con lo que nos podríamos perder pasos por meta y el cronómetro pierde precisión. La solución a ambos problemas, además de hacerla lo mas corta posible, la he encontrado en el hardware, al primero añadiendo un condensador un par de resistencias y el segundo lo solucionaría con un reloj de tiempo real pero consumiría pines que no tengo y dinero para conseguir unas milésimas más de precisión.

void pasoVuelta1(){
//Rutina de interrupcion por paso de vuelta
if (millis() > (tiempoS+1000)){
++vuelta;
}
}

Rutinas de la carrera a número de vueltas

Estas rutinas son las encargadas de gestionar la carrera a un número de vueltas predeterminado. No tienen nada especial.

void carreraVueltas(){
//Rutina principal de la carrera a vueltas.
reset(); //Reestablece las variables de los corredores
//Definir el numero de vueltas de carrera
lcd.clear();
lcd.print(“Cuantas vueltas?”);
lcd.setCursor(0, 1);
lcd.print(Nvueltas);
delay(1000);
while(analogRead(control2) < 525) {
while(analogRead(control) > 525) {
delay (100);
++Nvueltas;
lcd.setCursor(0, 1);
lcd.print(“                “);
lcd.setCursor(0, 1);
lcd.print(Nvueltas);
tone(sonido, 440, 100);
delay(500);
}
}

//Inicio carrera
lcd.clear();
lcd.print(“Preparados?”);
lcd.setCursor(0,1);
lcd.print(“Boton A”);
while(analogRead(control) < 900 ) {
delay(1);
}

semaforo();

//Prepara los elementos fijos de la pantalla para evitar parpadeos.
lcd.clear();
lcd.print(“Vuelta: “);
lcd.setCursor(9,0);
lcd.print(vuelta);
lcd.setCursor(0,1);
lcd.print(“Tiempo:”);
lcd.setCursor(10,1);

 

Rutinas de la carrera de Entrenamiento

Mas rutinas sin nada especial. Realmente son muy parecidas a las anteriores (casi copiar y pegar), pero con la condición de finalización de la carrera cambiada: en vez de por un contador se acaba al pulsar un botón..

void resistencia() {
/*Esta sera la rutina principal de la carrera
de resistencia*/
lcd.clear();
lcd.print(“Resistencia”);
semaforo();
lcd.clear();
}
void loop()  {lcd.clear();
lcd.print(“     BUENA”);
lcd.setCursor(0, 1);
lcd.print(“    CARRERA!”);
delay(1000);
seleccion();
switch (tp) {
case 0: //Carrera a numero de vueltas
carreraVueltas();
break;

case 1: //Carrera de resistencia
resistencia();
break;

case 2: //Entrenamientos
entrenamiento();
break;
}
}

void seleccion() {
/*
Seleccionar entre ts de carreras:
1: Número de vueltas.
2: Resistencia o a tiempo fijo.
3: Entrenamiento o vueltas libres
*/
t = 0;
lcd.clear();
lcd.print(” Elige el tipo”);
lcd.setCursor(0,1);
lcd.print(“   de carrera”);
delay(1000);

while(analogRead(control2) < 525) {
while(analogRead(control) > 525) {
delay (100);

lcd.clear();
lcd.print(“Tipo de carrera”);
lcd.setCursor(0,1);
lcd.print(menu[t]);
delay(500);
tone(sonido, 440, 100);
tp = t;
++t;
if (t > 2) t = 0;

}
}
}

Inicio y menú de tipo de carrera

En este bloque hay tres subrutinas diferentes:

  1. El germen de la carrera de resistencia. Por ahora saca el semáforo.
  2. Loop: Es obligatorio y lo uso para dar un mensaje de bienvenida y realizar la selección del tipo de carrera.
  3. La rutina del minimenú. No he utilizado ninguna librería de menús por que solo quería elegir entre tres opciones, y con desplazarme por un array me sobraba.

void resistencia() {
/*Esta sera la rutina principal de la carrera
de resistencia*/
lcd.clear();
lcd.print(“Resistencia”);
semaforo();
lcd.clear();
}
void loop()  {lcd.clear();
lcd.print(“     BUENA”);
lcd.setCursor(0, 1);
lcd.print(“    CARRERA!”);
delay(1000);
seleccion();
switch (tp) {
case 0: //Carrera a numero de vueltas
carreraVueltas();
break;

case 1: //Carrera de resistencia
resistencia();
break;

case 2: //Entrenamientos
entrenamiento();
break;
}
}

void seleccion() {
/*
Seleccionar entre ts de carreras:
1: Número de vueltas.
2: Resistencia o a tiempo fijo.
3: Entrenamiento o vueltas libres
*/
t = 0;
lcd.clear();
lcd.print(” Elige el tipo”);
lcd.setCursor(0,1);
lcd.print(“   de carrera”);
delay(1000);

while(analogRead(control2) < 525) {
while(analogRead(control) > 525) {
delay (100);

lcd.clear();
lcd.print(“Tipo de carrera”);
lcd.setCursor(0,1);
lcd.print(menu[t]);
delay(500);
tone(sonido, 440, 100);
tp = t;
++t;
if (t > 2) t = 0;

}
}
}

Rutinas comunes (semáforo, pasos de vuelta …)

Estas rutinas son las utilizadas de forma repetitiva en distintas partes del programa:

  1. Paso de vuelta: Se encarga de llevar el conteo del número de vueltas y de guardar la vuelta rápida.
  2. Final de carrera: Muestra la pantalla de final de carrera: los mejores tiempos de cada corredor (en este caso solo 1).
  3. El semáforo. Lo saco a través de un desplazador de registro 595.

 

void pasoDEvuelta(){
/*Calcula los tiempos de cada vuelta y los guarda si
son los de la vuelta rápida*/
tiempoF = millis(); //Calculo de tiempo de vuelta
tiempoV = tiempoF – tiempoS;
tiempoS = tiempoF; //Pone el tiempo de salida nuevo//Vuelta rapida
if (tiempoR > tiempoV) {
tiempoR = tiempoV;
vueltaR = vueltaB;
tone(sonido, 740, 250);
}
else {
tone(sonido, 250, 250);
}
vueltaB = vuelta;
}

void finVueltas() {
//Muestra la vuelta rapida
lcd.clear();
lcd.print(“V. rapida:”);
lcd.setCursor(10,0);
lcd.print(vueltaR);
lcd.setCursor(0,1);
lcd.print(“Tiempo:”);
lcd.setCursor(7,1);
lcd.print(“:”);
lcd.setCursor(10,1);
lcd.print(“:”);
lcd.setCursor(13,1);
lcd.print(“:”);
tipocrono = 2;
crono();
delay(5000);
}

 

void semaforo()
{
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, 0);
digitalWrite(latchPin, HIGH);
delay (100);

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, B10000000);
digitalWrite(latchPin, HIGH);
tone(sonido, 440, intervalo);
delay(intervalo);

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, B11000000);
digitalWrite(latchPin, HIGH);
tone(sonido, 540, intervalo);
delay (intervalo);

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, B11100000);
digitalWrite(latchPin, HIGH);
tone(sonido, 640, intervalo);
delay (intervalo);

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, B00010000);
digitalWrite(latchPin, HIGH);
tone(sonido, 740, intervalo*2);
delay (intervalo*2);

tiempoS = millis(); //Paso de salida (pone milis como 0)
tiempoS2 = millis();

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, LSBFIRST, 0);
digitalWrite(latchPin, HIGH);

delay(1000);
}

void reset(){
//Reestablece las variables de los corredores
vuelta = 1; //Contadores de vueltas
vueltaB = 1;
tiempoF = 0; // Tiempo de fin de vuelta
tiempoS = 0; //Tiempo de salida de vuelta
tiempoV = 0; //Tiempo de vuelta
tiempoR = 4294967295; //Tiempo de vuelta rapida. Es el valor maximo.
vueltaR = 1; //Vuelta rápida
}

 

Rutina de cronometro

Me encanta esta rutina. He disfrutado mucho programándola y pienso ampliarla. Se encarga de calcular los diferentes tiempos que tiene que mostrar la  pantalla. A través de una variable «tipocrono» elijo el formato del cronometro y el tiempo y eso es lo que me devuelve. Es una de la funciones mas utilizadas del todo el programa.

 

void crono(){/*Muestra los tiempos en pantalla.
Se basa en una variable “tipocrono” y segun su valor,
definido antes de cada llamada a “crono()”, se calcula
la variable “tiempocrono”, que es la que realmente se
muestra pasada de milisegundos a las unidades necesarias.

0 = Crono continuo de carreras a vuelta y entrenamientos
1 = Final de cada vuelta
2 = Final de carrera a vueltas
*/

switch (tipocrono) {
case 0: //crono continuo en vueltas y entrenamiento
tiempocrono = millis()-tiempoS;
break;
case 1: //fin de cada vuelta
tiempocrono = tiempoV;
break;
case 2: //final de carrera a vueltas
tiempocrono = tiempoR;
break;
}

//Calculo de los tiempos a mostrar
minut = (tiempocrono/60000);
seg = ((tiempocrono/1000)-(minut*60));
mil = (tiempocrono-(minut*60000)-(seg*1000));

//Muestra en LCD los tiempos
lcd.setCursor (8,1);
if (minut < 10) {lcd.print (“0″);}
lcd.print (minut);
lcd.setCursor (11,1);
if (seg < 10) {lcd.print (“0″);}
lcd.print (seg);
lcd.setCursor (14,1);
lcd.print (mil);
delay (100); //evita parpadeos
}

 

Archivo con el programa para su descarga AQUÍ

Anterior

El semáforo

Siguiente

Primera revisión del hardware del gestor de carreras

1 comentario

  1. Madre mía que chulo esta todo lo que tienes, algún día tendré
    que ver eso en vivo, está muy chulo.

    http://www.peysanet.com/

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Funciona con WordPress & Tema de Anders Norén