Holiday Special 19

Enter Your Holiday Season Project for a Chance to Win Over $20,000 Worth of Prizes and Gifts to Give!

Back to The Project14 homepage

Project14 Home
Monthly Themes
Monthly Theme Poll

 

Hello guys,

 

For this year's Holiday Special 19 project, I decided to embark on an fun and exciting journey to modify and upgrade an 50W BMW i8 electric baby car, which originally could barely move when i sat on it, to a fast and furious 5000W dual drive electric car which can hit top speeds of 60km/h. I was initially skeptical on whether this project was feasible and could be completed within the month. However, after overcoming many of the difficulties and challenges, I am very happy to finally present to all of you a compilation video of my journey in bringing this project to life.

 

Video

 

Video Contents

0:00 - Project Overview

0:14 - Main goals of electric car

0:32 - Comparison between Old and New components

3:16 - Gear Ratio calculation

3:59 - 3D Printing wheel and motor pulley

4:13 - Car throttle and brakes setup

5:32 - Controlling car front headlights

6:07 - Car power up sequence

8:36 - Car secondary battery

9:08 - Car secondary battery life calculations and implementation of sleep mode

9:36 - Optocoupler isolation for arduino microcontroller

10:24 - Assembly of electronics on pcb

10:46 - Installation of wheel pulley

11:09 - Mounting of brushless motor

11:31 - Overcoming flex of car plastic frame

12:20 - Car cooling system

12:40 - Battery charging and bms

13:01 - Programming of VESC

13:17 - Demonstration of power up sequence

13:40 - Journey of failures and success

 

 

 

 

 

 

Photos of key installations

 

BEFORE

After

 

AFTER

 

Unboxing the 50x 26650 Batteries

 

Assembled Battery (36v 25Ah)

 

10 Inch wheel with 3d printed pulley

 

Custom 3d printed mount for 36W car LED

 

3D printed steering wheel mount to attach 2x thumb throttle

 

Main Car Central Control Circuit (Front)

 

Main Car Central Control Circuit (Back)

 

Car Power Mosfet Circuit

 

Fusion 360 - Motor pulley

 

Fusion 360 - Wheel pulley

 

Fusion 360 - Steering wheel throttle mount

 

Fusion 360 - Spacer with bearing holder

Fusion 360 - Pulley Load Stress Simulation

 

 

 

 

Circuit Schematic

Electric Car Circuit Schematic

 

Parts & Components

 

(Element 14)

  • 6x IRFS7530 60V 240A power mosfet
  • 1x IRLZ44N N-channel mosfet
  • 2x FL817C Transistor Output Optocoupler
  • 2x H11L1 Digital Optocoupler
  • 4x 1/4w 200ohm resistor (for optocoupler)
  • 2x 1/4w 270ohm resistor (for optocoupler)
  • 6x 1/4w 1.5ohm resistor (for mosfet gate resistor)
  • 1x 1/4w 10kohm resistor (for mosfet pull down resistor)
  • 1x 1/4w 4.7kohm resistor (for mosfet pull down resistor)
  • 1x 2w 15ohm resistor
  • 1x 42v TVS
  • 1x 15V TVS
  • 1x L7805CVL7805CV linear regulator
  • 1x RECOM 0505 isolated dc-dc converter
  • 1x MCP1700T-3302E/TT 3.3v Low power regulator

 

(Flipsky)

  • 2x 6454 2450W Flipsky BLDC Motor
  • 2x Flipsky VESC 4.12v, 50A Continuous, 240A burst

 

(AliExpress)

  • 50x 26650 5000mah Lithium Ion Battery
  • 1x 3.3v Arduino Pro Mini
  • 1x 200W 42v to 12v dc-dc buck converter
  • 1x 150A resettable car fuse
  • 1x 18650-35E 3500mah Samsung battery
  • 4x 10 inch rubber pneumatic wheels with 12mm bearings
  • 2x Thumb Throttle
  • 2x 36W Car Headlights LED
  • 5v to 4.2v lithium charger
  • R551 Fingerprint scanner

 

 

Arduino Code

 

#include <Servo.h>
#include "LowPower.h"
#include <R551_Fingerprint.h>
#include <SoftwareSerial.h>


Servo leftVESC;
Servo rightVESC;


