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
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
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
Top Comments