Skip to content

Commit

Permalink
Added a test throttle and test inverter that can be used to test the …
Browse files Browse the repository at this point in the history
…new BLE system without actual hardware or can be used for testing. Fixed up some BLE bugs.
  • Loading branch information
collin80 committed Aug 2, 2016
1 parent b8d8c9e commit 6f0b32e
Show file tree
Hide file tree
Showing 9 changed files with 606 additions and 10 deletions.
92 changes: 92 additions & 0 deletions BLE_Characteristics.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
Outline of how to handle bluetooth:

Scan for BLE devices immediately when starting up. Get a list of them and then scan each one
to see if it implements the 0x3100 service. If not then ignore it. Otherwise, pick
the first one to implement the service and start using it to get the characteristics.

Characteristics are:
0x3101 - TimeRunning (in seconds)
4 bytes unsigned - number of seconds since GEVCU has started.

0x3102 - TrqReqAct
2 bytes signed - Requested Torque in 0.1Nm increments
2 bytes signed - Actual torque in 0.1Nm inc

0x3103 - ThrBrkLevels
2 bytes unsigned - Raw Throttle value (from ADC)
2 bytes unsigned - Raw brake value (from ADC)

0x3104 - Speed
2 bytes unsigned - Requested RPM (always positive even for reverse)
2 bytes unsigned - Actual RPM (always positive)

