Alex LinsdayNathan MiersenIan DeRushaMatthew HaleyAdam Snellings
Created April 22, 2023 © GPL3+

Power Wheels Upgrades

In spring 2023, UNCC offered a class to learn and apply EV power and control concepts to a ride-on powered toy car. This is team 5's result.

Advanced10 hours32
Power Wheels Upgrades

Things used in this project

Hardware components

Cadillac Escalade Power Wheels
×1
Argon
Particle Argon
×1
Hall Effect Pedal
×1
Resistor 100k ohm
Resistor 100k ohm
×1
Resistor 10k ohm
Resistor 10k ohm
×1
Phone Power Bank
×1
24V Kobalt Battery
×1
Kobalt Power Source Adapter
×1
BTS7960 Motor Driver Module
×2
ACS758LCB-100U Current Sensor Module
×1
12 gauge wires
×1
Various wire splice terminals
×1
16 gauge wires
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
Adafruit Dashboard

Hand tools and fabrication machines

Kobalt Cordless Impact Driver
Multimeter
Digilent Screwdriver
Digilent Screwdriver
Wire Stripper/Crimper

Story

Read more

Schematics

Control Schematic

This is the wiring diagram for our car. The red lines indicate power, the black indicate ground, and the other colors indicate signal and control wires.

Code

Power sensor code

C/C++
This is the initial Particle code for gathering, filtering, and uploading the voltage and current being output by the battery. The Argon connects to a pre-defined WiFi connection and uploads the data to the Particle cloud, which is then sent to an Adafruit dashboard through webhooks.
#define voltage A1 //define pins for current, voltage sensors and pedals
#define current A2

int voltageValue; //declare variables for power readings
int currentValue;

float filteringCurrent=0; //declare variables for low pass filtering
float filteringVoltage=0;
float filteredVoltage=0;
float filteredCurrent=0;

float voltageCalibration=3.3/4096*11; //declare power calibration values
float currentCalibration=3.3/4096*8.5;

unsigned long upload=10000; //declare reading and filtering intervals
unsigned long measure=20;

unsigned long uploadTime=0; //declare reading and filtering timers
unsigned long measureTime=0;

unsigned long currentTime=millis(); //declare time to compare to timers

int filterCoef=0.905; //declare 5Hz filter coefficient

void setup() 
{
Serial.begin(9600); //start serial behavior and connect to wifi
WiFi.on();
WiFi.connect();
}

void loop() 
{
currentTime=millis(); //get time to compare to timers

if(currentTime-measureTime>=measure) //reads and filters current & voltage
    {
     voltageValue=analogRead(voltage);
     currentValue=analogRead(current)-480;
     measureTime=currentTime;
     filteringVoltage=filterCoef*filteredVoltage+voltageValue*voltageCalibration*(1-filterCoef);
     filteredVoltage=filteringVoltage;
     filteringCurrent=filterCoef*filteredCurrent+currentValue*currentCalibration*(1-filterCoef);
     filteredCurrent=filteringCurrent;
    }

if(currentTime-uploadTime>=upload) //publishes current, voltage, and throttle values
    {
     Particle.publish("Voltage",String(filteredVoltage));
     Particle.publish("Current",String(filteredCurrent));
     uploadTime=currentTime;
    }
}

Variable Throttle Code

C/C++
This is our updated code with variable throttle controlled via a simple switch for reverse and the Hall effect throttle pedal.
#define voltage A1 //define pins for current, voltage sensors and pedals
#define current A2
#define pedal A3
#define reverse A4

#define leftForward D2 //define pins for speed controllers
#define leftReverse D3 //BTS7960 drivers have separate pins--
#define rightForward D5 //--for forward and reverse
#define rightReverse D4

int voltageValue; //declare variables for power readings
int currentValue;

int pedalVolts; //declare variables for throttle control
int throttle;

boolean isReverse = false; //declare boolean for reverse switch