//A0 - Left Analog Throttle
//A1 - Right Analog Throttle

//D2 - Left & Right Throttle Power

//D3 - R551 Finger Detection Signal
//D4 - R551 RX UART
//D5 - R551 TX UART
//D6 - Front LED Lights
//D7 - Main Power System
//D8 - Left ESC Signal Input
//D9 - Right ESC Signal Input
//D10 - Force Speed Limiter
//D11 - Buzzer
//D12 - Lights State Switch


unsigned long lastFingerOnSensorMicros = 0;


SoftwareSerial mySerial(5, 4); //RX, TX
R551_Fingerprint finger = R551_Fingerprint(&mySerial);


//for sound feedback
int delayTime = 50;
int toneFrequency = 2900;


int currentUserID = 0;


    
    int maxSpeedLimitArray[] = {100, 50, 50, 50};
    //For Speed limiting different users [Enter Speed limit from 0(Min) - 100(Max)]
    //currentUserID 1 - Amadeo
    //currentUserID 2 - Friend 1
    //currentUserID 3 - Friend 2
    //currentUserID 4 - Friend 3


    int currentSpeedLimiterMode = 1;
    //double tap - exit all modes (Mode 1) (100% throttle)
    //single tap - police slow mode (5km/h) (Mode 2) (25% throttle)
    //3 taps - trial mode slow (15km/h) (Mode 3) (35% throttle)
    //4 taps - trail mode fast (30km/h) (Mode 4) (45% throttle)


int lightSwitchState = 0;
boolean previousLightStateButtonHIGH = true;


boolean previousSpeedLimiterButtonHIGH = true;
unsigned long lastButtonPressedMillis = 0;
int consecutiveButtonPresses = 0;


void setup() {
  // put your setup code here, to run once:

  pinMode(2, OUTPUT);
  pinMode(3, INPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, INPUT);
  pinMode(9, INPUT);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, OUTPUT);
  pinMode(12, INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);


  attachInterrupt(digitalPinToInterrupt(3), fingerDetectionStateChanged, CHANGE);


  Serial.begin(115200);


//blink led 3 times to show power up
digitalWrite(LED_BUILTIN, HIGH);
delay(70);
digitalWrite(LED_BUILTIN, LOW);
delay(70);
digitalWrite(LED_BUILTIN, HIGH);
delay(70);
digitalWrite(LED_BUILTIN, LOW);
delay(70);
digitalWrite(LED_BUILTIN, HIGH);


Serial.println("Electric Car Central Control System Initialized!");

//Initialize fingerprint scanner


//Comment out this section if fingerprint baudrate has been changed

//finger.begin(57600);
//
//if(finger.verifyPassword()) {
//
//  finger.setSlowBaudRate();
//}

  finger.begin(28800);

   if (finger.verifyPassword()) {
    
      Serial.println("Found fingerprint sensor!");
      finger.getTemplateCount();
      Serial.print("Sensor contains "); Serial.print(finger.templateCount); Serial.println(" templates");
      delay(50);
      sleep(true);
  
  }else{
      Serial.println("Did not find fingerprint sensor");

      sleep(false);
  }

}

void loop() {


 if(currentUserID >= 1 && currentUserID <= 10) {


      if(digitalRead(3) == LOW) {
        
          runElectricCarMainCode();


          //check if user wants to change the current state of the light
          checkForLightStateChange();
          //check if user wants to force limit the speed due to police
          checkForSpeedLimiterModeChange();
          
      }else{


          //cut throttle
          leftVESC.writeMicroseconds(1500);
          rightVESC.writeMicroseconds(1500);
          
          playFingerDetectedSound();

          unsigned long initialFingerDetectedMillis = millis();
          unsigned long lastHighMillis = millis();

          unsigned long minimumTimeFingerOnSensor = 500;
          
          while ((millis() - initialFingerDetectedMillis <= minimumTimeFingerOnSensor && digitalRead(3) == HIGH) || (millis() - lastHighMillis < 50 && digitalRead(3) == LOW)) {
            
            if(digitalRead(3) == HIGH) {

              lastHighMillis = millis();
            }else{

              initialFingerDetectedMillis = millis();
            }
            
            delay(1);
          }

          if(millis() - initialFingerDetectedMillis >= minimumTimeFingerOnSensor) {
             //user placed finger on fingerprint scanner to power down the electric car
             currentUserID = 0;
             powerDownCar();
          }else{

            finger.sleep();
          }

      }

    }else{

      //car power on not yet authorized, continuously check fingerprint scanner for verification

      Serial.println("Time Finger Not On Sensor = " + String((micros() - lastFingerOnSensorMicros)/1000000.0) + "s");

      if(digitalRead(3) == LOW) {
      
          //finger not on sensor
          if(micros() - lastFingerOnSensorMicros > 3000000) {
            //finger not on sensor for 3s
                
                sleep(true);   
          }
       
        }else{

          //finger on sensor
          lastFingerOnSensorMicros = micros();
      
          delay(25);
  
          currentUserID = identifyFingerprint();          
        }    
       }
}

