Never Ride Alone : Système de sécurité vélo intelligent
Composants et fournitures
![]() |
|
× | 2 | |||
|
× | 1 | ||||
![]() |
|
× | 1 | |||
![]() |
|
× | 1 | |||
![]() |
|
× | 1 | |||
![]() |
|
× | 1 | |||
![]() |
|
× | 1 | |||
![]() |
|
× | 1 | |||
![]() |
|
× | 1 |
Outils et machines nécessaires
![]() |
|
À propos de ce projet
Bonjour, je suis Jesús Soriano, étudiant en génie électronique, entrepreneur... et cycliste !

Je fais du vélo depuis mes 12 ans, souvent seul. Lors de ces entraînements, j'ai eu de nombreuses chutes, accidents et incidents. Un jour, j'ai réalisé que si j'avais un grave accident et perdais connaissance, je pouvais mourir. J'étais seul en montagne !
Une petite vidéo illustre le scénario : vous roulez, profitez du paysage, soudain une voiture vous percute. Vous dévalez un talus, le conducteur prend la fuite. Vous restez inconscient, piégé, invisible.
Deux ou trois heures plus tard, votre famille s'inquiète. Les secours essaient de vous localiser et y parviennent après 4-5 heures. Vous serez probablement mort.
J'ai cherché des solutions existantes : Strava Summit (envoie position GPS aux amis en cas d'accident), Angi de Specialized (intégré au casque, appelle les secours en cas de collision). Problème : tous nécessitent un signal mobile. Batterie, GPS et signal mobile indispensables.
C'est ainsi qu'est né Never Ride Alone (NRA). J'ai trouvé Sigfox pour l'envoi de données en montagne, et quand j'ai découvert qu'ils avaient un Arduino avec module Sigfox... c'était parfait !

NRA fonctionne comme une alarme inversée. Quand vous partez rouler, vous activez NRA en appuyant sur un bouton. Les LED clignotent, améliorant la visibilité. Si NRA ne détecte aucun mouvement pendant 30 secondes, il active le mode urgence : buzze, et si personne ne l'arrête (vous avez oublié de l'éteindre), c'est que vous êtes blessé. Automatiquement, le module GPS démarre et envoie la position via le réseau Sigfox à tous vos contacts (amis, famille).

J'ai interrogé de nombreux cyclistes pour aboutir à un design minimaliste. Les cyclistes veulent un gadget simple à utiliser, avec bonne autonomie. Avec NRA, il suffit d'appuyer sur le bouton et de pédaler. Après l'entraînement, on l'arrête et on le recharge.
Le boîtier de NRA est en plastique ABS, imprimé en 3D à l'Université Polytechnique de Valence.

Vous pouvez éditer/télécharger les fichiers de conception NRA ici : Tinkercad
Pour cette construction, j'utilise : Arduino MKR1200, GPS Neo 6-m, bouton, 2 LED, résistance 1 kΩ, buzzer, capteur de vibration, batterie 3,7 V 150 mAh.

Configuration Arduino et SigFox. Voici ma configuration sur la page backend SigFox :

Quand l'Arduino envoie des données, le serveur convertit les données brutes en informations. J'utilise longitude, latitude, altitude. Avec ces infos, j'envoie un email à tous mes contacts (amis, famille...) avec un lien Google Maps pour localiser le cycliste blessé.

Voici comment tous les composants s'intègrent dans le boîtier :

D'abord le module GPS : seulement VCC, GND et fil de données. L'antenne est collée sur le cadre. L'Arduino se place par-dessus.

Ajout de la batterie connectée à l'Arduino et d'un interrupteur pour marche/arrêt.

Connecteur de charge de la batterie (doit être éteint pour charger).

J'ai utilisé un chargeur de batterie de drone pour charger NRA. Ça a parfaitement fonctionné.

Après le câblage, on place l'antenne au-dessus de l'Arduino. C'est un peu délicat d'assembler tous les composants dans ce boîtier, mais avec de l'attention c'est faisable.

Dans cette version, nous n'avons pas utilisé le capteur de vibration. Quand NRA est alimenté, il envoie la position GPS toutes les 10 minutes. Les résultats étaient bons.
Nous avons aussi testé NRA sur route. Expérience très positive.
C'est vraiment facile à utiliser et donne le sentiment d'avoir un bon gadget entre les mains.

Voilà l'histoire de Never Ride Alone, un gadget qui veille toujours sur vous. Quand vous roulez, vous savez que vous êtes en sécurité, car en cas d'accident, vous serez toujours localisé.

L'équipe Never Ride Alone a aussi essayé d'améliorer la planète. Lors de tous ces tests, nous avons ramassé tous les déchets vus en montagne. Selon les Objectifs de Développement Durable, nous avons tenté d'éliminer tous les déchets rencontrés.

Après ce ramassage, nous avons réalisé que la plupart des déchets en montagne viennent des cyclistes ! Beaucoup d'emballages de barres énergétiques, gels et pneus. Maintenant, nous essayons de sensibiliser tous les cyclistes. Pendant les courses, nous avons créé des points de propreté où les gens peuvent jeter leurs déchets sans s'arrêter ni perdre de temps. Cette zone est délimitée et, à la fin de la course, tout est nettoyé.