float filteringCurrent=0; //declare variables for low pass filtering
float filteringVoltage=0;
float filteredVoltage=0;
float filteredCurrent=0;

float voltageCalibration=3.3/4096*11; //declare power calibration values
float currentCalibration=3.3/4096*8.5;

unsigned long upload=10000; //declare reading and filtering intervals
unsigned long measure=20;

unsigned long uploadTime=0; //declare reading and filtering timers
unsigned long measureTime=0;

unsigned long currentTime=millis(); //declare time to compare to timers

int filterCoef=0.905; //declare 5Hz filter coefficient

SYSTEM_THREAD(ENABLED); //enable threading so throttle works w/out wifi

void setup() 
{
Serial.begin(9600); //start serial behavior and connect to wifi
WiFi.on();
WiFi.connect();

pinMode(leftForward,OUTPUT); //set pin modes for PWM
pinMode(leftReverse,OUTPUT);
pinMode(rightForward,OUTPUT);
pinMode(rightReverse,OUTPUT);
}

void loop() 
{
currentTime=millis(); //get time to compare to timers
pedalVolts=analogRead(pedal)-1300; //read voltage coming off pedal and adjust for zero bias

if(analogRead(reverse)>3000) //checks if reverse button is pressed via analog voltage
    {
     isReverse=true;
    }
else
    {
     isReverse=false;
    }

if (pedalVolts>0) //if throttle pedal is pressed
    {
     switch (isReverse) //reverse switch case
     {  
      case false: //if reverse not pressed, maps throttle to forward PWM outputs & sets backwards to 0
       analogWrite(leftReverse,0);
       analogWrite(rightReverse,0); 
       analogWrite(leftForward,map(pedalVolts,0,1800,0,255));
       analogWrite(rightForward,map(pedalVolts,0,1800,0,255));
      break;
      case true: //if reverse pressed, maps throttle to backward PWM outputs & sets forwards to 0
       analogWrite(leftForward,0);
       analogWrite(rightForward,0);
       analogWrite(leftReverse,map(pedalVolts,0,1800,0,255));
       analogWrite(rightReverse,map(pedalVolts,0,1800,0,255));
      break;
     }
    }
else //if throttle pedal is not pressed, sets all motion to 0
    {
     analogWrite(leftForward,0);
     analogWrite(rightForward,0);
     analogWrite(leftReverse,0);
     analogWrite(rightReverse,0);
    }    

if(currentTime-measureTime>=measure) //reads and filters current & voltage
    {
     voltageValue=analogRead(voltage);
     currentValue=analogRead(current)-480;
     measureTime=currentTime;
     filteringVoltage=filterCoef*filteredVoltage+voltageValue*voltageCalibration*(1-filterCoef);
     filteredVoltage=filteringVoltage;
     filteringCurrent=filterCoef*filteredCurrent+currentValue*currentCalibration*(1-filterCoef);
     filteredCurrent=filteringCurrent;
    }

if(currentTime-uploadTime>=upload) //publishes current, voltage, and throttle values
    {
     Particle.publish("Voltage",String(filteredVoltage));
     Particle.publish("Current",String(filteredCurrent));
     throttle=map(pedalVolts,0,1900,0,100);
     Particle.publish("Throttle",String(throttle));
     uploadTime=currentTime;
    }
}

Slew rate traction control

C/C++
This is the further refined Argon code, now with a restriction on the maximum increase in throttle output to the wheel motors. This ensures mashing on the throttle pedal won't break traction and spin the tires.
#define voltage A1 //define pins for current, voltage sensors and pedals
#define current A2
#define pedal A3
#define reverse A4

#define leftForward D2 //define pins for speed controllers
#define leftReverse D3 //BTS7960 drivers have separate pins--
#define rightForward D5 //--for forward and reverse
#define rightReverse D4

#define slewRate 5 //define max throttle increase in one loop

int voltageValue; //declare variables for power readings
int currentValue;

int pedalVolts; //declare variables for throttle control
int throttle;
int throttlePercent;