int identifyFingerprint() {


    //Identify Fingerprint
    
    //currentUserID 1 - Amadeo
    //currentUserID 2 - Friend 1
    //currentUserID 3 - Friend 2
    //currentUserID 4 - Friend 3
    
    int id = getFingerprintIDez();
    int currentUserID = 0;


    Serial.println("ID Number" + String(id));


    if(id == -10) {
        Serial.println("Please press finger");
        
    }else if(id == -1) {


        Serial.println("Finger Not Found In Database!");


    }else if (id <200) {
      
        switch(id) {

        case 1:
        Serial.println("Welcome Amadeo!");
        currentUserID = 1;
        break;

        case 2:
        Serial.println("Welcome Amadeo!");
        currentUserID = 1;
        break;

        case 3:
        Serial.println("Welcome Amadeo!");
        currentUserID = 1;
        break;

        case 4:
        Serial.println("Welcome Amadeo!");
        currentUserID = 1;
        break;

        case 5:
        Serial.println("Welcome Friend 1!");
        currentUserID = 2;
        break;

        case 6:
        Serial.println("Welcome Friend 1!");
        currentUserID = 2;
        break;

        case 7:
        Serial.println("Welcome Friend 2!");
        currentUserID = 3;
        break;
        
        case 8:
        Serial.println("Welcome Friend 2!");
        currentUserID = 3;
        break;
        
        case 9:
        Serial.println("Welcome Friend 3!");
        currentUserID = 4;
        break;


        case 10:
        Serial.println("Welcome Friend 3!");
        currentUserID = 4;
        break;

      }

      if(id >= 1 && id <= 10) {
          //fingerprint authentication success


           //wait for user to remove finger from fingerprint scanner, then put fingerprint scanner to sleep
           unsigned long fingerRemovedInitialMillis = millis();
           
            while(digitalRead(3) == HIGH || millis() - fingerRemovedInitialMillis > 100) {


              fingerRemovedInitialMillis = millis();
              delay(1);
            }


            finger.sleep();


            powerUpCar();
     
        }
      
    }
        
      return currentUserID;

}

void powerUpCar() {
  
  Serial.println("Car Powering Up!");


  //set default light state to off
  lightSwitchState = 0;
  
  //Power on left & right throttle
  digitalWrite(2, HIGH);
  //Turn on power mosfets
  digitalWrite(7, HIGH);
  //Turn on front lights
  digitalWrite(6, HIGH);
  delay(100);
  digitalWrite(6, LOW);
  delay(100);
  digitalWrite(6, HIGH);
  delay(100);
  digitalWrite(6, LOW);


  //turn on VESC signals
  leftVESC.invertPWMSignal();
  leftVESC.attach(8);
  rightVESC.invertPWMSignal();
  rightVESC.attach(9);


  
}

void powerDownCar() {


  Serial.println("Car Powering Down!");

  playCarPowerDownSound();

  //Turn on front lights
  pinMode(6, OUTPUT);
  digitalWrite(6, LOW);
  
  //Turn off power mosfets
  digitalWrite(7, LOW);


  //Power off left & right throttle
  digitalWrite(2, LOW);
  
  //Turn off vesc signals
  leftVESC.detach();
  rightVESC.detach();
  
  pinMode(8, INPUT);
  pinMode(9, INPUT);

  //wait for user to remove finger from fingerprint scanner, then put fingerprint scanner and arduino to sleep
  unsigned long fingerRemovedInitialMillis = millis();

  while(digitalRead(3) == HIGH || millis() - fingerRemovedInitialMillis > 100) {


    fingerRemovedInitialMillis = millis();
    delay(1);
  }


  sleep(true);
}