0x3105 - Modes (Writable)
1 byte - Power Mode (0 = Torque, 1 = Direct RPM control)
1 byte - Gear (0 = Neutral, 1 = Drive, 2 = Reverse, 3 = Error) - Writes to this are ignored.
1 byte - IsRunning (True if we're currently drive enabled) - Writes are ignored
1 Byte - IsFaulted (obviously, true if there is a fault) - Writes ignored
1 Byte - IsWarning (True is there is currently a warning in effect) - Writes Ignored
1 Byte - LogLevel (0 = Debugging, 1 = Info, 2 = Warn 3 = Error 4 = Off)

0x3106 - PowerStatus
2 bytes unsigned - Bus Voltage in tenths of a volt
2 bytes signed - Bus Current in tenths of an amp
2 bytes signed - Motor Current in tenths of an amp
2 bytes unsigned - Kilowatt hours left in the battery pack (in full kwh)
2 bytes signed - Mechanical power in 0.1 kw increments

0x3107 - Bitfields
4 Bytes unsigned - Bitfield 1 - All these bitfields are set by the motor controller code
4 Bytes unsigned - bitfield 2 - There doesn't seem to be any standardization here.
4 Bytes unsigned - bitfield 3 - And, only the Brusa controller code even touches these.
4 Bytes unsigned - bitfield 4

0x3108 - Temperature
2 bytes signed - Motor temperature in 0.1C increment
2 bytes signed - Inverter temperature in 0.1C increment
2 bytes signed - System temperature in 0.1C increment

0x3109 - DigitalIO (Writable)
2 bytes unsigned - Precharge resistance in ohms (used to figure out how long the timing should be)
1 byte - Which digital output to use for precharge
1 byte - Which digital output to use for the main contactor
1 byte - which digital output to use to actuate cooling fans
1 byte - Temperature in C at which to turn on the cooling fans
1 byte - Temperature in C at which to turn them off again
1 byte - Which digital output to use for brake lights
1 byte - Which digital output to use for reverse lights
1 byte - Which digital input to use for enabling motor controller drive
1 byte - which digital input to use for going into reverse (instead of drive)


0x310A - ThrottleIO (Writable)
1 Byte - Number of throttle inputs (only really relevant to POT based throttles). 1-3 inputs on ADC ports
1 Byte - Throttle type (1 = Standard Pot, 2 = Inverse Pot)
2 Bytes Unsigned - Throttle 1 Minimum Value
2 Bytes Unsigned - Throttle 2 Minimum Value
2 Bytes Unsigned - Throttle 1 Maximum Value
2 Bytes Unsigned - Throttle 2 Maximum Value

0x310B - ThrottleMap (Writable)
2 Bytes unsigned - Pedal position (1/10 of a %) where regen is at its maximum
2 bytes unsigned - Pedal position where regen is at its minimum
2 bytes unsigned - Pedal position where forward motion starts
2 bytes unsigned - Pedal position where 50% power is applied
1 byte - Percentage of full torque to use for minimum regen
1 byte - Percentage of full torque to use for maximum regen
1 byte - percentage of full torque to use for creep

0x310C - BrakeParam (Writable)
2 bytes unsigned - ADC value for minimum brake signal
2 bytes unsigned - ADC value for maximum brake signal
1 byte - Percentage of full torque to use for minimum brake regen
1 byte - Percentage of full torque to use for maximum brake regen


0x310D - MaxParams (Writable)
2 bytes unsigned - Nominal battery pack voltage (1/10 of a volt)
2 bytes unsigned - Maximum RPM to allow
2 bytes unsigned - Maximum torque to allow

0x31D0 - Update counter (just for debugging)
4 bytes unsigned - counter that increments as other updates happen. Probably shouldn't subscribe to this as a notification. But, useful for debugging
2 changes: 2 additions & 0 deletions DeviceTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum DeviceId { //unique device ID for every piece of hardware possible
BRUSA_DMC5 = 0x1001,
CODAUQM = 0x1002,
CKINVERTER = 0x1003,
TESTINVERTER =0x100F,
BRUSACHARGE = 0x1010,
TCCHCHARGE = 0x1011,
LEAR=0x1012,
Expand All @@ -53,6 +54,7 @@ enum DeviceId { //unique device ID for every piece of hardware possible
POTBRAKEPEDAL = 0x1032,
CANACCELPEDAL = 0x1033,
CANBRAKEPEDAL = 0x1034,
TESTACCEL = 0x104F,
EVICTUS = 0x4400,
ICHIP2128 = 0x1040,
ADABLUE = 0x1041,
Expand Down
2 changes: 2 additions & 0 deletions GEVCU.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "CanThrottle.h"
#include "CanBrake.h"
#include "PotThrottle.h"
#include "TestThrottle.h"
#include "PotBrake.h"
#include "BatteryManager.h"
#include "ThinkBatteryManager.h"
#include "MotorController.h"
#include "DmocMotorController.h"
#include "BrusaMotorController.h"
#include "CKMotorController.h"
#include "TestMotorController.h"
#include "Heartbeat.h"
#include "sys_io.h"
#include "CanHandler.h"
Expand Down
13 changes: 8 additions & 5 deletions GEVCU.ino
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,12 @@ void createObjects() {
PotThrottle *paccelerator = new PotThrottle();
CanThrottle *caccelerator = new CanThrottle();
PotBrake *pbrake = new PotBrake();
TestThrottle *testAccel = new TestThrottle();
CanBrake *cbrake = new CanBrake();
DmocMotorController *dmotorController = new DmocMotorController();
CodaMotorController *cmotorController = new CodaMotorController();
CKMotorController *ckMotorController = new CKMotorController();
TestMotorController *testMotorController = new TestMotorController();
DCDCController *dcdcController = new DCDCController();
BrusaMotorController *bmotorController = new BrusaMotorController();
ThinkBatteryManager *BMS = new ThinkBatteryManager();
Expand All @@ -227,7 +229,8 @@ void createObjects() {
ADAFRUITBLE *ble = new ADAFRUITBLE();
EVIC *eVIC = new EVIC();
}
void initializeDevices() {

void initializeDevices() {
DeviceManager *deviceManager = DeviceManager::getInstance();

//heartbeat is always enabled now
Expand Down Expand Up @@ -259,12 +262,12 @@ void createObjects() {
void setup() {
sys_boot_setup(); //sets digital outputs to "off" right as soon as the sketch gets control.

//delay(5000); //This delay lets you see startup. But it breaks DMOC645 really badly. You have to have comm way before 5 seconds.
//delay(5000); //This delay lets you see startup. But it breaks DMOC645 really badly. You have to have comm way before 5 seconds.

//initWiReach();
//initWiReach();
pinMode(BLINK_LED, OUTPUT);
digitalWrite(BLINK_LED, LOW);
SerialUSB.begin(CFG_SERIAL_SPEED);
SerialUSB.begin(CFG_SERIAL_SPEED);
SerialUSB.println(CFG_VERSION);
SerialUSB.print("Build number: ");
SerialUSB.println(CFG_BUILD_NUM);
Expand All @@ -285,7 +288,7 @@ void setup() {
uint8_t loglevel;
sysPrefs->read(EESYS_LOG_LEVEL, &loglevel);
//Logger::setLoglevel((Logger::LogLevel)loglevel);
Logger::setLoglevel((Logger::LogLevel)1);
Logger::setLoglevel((Logger::LogLevel)0);
sys_early_setup();
tickHandler = TickHandler::getInstance();
canHandlerEV = CanHandler::getInstanceEV();
Expand Down
156 changes: 156 additions & 0 deletions TestMotorController.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* TestMotorController.cpp
*
* Fake motor controller that takes throttle input and pretends like it's driving a motor.
* Used just for testing surrounding code (throttle input, status output, etc)
*
Copyright (c) 2016 Collin Kidder, Michael Neuweiler, Charles Galpin
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "TestMotorController.h"

TestMotorController::TestMotorController() : MotorController() {
prefsHandler = new PrefHandler(TESTINVERTER);

selectedGear = DRIVE;
commonName = "Test Inverter";
}

void TestMotorController::setup() {
TickHandler::getInstance()->detach(this);

Logger::info("add device: Test Inverter (id:%X, %X)", TESTINVERTER, this);

loadConfiguration();
MotorController::setup(); // run the parent class version of this function

running = true;
setPowerMode(modeTorque);
setSelectedGear(DRIVE);
setOpState(ENABLE);

TickHandler::getInstance()->attach(this, CFG_TICK_INTERVAL_MOTOR_CONTROLLER_DMOC);
}

void TestMotorController::handleTick() {
TestMotorControllerConfiguration *config = (TestMotorControllerConfiguration *)getConfiguration();

MotorController::handleTick(); //kick the ball up to papa

dcVoltage = 3600; //360v nominal voltage scaled up 10x

//use throttleRequested to produce some motor like output.
//throttleRequested ranges +/- 1000 so regen is possible here if the throttle has it set up.

if (powerMode == modeSpeed)
{
torqueRequested = 0;
if (throttleRequested > 0 && operationState == ENABLE && selectedGear != NEUTRAL)
speedRequested = (((long) throttleRequested * (long) config->speedMax) / 1000);
else
speedRequested = 0;

speedActual = ((speedActual * 8) + (speedRequested * 2)) / 10;
torqueActual = speedActual / 20;

//generate some baseline holding current to maintain the speed.
dcCurrent = speedRequested / 66;
//Then add some accelerating current for the difference between target and actual
dcCurrent += (speedRequested - speedActual) / 10;
}
else
{
if (selectedGear == DRIVE)
torqueRequested = (((long) throttleRequested * (long) config->torqueMax) / 1000L);
if (selectedGear == REVERSE)
torqueRequested = (((long) throttleRequested * -1 *(long) config->torqueMax) / 1000L);

torqueActual = ((torqueActual * 7) + (torqueRequested * 3)) / 10;
speedActual = torqueActual * 2;
if (speedActual < 0) speedActual = 0;

speedRequested = 0;

//generate some baseline holding current to maintain the speed.
dcCurrent = torqueRequested / 3;
//Then add some accelerating current for the difference between target and actual
dcCurrent += (torqueRequested - torqueActual) * 2;

}

acCurrent = (dcCurrent * 40) / 30; //A bit more current than DC bus

//Both dc current and dc voltage are scaled up 10, mech power should be in 0.1kw increments
//current*voltage = watts but there is inefficiency to deal with. and it's watts but we're scaled up 100x
//because of multipliers so need to scale down 100x to get to watts then by 100x again to get to 0.1kw
//100x100 = 10000 but inefficiency should make it even worse
mechanicalPower = (dcCurrent * dcVoltage) / 12000;

//Heat up or cool motor and inverter based on mechanical power being used.
//Assume ambient temperature is 18C
//These numbers are horrifically off from realistic physics at this point
//but we're trying to aid debugging, not making a perfect physics model.
temperatureMotor = 180 + abs(mechanicalPower);
temperatureInverter = 190 + abs(mechanicalPower);
temperatureSystem = (temperatureInverter + temperatureMotor) / 2;

Logger::debug(TESTINVERTER, "PowerMode: %i, Gear: %i", powerMode, selectedGear);
Logger::debug(TESTINVERTER, "TorqueReq: %i, SpeedReq: %i", torqueRequested, speedRequested);
Logger::debug(TESTINVERTER, "dcCurrent: %i, mechPower: %i", dcCurrent, mechanicalPower);

}

void TestMotorController::setGear(Gears gear) {
selectedGear = gear;
//if the gear was just set to drive or reverse and the DMOC is not currently in enabled
//op state then ask for it by name
if (selectedGear != NEUTRAL) {
operationState = ENABLE;
}
//should it be set to standby when selecting neutral? I don't know. Doing that prevents regen
//when in neutral and I don't think people will like that.
}

DeviceId TestMotorController::getId() {
return (TESTINVERTER);
}

uint32_t TestMotorController::getTickInterval()
{
return CFG_TICK_INTERVAL_MOTOR_CONTROLLER_DMOC;
}

void TestMotorController::loadConfiguration() {
TestMotorControllerConfiguration *config = (TestMotorControllerConfiguration *)getConfiguration();

if (!config) {
config = new TestMotorControllerConfiguration();
setConfiguration(config);
}

MotorController::loadConfiguration(); // call parent
}

void TestMotorController::saveConfiguration() {
MotorController::saveConfiguration();
}
Loading

0 comments on commit 6f0b32e

Please sign in to comment.