Sensor para tacos de salida en atletismo

Buenas,

Nuevo proyecto a ver que os parece...

Mi hijo hace atletismo, la idea es medir el tiempo de reacción entre el disparo y la salida de tacos, tengo que ver cómo lo hace un dispositivo profesional, (con que sensores)

El funcionamiento pienso, es que el sensor de los tacos de salida, empieza a leer después del disparo, y a contar el tiempo, hasta que el acelerómetro muestre un cambio(una mínima vibración) que será cuando su pie empuje hacia atrás.

Pido ayuda/consejos para poder iniciar el proyecto

El displayado de datos en principio será en un display, en la fase 2, será por trasmisión Bluetooth a un app móvil que generará tanto la orden del disparo simulado, como los resultados.

Mi idea, es utilizar cualquier Arduino, con soporte Bluetooth (estoy muy oxidado, me podéis echar un cable aquí también ya que no tengo idea que hay por el mercado ahora mismo) + Acelerometro 3 ejes ADXL335
Edito para poner que por casa tengo sin abrir una placa de desarrollo basada en el [ESP8266] que incluye Bluetooth y wifi creo

Un saludo y gracias de Antemano

Adjunto foto del borrador a grandes rasgos que acabo de hacer ahora mismo.

Veo varios errores posibles en la idea que te voy a presentar.
En los equipos profesionales debe haber una señal que indica el comienzo via el disparo que pone a 0 el error que describiré a continuación, si en cambio, tenemos que esperar el tiempo que le lleva a la onda de sonido que viaja a 342.3 m/seg, solamente con estar a 10 mts hablamos de 29.2 mseg.
Continúo con la idea: Supongamos que dices voy a colocar un micrófono y cuando se reciba una señal que alcance determinado umbral será el momento del disparo. Obviamente eso ocurrirá a un intante T tal que la señal llegue y sea alcance un umbral determinado x un circuito Schmitt Trigger.
La segunda parte es la reacción en el arranque. Dices y concuerdo de poner un acelerómetro, de nuevo esto tiene su velocidad de reacción asi que otro error involucrado.
Otra opción sería disponer de algun sensor de presión en los tacos de salida y al dejar de tener presión o al desbalancearse entre ambos tacos, considerarlo como el arranque. No se bien que consideras como fin de dicho momento? Si es cuando se desprende un zapato o cuando ambos lo hacen.

Muchas gracias por tu comentario,
el altavoz de disparo estará situado en el mismo sitio que uno profesional la distancia que recorre el sonido sería la misma, pienso.

Y si es cierto habrá que hacer pruebas de campo, para calibraciones de tiempos, añadiendo o quitando, incluyendo el tiempo de demora de las propias sentencias del programa.

iré colocando un link en GitHub

según avance el proyecto, y así me podeis ir orientado, en todos y cada uno de los errores.

Respecto a los sensores, efectivamente acabo de revisar un poco Internet y los sensores de presión (actuaban con 25kg) fueron sustituidos en los actualidad por acelerómetros.

Eso no quita que existe algo EXTRA que detecte el momento en que se dispara sin incurrir en el error de propagación de la onda. Estoy hilando fino pero asi debe ser. Quieres medir algo en uSeg de modo que no se puede permitir errores de ninguna naturaleza.
Bien, si los sensores de presión no van, entonces será el acelerómetro.
Obviamente descartemos un enlace inalámbrico para transmitir el arranque, asi que ya tienes para trabajar.
Detección de un disparo, aconsejo usar electrónica simple, mic + preamplificador + Schmitt Trigger que te dará el inicio de la cuenta
Luego el acelerómetro detectará el final.

Creo que no me expliqué bien, tengo las ideas en la cabeza pero no salen bien :wink:
Es un dispositivo para entrenamientos.

Al pulsar un botón en la app en el móvil, este enlazará con ESP8266 y daremos la orden de correr el programa y ya todo el proceso se correrá en nuestro ESP8266, solo interactuaremos con la app de nuestro móvil para iniciar de nuevo el programa, es decir:

Pulsa nos un botón en la pantalla de nuestro móvil (disparo) y envía la orden de iniciar el programa en el ESP8266:

1° Inicializamos el Acelerometro
2° Activaremos un buzzer (disparo de salida)
3° empezará a contar el timer
4° Nuestro dispositivo se quedará leyendo el acelerómetro y en el instante que nos de señal iremos a leer el timer, guardamos ese registro.
5° Enviamos de vuelta a la app del móvil los datos obtenidos

Es decir no usaremos un microfono para recoger el sonido del disparo, este se generará en el propio programa

Bueno, mucho mejor.
Y cual es el problema entonces?

