Project tutorial

Carfox: A Device to Find Them All © GPL3+

It makes your car smarter detecting accidents to help you where nobody can.

  • 3,736 views
  • 1 comment
  • 16 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)
3drag
3D Printer (generic)

Apps and online services

About this project

Why Carfox

The need of immediate assistance after a traffic accident is one of the greatest concerns in our society. By the time an accident takes place the procedure to identify and locate it can take minutes, hours or even days. This is a challenging problem that can be addressed using autonomous devices combined with standard IoT networks such as Sigfox. In particular, this technology provides a wide and deep coverage area including a geolocation system which is very useful in this type of applications.

This is the support of Carfox, a Sigfox-based device created to notify hospitals, authorities and even relatives when an accident takes place as fast as possible, providing useful information such as geolocation, severity and the source of the problem. This is totally transparent to the injured, is achieved using cost-effective components and can be used in any vehicle.

This project has been developed using MKRFOX1200 board, an Inertial Measure Unit (IMU) with 9 Degrees of Freedom (DoF) and a flexible force sensor. To supplement the functionality of Carfox, I created a dashboard in Losant platform to receive data from Sigfox backend and visualise the geolocation of the vehicle and characterise the kind of accident.

Hardware implementation

Before starting the project, you must solder some pins in the IMU that are not soldered in advance. Then, I have used a piece of perforated prototyping board for supporting the parts and reducing the size as can be seen in the figure below.

Later, you must solder each connection as it is shown in the next figure (see Fritzing for more detail). The IMU and MKRFOX1200 are communicated via I2C. Therefore, we need 4 wires to connect it completely (SDA, SCL, GND and VCC).

Now, we add the force sensor and a 100kΩ resistor. The idea is to create a voltage divider to measure forces adjusted to our scale (see Fritzing model). In particular, this flexible force sensor can measure a maximum of 100 lbs (45.36 kg).

The result should be something similar to the next figure (the protoboard is not necessary).

The fritzing of the project is the next one:

Some knowledge and characteristics of accelerometers are important to understand the code correctly and the way to manage them. The first thing that we must know is that these sensors measure acceleration which seems simple, however, we cannot forget acceleration caused by gravity. Therefore, accelerometers measurements, in a static position, are not zero, since one of its axes is near 9.81 m/s^2. Units could be provided in m/s^2 or simply in g. It means that 1 g is one times the gravity acceleration. In this case, accelerometer data are in mg (a one thousandth of g). This board has also gyroscopes which give information about angular velocity in rad/s or °/s.

This Inertial Measure Unit (IMU) provides several configurations with a programmable full-scale range for the accelerometer (±2 g, ±4 g, ±8 g, ±16 g) and gyroscope (±250 °/s, ±500 °/s, ±1000 °/s, ±2000 °/s). For the accelerometer I have chosen ±16 g since we do not need a high accuracy, but we want the widest range to detect high accelerations produced in accidents. In the case of the gyroscope, I have selected ±2000 °/s for the same reason.

Software resources

Arduino IDEIn this case I have Arduino IDE installed, you can download it on the official webpage or you can use the web editor.

For adding libraries it is very useful to use Arduino IDE tools, as can be seen in the link below.

https://www.arduino.cc/en/Guide/MKRFox1200

If you want to use the same IMU (is not necessary) you can see all the information required in the links below.

https://github.com/sparkfun/SparkFun_MPU-9250_Breakout_Arduino_Library

https://learn.sparkfun.com/tutorials/mpu-9250-hookup-guide#library-and-example-code

Sigfox

In Sigfox backend is important to know several things to surf easier. First, if you want to see incoming messages you must do the following actions.

If you want to forward messages to other platforms or to send an email when new data is received you can follow the next steps.

I implemented several callbacks. The first one to forward geolocation data to Losant platform, the second one to send an email with the same information plus the radius and the last to forward the rest of the values to Losant platform (Temperature data and high temperature, extreme temperature, accident and critical accident alarms). The configuration is shown below in the same order as explained.

Losant Platform

