Project showcase
Race Car Dashboard (Online Connectivity)

Race Car Dashboard (Online Connectivity) © CERN-OHL

The project was initially designed to acquire real-time race car data for BAJA-SAE competitions.

  • 25,649 views
  • 64 comments
  • 46 respects

Components and supplies

Apps and online services

About this project

The BAJA-SAE organizes events all across the globe and invites undergraduate students to participate in it. The competition includes fabricating and running a student build all terrain vehicles in a number of different challenges. The project initially began to provide a visual display of car performance to the driver and the pit crew. The dashboard includes a 3.2” TFT display with built in SD card module, a GPS receiver, and NodeMCU all interfaced to an Arduino Mega.

The dashboard display includes:

  • Speedometer
  • Clock
  • Lap Counter
  • Odometer
  • Performance Meter

The dashboard tracks location and calculates speed using a SkyTraq GPS and displays it on the LCD Display. The data is also uploaded in real time to a Google spreadsheet by NodeMCU using the PushingBox (as a gateway between ESP and Google) which can be accessed by crew members to track location and performance of the vehicle. A brief discussion of the major functions are as below:

Lap Counter

The idea behind building a lap counter was to overcome the the absence of any lap information to the driver in a race (display boards far away from race track). The lap counter is designed in such a way that it requires no prior information of the race track and builds its data during the race. It uses GPS to track location and increments the lap counter after each entry to the initial start point.

Speedometer and Odometer

One of the essential parameters to the driver is the current speed of the vehicle and it is calculated using distance and time measurements from the GPS. Along with speed measurements, total distance traveled is also calculated using distance measurements between two GPS points and all the data is stored in an SD card module as well as updated in a Google spreadsheet.

Clock

An on board clock acts as a reminder to the driver for time left till race end, refueling plans and maintenance checks.

Metalic pull-button

Metallic push-buttons provides with easy change between display options and as a turn on switch for the system and adds to the final appearance of the system.

NodeMCU

NodeMCU uploads and updates the Google Spreadsheet and makes the project go all IOT! Implementing the NodeMCU to upload data to the spreadsheet is the trickiest part. Here's a good tutorial to how to do it by Stephen Borsay.

To further link the spreadsheet to Google Fusion table and have the location points displayed over the map, follow the steps in the tutorial below:

Shown below is a graphic representation and comparison of data for different laps to analyze performance, optimize fueling and maintenance timings (using Python).

UpdateRegarding Fusion Tables:

Fusion Table is going to be turned down by google on Dec 3, 2019. Please find the official mail attached below.

Code

DashboardArduino
#include<SD.h>
#include<SPI.h>
#include <TFT_HX8357.h>                  // Hardware-specific library For 3.2" LCD
#include <TinyGPS.h>                     //Library for GPS


TFT_HX8357 tft = TFT_HX8357();           // Invoke custom library for LCD


#define BU_BMP 1     // Temporarily flip the TFT coords for standard Bottom-Up bit maps
#define TD_BMP 0     // Draw inverted Top-Down bitmaps in standard coord frame



//Colors Defined

#define LTBLUE    0xB6DF
#define LTTEAL    0xBF5F
#define LTGREEN   0xBFF7
#define LTCYAN    0xC7FF
#define LTRED     0xFD34
#define LTMAGENTA 0xFD5F
#define LTYELLOW  0xFFF8
#define LTORANGE  0xFE73
#define LTPINK    0xFDDF
#define LTPURPLE  0xCCFF
#define LTGREY    0xE71C

#define BLUE      0x001F
#define TEAL      0x0438
#define GREEN     0x07E0
#define CYAN      0x07FF
#define RED       0xF800
#define MAGENTA   0xF81F
#define YELLOW    0xFFE0
#define ORANGE    0xFC00
#define PINK      0xF81F
#define PURPLE    0x8010
#define GREY      0xC618
#define WHITE     0xFFFF
#define BLACK     0x0000

#define DKBLUE    0x000D
#define DKTEAL    0x020C
#define DKGREEN   0x03E0
#define DKCYAN    0x03EF
#define DKRED     0x6000
#define DKMAGENTA 0x8008
#define DKYELLOW  0x8400
#define DKORANGE  0x8200
#define DKPINK    0x9009
#define DKPURPLE  0x4010
#define DKGREY    0x4A49



//WIFI VARIABLES

#define DST_HOST "213.186.33.19"       //IP Address (Pushingbox)
String devid="vAFCC5109D44ADC6";       //Device Id