void runElectricCarMainCode() {


//Output Signal Range to VESC 700us (Minimum) to 1500us (Center) to 2300us (Maximum)


int brakePWMPulseWidth = 1500.0 - ((analogRead(A0) - 235.0) / 575.0) * (maxSpeedLimitArray[currentUserID -1]/100.0 * 800.0);


int throttlePWMPulseWidth = 1500.0 + ((analogRead(A1) - 240.0) / 570.0) * (maxSpeedLimitArray[currentUserID -1]/100.0 * 800.0); 


if(currentSpeedLimiterMode == 2) {


    //speed limit 5km/h
    throttlePWMPulseWidth = 1500.0 + ((analogRead(A1) - 240.0) / 570.0) * (0.25 * 800.0);
    
}else if(currentSpeedLimiterMode == 3) {


    //speed limit 15km/h
    throttlePWMPulseWidth = 1500.0 + ((analogRead(A1) - 240.0) / 570.0) * (0.35 * 800.0);
    
}else if(currentSpeedLimiterMode == 4) {


    //speed limit 30km/hv
    throttlePWMPulseWidth = 1500.0 + ((analogRead(A1) - 240.0) / 570.0) * (0.45 * 800.0);
}


int outputPWMPulseWidth = 0.0;


if(brakePWMPulseWidth < 1460) {

  //user is applying brake
  //ignore throttle input and apply brakes
  outputPWMPulseWidth = brakePWMPulseWidth;
  
}else if(throttlePWMPulseWidth < 1535){
  
  //user is not applying throttle and not applying brakes
  outputPWMPulseWidth = 1500.0;
  
}else{
  
  //user is applying throttle and not applying brakes
  outputPWMPulseWidth = throttlePWMPulseWidth;
  
}


leftVESC.writeMicroseconds(outputPWMPulseWidth);
rightVESC.writeMicroseconds(outputPWMPulseWidth);


Serial.println("Pulse Width = " + String(outputPWMPulseWidth) + "us");


delay(5);


}

void checkForLightStateChange() {

  if(previousLightStateButtonHIGH == true) {

          if(digitalRead(12) == LOW) {

          unsigned long firstEnterMillis = millis();
          boolean pressSuccess = true;
          
          while(millis() - firstEnterMillis < 5) {
          
            if(digitalRead(12) == HIGH) {
          
              pressSuccess = false;
            }
          
          }

          if(pressSuccess) {
          //light switch state 0 = OFF
          //light switch state 1 = on 25%
          //light switch state 2 = on 50%
          //light switch state 3 = on 100%
          
            if(lightSwitchState == 3) {


              lightSwitchState = 0;
            }else {


              lightSwitchState++;
            }

            Serial.println("StateChange = " + String(lightSwitchState));


              if(lightSwitchState == 0) {


                analogWrite(6, 0);
                
              }else if(lightSwitchState == 1) {


                analogWrite(6, 64);
                
              }else if(lightSwitchState == 2) {


                analogWrite(6, 127);
                
              }else if(lightSwitchState == 3) {


                analogWrite(6, 255);
                
              }

              previousLightStateButtonHIGH = false;         
          }

        }

  }else{

    //user havent released button yet
    if(digitalRead(12) == HIGH) {

          unsigned long firstEnterMillis = millis();
          boolean relaseSuccess = true;
          
          while(millis() - firstEnterMillis < 10) {
          
            if(digitalRead(12) == LOW) {
          
              relaseSuccess = false;
            }
          
          }

          if(relaseSuccess) {

              previousLightStateButtonHIGH = true;
          }

        }
  }
}