Losant is the platform that I have chosen to represent data, where temperature data inside the vehicle is shown, two panels to check if there are alarms regarding accident or high temperature and, finally, there is a map with the current geolocation.

For using this platform I highly recommend the tutorial made by Brandon Cannaday explaining how can be used Losant with Sigfox devices. I followed it step by step and all worked properly.

3D Design

Autodesk Fusion 360

I used Fusion 360 to design the cover of Carfox. It is a very complete and easy to use design software. Although, if you are new in 3D design there are many programs that are simpler than this one and very useful for developing small projects.

https://www.autodesk.com/products/fusion-360/overview

Ultimaker Cura Software

I used Cura software to export to .gcode the .stl file that provides fusion 360 (or any other). Do not forget to select the correct 3D printing before exporting.

https://ultimaker.com/en/products/ultimaker-cura-software

Video demonstration

Here you have the video demonstration.

Custom parts and enclosures

Carfox cover

Schematics

Carfox implementation
carfox_C1XJOjC5WO.fzz

Code

CarfoxArduino
This piece of code was developed to send automatic alarm messages when a vehicle accident occurs via Sigfox using the Arduino MKR Fox 1200. The information sent is related to geolocation, temperature and the cause of the accident.
/* Carfox: a device to find them all
   by: Luis Roda Sánchez
   date: October 25, 2017
   Based on Kris Winer sketch for managing MPU-9250 and modified by Brent Wilkins.
   Sketch runs on the MKR FOX 1200.

  Hardware setup:
  MPU9250 Breakout --------- Arduino MKRFOX1200
  VDD ---------------------- VCC (3.3V)
  SDA ----------------------- 11
  SCL ----------------------- 12
  GND ---------------------- GND
*/

#include <SigFox.h>
#include "MPU9250.h"

boolean SerialDebug;  // Set to true to get Serial output for debugging.

// Pin definitions:
int buzzer  = 5;  // Set up pin 5 for buzzer.
int pressure = 0;
int pressurePin = A1;  // Set up analog pin 1 for pressure sensor.
int absAx = 900;
int absAy = 900;
int absAz = 900;
int freeFall = 900;
float temperature = 25;
boolean firstTime = true;

// Limits (adjusted for testing,
// they should be modified for real case):
int accelerationLimit = 3000;
int pressureLimit = 200;  // Around 25 kg.
int freeFallLimit = 300;
uint8_t highThreshold = 29;
uint8_t extremeThreshold = 32;
boolean holdHighTemperature = false;
boolean holdExtremeTemperature = false;

// Sigfox message structure:
typedef struct __attribute__ ((packed)) sigfox_message {
  uint8_t temperatureSigfox;
  uint8_t highTemp = 0;
  uint8_t extremeTemp = 0;
  uint8_t accident = 0;
  uint8_t criticalAccident = 0;
} SigfoxMessage;

// stub for message which will be sent
SigfoxMessage msg;

MPU9250 IMU;

void setup()
{
  Wire.begin();
  Serial.begin(115200);
  if (!SigFox.begin()) {
    // Something is really wrong, try rebooting
    // Reboot is useful if we are powering the board using an unreliable
    // power source (eg. solar panels or other energy harvesting methods).
    reboot();
  }

  // Send module to standby until we need to send a message.
  SigFox.end();
  SigFox.debug();

  SerialDebug = true; // Set to true to get Serial output for debugging.
  pinMode(buzzer, OUTPUT);

  // Read the WHO_AM_I register, this is a good test of communication.
  byte c = IMU.readByte(MPU9250_ADDRESS, WHO_AM_I_MPU9250);
  if (SerialDebug) {
    Serial.print("MPU9250 "); Serial.print("I AM "); Serial.print(c, HEX);
    Serial.print(" I should be "); Serial.println(0x71, HEX);
  }

  if (c == 0x71) // WHO_AM_I should always be 0x68.
  {
    if (SerialDebug) {
      Serial.println("MPU9250 is online...");
    }
    IMU.initMPU9250();
    // Initialize device for active mode read of accelerometer, gyroscope
    // and temperature.
    if (SerialDebug) {
      Serial.println("MPU9250 initialized for active data mode....");
    }
  } // if (c == 0x71)
}

