Project tutorial
Never Ride Alone

Never Ride Alone © GPL3+

NRA allows us, in case of bike accident, to locate the injured person through the Sigfox network. It also improves visibility on the road.

  • 4,652 views
  • 15 comments
  • 27 respects

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)

About this project

Hello everybody, I am Jesús Soriano, student of electronic engineering, entrepreneur... and cyclist!

I have been riding my bike since I was 12, and I usually train alone. During these trainings, I have had lots of falls, accidents and other mishaps. One day, I realised that, if I had a hard accident and I lost consciousness, I could die. I was alone in the mountains!

It is easier to understand what I mean with a little video. Imagine: You are riding your bike, enjoying the landscape and suddenly, a car appears and runs over you. You fall down a slope, and the driver runs away. You stay unconscious, trapped, invisible to the world.

Two or three hours later, your family will start to worry. The emergency services will try to locate you, and they will after 4-5 hours. You will be probably dead.

Story video: the accident

So I started doing research, and I saw many interesting options: First, an app, Strava Summit. In case of accident, it could send the gps position to friends. Angi, from Specialized, was also an option. The system was integrated on the helmet, and in case of colision, it could ask for help. But it was a problem. All of these systems needed a mobile phone signal to work. Battery, gps and mobile phone signal were necessary.

That is how Never Ride Alone, NRA, came into being. I found Sigfox as my solution for data sending in the mountains, and when I discovered that they had an arduino with sigfox module... It was awesome!

I decided that NRA was going to work like an inverted alarm. When you go cycling, you activate NRA by pressing a button. Leds start blinking, while they improve visibility too. If NRA detects no motion in 30 seconds, it activates emergency mode. It starts buzzing, and if nobody stops it (maybe you are not in danger, but you forgot to turn it off) is that you are injured. Automatically, the gps module starts working in NRA, and it sends gps location through the SigFox network to all of your friends/family.

I asked lots of cyclists, and I came up with a minimalist design. Cyclists want a gadget that is easy to use, and with a good battery. With NRA, you only have to press the button and pedal. After training, you only have to stop it and charge it.

The case of NRA was made of ABS plastic. I used a 3D printer, provided by The Polythecnic University of Valencia.

You can edit/download NRA design files here:

https://www.tinkercad.com/things/fmkE685dNfP-nra-v01

For this build, I am using an Arduino MKR1200, a Neo 6-m GPS, a button, 2 leds, one 1k Ohm resistor, a buzzer, a vibration sensor and a 3.7v 150mah battery.

After that, I started with Arduino and SigFox configuration. This is my configuration on the SigFox Backend Page:

When the Arduino MKR1200 sends data, this server converts the raw dat givens into information. In my case, I use the longitude, latitude and altitude. With this information, I send an e-mail to all the people in my settings (friends, family...) so they can click on the google maps link, and know where the injured biker is.

This is how all the components are fitted in the case:

First of all, the GPS module. We only need positive, negative, and the data transfer wire. The antenna goes glued on the frame. The arduino will go over it.

We also add the battery, connected to the arduino, and also a switch. This way, we can turn it on/off.

We have the battery connector, so we can charge NRA. It should be off.

During this project I have used a drone battery charger for charging NRA. It worked perfectly.

After making the wiring, we put the antenna over the arduino. It is a little bit complicated to join all the components in this shell, but with attention it can be done.

In this build, we didn´t use the vibration sensor. In this case, when NRA is powered, it starts sending GPS position every 10 minutes. The results where good.

First time powered

We also tested NRA along the roads. It was a very good experience.

It is really easy to use, and it gives you the feeling you have a good gadget on your hands.

So this is Never Ride Alone history, a gadget that is aware always of you. Whenever you ride, you know you are safe, because in case of accident, you will be always located.

From Never Ride Alone team we have also tried to improve our planet. During all of these ride tests, we also have picked up all trash we have seen around the mountains. According to the Sustainable Development Goals, we tried to remove all the bin we saw during the rides.

After picking up all of this, we realized that most of the trash in the mountains comes from cyclists! We have picked up lots of energetic bars plastics, gels and tires. Now, we are trying to raise awareness among all cyclists. During races, we have created clean points, where people can throw all their rubbish without stopping and losing time. This area is bounded and, when the race finishes, it is all cleaned.

Thanks for reading, and ride safe :)

Code

NEVER RIDE ALONE V.0.1Arduino
NRA code. It doesn´t use sensors. It sends all the gps data every 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>//we include TinyGPS

#define WAITING_TIME 10 //waiting time during every message
#define GPS_PIN 2 //(tranistor pin, for energy saving. not used now)
#define GPS_INFO_BUFFER_SIZE 128

bool debug = true; ////////DEBUG/////