En principio antes de postear tenía dudas de los módulos que podría o no utilizar, y esa era la pregunta básicamente, pero según he ido recopilando electrónica que tenía en el trastero, fui sacando una placa de desarrollo NodeMCU ESP8266 y un módulo Acelerómetro ADXL345, pienso que nos podemos apoyar de momento con esto, qué opinas?

Las dudas empezarán más adelante estoy leyendo a cerca del ESP2866 y las interacciones con su librerías del IDE de Arduino que es con el que voy a trabajar

Con el ADXL345 y sus librerías ya trabajé en alguna ocasión con él.

Si claro.
Yo creo que deberías empezar tal vez haciendo que el ESP8266 actue como AP, tu te conectas a ese ESP8266 y su AP por ejemplo en 192.168.4.1 como se suele hacer para lo cual hay muchos ejemplos y la pagina simple que creas puede tener un boton de START y tmb recibir los datos del ESP266 cuando comience la cuenta.
Para hacer eso te recomiendo este sitio ESP8266 NodeMCU Access Point (AP) for Web Server | Random Nerd Tutorials

Acá tienes como crear un boton

y el mismo ejemplo inicial


modificado porque en lugar de temperatura y humedad querras mostrar los milisgundos transcurridos.

Otro ejemplo similar que puede ayudar

1 Like

Muchas gracias, si empezaré así

Ya lo tengo casi acabado,

Pero tengo algún problema con tres botones de configuración de sensibilidad (con el método indexOf) que añadí, no logro que me funcionen correctamente, alguna idea donde puede estar el fallo?

En principio el resto me funciona bien,
Pero al clickear en sens Baja no va se me va a sens alta e igual con la Media....

/*
  Este programa pretende medir los tiempos de reacción y cuantificar las mejorías en los entrenamientos de un atleta.
  Para ello utilizaremos una Placa de desarrollo NodeMCU ESP8266 y Módulo Acelerómetro CJMCU ADXL345
  Un Buzzer que hará las veces de pistoletazo de salida
  y todo controlado a través de un enlace WIFI y a unservidor Web
*/

#include <SparkFun_ADXL345.h> // Esta biblioteca le permite comunicar con el acelerómetro ADXL345
#include <ESP8266WebServer.h> // Biblioteca se utiliza para simplificar la creación de un servidor web.

ADXL345 adxl = ADXL345();
unsigned long timer1 = 0;
unsigned long timer2 = 0;
unsigned long tiempo_desde_disparo = 0;
int sensibilidad = 50;
unsigned long resultado = 0;
String sensibilidadSTR = " MEDIA";
int PinBUZZER = 2;                              //Definimos el pin de salida - GPIO2 / D4

const char ssid[] = "Club-Atletismo-Leganes";   //Definimos la SSDI de nuestro servidor WiFi -nombre de red-
const char password[] = "complejoeuropa";       //Definimos la contraseña de nuestro servidor
WiFiServer server(80);                          //TCPservidor en el puerto 80

// -------------------------------------------------------------------------------------------------------------
void setup() {

  Serial.begin(115200);
  Serial.println("Iniciar");
  Serial.println();

  adxl.powerOn();                               // Power on the ADXL345
  adxl.setRangeSetting(2);                      // Definir el rango del Acelerómetro, valor en 2g

  pinMode(PinBUZZER, OUTPUT);                   // Inicializamos el GPIO2 como salida (Buzzer)
  digitalWrite(PinBUZZER, LOW);                 // Dejamos inicialmente el GPIO2 apagado

  server.begin();                               // Inicializamos el servidor
  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid, password);                  // Red con clave, en el canal 1 y visible

  Serial.println();
  Serial.print("Direccion IP Access Point - por defecto: ");      //Imprime la dirección IP
  Serial.println(WiFi.softAPIP());
  Serial.print("Direccion MAC Access Point: ");                   //Imprime la dirección MAC
  Serial.println(WiFi.softAPmacAddress());

}


int medX() {                                    // Función que devuelve una muestra pònderada de 10 medidas del eje X

  int x, y, z;
  int mX = 0;

  for (int i = 0; i < 10; i++) {
    adxl.readAccel(&x, &y, &z);
    mX = mX + x;
  }
  mX = mX / 10;
  return mX;
}