void loop()
{
  if (IMU.readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)
  {
    IMU.readAccelData(IMU.accelCount);  // Read the x/y/z accelerometer values.
    IMU.getAres();

    // Now we'll calculate the acceleration value into actual g's
    // This depends on scale being set, in this case the maximum range (+/- 16 g) (modified in library).
    IMU.ax = (float)IMU.accelCount[0] * IMU.aRes * 1000;
    IMU.ay = (float)IMU.accelCount[1] * IMU.aRes * 1000;
    IMU.az = (float)IMU.accelCount[2] * IMU.aRes * 1000;

    IMU.readGyroData(IMU.gyroCount);  // Read the x/y/z gyroscope values.
    IMU.getGres();

    // Calculate the gyro value into actual degrees per second
    // This depends on scale being set, in this case +/- 2000 DPS (modified in library).
    IMU.gx = (float)IMU.gyroCount[0] * IMU.gRes;
    IMU.gy = (float)IMU.gyroCount[1] * IMU.gRes;
    IMU.gz = (float)IMU.gyroCount[2] * IMU.gRes;

    pressure = analogRead(pressurePin);
    float pressureKgs = (float)pressure / 357 * 45.3592;

    IMU.tempCount = IMU.readTempData();  // Read the adc values.
    IMU.temperature = ((float) IMU.tempCount) / 333.87 + 21.0;

    if (SerialDebug) {
      // Pressure in kilograms.
      Serial.print("Pressure is ");
      Serial.print(pressureKgs);
      Serial.print(" kg");
      Serial.println();

      // Temperature in degrees Centigrade.
      Serial.print("Temperature is ");  Serial.print(IMU.temperature, 1);
      Serial.println(" degrees C");

      // Accelerations in mg.
      Serial.println("Accelerations:");
      Serial.print("X: "); Serial.print(IMU.ax);
      Serial.print(" mg ");
      Serial.print("Y: "); Serial.print(IMU.ay);
      Serial.print(" mg ");
      Serial.print("Z: "); Serial.print(IMU.az);
      Serial.println(" mg ");
      Serial.println();

      // Gyro values in degrees/sec.
      Serial.println("Gyro rates:");
      Serial.print("X: "); Serial.print(IMU.gx, 3);
      Serial.print(" degrees/sec ");
      Serial.print("Y: "); Serial.print(IMU.gy, 3);
      Serial.print(" degrees/sec ");
      Serial.print("Z: "); Serial.print(IMU.gz, 3);
      Serial.println(" degrees/sec");
      Serial.println();

      // Set alarms to false.
      msg.highTemp = 0;
      msg.extremeTemp = 0;
      msg.accident = 0;
      msg.criticalAccident = 0;
    } // if (SerialDebug)
  } // if (IMU.readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01)

  absAx = abs(IMU.ax);  // Absolute values.
  absAy = abs(IMU.ay);
  absAz = abs(IMU.az);

  msg.temperatureSigfox = (uint8_t)IMU.temperature; // Copy temperature to sigfox message.

  if (firstTime) { // Sending to refresh data at the beginning.
    firstTime = false;
    // Sending message via Sigfox.
    send2Sigfox();
  }

  if (msg.temperatureSigfox > highThreshold) {  // High Temperature.
    if (!holdHighTemperature) {  // To avoid sending repeated messages.
      holdExtremeTemperature = false;
      msg.highTemp = 1;
      holdHighTemperature = true;
      // Sending message via Sigfox.
      send2Sigfox();
    }
    else {
      if (msg.temperatureSigfox > extremeThreshold) {  // Extreme Temperature.
        if (!holdExtremeTemperature) {
          msg.extremeTemp = 1;
          holdExtremeTemperature = true;
          // Sending message via Sigfox.
          send2Sigfox();
          if (!SerialDebug) {
            do {
              delay(720000);  // Resend every 12 minutes.
              send2Sigfox();
            } while (1);
          }
        }
        else {
        }
      }
      else {
        if (msg.temperatureSigfox < (extremeThreshold - 1)) {  // Hysteresis to avoid errors and too many sendings.
          holdExtremeTemperature = false;
        }
      }
    }
  }
  else {
    if (msg.temperatureSigfox < (highThreshold - 1) && (holdHighTemperature == true || holdExtremeTemperature == true)) {  // Hysteresis to avoid errors and too many sendings.
      holdHighTemperature = false;
      holdExtremeTemperature = false;
      msg.highTemp = 0;
      msg.extremeTemp = 0;
      // Sending message via Sigfox to refresh data.
      send2Sigfox();
    }
  }

  if (absAx > accelerationLimit || absAy > accelerationLimit || absAz > accelerationLimit) {
    alarm(1);
  }
  if (IMU.az < 0) {
    alarm(4);
  }
  if (pressure > pressureLimit) {
    alarm(2);
  }
  freeFall = absAx + absAy + absAz;
  if (freeFall < freeFallLimit) {
    alarm(3);
  }
}