//GPS VARIABLES

TinyGPS gps;
float   lat;                //Latitude
float   lon;                //Longitude
float   gps_speed;
float   gps_speedold=0;
String  gps_clock;
int     year;
byte    month, day, hour, minute, second, hundredths;
unsigned long age;



//Lap Counter Variables

int   lap=0;
int   lap_flag=0;
int   lap_initialize=0;
int   lap_first=0;
float lat_initial;
float lon_initial;
float lat_old;
float lon_old;


//Distance Variables

float distance=0;
float lap_distance=0;
float lap_reset=0;
float per_distance=0;
float avg_distance=0;
float avg_speed=0;
float lapspeed_average=0;
unsigned long lap_start=0;
unsigned long time_old=0;;
unsigned long server_update=0;
unsigned long disp=0;


//SD Variables

File Jat;
int sd_check=1;
String sd_data;

//Button Variable

int button=13;
int but_state=0;
int sel=1;

void setup() 

{

    tft.init();                                //Initialize LCD
    tft.fillScreen(TFT_BLACK);
    
    pinMode(12,INPUT);
            
  while(digitalRead(12)==0) 
  {
    delay(1000);
  }
  
  
  SD.begin(53);
  

  Serial1.begin(9600);                      //Begins GPS Communication

  lcd_setup();

  bootup();
 
  time_old=millis();

  but_state=digitalRead(12);
 
 
 }

void loop() 
{


  gps_function();

  print_date(gps);
 
  lap_counter();
 
  distance_function();
 
  data_log();
  
  if(digitalRead(12)!=but_state)
   {
    
     but_state=digitalRead(12);
  
     if(sel==4)
      { 
       sel=1;
     }
      
     else
     { 
      sel++;
     }
     
   } 

    switch(sel)
     {
    
      case 1 :
             prints();
             break;

      case 2 :
             print_speed();
             break;
           
      case 3 :
             print_time();
             break;
           
      case 4 :
            print_dis();
            break;
     }

   
  
  if(millis()-server_update>14000)
  {
    delay(200);
    google();
    server_update=millis();
    
  }

  delay(600);
  
}

void lcd_setup()

{
    
    //The syste begins by loading an image/logo in bitmap format
    
    tft.setRotation(1);
    tft.fillScreen(TFT_WHITE);
    tft.setTextSize(1);
    tft.setTextFont(4);
    tft.setTextColor(0x0000); 
    tft.setCursor (190, 60);
    drawBMP("untitled.bmp", 90, 20, BU_BMP);                             //Change untitled to any bitmap image name you wish to use
    delay(3000);
    tft.setRotation(1);
    tft.fillScreen(0x07FF);
    tft.print("WELCOME"); 
    tft.setTextSize(2); 
    tft.setCursor (149,135);        
    tft.print("JATAYU");
    tft.setTextSize(1);
    tft.setCursor (135, 235);        
    tft.print("Thrive Till The End !");
    
    delay(3000);

}

void bootup()

{
    tft.fillScreen(TFT_BLACK);
    tft.setTextSize(1);
    tft.setTextFont(4);
    tft.setTextColor(TFT_WHITE); 
    tft.setCursor(150,20);
    tft.print("BOOTING UP..");
    tft.setCursor(100,95);
    tft.print("SD");
    tft.setCursor(100,180);
    tft.print("WIFI");
    tft.setCursor(100,260);
    tft.print("GPS");

    sd_setup();
    wifi_setup();
    gps_setup();

    delay(2000);

} 

void sd_setup()

{
 
 Jat=SD.open("MAIT.csv",FILE_WRITE);

 if(Jat)
   {
    tft.setCursor(250,95);
    Jat.print(" Time, Latitude, Longitude, Lap, Lap Length(Km), Distance(Km), Lap Travel(Km), Speed(Km/hr), Average Lap Speed(Km/hr)");
    tft.print("Okay");
    Jat.close();
   }
   
 else
   {
    tft.setCursor(250,95);
    tft.print("Error");
   }

}

void wifi_setup()
{
  
  Serial2.begin(115200);
 
  String ssid ="MBLAZE-EC315-EFC8";

   String password="80C77D93";

   Serial2.println("AT+RST");              //To Reset Module
     
   
  if(Serial2.find("CONNECTED"))
    
    {
    }

    
   else;
    
  delay(4000);
   
  String cmd ="AT+CWJAP=\"" + ssid + "\",\"" + password + "\"";
     
 Serial2.println(cmd);                   //To Setup wifi

   
  if(Serial2.find("CONNECTED"))
    
    {
    
      tft.setCursor(250,180);
      tft.print("Okay");
   
    }

    
   else
    {
    
      tft.setCursor(250,180);
      tft.print("Error");
   
    }
  
   Serial2.println("AT+CIPMUX=0");         //To setup a single link

   delay(3000);

}