int lastThrottle=0; //declare variables for slew rate
int throttleOut;

boolean isReverse = false; //declare boolean for reverse switch

float filteringCurrent=0; //declare variables for low pass filtering
float filteringVoltage=0;
float filteredVoltage=0;
float filteredCurrent=0;

float voltageCalibration=3.3/4096*11; //declare power calibration values
float currentCalibration=3.3/4096*8.5;

unsigned long upload=10000; //declare reading and filtering intervals
unsigned long measure=20;

unsigned long uploadTime=0; //declare reading and filtering timers
unsigned long measureTime=0;

unsigned long currentTime=millis(); //declare time to compare to timers

int filterCoef=0.905; //declare 5Hz filter coefficient

SYSTEM_THREAD(ENABLED); //enable threading so throttle runs w/out wifi

void setup() 
{
Serial.begin(9600); //start serial behavior and connect to wifi
WiFi.on();
WiFi.connect();

pinMode(leftForward,OUTPUT); //set pin modes for PWM
pinMode(leftReverse,OUTPUT);
pinMode(rightForward,OUTPUT);
pinMode(rightReverse,OUTPUT);
}

void loop() 
{
currentTime=millis(); //get time to compare to timers

pedalVolts=analogRead(pedal)-1300; //read voltage coming off pedal and adjust for zero bias
throttle=map(pedalVolts,0,1900,0,255); //map pedal voltage to pwm
throttleOut=min(throttle,lastThrottle+slewRate); //max throttle increase is slew, max decrease is full
lastThrottle=throttleOut; //record last output for slew comparison

if(analogRead(reverse)>3000) //checks if reverse button is pressed via analog voltage
    {
     isReverse=true;
    }
else
    {
     isReverse=false;
    }

if (pedalVolts>0) //if throttle pedal is pressed
    {
     switch (isReverse) //reverse switch case
     {  
      case false: //if reverse not pressed, maps throttle to forward PWM outputs & sets backwards to 0
       analogWrite(leftReverse,0);
       analogWrite(rightReverse,0); 
       analogWrite(leftForward,throttleOut);
       analogWrite(rightForward,throttleOut);
      break;
      case true: //if reverse pressed, maps throttle to backward PWM outputs & sets forwards to 0
       analogWrite(leftForward,0);
       analogWrite(rightForward,0);
       analogWrite(leftReverse,throttleOut);
       analogWrite(rightReverse,throttleOut);
      break;
     }
    }
else //if throttle pedal is not pressed, sets all motion to 0
    {
     analogWrite(leftForward,0);
     analogWrite(rightForward,0);
     analogWrite(leftReverse,0);
     analogWrite(rightReverse,0);
    }    

if(currentTime-measureTime>=measure) //reads and filters current & voltage
    {
     voltageValue=analogRead(voltage);
     currentValue=analogRead(current)-480;
     measureTime=currentTime;
     filteringVoltage=filterCoef*filteredVoltage+voltageValue*voltageCalibration*(1-filterCoef);
     filteredVoltage=filteringVoltage;
     filteringCurrent=filterCoef*filteredCurrent+currentValue*currentCalibration*(1-filterCoef);
     filteredCurrent=filteringCurrent;
    }

if(currentTime-uploadTime>=upload) //publishes current, voltage, and throttle values
    {
     Particle.publish("Voltage",String(filteredVoltage));
     Particle.publish("Current",String(filteredCurrent));
     throttlePercent=map(throttleOut,0,255,0,100);
     Particle.publish("Throttle",String(throttlePercent));
     uploadTime=currentTime;
    }
}

Credits

Alex Linsday
1 project • 1 follower
Contact
Nathan Miersen
1 project • 1 follower
UNC Charlotte Mechanical Engineering Student
Contact
Ian DeRusha
0 projects • 1 follower
Contact
Matthew Haley
1 project • 1 follower
Contact
Adam Snellings
1 project • 1 follower
Contact

Comments

Please log in or sign up to comment.