// -------------------------------------------------------------------------------------------------------------
void loop()
{
  WiFiClient client = server.available();               // Comprueba si el cliente ha conectado
  if (!client) {
    return;
  }

  Serial.println("nuevo cliente");                      // Espera hasta que el cliente envía alguna petición
  
  while (!client.available()) {
    delay(1);
  }

  Serial.printf("Clientes conectados al Access Point: %dn", WiFi.softAPgetStationNum());   // Imprime el número de clientes conectados

  String peticion = client.readStringUntil('r');          // Lee la petición
  Serial.println(peticion);
  client.flush();

  int x, y, z, x1;
  String salida = "<h2>**************</h2>";


  if (peticion.indexOf('/START=L') != -1) {               // Comprueba la petición de sensibilidad
    sensibilidad = 90;
    sensibilidadSTR = "<h2>Sensibilidad: BAJA</h2>";
  }
  if (peticion.indexOf('/START=M') != -1) {
    sensibilidad = 50;
    sensibilidadSTR = "<h2>Sensibilidad: MEDIA</h2>";
  }
  if (peticion.indexOf('/START=H') != -1) {
    sensibilidad = 20;
    sensibilidadSTR = "<h2>Sensibilidad: ALTA</h2>";
  }

  if (peticion.indexOf('/START=O') != -1) {                // Comprueba la petición de DISPARO

    digitalWrite(PinBUZZER, HIGH);                         //  Activamos el BUZZER ( DISPARO !!! )
    timer1 = millis();
    x = medX();


    // x=x-Xcal;

    while (true)                                           //Realizar este bucle mientras NO tengamos(una mínima) Accelearación en el EJE de las X
    {
      x1 = medX();
      if ((x1 - x) > sensibilidad || (x1 - x) < -sensibilidad) {  //  Accelearación +-50(Sensibilidad Media) en el EJE de las X  ******
        x1 = medX();
        timer2 = millis();
        resultado = timer2 - timer1;
        if (resultado < 100) {                              //Por debajo de 100ms en Atletismo se considera SALIDA NULA   ************
          salida = "<h2 style='color:red'>** SALIDA NULA **</h2>";
        }
        else {
          salida = "<h2 style='color:green'>* SALIDA CORRECTA *</h2>";
        }
        break;
      }
      else
      {
        tiempo_desde_disparo = millis();
        if ((tiempo_desde_disparo - timer1) > 3000) {  // **********  Apagamos el buzzer 3 segundos después del disparo y colocamos el mensaje "SIN SALIDA"
          digitalWrite(PinBUZZER, LOW);
          salida = "<h2 style='color:orange'>- NO HUBO SALIDA -</h2>";
          break;
        }
      }
    }
  }

  if (peticion.indexOf('START=F') != -1) {  // RESET valores
    x = 0;
    x1 = 0;
    resultado = 0;
    timer1 = 0;
    timer2 = 0;
    String salida = "<h2>*******-******</h2>";
  }

  // *******************************************************    Envía la página HTML de respuesta al cliente
  client.println("HTTP/1.1 200 OK");
  client.println("");                                        // No olvidar esta línea de separación
  client.println("<!DOCTYPE HTML>");
  client.println("<meta charset='UTF-8'>");
  client.println("<meta name='MobileOptimized' content='width' />");
  client.println("<html>");

  client.println("<body style='background-color:black;'>");   // Web Page Heading
  client.println("<font color='grey'>");

  client.println("<center><h1 style='color:orange'>CLUB ATLETISMO</h1>");
  client.println("<center><h1 style='color:orange'>LEGANES</h1>");

  client.println("<h2>Tiempos de Reacción</h2>");
  client.println("<p>- Salida de Tacos -</p>");
  client.println("<p><100ms: SALIDA NULA</p>");

  //client.println(x);
  client.println("<br><br>");

  client.println("<h1 style='color:yellow'>");
  client.print(resultado);                           // Mostramos el resultado, es decir el TR (Tiempo de reacción)
  client.println(" ms</h1>");
  client.println("<br><br>");
  client.println(salida);                            // Mostramos Salida NULA o CORRECTA
  client.println("<br><br>");

  //Se crean botones con estilos
  client.println("<button type='button' onClick=location.href='START=O' style='margin:auto; background-color:green; color:#A9F5A9; padding:5px; border:2px solid black; width:200;'><h2> -- GO --</h2> </button>");
  client.println("<button type='button' onClick=location.href='START=F' style='margin:auto; background-color:red; color:#F6D8CE; padding:5px; border:2px solid black; width:200;'><h2> RESET</h2> </button><br><br>");

  client.println(sensibilidadSTR);                      //Mostramos la sensibilidad del Acelerómetro

  //Se crean 3 botones para modificar la sensibilidad del Acelerómetro

  client.println("<button type='button' onClick=location.href='/START=L'> Sens BAJA </button>");
  client.println("<button type='button' onClick=location.href='/START=M'> Sens MEDIA </button>");
  client.println("<button type='button' onClick=location.href='/START=H'> Sens ALTA </button><br><br>");

  client.println("</center></font></body></html>");
  delay(1);
  Serial.println("Petición finalizada");          // Se finaliza la petición al cliente. Se inicaliza la espera de una nueva petición.
}