void alarm(unsigned char option) {  // Alarm goes off.
  int index = 9;
  switch (option) {
    case 1:
      if (SerialDebug) {
        Serial.println("ACCEL ALARM");
      }
      msg.accident = 1;
      send2Sigfox();
      do {
        analogWrite(buzzer, 50);
        delay(100);
        analogWrite(buzzer, 0);
        delay(200);
        if (SerialDebug) {
          index--;
        }
      } while (index > 7);
      break;

    case 2:
      if (SerialDebug) {
        Serial.println("PRESSURE ALARM");
      }
      msg.accident = 1;
      send2Sigfox();
      do {
        analogWrite(buzzer, 50); // Pin 5 to high.
        delay(300);
        analogWrite(buzzer, 0); // Pin 5 to low.
        delay(200);
        if (SerialDebug) {  // Infinite loop if a real case.
          index--;
        }
      } while (index > 6);
      break;

    case 3:
      if (SerialDebug) {
        Serial.println("FREEFALL ALARM");
      }
      msg.criticalAccident = 1;
      send2Sigfox();
      do {
        analogWrite(buzzer, 90);
        delay(100);
        analogWrite(buzzer, 0);
        delay(200);
        if (SerialDebug) {
          index--;
        }
      } while (index > 5);
      break;

    case 4:
      if (SerialDebug) {
        Serial.println("ROLL-OVER ALARM");
      }
      msg.criticalAccident = 1;
      send2Sigfox();
      do {
        analogWrite(buzzer, 50);
        delay(100);
        analogWrite(buzzer, 0);
        delay(200);
        if (SerialDebug) {
          index--;
        }
      } while (index > 2);
      break;
  }
}

void send2Sigfox() {
  // Start the module
  SigFox.begin();
  // Wait at least 30ms after first configuration (100ms before)
  delay(100);
  // Clear all pending interrupts
  SigFox.status();
  delay(1);
  SigFox.beginPacket();
  SigFox.write((uint8_t*)&msg, 5);
  SigFox.endPacket();
}

void reboot() {
  NVIC_SystemReset();
  while (1);
}

Comments

Similar projects you might like

Hacking My Toaster

Project tutorial by javier muñoz sáez

  • 306 views
  • 5 comments
  • 9 respects

Rickroll Box

Project showcase by slagestee

  • 916 views
  • 0 comments
  • 4 respects

START: A STandalone ARduino Terminal

Project tutorial by Alessio Villa

  • 769 views
  • 0 comments
  • 4 respects

Music Reactive LED Strip

Project showcase by buzzandy

  • 289 views
  • 2 comments
  • 10 respects

Pavlov's Cat

Project tutorial by Arduino

  • 489 views
  • 0 comments
  • 2 respects

Infrared Controlled Logic Puzzle -- Lights On!

Project tutorial by FIELDING

  • 163 views
  • 0 comments
  • 5 respects
Add projectSign up / Login