Never Ride Alone: Sistema inteligente de seguridad para bicicletas
Componentes y suministros
| LED 5 mm: Rojo | × | 2 |
| u-blox Ublox neo 6m GPS | × | 1 |
| Arduino MKR Fox 1200 | × | 1 |
| Batería 3.7 V | × | 1 |
| Zumbador (Buzzer) | × | 1 |
| Pulsador SparkFun 12mm | × | 1 |
| Interruptor deslizante | × | 1 |
| Resistencia 1k ohm | × | 1 |
| Seeed Grove - Sensor de vibración (SW-420) | × | 1 |
Herramientas y máquinas necesarias
- Impresora 3D (genérica)
Sobre este proyecto
Hola a todos, soy Jesús Soriano, estudiante de ingeniería electrónica, emprendedor... ¡y ciclista!
Monto en bici desde los 12 años, y suelo entrenar solo. Durante estos entrenamientos, he tenido muchas caídas, accidentes y otros percances. Un día me di cuenta de que, si tenía un accidente fuerte y perdía el conocimiento, podía morir. ¡Estaba solo en la montaña!
Es más fácil de entender con un pequeño vídeo. Imagina: Vas en bici, disfrutando el paisaje y de repente aparece un coche y te arrolla. Caes por una ladera, y el conductor huye. Te quedas inconsciente, atrapado, invisible para el mundo.
Dos o tres horas después, tu familia empezará a preocuparse. Los servicios de emergencia intentarán localizarte, y lo harán tras 4-5 horas. Probablemente estarás muerto.
Así que empecé a investigar, y vi muchas opciones interesantes: Primero, una app, Strava Summit. En caso de accidente, podía enviar la posición GPS a amigos. Angi, de Specialized, también era una opción. El sistema estaba integrado en el casco, y en caso de colisión, podía pedir ayuda. Pero había un problema. Todos estos sistemas necesitaban señal de móvil para funcionar. Batería, GPS y señal de móvil eran necesarios.
Ahí nació Never Ride Alone (NRA). Encontré Sigfox como solución para envío de datos en la montaña, y cuando descubrí que tenían un Arduino con módulo Sigfox... ¡Fue genial!
Decidí que NRA funcionaría como una alarma invertida. Cuando sales a rodar, activas NRA pulsando un botón. Los LEDs empiezan a parpadear, mejorando también la visibilidad. Si NRA detecta ausencia de movimiento durante 30 segundos, activa el modo emergencia. Empieza a pitar, y si nadie lo detiene (quizás no estás en peligro, pero olvidaste apagarlo), significa que estás herido. Automáticamente, el módulo GPS empieza a funcionar en NRA, y envía la ubicación GPS a través de la red Sigfox a todos tus amigos/familiares.
Pregunté a muchos ciclistas y llegué a un diseño minimalista. Los ciclistas quieren un gadget fácil de usar y con buena batería. Con NRA, solo tienes que pulsar el botón y pedalear. Tras el entrenamiento, solo tienes que pararlo y cargarlo.
La carcasa de NRA es de plástico ABS. Usé una impresora 3D, proporcionada por la Universidad Politécnica de Valencia.
Puedes editar/descargar los archivos de diseño de NRA aquí:
https://www.tinkercad.com/things/fmkE685dNfP-nra-v01
Para esta construcción, uso un Arduino MKR1200, un GPS Neo 6-m, un botón, 2 LEDs, una resistencia de 1k Ohm, un zumbador, un sensor de vibración y una batería de 3.7V 150mAh.
Después, empecé con la configuración de Arduino y Sigfox. Esta es mi configuración en la página backend de Sigfox:
Cuando el Arduino MKR1200 envía datos, este servidor convierte los datos brutos en información. En mi caso, uso longitud, latitud y altitud. Con esta información, envío un email a todas las personas en mis ajustes (amigos, familia...) para que puedan hacer clic en el enlace de Google Maps y saber dónde está el ciclista herido.
Así encajan todos los componentes en la carcasa:
Primero, el módulo GPS. Solo necesitamos positivo, negativo y el cable de datos. La antena va pegada en el marco. El Arduino irá encima.
También añadimos la batería, conectada al Arduino, y un interruptor. Así podemos encender/apagar.
Tenemos el conector de batería para cargar NRA. Debe estar apagado.
Durante este proyecto usé un cargador de baterías de dron para cargar NRA. Funcionó perfectamente.
Tras hacer el cableado, ponemos la antena sobre el Arduino. Es un poco complicado unir todos los componentes en esta carcasa, pero con atención se puede hacer.
En esta construcción no usamos el sensor de vibración. En este caso, cuando NRA se enciende, empieza a enviar posición GPS cada 10 minutos. Los resultados fueron buenos.
También probamos NRA por carreteras. Fue una experiencia muy buena.
Es realmente fácil de usar, y te da la sensación de tener un buen gadget en tus manos.
Así es la historia de Never Ride Alone, un gadget que siempre está pendiente de ti. Siempre que montes, sabes que estás seguro, porque en caso de accidente, siempre serás localizado.
Desde el equipo de Never Ride Alone también hemos intentado mejorar nuestro planeta. Durante todas estas pruebas de ruta, también hemos recogido toda la basura que hemos visto por la montaña. Según los Objetivos de Desarrollo Sostenible, intentamos eliminar todos los residuos que vimos durante las rutas.
Tras recoger todo esto, nos dimos cuenta de que la mayor parte de la basura en la montaña viene de ciclistas. Hemos recogido muchos plásticos de barritas energéticas, geles y neumáticos. Ahora intentamos concienciar a todos los ciclistas. Durante carreras, hemos creado puntos limpios, donde la gente puede tirar su basura sin parar y perder tiempo. Esta zona está delimitada y, al terminar la carrera, se limpia todo.
Gracias por leer, ¡y pedalead seguro :)
Código
- NEVER RIDE ALONE V.0.1
NEVER RIDE ALONE V.0.1 Arduino
Código NRA. No usa sensores. Envía todos los datos GPS cada 10 minutos.
///// NEVER RIDE ALONE V.0.1 /////
//.............................//
////AUTOR: JESUS SORIANO ADAM////
////////FECHA: 05/05/2019 ///////
/////////////////////////////////
#include <SigFox.h>
#include <ArduinoLowPower.h>
#include <TinyGPS.h>// incluimos TinyGPS
#define WAITING_TIME 10 // tiempo de espera entre mensajes
#define GPS_PIN 2 //(pin transistor, para ahorro energía. no usado ahora)
#define GPS_INFO_BUFFER_SIZE 128
bool debug = true;
////////DEBUG/////
const int backlights = 4;
const int pulsador = 5;
const int audio = 3;
TinyGPS gps; // Objeto GPS
// Variables de datos GPS
int year;
byte month, day, hour, minute, second, hundredths;
unsigned long chars;
unsigned short sentences, failed_checksum;
char GPS_info_char;
char GPS_info_buffer[GPS_INFO_BUFFER_SIZE];
unsigned int received_char;
bool message_started = false;
int i = 0;
// Estructura de coordenadas GPS, 12 bytes en plataformas de 32 bits
struct gpscoord {
float a_latitude; // 4 bytes
float a_longitude; // 4 bytes
float a_altitude; // 4 bytes
};
float latitude = 0.0f;
float longitude = 0.0f;
float altitud = 0;
//////////////// Función de espera ////////////////
void Wait(int m, bool s) {
// m minutos de espera
// s pulsos lentos de LED
if (debug) {
Serial.print("Waiting: ");
Serial.print(m);
Serial.println(" min.");
}
digitalWrite(LED_BUILTIN, LOW);
if (s) {
int seg = m * 30;
for (int i = 0; i < seg; i++) {
digitalWrite(backlights, HIGH);
delay(500);
digitalWrite(backlights, LOW);
delay(500);
}
} else {
int seg = m * 15;
for (int i = 0; i < seg; i++) {
digitalWrite(backlights, HIGH);
delay(500);
digitalWrite(backlights, LOW);
delay(500);
}
}
}
/////////////////// Función Envío Sigfox ////////////////
void SendSigfox(String data) {
if (debug) {
Serial.print("Sending: ");
Serial.println(data);
if (data.length() > 12) {
Serial.println("Message too long, only first 12 bytes will be sent");
}
}
// Iniciar módulo
SigFox.begin();
// Esperar al menos 30ms tras primera configuración (100ms antes)
delay(100);
// Limpiar interrupciones pendientes
SigFox.status();
delay(1);
if (debug) SigFox.debug();
delay(100);
SigFox.beginPacket();
SigFox.print(data);
if (debug) {
int ret = SigFox.endPacket(true); // enviar buffer a red SIGFOX y esperar respuesta
if (ret > 0) {
Serial.println("No transmission");
} else {
Serial.println("Transmission ok");
}
Serial.println(SigFox.status(SIGFOX));
Serial.println(SigFox.status(ATMEL));
if (SigFox.parsePacket()) {
Serial.println("Response from server:");
while (SigFox.available()) {
Serial.print("0x");
Serial.println(SigFox.read(), HEX);
}
} else {
Serial.println("Could not get any response from the server");
Serial.println("Check the SigFox coverage in your area");
Serial.println("If you are indoor, check the 20dB coverage or move near a window");
}
Serial.println();
} else {
SigFox.endPacket();
}
SigFox.end();
}
//////////////////// Convertir GPS ////////////////////
/* Convierte datos float GPS a Char data */
String ConvertGPSdata(const void* data, uint8_t len) {
uint8_t* bytes = (uint8_t*)data;
String cadena;
if (debug) {
Serial.print("Length: ");
Serial.println(len);
}
for (uint8_t i = len - 1; i < len; --i) {
if (bytes[i] < 12) {
cadena.concat(byte(0)); // No probado
}
cadena.concat(char(bytes[i]));
if (debug) Serial.print(bytes[i], HEX);
}
if (debug) {
Serial.println("");
Serial.print("String to send: ");
Serial.println(cadena);
}
return cadena;
}
////////////////////////// Obtener posición GPS //////////////////////////
String GetGPSpositon() {
int messages_count = 0;
String pos;
if (debug) Serial.println("GPS ON");
digitalWrite(GPS_PIN, HIGH); // Encender GPS
Wait(1, false);
while (messages_count < 5000) {
while (Serial1.available()) {
int GPS_info_char = Serial1.read();
if (GPS_info_char == '$') messages_count++; // inicio de mensaje. Contar mensajes.
if (debug) {
if (GPS_info_char == '$') {
message_started = true;
received_char = 0;
} else if (GPS_info_char == '*') {
// fin de mensaje
for (i = 0; i < received_char; i++) {
Serial.write(GPS_info_buffer[i]); // escribe mensaje en PC una vez recibido completo
}
Serial.println();
message_started = false; // listo para nuevo mensaje
} else if (message_started == true) {
if (received_char <= GPS_INFO_BUFFER_SIZE) {
GPS_info_buffer[received_char] = GPS_info_char;
received_char++;
} else {
// reset por desbordamiento
message_started = false;
received_char = 0;
}
}
}
if (gps.encode(GPS_info_char)) {
gps.f_get_position(&latitude, &longitude);
altitud = gps.altitude() / 100;
// Almacenar coordenadas en estructura dedicada
gpscoord coords = {altitud, longitude, latitude};
gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths);
if (debug) {
Serial.println();
Serial.println();
Serial.print("Latitud/Longitud: ");
Serial.print(latitude, 5);
Serial.print(", ");
Serial.println(longitude, 5);
Serial.println();
Serial.print("Fecha: ");
Serial.print(day, DEC);
Serial.print("/");
Serial.print(month, DEC);
Serial.print("/");
Serial.print(year);
Serial.print(" Hora: ");
Serial.print(hour, DEC);
Serial.print(":");
Serial.print(minute, DEC);
Serial.print(":");
Serial.print(second, DEC);
Serial.print(".");
Serial.println(hundredths, DEC);
Serial.print("Altitud (metros): ");
Serial.println(gps.f_altitude());
Serial.print("Rumbo (grados): ");
Serial.println(gps.f_course());
Serial.print("Velocidad(kmph): ");
Serial.println(gps.f_speed_kmph());
Serial.print("Satelites: ");
Serial.println(gps.satellites());
Serial.println();
digitalWrite(backlights, HIGH);
delay(500);
digitalWrite(backlights, LOW);
delay(500);
}
gps.stats(&chars, &sentences, &failed_checksum);
if (debug) Serial.println("GPS turned off");
digitalWrite(GPS_PIN, LOW); // Apagar GPS
pos = ConvertGPSdata(&coords, sizeof(gpscoord)); // Enviar datos
return pos;
}
}
}
pos = "No Signal";
}
////////////////// SETUP //////////////////
void setup() {
pinMode(4, OUTPUT);
pinMode(backlights, OUTPUT);
pinMode(pulsador, INPUT);
pinMode(audio, OUTPUT);
if (debug) {
Serial.begin(9600);
while (!Serial) {} // esperar puerto serie. Necesario solo para puerto USB nativo
Serial.println("Serial Connected");
}
// Serial1 pines 13-14 para conexión 3.3V a GPS.
Serial1.begin(9600);
while (!Serial1) {}
if (debug) {
Serial.println("GPS Connected");
}
pinMode(GPS_PIN, OUTPUT); // pin interruptor GPS
if (!SigFox.begin()) {
Serial.println("Shield error or not present!");
return;
}
// Habilitar LED debug y desactivar deep sleep automático
if (debug) {
SigFox.debug();
} else {
SigFox.end(); // Enviar módulo al sueño más profundo
}
}
////////////////////// LOOP //////////////////////////
void loop() {
String position_data;
position_data = GetGPSpositon();
SendSigfox(position_data);
Wait(WAITING_TIME, false);
}
Piezas personalizadas y cajas
Encaja el MKR1200, la batería, el botón, los LEDs, GPS y el zumbador.
nra_v_0_1_qCsc7I6QvO.zip
Esquemáticos
Nota: la foto del Arduino no es el MKR1200
Proceso de manufactura
- Circuitos integrados (CI): chips compactos y potentes que impulsan la electrónica moderna
- Proyecto Dados Digitales Arduino: Construye tu Propio Generador de Números Aleatorios con LCD
- Sistema de Sensor de Temperatura Multiposición para Integración en Hogar Inteligente
- Mando USB para juegos DIY con Arduino – Construye tu propio gamepad de alto rendimiento
- Construye un juego Tetris con Arduino Nano en matriz LED 16x8 DIY
- MotionSense: Detección inteligente de intrusos con Arduino y ESP8266
- Construye una Caja Puzle IoT Segura con Arduino MKR Bundle – Guía Paso a Paso
- Kit MOSMusic: Control de Iluminación de Alta Potencia Sincronizada con Música vía MOSFET y Arduino
- Componentes Esenciales de una Taladradora: Visión General Completa
- Componentes Clave del Motor de Combustión Interna
- Dominando la sentencia switch en C#: Sintaxis, ejemplos y mejores prácticas