Merci de votre lecture, et roulez prudemment :)
Code
- NEVER RIDE ALONE V.0.1
NEVER RIDE ALONE V.0.1 Arduino
Code NRA. N'utilise pas les capteurs. Envoie toutes les données GPS toutes les 10 minutes.
///// NEVER RIDE ALONE V.0.1 ///// //.............................// ////AUTOR: JESUS SORIANO ADAM//// ////////FECHA: 05/05/2019 /////// /////////////////////////////////
#include <SigFox.h>
#include <ArduinoLowPower.h>
#include <TinyGPS.h>// inclusion TinyGPS
#define WAITING_TIME 10 // temps d'attente entre messages (min)
#define GPS_PIN 2 // (broche transistor, pour économie d'énergie. non utilisé maintenant)
#define GPS_INFO_BUFFER_SIZE 128
bool debug = true; ////////DEBUG/////
const int backlights = 4;
const int pulsador = 5;
const int audio = 3;
TinyGPS gps; // Objet GPS
// Variables données 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;
// Structure coordonnées GPS, 12 octets sur plateformes 32 bits
struct gpscoord {
float a_latitude; // 4 octets
float a_longitude; // 4 octets
float a_altitude; // 4 octets
};
float latitude = 0.0f;
float longitude = 0.0f;
float altitud = 0;
// Fonction d'attente
void Wait(int m, bool s) {
// m minutes à attendre
// s clignotement lent des 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);
}
}
}
// Fonction envoi 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");
}
}
SigFox.begin();
delay(100);
SigFox.status();
delay(1);
if (debug) SigFox.debug();
delay(100);
SigFox.beginPacket();
SigFox.print(data);
if (debug) {
int ret = SigFox.endPacket(true);
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();
}
// Conversion données GPS
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)); // Non testé
}
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;
}
// Obtention position GPS
String GetGPSpositon() {
int messages_count = 0;
String pos;
if (debug) Serial.println("GPS ON");
digitalWrite(GPS_PIN, HIGH); // Allume GPS
Wait(1, false);
while (messages_count < 5000) {
while (Serial1.available()) {
int GPS_info_char = Serial1.read();
if (GPS_info_char == '$') messages_count++; // début message
if (debug) {
if (GPS_info_char == '$') {
message_started = true;
received_char = 0;
} else if (GPS_info_char == '*') {
for (i = 0; i < received_char; i++) {
Serial.write(GPS_info_buffer[i]);
}
Serial.println();
message_started = false;
} else if (message_started == true) {
if (received_char <= GPS_INFO_BUFFER_SIZE) {
GPS_info_buffer[received_char] = GPS_info_char;
received_char++;
} else {
message_started = false;
received_char = 0;
}
}
}
if (gps.encode(GPS_info_char)) {
gps.f_get_position(&latitude, &longitude);
altitud = gps.altitude() / 100;
// Stockage coordonnées dans structure
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); // Éteint GPS
pos = ConvertGPSdata(&coords, sizeof(gpscoord));
return pos;
}
}
}
pos = "No Signal";
return pos;
}
// SETUP
void setup() {
pinMode(4, OUTPUT);
pinMode(backlights, OUTPUT);
pinMode(pulsador, INPUT);
pinMode(audio, OUTPUT);
if (debug) {
Serial.begin(9600);
while (!Serial) {}
Serial.println("Serial Connected");
}
// Serial1 broches 13-14 pour connexion 3.3V au GPS
Serial1.begin(9600);
while (!Serial1) {}
if (debug) {
Serial.println("GPS Connected");
}
pinMode(GPS_PIN, OUTPUT);
if (!SigFox.begin()) {
Serial.println("Shield error or not present!");
return;
}
if (debug) {
SigFox.debug();
} else {
SigFox.end(); // Sommeil profond
}
}
// LOOP
void loop() {
String position_data;
position_data = GetGPSpositon();
SendSigfox(position_data);
Wait(WAITING_TIME, false);
}
Pièces personnalisées et boîtiers
Adapté au MKR1200, batterie, bouton, LED, GPS et buzzer.
nra_v_0_1_qCsc7I6QvO.zipSchémas
Note : la photo de l'Arduino n'est pas le MKR1200
Processus de fabrication
- Circuits intégrés (CI) : puces compactes et puissantes alimentant l'électronique moderne
- Maîtriser l'instruction Switch C# : Syntaxe, exemples et bonnes pratiques
- Dé numérique Arduino sur écran LCD 16x2 – Générateur aléatoire à bouton poussoir
- Système de Capteur de Température Multi-Position pour Intégration Domotique
- Manette de jeu USB Arduino DIY – Construisez votre propre gamepad haute performance
- Jeu Tetris sur Matrice LED 16×8 DIY avec Arduino Nano
- Kit MOSMusic : MOSFET, Arduino et plus pour éclairage musical
- Composants Essentiels d'une Perceuse : Vue d'Ensemble Complète
- Composants clés d'un moteur à combustion interne