Modifica estas lineas

  if (peticion.indexOf('/START=L') == 11) {               // Comprueba la petición de sensibilidad
    Serial.println("Sensibildad LOW");
    sensibilidad = 90;
    sensibilidadSTR = "<h2>Sensibilidad: BAJA</h2>";
  }
  if (peticion.indexOf('/START=M') == 11) {
    Serial.println("Sensibildad MEDIA");
    sensibilidad = 50;
    sensibilidadSTR = "<h2>Sensibilidad: MEDIA</h2>";
  }
  if (peticion.indexOf('/START=H') == 11) {
    Serial.println("Sensibildad ALTA");
    sensibilidad = 20;
    sensibilidadSTR = "<h2>Sensibilidad: ALTA</h2>";
  }

y funcionará.

1 Like

Eres un crack, funcionado correctamente,

ahora le daré una vuelta a las GPIO que son unt yanto especialitas en el arranque.

he podido ver que me hace cosas extrañas en GPIO2 donde tengo conectado el transistor, y mirando los datos técnicos he podido ver que al arrancar / reiniciar / activar, GPIO2 DEBE estar ALTO.

Como es la GPIO2 con la que voy a trabajar voy a ver como soluciono que no se me tire a masa por el transistor esa salida, ya que no arrancará, porque tiene que estar a nivel alto...

Colocando un PNP en lugar de NPN, el transistor estará abierto al arrancar dejando esa GPIO2 a nivel Alto, y cuando quiera activar el buzzer dejo esa salida a nivel Bajo... Voy a probar. . . .

Edito: cambiando al GPIO15 funciona correctamente ya que esta tiene que tener una configuración a nivel BAJO al arrancar

Hay ciertas reservas con los pines a usar en el ESP8266.
Revisa esto

Label GPIO Input Output Notes
D0 GPIO16 no interrupt no PWM or I2C support HIGH at boot
used to wake up from deep sleep
D1 GPIO5 OK OK often used as SCL (I2C)
D2 GPIO4 OK OK often used as SDA (I2C)
D3 GPIO0 pulled up OK connected to FLASH button, boot fails if pulled LOW
D4 GPIO2 pulled up OK HIGH at boot
connected to on-board LED, boot fails if pulled LOW
D5 GPIO14 OK OK SPI (SCLK)
D6 GPIO12 OK OK SPI (MISO)
D7 GPIO13 OK OK SPI (MOSI)
D8 GPIO15 pulled to GND OK SPI (CS)
Boot fails if pulled HIGH
RX GPIO3 OK RX pin HIGH at boot
TX GPIO1 TX pin OK HIGH at boot
debug output at boot, boot fails if pulled LOW
A0 ADC0 Analog Input X

Ahi veras que GPIO2 o sea D4 esta pulled up, o sea tiene un resistor a 3.3V y esta HIGH en el arranque.
Tienes otros pines sin observaciones, usa alguno que diga OK

Olvidé explicar porque puse 11.
11 es la posicón que devuelve esta consulta

peticion.indexOf('/START=L')

cuando es verdadera.

1 Like

Si cambié GPIO2 por GPIO15 que el boot lo deja a GND a y asi no tengo problemas, y dejo el transistor NPN
De momento funcionando...

Gracias de nuevo

Una pregunta mas,

Como tengo que afinar tiempos
Y el tiempo de medida un bucle de 25 medidas y despues me saca una media ponderada u alguna instrucción mas...

Queria restar a la salida esos tiempos,

Veo que el modulo trabaja a 80-160Mhz

No se si esa frecuencia de reloj es la de cada instrucción,
es decir 1/160.000.000= 625 picoSegundos? Voy bien encaminado o el tiempo de cada instrucción no es equiparable a la frecuencia de reloj a la que trabaja.

Aunque yo creo que seria despreciable del orden de 2 o 3microSegundos

O mirandolo bien el cuello se botella seria el acelerómetro, no se, miraré cuanto tarda en recolectar una muestra, porque si es 1ms en este caso si seria sustancial, no mucho pero si sustancial

Tienes para medir micros() en microsegundos y millis() en milisegundos.
Las instrucciones puede ocupar 1,2, o mas ciclos reloj asi que la cuenta no es la de 1 ciclo.
Tienes que hacer un estudio pormenorizado de las instrucciones que el compilador ha usado para saber el tiempo involucrado.
Un promedio mas o menos efectivo sería usar micros y ver el tiempo que te indica. Con eso podrias ir acotando errores.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.