void gps_setup()
{

  gps_function();
 
 if(lat!=1000)
 {
      tft.setCursor(250,260);
      tft.print("Okay");
  
 }

 else
 {
      tft.setCursor(250,260);
      tft.print("Connecting");
   
 }
 
}

void gps_function()
{  
 
  smartdelay(600);
   
  gps.f_get_position(&lat, &lon, &age);
     
      
}


//To Read GPS Data
static void smartdelay(unsigned long ms)
{
  unsigned long start = millis();
  
  do 
  {
  
    while (Serial1.available())
      gps.encode(Serial1.read());

  } while (millis() - start < ms);

}


//Date Funtion Of GPS
void print_date(TinyGPS &gps)
{
  
  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);

  if (age == TinyGPS::GPS_INVALID_AGE)
    { 
      gps_clock="Connecting";
    }
   
   else
  {
    
    gps_speed=gps.f_speed_kmph();
      
    if(lap_initialize==1)
    {
      if(abs(TinyGPS::distance_between(lat,lon,lat_old,lon_old))<100)
      {
        distance = distance+(abs(TinyGPS::distance_between(lat,lon,lat_old,lon_old)))/1000;
        lat_old = lat;
        lon_old = lon;
      }
    }
    
    if(lap_initialize==0)
     { 
       lap_initialize=1;
       lat_initial=lat;
       lon_initial=lon;
       lat_old=lat;
       lon_old=lon;
       
     }
    
    if(minute<30)
      {
        if(second<10)
        {
          gps_clock=String(hour+5)+":"+String(minute+30)+":"+"0"+String(second);    
        }
        
        else
        {
         gps_clock=String(hour+5)+":"+String(minute+30)+":"+String(second);    
        }
      }
      
    else
      {
        if((minute-30)<10)
        {
          if(second<10)
          {
            gps_clock=String(hour+6)+":"+"0"+String(minute-30)+":"+"0"+String(second);
          }
          else
          {
            gps_clock=String(hour+6)+":"+"0"+String(minute-30)+":"+String(second);
          }
         }
        else
        {
          if(second<10)
         {
           gps_clock=String(hour+6)+":"+String(minute-30)+":"+"0"+String(second);        
         } 
         else
         {
         gps_clock=String(hour+6)+":"+String(minute-30)+":"+String(second);        
         }
        }
        
      }
  }
     
}


//Lap Counter
void lap_counter()
{
  
  if(lap_initialize==1)
  {
    
    if((abs(TinyGPS::distance_between(lat,lon,lat_initial,lon_initial))<10)&&(lap_flag==1))
      {
       
        lap=lap+1;
    
        lapspeed_average=avg_speed;
    
        lap_reset=distance;
    
        avg_distance=distance/lap;
    
        lap_flag=0;
    
        lap_start=millis();
  
       }

      if((abs(TinyGPS::distance_between(lat,lon,lat_initial,lon_initial))>100)&&(lap_flag==0))
        {
         lap_flag=1;
        }

      }

}

//Lap Distance Function
void distance_function()

 {

   lap_distance=distance-lap_reset;
  
   per_distance=100*lap_distance/avg_distance;
  
   avg_speed=3600000*lap_distance/(millis()-lap_start);

 }

void data_log()
{
  Jat=SD.open("MAIT.csv",FILE_WRITE);
 
  if(Jat)
   {
     sd_data =  String(gps_clock) + "," + String(lat,6) + "," + String(lon,6) + "," + String(lap) + "," + String(avg_distance) + "," + String(distance) + "," + String(lap_distance) + "," + String(gps_speed) + "," + String(avg_speed);
    
     Jat.println(sd_data);
    Jat.close();
    sd_check=1;
   }

  else
   {
     sd_check=0; 
   }

}