const int backlights=4;
const int pulsador=5;
const int audio=3;   

TinyGPS gps;//GPS Object

//GPS data variables
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;

// GPS coordinate structure, 12 bytes size on 32 bits platforms
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;


//////////////// Waiting function //////////////////
void Wait(int m, bool s) {
  //m minutes to wait
  //s slow led pulses
  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);  

    }
  }
}

/////////////////// Sigfox Send Data function ////////////////
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");
    }
  }

  // Remove EOL
  //data.trim();

  // Start the module
  SigFox.begin();
  // Wait at least 30mS after first configuration (100mS before)
  delay(100);
  // Clears all pending interrupts
  SigFox.status();
  delay(1);
  if (debug) SigFox.debug();
  delay(100);

  SigFox.beginPacket();
  SigFox.print(data);


  if (debug) {
    int ret = SigFox.endPacket(true);  // send buffer to SIGFOX network and wait for a response
    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();
}


//////////////////  Convert GPS function  //////////////////
/* Converts GPS float data to 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)); // Not tested
    }
    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;
}


////////////////////////// Get GPS position function/////////////////////
String GetGPSpositon() {

  int messages_count = 0;
  String pos;

  if (debug) Serial.println("GPS ON");
  digitalWrite(GPS_PIN, HIGH); //Turn GPS on
  Wait(1, false);
  while (messages_count < 5000) {
    while (Serial1.available()) {

      int GPS_info_char = Serial1.read();

      if (GPS_info_char == '$') messages_count ++; // start of message. Counting messages.


      if (debug) {
        if (GPS_info_char == '$') { // start of message
          message_started = true;
          received_char = 0;
        } else if (GPS_info_char == '*') { // end of message
          for (i = 0; i < received_char; i++) {
            Serial.write(GPS_info_buffer[i]); // writes the message to the PC once it has been completely received
          }
          Serial.println();
          message_started = false; // ready for the new message
        } else if (message_started == true) { // the message is already started and I got a new character
          if (received_char <= GPS_INFO_BUFFER_SIZE) { // to avoid buffer overflow
            GPS_info_buffer[received_char] = GPS_info_char;
            received_char++;
          } else { // resets everything (overflow happened)
            message_started = false;
            received_char = 0;
          }
        }
      }

      if (gps.encode(GPS_info_char)) {
        gps.f_get_position(&latitude, &longitude);
        altitud = gps.altitude() / 100;

        // Store coordinates into dedicated 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); //GPS turned off
        pos = ConvertGPSdata(&coords, sizeof(gpscoord)); //Send data
        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) {}// wait for serial port to connect. Needed for native USB port only
    Serial.println("Serial Connected");
  }


  //Serial1 pins 13-14 for 3.3V connection to GPS.
  Serial1.begin(9600);
  while (!Serial1) {}
  if (debug) {
    Serial.println("GPS Connected");
  }

  pinMode(GPS_PIN, OUTPUT); //pin de interruptor del GPS

  if (!SigFox.begin()) {
    Serial.println("Shield error or not present!");
    return;
  }

  // Enable debug led and disable automatic deep sleep
  if (debug) {
    SigFox.debug();
  } else {
    SigFox.end(); // Send the module to the deepest sleep
  }


}



//////////////////////LOOP////////////////////////

void loop() {
   
  
  String position_data;
  position_data = GetGPSpositon();
  SendSigfox(position_data);

  Wait(WAITING_TIME, false);

 
 
 


}

Custom parts and enclosures

NRA case
It fits the mkr1200, the battery, a button, the leds, GPS and the buzzer.
NRA case
It fits the mkr1200, the battery, a button, the leds, GPS and the buzzer.
nra_v_0_1_qCsc7I6QvO.zip

Schematics

NRA diagram
Note that the arduinos photo is not the mkr1200
Pinout pgrjq9ebgm

Comments

Similar projects you might like

How to Build a DIY Arduino-Based Smart Home Hub with 1Sheeld

Project tutorial by amrmostaafaa

  • 16,130 views
  • 1 comment
  • 43 respects

USBcycle: Ride Through Your Virtual World!

Project showcase by Tazling

  • 13,666 views
  • 23 comments
  • 23 respects

Arduino-Powered Water Bottle

Project tutorial by Md. Khairul Alam

  • 9,295 views
  • 5 comments
  • 35 respects

Fairytale

Project in progress by siliconchris

  • 1,054 views
  • 3 comments
  • 3 respects

Third Eye for The Blind

Project tutorial by Muhammed Azhar

  • 82,171 views
  • 47 comments
  • 158 respects

Otto DIY+ Arduino Bluetooth robot easy to 3D Print

Project tutorial by Team Otto builders

  • 75,238 views
  • 135 comments
  • 217 respects
Add projectSign up / Login