void checkForSpeedLimiterModeChange() {

  if(previousSpeedLimiterButtonHIGH == true) {

          if(digitalRead(10) == LOW) {

          unsigned long firstEnterMillis = millis();
          boolean pressSuccess = true;
          
          while(millis() - firstEnterMillis < 5) {
          
            if(digitalRead(10) == HIGH) {
          
              pressSuccess = false;
            }
          
          }

          if(pressSuccess) {

              if(millis() - lastButtonPressedMillis <= 300) {
                
                //button is consecutively pressed
                consecutiveButtonPresses++;

              }else{

                //button is pressed first time
                consecutiveButtonPresses = 1;
                
              }
              
              lastButtonPressedMillis = millis();
              
              previousSpeedLimiterButtonHIGH = false;
            
          }

        }else{

            //button is not pressed
            if(millis() - lastButtonPressedMillis > 300 && consecutiveButtonPresses != 0) {
    
              //user finished selecting mode
              if(consecutiveButtonPresses == 1) {
                  //limit speed to 5km/h
                  currentSpeedLimiterMode = 2;
                  playTone(1);
                
              }else if(consecutiveButtonPresses == 2) {
                  //REMOVE SPEED LIMIT
                  currentSpeedLimiterMode = 1;
                  playSuccessSound();
                
              }else if(consecutiveButtonPresses == 3) {
                  //limit speed to 15km/h
                  currentSpeedLimiterMode = 3;
                  playTone(3);


              }else if(consecutiveButtonPresses == 4) {
                  //limit speed to 30km/h
                  currentSpeedLimiterMode = 4;
                  playTone(4);

              }
                  consecutiveButtonPresses = 0;
            }
        }
        
  }else{

    //user havent released button yet
    if(digitalRead(10) == HIGH) {

          unsigned long firstEnterMillis = millis();
          boolean relaseSuccess = true;
          
          while(millis() - firstEnterMillis < 10) {
          
            if(digitalRead(10) == LOW) {
          
              relaseSuccess = false;
            }
          
          }

          if(relaseSuccess) {

              previousSpeedLimiterButtonHIGH = true;
          }
        }
  }

}

void sleep(boolean shouldSleepFingerprint) {

    Serial.println("Sleep!");
    delay(30);

    if(shouldSleepFingerprint) {


          finger.sleep();
    }
    
    digitalWrite(LED_BUILTIN, LOW);
    
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); 


    digitalWrite(LED_BUILTIN, HIGH);


    Serial.println("Woke Up!");

}

void fingerDetectionStateChanged() {

    lastFingerOnSensorMicros = micros();
}

// returns -1 if failed, -10 if no finger, otherwise returns ID #
int getFingerprintIDez() {
  long initialMillis = millis();
  int p = finger.getImage();
  if (p == FINGERPRINT_NOFINGER) return -10;
  else if (p != FINGERPRINT_OK)  return -1;

  if(p == FINGERPRINT_OK) {

    playFingerDetectedSound();
  }
  
  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.fingerSearch();
  if (p != FINGERPRINT_OK) {
    playFailSound();
    return -1;
  }

  // found a match!
  Serial.print("Found ID #"); Serial.print(finger.fingerID); 
  Serial.print(" with confidence of "); Serial.println(finger.confidence);
  Serial.println("Time Taken:" + String((millis() - initialMillis)/1000.0) + "s");

  playSuccessSound();

  return finger.fingerID; 
}

void playCarPowerDownSound() {

  tone(11, toneFrequency);
  delay(1000);
  noTone(11);
}

void playFingerDetectedSound() {

  tone(11, toneFrequency);
  delay(delayTime);
  noTone(11);
}

void playSuccessSound() {

  tone(11, toneFrequency);
  delay(delayTime);
  noTone(11);
  delay(delayTime);
  tone(11, toneFrequency + 700);
  delay(delayTime);
  noTone(11);
  delay(delayTime);
  
}

void playFailSound() {

  tone(11, toneFrequency);
  delay(delayTime);
  noTone(11);
  delay(delayTime);
  tone(11, toneFrequency);
  delay(delayTime);
  noTone(11);
  delay(delayTime);
  tone(11, toneFrequency - 400);
  delay(delayTime + 50);
  noTone(11);
  delay(delayTime);
}

void playTone(int count) {

  for(int i = 0; i < count; i++) {
      tone(11, toneFrequency);
      delay(50);
      noTone(11);
      delay(50);
  }
}

 

Special Thanks

I would like to give a special thanks to my friend Soon Yee for helping me out with this journey and always inspiring me with new ideas and to the Educational Center (Intuitional International) for supporting me and allowing me to enhance my skills and knowledge. They also do an amazing job at training people: https://www.intuitioninternational.com/adult-courses