//Google Data Update
void google()
{
  
  String cmd = "AT+CIPSTART=\"TCP\",\"";
  cmd += DST_HOST;
  cmd += "\",80";

  
  Serial2.println(cmd);

  delay(500);
  
  cmd =  "GET /pushingbox?devid=";
  cmd += devid;
  cmd += "&lat="+String(lat,6)+"&lon="+String(lon,6)+"&lap="+String(lap)+"&lal="+String(avg_distance)+"&dis="+String(distance)+"&lac="+String(lap_distance)+"&spd="+String(gps_speed)+"&avg="+String(avg_speed)+" HTTP/1.1\r\n";     //construct http GET request
  cmd += "Host: api.pushingbox.com\r\n\r\n"; 

  String str= "AT+CIPSEND=";
  str += String(cmd.length());   
  Serial2.println(str);
  
  delay(500);

     Serial2.println(cmd);

}

void prints()
{
  
  tft.fillScreen(0x07FF);
  
    if(lap==0)
  {
    
    tft.setTextSize(1);
    tft.setTextFont(4);
    tft.setTextColor(TFT_BLACK); 
    tft.setCursor (25, 25);
    tft.print("dis  ");
    tft.print(" -");
    tft.setCursor (360, 25);
    tft.print("Lap   ");
    tft.print(lap);
    tft.setCursor (170, 45);
    tft.print(gps_clock);
    tft.setTextSize(4);
    tft.setCursor (110, 100);
    tft.print(gps_speed);
    tft.setTextSize(1);
    tft.setCursor (345, 150);
    tft.print(" Km/Hr");
    tft.setCursor (195, 200);
    tft.print(distance);
    tft.print(" km"); 
    tft.setCursor (28, 260);
    tft.print(" -");
    tft.print("  % ");
    if(sd_check==0)
     {
      tft.setCursor (220, 260);
      tft.print("***");
     }
    tft.setCursor (330, 240);
    tft.print("Avg  ");
    tft.print(avg_speed);
    tft.setCursor (330, 280);
    tft.print("Lav  ");
    tft.print(" -");
     
  }
    else
   {
    tft.setTextSize(1);
    tft.setTextFont(4);
    tft.setTextColor(TFT_BLACK); 
    tft.setCursor (25, 25);
    tft.print("dis  ");
    tft.print(avg_distance);
    tft.setCursor (360, 25);
    tft.print("Lap   ");
    tft.print(lap);
    tft.setCursor (180, 45);
    tft.print(gps_clock);
    tft.setTextSize(4);
    tft.setCursor (110, 100);
    tft.print(gps_speed);
    tft.setTextSize(1);
    tft.setCursor (345, 150);
    tft.print(" Km/Hr");
    tft.setCursor (195, 200);
    tft.print(distance);
    tft.print(" km"); 
    tft.setCursor (28, 260);
    tft.print(per_distance);
    tft.print("  % ");
    if(sd_check==0)
     {
      tft.setCursor (220, 260);
      tft.print("***");
     }
    tft.setCursor (330, 240);
    tft.print("Avg  ");
    tft.print(avg_speed);
    tft.setCursor (330, 280);
    tft.print("Lav  ");
    tft.print(lapspeed_average);
    
}

}

void print_speed()
{
    tft.fillScreen(0x07FF);
    tft.setTextSize(4);
    tft.setTextFont(4);
    tft.setTextColor(TFT_BLACK); 
    tft.setCursor (140, 105);
    tft.print(gps_speed);
    tft.setTextSize(2);
    tft.setCursor (170, 250);
    tft.print("Km/Hr");
    
}

void print_time()
{
    tft.fillScreen(0x07FF);
    tft.setTextSize(4);
    tft.setTextFont(4);
    tft.setTextColor(TFT_BLACK); 
    tft.setCursor (50, 105);
    tft.print(gps_clock);
    
}

void print_dis()
{
    tft.fillScreen(0x07FF);
    tft.setTextSize(4);
    tft.setTextFont(4);
    tft.setTextColor(TFT_BLACK); 
    tft.setCursor (140, 105);
    tft.print(distance);
    tft.setTextSize(2);
    tft.setCursor (200, 250);
    tft.print("Km");
    
}

Schematics

Schematic Diagram
Schematic rrxjv0gvwl

Comments

Similar projects you might like

Arduino - Web-Based Car Race Brick Game

Project tutorial by Homer

  • 14,401 views
  • 6 comments
  • 62 respects

Waterman

Project showcase by Team Technozz

  • 4,099 views
  • 2 comments
  • 19 respects

Car Sensor monitoring & Alarm

Project showcase by petemnz

  • 12,633 views
  • 9 comments
  • 28 respects

RC Car to BT Car Controlled with Blynk

Project tutorial by Team Daxes Hacks

  • 3,598 views
  • 2 comments
  • 6 respects
Add projectSign up / Login