From 6a3e823ebf75e182482a741e9420dcdb131948db Mon Sep 17 00:00:00 2001 From: Michael Neuweiler Date: Wed, 9 Oct 2013 17:16:43 +0200 Subject: [PATCH 1/4] some more clean-up on variable types --- PotBrake.cpp | 4 ++-- PotBrake.h | 4 ++-- PotThrottle.cpp | 4 ++-- PotThrottle.h | 4 ++-- Throttle.cpp | 6 +++--- Throttle.h | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/PotBrake.cpp b/PotBrake.cpp index 0ec9964..36e588b 100644 --- a/PotBrake.cpp +++ b/PotBrake.cpp @@ -75,11 +75,11 @@ void PotBrake::setup() { TickHandler::getInstance()->attach(this, CFG_TICK_INTERVAL_POT_THROTTLE); } -int PotBrake::getRawThrottle1() { +uint16_t PotBrake::getRawThrottle1() { return rawLevel1; } -int PotBrake::getRawThrottle2() { +uint16_t PotBrake::getRawThrottle2() { return rawLevel2; } diff --git a/PotBrake.h b/PotBrake.h index 0b88ae1..619f922 100644 --- a/PotBrake.h +++ b/PotBrake.h @@ -53,8 +53,8 @@ class PotBrake: public Throttle { void setup(); void handleTick(); BrakeStatus getStatus(); - int getRawThrottle1(); - int getRawThrottle2(); + uint16_t getRawThrottle1(); + uint16_t getRawThrottle2(); DeviceId getId(); DeviceType getType(); void saveConfiguration(); diff --git a/PotThrottle.cpp b/PotThrottle.cpp index 3432f46..6678fb2 100644 --- a/PotThrottle.cpp +++ b/PotThrottle.cpp @@ -117,11 +117,11 @@ void PotThrottle::setup() { TickHandler::getInstance()->attach(this, CFG_TICK_INTERVAL_POT_THROTTLE); } -int PotThrottle::getRawThrottle1() { +uint16_t PotThrottle::getRawThrottle1() { return rawLevel1; } -int PotThrottle::getRawThrottle2() { +uint16_t PotThrottle::getRawThrottle2() { return rawLevel2; } diff --git a/PotThrottle.h b/PotThrottle.h index e9c87e1..6c09c6f 100644 --- a/PotThrottle.h +++ b/PotThrottle.h @@ -52,8 +52,8 @@ class PotThrottle: public Throttle { void handleTick(); void setup(); ThrottleStatus getStatus(); - int getRawThrottle1(); - int getRawThrottle2(); + uint16_t getRawThrottle1(); + uint16_t getRawThrottle2(); void saveEEPROM(); void saveConfiguration(); diff --git a/Throttle.cpp b/Throttle.cpp index 850b645..6f5c285 100644 --- a/Throttle.cpp +++ b/Throttle.cpp @@ -74,8 +74,8 @@ uint8_t Throttle::getSubtype() { /* * Give default versions that return 0. Override in a child class if you implement the throttle */ -int Throttle::getRawThrottle1() {return 0;} -int Throttle::getRawThrottle2() {return 0;} +uint16_t Throttle::getRawThrottle1() {return 0;} +uint16_t Throttle::getRawThrottle2() {return 0;} /* * Return the tick interval for this throttle. Override in a child class @@ -99,7 +99,7 @@ void Throttle::mapThrottle(int16_t currentPosition) { if (currentPosition <= positionRegenStart) { range = positionRegenStart; value = range - currentPosition; - level = (signed long) ((signed long) (-10) * maximumRegen * value / range); + level = -10 * maximumRegen * value / range; } } diff --git a/Throttle.h b/Throttle.h index f9de25e..57f1b41 100644 --- a/Throttle.h +++ b/Throttle.h @@ -49,8 +49,8 @@ class Throttle: public Device { virtual void handleTick(); virtual int16_t getLevel(); - virtual int getRawThrottle1(); - virtual int getRawThrottle2(); + virtual uint16_t getRawThrottle1(); + virtual uint16_t getRawThrottle2(); void detectThrottle(); void detectThrottleMin(); From 117027a9bea8fa697563ef0d80299489ca0cf087 Mon Sep 17 00:00:00 2001 From: Michael Neuweiler Date: Wed, 9 Oct 2013 19:25:05 +0200 Subject: [PATCH 2/4] new throttle design, first ideas --- Throttle.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Throttle.cpp b/Throttle.cpp index 6f5c285..ac3472f 100644 --- a/Throttle.cpp +++ b/Throttle.cpp @@ -42,8 +42,17 @@ Throttle::~Throttle() { void Throttle::handleTick() { Device::handleTick(); - if ( throttleDetector != NULL ) { + + if (throttleDetector) throttleDetector->handleTick(); + + rawValues = acquireData(); // get the raw data from pot's or e.g. send a query to the ECU (can msg) but don't wait for response + if (validate(rawValues)) { // validate the raw data + throttlePosition = calculateThrottlePosition(rawValues); // bring the raw data into a range of 0-1000 (without mapping) + level = mapThrottle(throttlePosition); // apply mapping of the 0-1000 range to the user defined range + } else { + level = 0; + Logger::error("invaild input values, setting throttle to 0"); } } From db50e8a95de6c8d626f683b3bd8ab3f3c9be6e5d Mon Sep 17 00:00:00 2001 From: Michael Neuweiler Date: Sun, 13 Oct 2013 00:56:58 +0200 Subject: [PATCH 3/4] refactored throttles, added creep, minregen, etc. --- BrusaMotorController.cpp | 4 + BrusaMotorController.h | 1 + Device.cpp | 63 +++--- Device.h | 23 ++- GEVCU.ino | 8 +- MotorController.cpp | 8 + MotorController.h | 1 + PotBrake.cpp | 300 +++++++++++++-------------- PotBrake.h | 67 +++--- PotThrottle.cpp | 433 +++++++++++++++++++-------------------- PotThrottle.h | 74 ++++--- SerialConsole.cpp | 223 ++++++++++---------- SerialConsole.h | 3 +- Throttle.cpp | 252 ++++++++--------------- Throttle.h | 93 +++------ ThrottleDetector.cpp | 28 ++- ThrottleDetector.h | 5 +- config.h | 15 +- eeprom_layout.h | 7 +- ichip_2128.cpp | 149 ++++++++------ ichip_2128.h | 2 + website/src/config.htm | 38 ++-- website/src/config.xml | 5 +- website/src/control.js | 31 ++- website/website.img | Bin 32236 -> 33317 bytes 25 files changed, 941 insertions(+), 892 deletions(-) diff --git a/BrusaMotorController.cpp b/BrusaMotorController.cpp index a02cb0c..fe96c5f 100644 --- a/BrusaMotorController.cpp +++ b/BrusaMotorController.cpp @@ -241,4 +241,8 @@ DeviceId BrusaMotorController::getId() { return BRUSA_DMC5; } +uint32_t BrusaMotorController::getTickInterval() { + return CFG_TICK_INTERVAL_MOTOR_CONTROLLER_BRUSA; +} + #endif diff --git a/BrusaMotorController.h b/BrusaMotorController.h index 704795f..3d58a21 100644 --- a/BrusaMotorController.h +++ b/BrusaMotorController.h @@ -141,6 +141,7 @@ class BrusaMotorController: public MotorController, CanObserver { void setup(); BrusaMotorController(); DeviceId getId(); + virtual uint32_t getTickInterval(); private: // DMC_TRQS2 diff --git a/Device.cpp b/Device.cpp index 394ee27..d58b981 100644 --- a/Device.cpp +++ b/Device.cpp @@ -1,47 +1,46 @@ /* * Device.cpp * -Copyright (c) 2013 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. + Copyright (c) 2013 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 "Device.h" Device::Device() { + deviceConfiguration = NULL; + prefsHandler = NULL; } //Empty functions to handle these callbacks if the derived classes don't void Device::setup() { - } void Device::handleTick() { - } void Device::handleMessage(uint32_t msgType, void* message) { - switch (msgType) - { + switch (msgType) { case MSG_STARTUP: this->setup(); break; @@ -56,7 +55,17 @@ DeviceId Device::getId() { return INVALID; } -uint32_t Device::getTickInterval() -{ - return 0; +void Device::loadConfiguration() { +} + +void Device::saveConfiguration() { +} + +DeviceConfiguration *Device::getConfiguration() { + return this->deviceConfiguration; } + +void Device::setConfiguration(DeviceConfiguration *configuration) { + this->deviceConfiguration = configuration; +} + diff --git a/Device.h b/Device.h index 2e10528..141ad0f 100644 --- a/Device.h +++ b/Device.h @@ -34,20 +34,37 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "PrefHandler.h" #include "Sys_Messages.h" -class Device : public TickObserver { +/* + * A abstract class to hold device configuration. It is to be accessed + * by sub-classes via getConfiguration() and then cast into its + * correct sub-class. + */ +class DeviceConfiguration { + +}; + +/* + * A abstract class for all Devices. + */ +class Device: public TickObserver { public: Device(); virtual void setup(); virtual void handleTick(); - virtual void handleMessage(uint32_t msgType, void* message); + virtual void handleMessage(uint32_t, void* ); virtual DeviceType getType(); virtual DeviceId getId(); - virtual uint32_t getTickInterval(); + + virtual void loadConfiguration(); + virtual void saveConfiguration(); + DeviceConfiguration *getConfiguration(); + void setConfiguration(DeviceConfiguration *); protected: PrefHandler *prefsHandler; private: + DeviceConfiguration *deviceConfiguration; // reference to the currently active configuration }; #endif /* DEVICE_H_ */ diff --git a/GEVCU.ino b/GEVCU.ino index 3afac0c..c2dba91 100644 --- a/GEVCU.ino +++ b/GEVCU.ino @@ -209,37 +209,31 @@ void initializeDevices() { // CFG_THROTTLE_NONE = not used (valid only for second value and should not be needed due to calibration/detection) Throttle *accelerator = new PotThrottle(CFG_THROTTLE1_PIN, CFG_THROTTLE2_PIN); Logger::info("add device: PotThrottle (%X)", accelerator); - //accelerator->setup(); deviceManager->addDevice(accelerator); #endif #ifdef CFG_ENABLE_DEVICE_CAN_THROTTLE Throttle *accelerator = new CanThrottle(); Logger::info("add device: CanThrottle (%X)", accelerator); - //accelerator->setup(); deviceManager->addDevice(accelerator); #endif #ifdef CFG_ENABLE_DEVICE_POT_BRAKE - Throttle *brake = new PotBrake(CFG_BRAKE_PIN, CFG_THROTTLE_NONE); //set up the brake input as the third ADC input from the shield. + Throttle *brake = new PotBrake(CFG_BRAKE_PIN); //set up the brake input as the third ADC input from the shield. Logger::info("add device: PotBrake (%X)", brake); - //brake->setup(); deviceManager->addDevice(brake); #endif #ifdef CFG_ENABLE_DEVICE_CAN_THROTTLE_BRAKE Throttle *brake = new CanThrottle(); Logger::info("add device: CanThrottle brake (%X)", brake); - //brake->setup(); deviceManager->addDevice(brake); #endif #ifdef CFG_ENABLE_DEVICE_MOTORCTRL_DMOC_645 MotorController *motorController = new DmocMotorController(); //instantiate a DMOC645 device controller as our motor controller Logger::info("add device: DMOC645 (%X)", motorController); - //motorController->setup(); deviceManager->addDevice(motorController); #endif #ifdef CFG_ENABLE_DEVICE_MOTORCTRL_BRUSA_DMC5 MotorController *motorController = new BrusaMotorController(); //instantiate a Brusa DMC5 device controller as our motor controller Logger::info("add device: Brusa DMC5 (%X)", motorController); -// motorController->setup(); deviceManager->addDevice(motorController); #endif #ifdef CFG_ENABLE_DEVICE_BMS_THINK diff --git a/MotorController.cpp b/MotorController.cpp index 0c77f4a..1053813 100644 --- a/MotorController.cpp +++ b/MotorController.cpp @@ -339,3 +339,11 @@ uint32_t MotorController::getStatusBitfield3() { uint32_t MotorController::getStatusBitfield4() { return statusBitfield4; } + +uint32_t MotorController::getTickInterval() { + return 0; +} + +bool MotorController::isReady() { + return false; +} diff --git a/MotorController.h b/MotorController.h index 02d2682..bb97a04 100644 --- a/MotorController.h +++ b/MotorController.h @@ -60,6 +60,7 @@ class MotorController: public Device { DeviceType getType(); virtual void setup(); void handleTick(); + virtual uint32_t getTickInterval(); bool isReady(); bool isRunning(); diff --git a/PotBrake.cpp b/PotBrake.cpp index 36e588b..c85292e 100644 --- a/PotBrake.cpp +++ b/PotBrake.cpp @@ -1,26 +1,26 @@ /* * PotBrake.cpp * -Copyright (c) 2013 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. + Copyright (c) 2013 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. */ @@ -28,187 +28,175 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef CFG_ENABLE_DEVICE_POT_BRAKE #include "PotBrake.h" -//initialize by telling the code which two ADC channels to use (or set channel 2 to 255 to disable) -PotBrake::PotBrake(uint8_t brake1, uint8_t brake2) : +/* + * Constructor + * Set which ADC channel to use + */ +PotBrake::PotBrake(uint8_t brake1) : Throttle() { - brake1ADC = brake1; - brake2ADC = brake2; - if (brake2 == 255) - numberPotMeters = 1; - else - numberPotMeters = 2; + brake1AdcPin = brake1; brakeStatus = OK; - //analogReadResolution(12); } +/* + * Setup the device. + */ void PotBrake::setup() { TickHandler::getInstance()->detach(this); // unregister from TickHandler first Throttle::setup(); //call base class - //set digital ports to inputs and pull them up - //all inputs currently active low + + //set digital ports to inputs and pull them up all inputs currently active low //pinMode(THROTTLE_INPUT_BRAKELIGHT, INPUT_PULLUP); //Brake light switch -#ifndef USE_HARD_CODED - if (prefsHandler->checksumValid()) { //checksum is good, read in the values stored in EEPROM - prefsHandler->read(EETH_BRAKE_MIN, &minimumLevel1); - prefsHandler->read(EETH_BRAKE_MAX, &maximumLevel1); - prefsHandler->read(EETH_MAX_BRAKE_REGEN, &maximumRegen); - prefsHandler->read(EETH_MIN_BRAKE_REGEN, &minimumRegen); - Logger::debug(POTBRAKEPEDAL, "BRAKE MIN: %l MAX: %l", minimumLevel1, maximumLevel1); - Logger::debug(POTBRAKEPEDAL, "Min: %l MaxRegen: %l", minimumRegen, maximumRegen); - } - else { //checksum invalid. Reinitialize values and store to EEPROM - - //these four values are ADC values - //The next three are tenths of a percent - maximumRegen = BrakeMaxRegenValue; //percentage of full power to use for regen at brake pedal transducer - minimumRegen = BrakeMinRegenValue; - minimumLevel1 = BrakeMinValue; - maximumLevel1 = BrakeMaxValue; - saveEEPROM(); - } -#else - maximumRegen = BrakeMaxRegenValue; - minimumRegen = BrakeMinRegenValue; - minimumLevel1 = BrakeMinValue; - maximumLevel1 = BrakeMaxValue; -#endif - TickHandler::getInstance()->attach(this, CFG_TICK_INTERVAL_POT_THROTTLE); -} -uint16_t PotBrake::getRawThrottle1() { - return rawLevel1; + loadConfiguration(); + TickHandler::getInstance()->attach(this, CFG_TICK_INTERVAL_POT_THROTTLE); } -uint16_t PotBrake::getRawThrottle2() { - return rawLevel2; +/* + * Process a timer event. + */ +void PotBrake::handleTick() { + Throttle::handleTick(); // Call parent which controls the workflow } -uint16_t PotBrake::calcBrake(uint16_t clampedVal, uint16_t minVal, uint16_t maxVal) { - return map(constrain(clampedVal, minVal, maxVal), minVal, maxVal, (uint16_t)0, (uint16_t)1000); +/* + * Retrieve raw input signals from the brake hardware. + */ +RawSignalData *PotBrake::acquireRawSignal() { + sys_io_adc_poll(); + rawSignal.input1 = getAnalog(brake1AdcPin); + return &rawSignal; } /* - the brake only really uses one variable input and uses different parameters - - story time: this code will start at ThrottleMaxRegen when applying the brake. It - will do this even if you're currently flooring it. The accelerator pedal is ignored - if there is any pressure detected on the brake. This is a sort of failsafe. It should - not be possible to go racing down the road with a stuck accelerator. As soon as the - brake is pressed it overrides the accelerator signal. Sorry, no standing burn outs. - + * Perform sanity check on the ADC input values. */ -void PotBrake::doBrake() { - uint16_t calcBrake1, calcBrake2, clampedVal, tempLow, temp; -// static uint16_t brakeAvg = 0, brakeFeedback = 0; //used to create proportional control - - clampedVal = rawLevel1; +bool PotBrake::validateSignal(RawSignalData *rawValues) { + PotBrakeConfiguration *config = (PotBrakeConfiguration *) getConfiguration(); + brakeStatus = OK; - if (maximumLevel1 == 0) { //brake processing disabled if Max is 0 - level = 0; - return; + if (rawSignal.input1 > (config->maximumLevel1 + CFG_THROTTLE_TOLERANCE)) { + brakeStatus = ERR_HIGH_T1; + Logger::error(POTBRAKEPEDAL, "ERR_HIGH_T1: brake 1 value out of range: %l", rawSignal.input1); + // even if it's too high, let it process and apply full regen + // return false; } - - //We do not raise a fault of the brake goes too high. We just clamp. This will lock regen on full blast. - if (rawLevel1 > maximumLevel1) { - clampedVal = maximumLevel1; + if (rawSignal.input1 < (config->minimumLevel1 - CFG_THROTTLE_TOLERANCE)) { + brakeStatus = ERR_LOW_T1; + Logger::error(POTBRAKEPEDAL, "ERR_LOW_T1: brake 1 value out of range: %l ", rawSignal.input1); + return false; } - tempLow = 0; - if (minimumLevel1 > 14) { - tempLow = minimumLevel1 - 15; - } - if (rawLevel1 < minimumLevel1) { - if (rawLevel1 < tempLow) { - brakeStatus = ERR_LOW_T1; - //Logger::debug(POTBRAKEPEDAL, "B"); - } - clampedVal = minimumLevel1; - } + return true; +} - if (!(brakeStatus == OK)) { - level = 0; //no throttle if there is a fault - return; - } - calcBrake1 = calcBrake(clampedVal, minimumLevel1, maximumLevel1); - //Logger::debug(POTBRAKEPEDAL, "calcBrake: %d", calcBrake1); - - //Apparently all is well with the throttle input - //so go ahead and calculate the proper throttle output - - //still use this smoothing/easing code for the brake. It works quickly enough -// brakeAvg += calcBrake1; -// brakeAvg -= brakeFeedback; -// brakeFeedback = brakeAvg >> 4; - - level = 0; //by default we give zero throttle - - //I suppose I should explain. This prevents flutter in the ADC readings of the brake from slamming - //regen on intermittantly just because the value fluttered a couple of numbers. This makes sure - //that we're actually pushing the pedal. Without this even a small flutter at the brake will send - //ThrottleMaxRegen regen out and ignore the accelerator. That'd be unpleasant. - if (calcBrake1 < 15) { - level = 0; - return; - } +/* + * Convert the raw ADC values to a range from 0 to 1000 (per mille) according + * to the specified range and the type of potentiometer. + */ +uint16_t PotBrake::calculatePedalPosition(RawSignalData *rawValues) { + PotBrakeConfiguration *config = (PotBrakeConfiguration *) getConfiguration(); + uint16_t calcBrake1, clampedLevel; - if (maximumRegen != 0) { //is the brake regen even enabled? - int range = maximumRegen - minimumRegen; //we start the brake at ThrottleMaxRegen so the range is reduced by that amount - //Logger::debug(POTBRAKEPEDAL, "range: %d", range); - //Logger::debug(POTBRAKEPEDAL, "brakeFeedback: %d", brakeFeedback); - if (range < 1) { //detect stupidity and abort - level = 0; - return; - } - level = -10 * range * calcBrake1 / 1000; - level -= 10 * minimumRegen; - //Logger::debug(POTBRAKEPEDAL, "level: %d", level); - } + if (config->maximumLevel1 == 0) //brake processing disabled if max is 0 + return 0; -} + clampedLevel = constrain(rawSignal.input1, config->minimumLevel1, config->maximumLevel1); + calcBrake1 = map(clampedLevel, config->minimumLevel1, config->maximumLevel1, (uint16_t) 0, (uint16_t) 1000); -//right now only the first throttle ADC port is used. Eventually the second one should be used to cross check so dumb things -//don't happen. Also, right now values of ADC outside the proper range are just clamped to the proper range. -void PotBrake::handleTick() { - sys_io_adc_poll(); + //This prevents flutter in the ADC readings of the brake from slamming regen on intermittently + // just because the value fluttered a couple of numbers. This makes sure that we're actually + // pushing the pedal. Without this even a small flutter at the brake will send minregen + // out and ignore the accelerator. That'd be unpleasant. + if (calcBrake1 < 15) + calcBrake1 = 0; - rawLevel1 = getAnalog(brake1ADC); - if (numberPotMeters > 1) - rawLevel2 = getAnalog(brake2ADC); + return calcBrake1; +} +/* + * Overrides the standard implementation of throttle mapping as different rules apply to + * brake based regen. + */ +int16_t PotBrake::mapPedalPosition(int16_t pedalPosition) { + ThrottleConfiguration *config = (ThrottleConfiguration *) getConfiguration(); + int16_t brakeLevel, range; - // Call parent handleTick - Throttle::handleTick(); + range = config->maximumRegen - config->minimumRegen; + brakeLevel = -10 * range * pedalPosition / 1000; + brakeLevel -= 10 * config->minimumRegen; + //Logger::debug(POTBRAKEPEDAL, "level: %d", level); - brakeStatus = OK; - doBrake(); + return brakeLevel; } +/* + * Return the brake's current status + */ PotBrake::BrakeStatus PotBrake::getStatus() { return brakeStatus; } +/* + * Return the device ID + */ DeviceId PotBrake::getId() { return (POTBRAKEPEDAL); } +/* + * Return the device type + */ DeviceType PotBrake::getType() { return (DEVICE_BRAKE); } -void PotBrake::saveEEPROM() { - prefsHandler->write(EETH_BRAKE_MIN, minimumLevel1); - prefsHandler->write(EETH_BRAKE_MAX, maximumLevel1); - prefsHandler->write(EETH_MAX_BRAKE_REGEN, maximumRegen); - prefsHandler->write(EETH_MIN_BRAKE_REGEN, minimumRegen); - prefsHandler->saveChecksum(); +/* + * Load the device configuration. + * If possible values are read from EEPROM. If not, reasonable default values + * are chosen and the configuration is overwritten in the EEPROM. + */ +void PotBrake::loadConfiguration() { + PotBrakeConfiguration *config = new PotBrakeConfiguration(); + +#ifndef USE_HARD_CODED + if (prefsHandler->checksumValid()) { //checksum is good, read in the values stored in EEPROM + prefsHandler->read(EETH_BRAKE_MIN, &config->minimumLevel1); + prefsHandler->read(EETH_BRAKE_MAX, &config->maximumLevel1); + prefsHandler->read(EETH_MAX_BRAKE_REGEN, &config->maximumRegen); + prefsHandler->read(EETH_MIN_BRAKE_REGEN, &config->minimumRegen); + Logger::debug(POTBRAKEPEDAL, "BRAKE MIN: %l MAX: %l", config->minimumLevel1, config->maximumLevel1); + Logger::debug(POTBRAKEPEDAL, "Min: %l MaxRegen: %l", config->minimumRegen, config->maximumRegen); + } else { //checksum invalid. Reinitialize values and store to EEPROM + + //these four values are ADC values + //The next three are tenths of a percent + config->maximumRegen = BrakeMaxRegenValue; //percentage of full power to use for regen at brake pedal transducer + config->minimumRegen = BrakeMinRegenValue; + config->minimumLevel1 = BrakeMinValue; + config->maximumLevel1 = BrakeMaxValue; + saveConfiguration(); + } +#else + config->maximumRegen = BrakeMaxRegenValue; + config->minimumRegen = BrakeMinRegenValue; + config->minimumLevel1 = BrakeMinValue; + config->maximumLevel1 = BrakeMaxValue; +#endif + + setConfiguration(config); } +/* + * Store the current configuration to EEPROM + */ void PotBrake::saveConfiguration() { - Logger::info(POTBRAKEPEDAL, "Saving brake settings"); - TickHandler::getInstance()->detach(this); // unregister from TickHandler first - setMinumumLevel1(throttleDetector->getThrottle1Min()); - setMaximumLevel1(throttleDetector->getThrottle1Max()); - saveEEPROM(); - TickHandler::getInstance()->attach(this, getTickInterval()); + PotBrakeConfiguration *config = (PotBrakeConfiguration *) getConfiguration(); + + prefsHandler->write(EETH_BRAKE_MIN, config->minimumLevel1); + prefsHandler->write(EETH_BRAKE_MAX, config->maximumLevel1); + prefsHandler->write(EETH_MAX_BRAKE_REGEN, config->maximumRegen); + prefsHandler->write(EETH_MIN_BRAKE_REGEN, config->minimumRegen); + prefsHandler->saveChecksum(); } #endif // CFG_ENABLE_DEVICE_POT_BRAKE diff --git a/PotBrake.h b/PotBrake.h index 619f922..1fc8322 100644 --- a/PotBrake.h +++ b/PotBrake.h @@ -1,26 +1,26 @@ /* * PotBrake.h * -Copyright (c) 2013 Collin Kidder, Michael Neuweiler, Charles Galpin + Copyright (c) 2013 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: + 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 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. + 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. */ @@ -33,38 +33,47 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "sys_io.h" #include "TickHandler.h" #include "Logger.h" -#include "ThrottleDetector.h" #define THROTTLE_INPUT_BRAKELIGHT 2 +/* + * The extended configuration class with additional parameters for PotBrake + * + * NOTE: Because of ThrottleDetector, this currently MUST be the same as PotThrottleConfiguratin !!! + */ +class PotBrakeConfiguration: public PotThrottleConfiguration { +}; + class PotBrake: public Throttle { public: enum BrakeStatus { OK, ERR_LOW_T1, - ERR_LOW_T2, ERR_HIGH_T1, - ERR_HIGH_T2, - ERR_MISMATCH, ERR_MISC }; - PotBrake(uint8_t throttle1, uint8_t throttle2); + PotBrake(uint8_t throttle1); void setup(); void handleTick(); - BrakeStatus getStatus(); - uint16_t getRawThrottle1(); - uint16_t getRawThrottle2(); DeviceId getId(); + BrakeStatus getStatus(); DeviceType getType(); + + RawSignalData *acquireRawSignal(); + + void loadConfiguration(); void saveConfiguration(); - void saveEEPROM(); + +protected: + bool validateSignal(RawSignalData *); + uint16_t calculatePedalPosition(RawSignalData *); + int16_t mapPedalPosition(int16_t); private: - uint8_t brake1ADC, brake2ADC; //which ADC pin each are on + uint8_t brake1AdcPin; //which ADC pin each are on BrakeStatus brakeStatus; - uint16_t calcBrake(uint16_t, uint16_t, uint16_t); - void doBrake(); + RawSignalData rawSignal; }; #endif /* POT_BRAKE_H_ */ diff --git a/PotThrottle.cpp b/PotThrottle.cpp index 923c7da..5ba7917 100644 --- a/PotThrottle.cpp +++ b/PotThrottle.cpp @@ -1,26 +1,26 @@ /* * PotThrottle.cpp * -Copyright (c) 2013 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. + Copyright (c) 2013 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. */ @@ -28,259 +28,252 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifdef CFG_ENABLE_DEVICE_POT_THROTTLE #include "PotThrottle.h" -//initialize by telling the code which two ADC channels to use (or set channel 2 to 255 to disable) -PotThrottle::PotThrottle(uint8_t throttle1, uint8_t throttle2) : Throttle() { - throttle1ADC = throttle1; - throttle2ADC = throttle2; - if (throttle2 == CFG_THROTTLE_NONE) - numberPotMeters = 1; - else - numberPotMeters = 2; +/* + * Constructor + * Set which two ADC channels to use (or set channel 2 to 255 to disable) + */ +PotThrottle::PotThrottle(uint8_t throttle1Pin, uint8_t throttle2Pin) : + Throttle() { + throttle1AdcPin = throttle1Pin; + throttle2AdcPin = throttle2Pin; throttleStatus = OK; - throttleMaxErr = ThrottleMaxErrValue; //in tenths of a percent. So 25 = max 2.5% difference - //analogReadResolution(12); } +/* + * Setup the device. + */ void PotThrottle::setup() { TickHandler::getInstance()->detach(this); // unregister from TickHandler first Throttle::setup(); //call base class - //set digital ports to inputs and pull them up - //all inputs currently active low - //pinMode(THROTTLE_INPUT_BRAKELIGHT, INPUT_PULLUP); //Brake light switch - -#ifndef USE_HARD_CODED - if (prefsHandler->checksumValid()) { //checksum is good, read in the values stored in EEPROM - Logger::debug(POTACCELPEDAL, "Valid checksum so using stored throttle config values"); - prefsHandler->read(EETH_MIN_ONE, &minimumLevel1); - prefsHandler->read(EETH_MAX_ONE, &maximumLevel1); - prefsHandler->read(EETH_MIN_TWO, &minimumLevel2); - prefsHandler->read(EETH_MAX_TWO, &maximumLevel2); - prefsHandler->read(EETH_REGEN, &positionRegenStart); - prefsHandler->read(EETH_FWD, &positionForwardMotionStart); - prefsHandler->read(EETH_MAP, &positionHalfPower); - prefsHandler->read(EETH_MAX_ACCEL_REGEN, &maximumRegen); - prefsHandler->read(EETH_NUM_THROTTLES, &numberPotMeters); - prefsHandler->read(EETH_THROTTLE_TYPE, &throttleSubType); - // ** This is potentially a condition that is only met if you don't have the EEPROM hardware ** - // If preferences have never been set before, numThrottlePots and throttleSubType - // will both be zero. We really should refuse to operate in this condition and force - // calibration, but for now at least allow calibration to work by setting numThrottlePots = 2 - if (numberPotMeters == 0 && throttleSubType == 0) { - Logger::debug(POTACCELPEDAL, "THROTTLE APPEARS TO NEED CALIBRATION/DETECTION - choose 'z' on the serial console menu"); - numberPotMeters = 2; - } - - Logger::debug(POTACCELPEDAL, "# of pots: %d subtype: %d", numberPotMeters, throttleSubType); - Logger::debug(POTACCELPEDAL, "T1 MIN: %l MAX: %l T2 MIN: %l MAX: %l", minimumLevel1, maximumLevel1, minimumLevel2, maximumLevel2); - Logger::debug(POTACCELPEDAL, "Regen: %l Fwd: %l Map: %l MaxRegen: %d", positionRegenStart, positionForwardMotionStart, positionHalfPower, maximumRegen); - } - else { //checksum invalid. Reinitialize values and store to EEPROM - Logger::debug(POTACCELPEDAL, "Invalid checksum so using hard coded throttle config values"); - - //The next three are tenths of a percent - positionRegenStart = ThrottleRegenValue; - positionForwardMotionStart = ThrottleFwdValue; - positionHalfPower = ThrottleMapValue; - maximumRegen = ThrottleMaxRegenValue; //percentage of full power to use for regen at throttle - minimumLevel1 = Throttle1MinValue; - maximumLevel1 = Throttle1MaxValue; - minimumLevel2 = Throttle2MinValue; - maximumLevel2 = Throttle2MaxValue; - numberPotMeters = ThrottleNumPots; - throttleSubType = ThrottleSubtype; - - prefsHandler->write(EETH_MIN_ONE, minimumLevel1); - prefsHandler->write(EETH_MAX_ONE, maximumLevel1); - prefsHandler->write(EETH_MIN_TWO, minimumLevel2); - prefsHandler->write(EETH_MAX_TWO, maximumLevel2); - prefsHandler->write(EETH_REGEN, positionRegenStart); - prefsHandler->write(EETH_FWD, positionForwardMotionStart); - prefsHandler->write(EETH_MAP, positionHalfPower); - prefsHandler->write(EETH_MAX_ACCEL_REGEN, maximumRegen); - prefsHandler->write(EETH_NUM_THROTTLES, numberPotMeters); - prefsHandler->write(EETH_THROTTLE_TYPE, throttleSubType); - prefsHandler->saveChecksum(); - } -#else - Logger::debug(POTACCELPEDAL, "#define USE_HARD_CODED so using hard coded throttle config values"); - positionRegenStart = ThrottleRegenValue; - positionForwardMotionStart = ThrottleFwdValue; - positionHalfPower = ThrottleMapValue; - maximumRegen = ThrottleMaxRegenValue; //percentage of full power to use for regen at throttle - minimumLevel1 = Throttle1MinValue; - maximumLevel1 = Throttle1MaxValue; - minimumLevel2 = Throttle2MinValue; - maximumLevel2 = Throttle2MaxValue; -#endif + //set digital ports to inputs and pull them up all inputs currently active low + //pinMode(THROTTLE_INPUT_BRAKELIGHT, INPUT_PULLUP); //Brake light switch + loadConfiguration(); TickHandler::getInstance()->attach(this, CFG_TICK_INTERVAL_POT_THROTTLE); } -uint16_t PotThrottle::getRawThrottle1() { - return rawLevel1; -} - -uint16_t PotThrottle::getRawThrottle2() { - return rawLevel2; +/* + * Process a timer event. + */ +void PotThrottle::handleTick() { + Throttle::handleTick(); // Call parent which controls the workflow } /* - * Convert the raw ADC values to a range from 0 to 1000 (per mille) according - * to the specified range and the type of potentiometer. + * Retrieve raw input signals from the throttle hardware. */ -uint16_t PotThrottle::calcThrottle(uint16_t clampedVal, uint16_t minVal, uint16_t maxVal) { - return map(constrain(clampedVal, minVal, maxVal), minVal, maxVal, (uint16_t)0, (uint16_t)1000); +RawSignalData *PotThrottle::acquireRawSignal() { + PotThrottleConfiguration *config = (PotThrottleConfiguration *) getConfiguration(); + + sys_io_adc_poll(); + + rawSignal.input1 = getAnalog(throttle1AdcPin); + if (config->numberPotMeters > 1) + rawSignal.input2 = getAnalog(throttle2AdcPin); + + return &rawSignal; } /* - * Perform sanity check on the ADC input values, convert them to per mille values - * optionally average two throttle inputs and in the end map the calculated throttle - * position to a regen/acceleration curve. + * Perform sanity check on the ADC input values. */ -void PotThrottle::doAccel() { - uint16_t calcThrottle1, calcThrottle2, clampedLevel, tempLow; - throttleAverage = 0; - throttleFeedback = 0; - - clampedLevel = rawLevel1; - - //The below code now only faults if the value of the ADC is 15 outside of the range +/- - //otherwise we'll just clamp - if (rawLevel1 > maximumLevel1) { - if (rawLevel1 > (maximumLevel1 + CFG_THROTTLE_TOLERANCE)) { - throttleStatus = ERR_HIGH_T1; - Logger::error(POTACCELPEDAL, "ERR_HIGH_T1: throttle 1 value out of range: %l", rawLevel1); - } - clampedLevel = maximumLevel1; - } - tempLow = 0; - if (minimumLevel1 > (CFG_THROTTLE_TOLERANCE - 1)) { - tempLow = minimumLevel1 - CFG_THROTTLE_TOLERANCE; +bool PotThrottle::validateSignal(RawSignalData *rawValues) { + PotThrottleConfiguration *config = (PotThrottleConfiguration *) getConfiguration(); + uint16_t calcThrottle1, calcThrottle2; + throttleStatus = OK; + + if (rawSignal.input1 > (config->maximumLevel1 + CFG_THROTTLE_TOLERANCE)) { + throttleStatus = ERR_HIGH_T1; + Logger::error(POTACCELPEDAL, "ERR_HIGH_T1: throttle 1 value out of range: %l", rawSignal.input1); + return false; } - if (rawLevel1 < minimumLevel1) { - if (rawLevel1 < tempLow) { - throttleStatus = ERR_LOW_T1; - Logger::error(POTACCELPEDAL, "ERR_LOW_T1: throttle 1 value out of range: %l ", rawLevel1); - } - clampedLevel = minimumLevel1; + if (rawSignal.input1 < (config->minimumLevel1 - CFG_THROTTLE_TOLERANCE)) { + throttleStatus = ERR_LOW_T1; + Logger::error(POTACCELPEDAL, "ERR_LOW_T1: throttle 1 value out of range: %l ", rawSignal.input1); + return false; } - calcThrottle1 = calcThrottle(clampedLevel, minimumLevel1, maximumLevel1); - //Logger::debug(POTACCELPEDAL, "calc throttle: %i", calcThrottle1); - - if (numberPotMeters > 1) { //can only do these things if there are two or more pots - clampedLevel = rawLevel2; - if (rawLevel2 > maximumLevel2) { - if (rawLevel2 > (maximumLevel2 + CFG_THROTTLE_TOLERANCE)) { - throttleStatus = ERR_HIGH_T2; - Logger::error(POTACCELPEDAL, "ERR_HIGH_T2: throttle 2 value out of range: %l", rawLevel2); - } - clampedLevel = maximumLevel2; + if (config->numberPotMeters > 1) { + if (rawSignal.input2 > (config->maximumLevel2 + CFG_THROTTLE_TOLERANCE)) { + throttleStatus = ERR_HIGH_T2; + Logger::error(POTACCELPEDAL, "ERR_HIGH_T2: throttle 2 value out of range: %l", rawSignal.input2); + return false; } - tempLow = 0; - if (minimumLevel2 > (CFG_THROTTLE_TOLERANCE - 1)) { - tempLow = minimumLevel2 - CFG_THROTTLE_TOLERANCE; + if (rawSignal.input2 < (config->minimumLevel2 - CFG_THROTTLE_TOLERANCE)) { + throttleStatus = ERR_LOW_T2; + Logger::error(POTACCELPEDAL, "ERR_LOW_T2: throttle 2 value out of range: %l", rawSignal.input2); + return false; } - if (rawLevel2 < minimumLevel2) { - if (rawLevel2 < tempLow) { - throttleStatus = ERR_LOW_T2; - Logger::error(POTACCELPEDAL, "ERR_LOW_T2: throttle 2 value out of range: %l", rawLevel2); - } - clampedLevel = minimumLevel2; - } - - calcThrottle2 = calcThrottle(clampedLevel, minimumLevel2, maximumLevel2); - if ((calcThrottle1 - throttleMaxErr) > calcThrottle2) { //then throttle1 is too large compared to 2 + calcThrottle1 = map(constrain(rawSignal.input1, config->minimumLevel1, config->maximumLevel1), config->minimumLevel1, config->maximumLevel1, + (uint16_t) 0, (uint16_t) 1000); + calcThrottle2 = map(constrain(rawSignal.input2, config->minimumLevel2, config->maximumLevel2), config->minimumLevel2, config->maximumLevel2, + (uint16_t) 0, (uint16_t) 1000); + if ((calcThrottle1 - ThrottleMaxErrValue) > calcThrottle2) { //then throttle1 is too large compared to 2 throttleStatus = ERR_MISMATCH; Logger::error(POTACCELPEDAL, "throttle 1 too high (%l) compared to 2 (%l)", calcThrottle1, calcThrottle2); + return false; } - if ((calcThrottle2 - throttleMaxErr) > calcThrottle1) { //then throttle2 is too large compared to 1 + if ((calcThrottle2 - ThrottleMaxErrValue) > calcThrottle1) { //then throttle2 is too large compared to 1 throttleStatus = ERR_MISMATCH; Logger::error(POTACCELPEDAL, "throttle 2 too high (%l) compared to 1 (%l)", calcThrottle1, calcThrottle2); + return false; } - - calcThrottle1 = (calcThrottle1 + calcThrottle2) / 2; //temp now the average of the two - } - - if (throttleStatus != OK) { - level = 0; //no throttle if there is a fault - Logger::error(POTACCELPEDAL, "throttle faulted (status=%d), setting level to 0", throttleStatus); - return; } - - //Apparently all is well with the throttle input - //so go ahead and calculate the proper throttle output - - //throttleAverage += calcThrottle1; - //throttleAverage -= throttleFeedback; - //throttleFeedback = throttleAverage >> 4; - mapThrottle(calcThrottle1); + return true; } -//right now only the first throttle ADC port is used. Eventually the second one should be used to cross check so dumb things -//don't happen. Also, right now values of ADC outside the proper range are just clamped to the proper range. -void PotThrottle::handleTick() { - sys_io_adc_poll(); - - rawLevel1 = getAnalog(throttle1ADC); - if (numberPotMeters > 1) - rawLevel2 = getAnalog(throttle2ADC); +/* + * Convert the raw ADC values to a range from 0 to 1000 (per mille) according + * to the specified range and the type of potentiometer. + */ +uint16_t PotThrottle::calculatePedalPosition(RawSignalData *rawValues) { + PotThrottleConfiguration *config = (PotThrottleConfiguration *) getConfiguration(); + uint16_t calcThrottle1, calcThrottle2, clampedLevel; - // Call parent handleTick - Throttle::handleTick(); + clampedLevel = constrain(rawSignal.input1, config->minimumLevel1, config->maximumLevel1); + calcThrottle1 = map(clampedLevel, config->minimumLevel1, config->maximumLevel1, (uint16_t) 0, (uint16_t) 1000); - throttleStatus = OK; - doAccel(); + if (config->numberPotMeters > 1) { + clampedLevel = constrain(rawSignal.input2, config->minimumLevel2, config->maximumLevel2); + calcThrottle2 = map(clampedLevel, config->minimumLevel2, config->maximumLevel2, (uint16_t) 0, (uint16_t) 1000); + calcThrottle1 = (calcThrottle1 + calcThrottle2) / 2; // now the average of the two + } + return calcThrottle1; } +/* + * Is the throttle faulted? + */ bool PotThrottle::isFaulted() { return throttleStatus != OK; } +/* + * Return the throttle's current status + */ PotThrottle::ThrottleStatus PotThrottle::getStatus() { return throttleStatus; } - +/* + * Return the device ID + */ DeviceId PotThrottle::getId() { return (POTACCELPEDAL); } -void PotThrottle::saveEEPROM() { - prefsHandler->write(EETH_MIN_ONE, minimumLevel1); - prefsHandler->write(EETH_MAX_ONE, maximumLevel1); - prefsHandler->write(EETH_MIN_TWO, minimumLevel2); - prefsHandler->write(EETH_MAX_TWO, maximumLevel2); - prefsHandler->write(EETH_REGEN, positionRegenStart); - prefsHandler->write(EETH_FWD, positionForwardMotionStart); - prefsHandler->write(EETH_MAP, positionHalfPower); - prefsHandler->write(EETH_MAX_ACCEL_REGEN, maximumRegen); - prefsHandler->write(EETH_NUM_THROTTLES, numberPotMeters); - prefsHandler->write(EETH_THROTTLE_TYPE, throttleSubType); - prefsHandler->saveChecksum(); +/* + * Load the device configuration. + * If possible values are read from EEPROM. If not, reasonable default values + * are chosen and the configuration is overwritten in the EEPROM. + */ +void PotThrottle::loadConfiguration() { + PotThrottleConfiguration *config = new PotThrottleConfiguration(); + +#ifndef USE_HARD_CODED + if (prefsHandler->checksumValid()) { //checksum is good, read in the values stored in EEPROM + Logger::debug(POTACCELPEDAL, "Valid checksum so using stored throttle config values"); + prefsHandler->read(EETH_MIN_ONE, &config->minimumLevel1); + prefsHandler->read(EETH_MAX_ONE, &config->maximumLevel1); + prefsHandler->read(EETH_MIN_TWO, &config->minimumLevel2); + prefsHandler->read(EETH_MAX_TWO, &config->maximumLevel2); + prefsHandler->read(EETH_REGEN_MIN, &config->positionRegenMinimum); + prefsHandler->read(EETH_REGEN_MAX, &config->positionRegenMaximum); + prefsHandler->read(EETH_FWD, &config->positionForwardMotionStart); + prefsHandler->read(EETH_MAP, &config->positionHalfPower); + prefsHandler->read(EETH_CREEP, &config->creep); + prefsHandler->read(EETH_MIN_ACCEL_REGEN, &config->minimumRegen); + prefsHandler->read(EETH_MAX_ACCEL_REGEN, &config->maximumRegen); + prefsHandler->read(EETH_NUM_THROTTLES, &config->numberPotMeters); + prefsHandler->read(EETH_THROTTLE_TYPE, &config->throttleSubType); + + // ** This is potentially a condition that is only met if you don't have the EEPROM hardware ** + // If preferences have never been set before, numThrottlePots and throttleSubType + // will both be zero. We really should refuse to operate in this condition and force + // calibration, but for now at least allow calibration to work by setting numThrottlePots = 2 + if (config->numberPotMeters == 0 && config->throttleSubType == 0) { + Logger::debug(POTACCELPEDAL, "THROTTLE APPEARS TO NEED CALIBRATION/DETECTION - choose 'z' on the serial console menu"); + config->numberPotMeters = 2; + } + + // some safety precautions for new values (depending on eeprom, they might be completely off). + if (config->creep > 100 || config->positionRegenMaximum > 1000 || config->minimumRegen > 100) { + config->creep = ThrottleCreepValue; + config->positionRegenMaximum = ThrottleRegenMaxValue; + config->minimumRegen = ThrottleMinRegenValue; + } + + Logger::debug(POTACCELPEDAL, "# of pots: %d subtype: %d", config->numberPotMeters, config->throttleSubType); + Logger::debug(POTACCELPEDAL, "T1 MIN: %l MAX: %l T2 MIN: %l MAX: %l", config->minimumLevel1, config->maximumLevel1, + config->minimumLevel2, config->maximumLevel2); + Logger::debug(POTACCELPEDAL, "RegenMax: %l RegenMin: %l Fwd: %l Map: %l", config->positionRegenMaximum, + config->positionRegenMinimum, config->positionForwardMotionStart, config->positionHalfPower); + Logger::debug(POTACCELPEDAL, "MinRegen: %d MaxRegen: %d", config->minimumRegen, config->maximumRegen); + } else { //checksum invalid. Reinitialize values and store to EEPROM + Logger::warn(POTACCELPEDAL, "Invalid checksum so using hard coded throttle config values"); + + config->minimumLevel1 = Throttle1MinValue; + config->maximumLevel1 = Throttle1MaxValue; + config->minimumLevel2 = Throttle2MinValue; + config->maximumLevel2 = Throttle2MaxValue; + config->positionRegenMinimum = ThrottleRegenMinValue; + config->positionRegenMaximum = ThrottleRegenMaxValue; + config->positionForwardMotionStart = ThrottleFwdValue; + config->positionHalfPower = ThrottleMapValue; + config->creep = ThrottleCreepValue; + config->minimumRegen = ThrottleMinRegenValue; //percentage of minimal power to use when regen starts + config->maximumRegen = ThrottleMaxRegenValue; //percentage of full power to use for regen at throttle + config->numberPotMeters = ThrottleNumPots; + config->throttleSubType = ThrottleSubtype; + + saveConfiguration(); + } +#else + Logger::info(POTACCELPEDAL, "#define USE_HARD_CODED so using hard coded throttle config values"); + config->minimumLevel1 = Throttle1MinValue; + config->maximumLevel1 = Throttle1MaxValue; + config->minimumLevel2 = Throttle2MinValue; + config->maximumLevel2 = Throttle2MaxValue; + config->positionRegenMinimum = ThrottleRegenMinValue; + config->positionRegenMaximum = ThrottleRegenMaxValue; + config->positionForwardMotionStart = ThrottleFwdValue; + config->positionHalfPower = ThrottleMapValue; + config->creep = ThrottleCreepValue; + config->minimumRegen = ThrottleMinRegenValue; //percentage of minimal power to use when regen starts + config->maximumRegen = ThrottleMaxRegenValue;//percentage of full power to use for regen at throttle + if (throttle2AdcPin == CFG_THROTTLE_NONE) + config->numberPotMeters = 1; + else + config->numberPotMeters = 2; + config->throttleSubType = ThrottleSubtype; + +#endif + + setConfiguration(config); } +/* + * Store the current configuration to EEPROM + */ void PotThrottle::saveConfiguration() { - Logger::info(POTACCELPEDAL, "Saving throttle settings"); - TickHandler::getInstance()->detach(this); // unregister from TickHandler first - setMinumumLevel1(throttleDetector->getThrottle1Min()); - setMaximumLevel1(throttleDetector->getThrottle1Max()); - setNumberPotMeters(throttleDetector->getPotentiometerCount()); - if ( getNumberPotMeters() > 1 ) { - setMinimumLevel2(throttleDetector->getThrottle2Min()); - setMaximumLevel2(throttleDetector->getThrottle2Max()); - } - else { - setMinimumLevel2(0); - setMaximumLevel2(0); - } - setSubtype(throttleDetector->getSubtype()); - saveEEPROM(); - - TickHandler::getInstance()->attach(this, getTickInterval()); + PotThrottleConfiguration *config = (PotThrottleConfiguration *) getConfiguration(); + + prefsHandler->write(EETH_MIN_ONE, config->minimumLevel1); + prefsHandler->write(EETH_MAX_ONE, config->maximumLevel1); + prefsHandler->write(EETH_MIN_TWO, config->minimumLevel2); + prefsHandler->write(EETH_MAX_TWO, config->maximumLevel2); + prefsHandler->write(EETH_REGEN_MIN, config->positionRegenMinimum); + prefsHandler->write(EETH_REGEN_MAX, config->positionRegenMaximum); + prefsHandler->write(EETH_FWD, config->positionForwardMotionStart); + prefsHandler->write(EETH_MAP, config->positionHalfPower); + prefsHandler->write(EETH_CREEP, config->creep); + prefsHandler->write(EETH_MIN_ACCEL_REGEN, config->minimumRegen); + prefsHandler->write(EETH_MAX_ACCEL_REGEN, config->maximumRegen); + prefsHandler->write(EETH_NUM_THROTTLES, config->numberPotMeters); + prefsHandler->write(EETH_THROTTLE_TYPE, config->throttleSubType); + prefsHandler->saveChecksum(); } - #endif //CFG_ENABLE_DEVICE_POT_THROTTLE diff --git a/PotThrottle.h b/PotThrottle.h index c466e66..ffb6e55 100644 --- a/PotThrottle.h +++ b/PotThrottle.h @@ -1,26 +1,26 @@ /* * PotThrottle.h * -Copyright (c) 2013 Collin Kidder, Michael Neuweiler, Charles Galpin + Copyright (c) 2013 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: + 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 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. + 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. */ @@ -32,11 +32,26 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "Throttle.h" #include "sys_io.h" #include "TickHandler.h" -#include "ThrottleDetector.h" #include "Logger.h" #define THROTTLE_INPUT_BRAKELIGHT 2 +/* + * The extended configuration class with additional parameters for PotThrottle + */ +class PotThrottleConfiguration: public ThrottleConfiguration { +public: + /* + * Allows subclasses to have sub types for their pedal type + * 0 - unknown type (prefs will return 0 if never set) + * 1 - standard linear potentiometer (low-high). If 2 pots, both are low-high and the 2nd mirrors the 1st. + * 2 - inverse potentiometer (high-low). If 2 pots, then 1st is low-high and 2nd is high-low) + */ + uint8_t throttleSubType; + uint16_t minimumLevel1, maximumLevel1, minimumLevel2, maximumLevel2; // values for when the pedal is at its min and max for each input + uint8_t numberPotMeters; // the number of potentiometers to be used. Should support three as well since some pedals really do have that many +}; + class PotThrottle: public Throttle { public: enum ThrottleStatus { @@ -49,26 +64,25 @@ class PotThrottle: public Throttle { ERR_MISC }; - void handleTick(); + PotThrottle(uint8_t throttle1, uint8_t throttle2); void setup(); + void handleTick(); + DeviceId getId(); bool isFaulted(); ThrottleStatus getStatus(); - uint16_t getRawThrottle1(); - uint16_t getRawThrottle2(); - void saveEEPROM(); + RawSignalData *acquireRawSignal(); + + void loadConfiguration(); void saveConfiguration(); - PotThrottle(uint8_t throttle1, uint8_t throttle2); - DeviceId getId(); +protected: + bool validateSignal(RawSignalData *); + uint16_t calculatePedalPosition(RawSignalData *); private: - uint16_t throttleAverage, throttleFeedback; //used to create proportional control - uint8_t throttle1ADC, throttle2ADC; //which ADC pin each are on - byte throttleMaxErr; + uint8_t throttle1AdcPin, throttle2AdcPin; //which ADC pin each are on ThrottleStatus throttleStatus; - - uint16_t calcThrottle(uint16_t, uint16_t, uint16_t); - void doAccel(); + RawSignalData rawSignal; }; #endif /* POT_THROTTLE_H_ */ diff --git a/SerialConsole.cpp b/SerialConsole.cpp index 048d33e..3f87885 100644 --- a/SerialConsole.cpp +++ b/SerialConsole.cpp @@ -61,10 +61,8 @@ void SerialConsole::printMenu() { //Show build # here as well in case people are using the native port and don't get to see the start up messages SerialUSB.print("Build number: "); SerialUSB.println(CFG_BUILD_NUM); - SerialUSB.println("Motor Controller Status: isRunning: " - + String(motorController->isRunning()) - + " isFaulted: " - + String(motorController->isFaulted())); + SerialUSB.println( + "Motor Controller Status: isRunning: " + String(motorController->isRunning()) + " isFaulted: " + String(motorController->isFaulted())); SerialUSB.println("System Menu:"); SerialUSB.println(); SerialUSB.println("Enable line endings of some sort (LF, CR, CRLF)"); @@ -79,65 +77,49 @@ void SerialConsole::printMenu() { SerialUSB.println("U,I = test EEPROM routines"); SerialUSB.println("A = dump system eeprom values"); SerialUSB.println("z = detect throttle min/max, num throttles and subtype"); - SerialUSB.println("Z = save detected throttle values"); + SerialUSB.println("Z = save throttle values"); SerialUSB.println("b = detect brake min/max"); - SerialUSB.println("B = Save detected brake values"); + SerialUSB.println("B = save brake values"); SerialUSB.println("p = enable wifi passthrough (reboot required to resume normal operation)"); SerialUSB.println(); SerialUSB.println("Config Commands (enter command=newvalue). Current values shown in parenthesis:"); - Logger::console("TORQ=%i - Set torque upper limit (tenths of a Nm)", - motorController->getTorqueMax()); - Logger::console("RPMS=%i - Set maximum RPMs", - motorController->getSpeedMax()); - Logger::console("REVLIM=%i - How much torque to allow in reverse (Tenths of a percent)", - motorController->getReversePercent()); + Logger::console("TORQ=%i - Set torque upper limit (tenths of a Nm)", motorController->getTorqueMax()); + Logger::console("RPMS=%i - Set maximum RPMs", motorController->getSpeedMax()); + Logger::console("REVLIM=%i - How much torque to allow in reverse (Tenths of a percent)", motorController->getReversePercent()); - if ( accelerator != NULL ) { - Logger::console("TPOT=%i - Number of pots to use (1 or 2)", - accelerator->getNumberPotMeters()); - Logger::console("TTYPE=%i - Set throttle subtype (1=std linear, 2=inverse)", - accelerator->getSubtype()); - Logger::console("T1MN=%i - Set throttle 1 min value", - accelerator->getMinimumLevel1()); - Logger::console("T1MX=%i - Set throttle 1 max value", - accelerator->getMaximumLevel1()); - Logger::console("T2MN=%i - Set throttle 2 min value", - accelerator->getMinimumLevel2()); - Logger::console("T2MX=%i - Set throttle 2 max value", - accelerator->getMaximumLevel2()); - Logger::console("TRGN=%i - Tenths of a percent of pedal where regen starts", - accelerator->getPositionRegenStart()); - Logger::console("TFWD=%i - Tenths of a percent of pedal where forward motion starts", - accelerator->getPositionForwardMotionStart()); - Logger::console("TMAP=%i - Tenths of a percent of pedal where 50% throttle will be", - accelerator->getPositionHalfPower()); - Logger::console("TMRN=%i - Percent of full torque to use for throttle regen", - accelerator->getMaximumRegen()); + if (accelerator && accelerator->getConfiguration()) { + PotThrottleConfiguration *config = (PotThrottleConfiguration *) accelerator->getConfiguration(); + Logger::console("TPOT=%i - Number of pots to use (1 or 2)", config->numberPotMeters); + Logger::console("TTYPE=%i - Set throttle subtype (1=std linear, 2=inverse)", config->throttleSubType); + Logger::console("T1MN=%i - Set throttle 1 min value", config->minimumLevel1); + Logger::console("T1MX=%i - Set throttle 1 max value", config->maximumLevel1); + Logger::console("T2MN=%i - Set throttle 2 min value", config->minimumLevel2); + Logger::console("T2MX=%i - Set throttle 2 max value", config->maximumLevel2); + Logger::console("TRGNMAX=%i - Tenths of a percent of pedal where regen is at max", config->positionRegenMaximum); + Logger::console("TRGNMIN=%i - Tenths of a percent of pedal where regen is at min", config->positionRegenMinimum); + Logger::console("TFWD=%i - Tenths of a percent of pedal where forward motion starts", config->positionForwardMotionStart); + Logger::console("TMAP=%i - Tenths of a percent of pedal where 50% throttle will be", config->positionHalfPower); + Logger::console("TMINRN=%i - Percent of full torque to use for min throttle regen", config->minimumRegen); + Logger::console("TMAXRN=%i - Percent of full torque to use for max throttle regen", config->maximumRegen); + Logger::console("TCREEP=%i - Percent of full torque to use for creep (0=disable)", config->creep); } - if ( brake != NULL ) { - Logger::console("B1MN=%i - Set brake min value", - brake->getMinimumLevel1()); - Logger::console("B1MX=%i - Set brake max value", - brake->getMaximumLevel1()); - Logger::console("BMINR=%i - Percent of full torque for start of brake regen", - brake->getMinimumRegen()); - Logger::console("BMAXR=%i - Percent of full torque for maximum brake regen", - brake->getMaximumRegen()); + if (brake && brake->getConfiguration()) { + PotThrottleConfiguration *config = (PotThrottleConfiguration *) brake->getConfiguration(); + Logger::console("B1MN=%i - Set brake min value", config->minimumLevel1); + Logger::console("B1MX=%i - Set brake max value", config->maximumLevel1); + Logger::console("BMINR=%i - Percent of full torque for start of brake regen", config->minimumRegen); + Logger::console("BMAXR=%i - Percent of full torque for maximum brake regen", config->maximumRegen); } - Logger::console("PREC=%i - Precharge capacitance (uf)", - DeviceManager::getInstance()->getMotorController()->getPrechargeC()); - Logger::console("PRER=%i - Precharge resistance (1/10 of ohm)", - DeviceManager::getInstance()->getMotorController()->getPrechargeR()); - Logger::console("NOMV=%i - Nominal system voltage (1/10 of a volt)", - DeviceManager::getInstance()->getMotorController()->getNominalV()); + Logger::console("PREC=%i - Precharge capacitance (uf)", DeviceManager::getInstance()->getMotorController()->getPrechargeC()); + Logger::console("PRER=%i - Precharge resistance (1/10 of ohm)", DeviceManager::getInstance()->getMotorController()->getPrechargeR()); + Logger::console("NOMV=%i - Nominal system voltage (1/10 of a volt)", DeviceManager::getInstance()->getMotorController()->getNominalV()); Logger::console("PRELAY=%i - Which output to use for precharge contactor (255 to disable)", DeviceManager::getInstance()->getMotorController()->getPrechargeRelay()); Logger::console("MRELAY=%i - Which output to use for main contactor (255 to disable)", DeviceManager::getInstance()->getMotorController()->getMainRelay()); - Logger::console("LOGLEVEL=%i - set log level (0=debug, 1=info, 2=warn, 3=error, 4=off)", - Logger::getLogLevel()); + Logger::console("LOGLEVEL=%i - set log level (0=debug, 1=info, 2=warn, 3=error, 4=off)", Logger::getLogLevel()); } /* There is a help menu (press H or h or ?) @@ -180,7 +162,8 @@ void SerialConsole::handleConsoleCmd() { comparison purposes. */ void SerialConsole::handleConfigCmd() { - + PotThrottleConfiguration *acceleratorConfig = NULL; + PotThrottleConfiguration *brakeConfig = NULL; Throttle *accelerator = DeviceManager::getInstance()->getAccelerator(); Throttle *brake = DeviceManager::getInstance()->getBrake(); @@ -200,6 +183,11 @@ void SerialConsole::handleConfigCmd() { if (i >= ptrBuffer) return; //or, we could use this to display the parameter instead of setting + if (accelerator) + acceleratorConfig = (PotThrottleConfiguration *) accelerator->getConfiguration(); + if (brake) + brakeConfig = (PotThrottleConfiguration *) brake->getConfiguration(); + cmdString.toUpperCase(); if (cmdString == String("TORQ")) { newValue = atoi((char *) (cmdBuffer + i)); @@ -217,128 +205,155 @@ void SerialConsole::handleConfigCmd() { DeviceManager::getInstance()->getMotorController()->setReversePercent(newValue); DeviceManager::getInstance()->getMotorController()->saveEEPROM(); } else if (cmdString == String("TPOT")) { - if ( accelerator != NULL ) { + if (accelerator) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting # of Throttle Pots to %i", newValue); - accelerator->setNumberPotMeters(newValue); - accelerator->saveEEPROM(); + acceleratorConfig->numberPotMeters = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } } else if (cmdString == String("TTYPE")) { - if ( accelerator != NULL ) { + if (accelerator != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Throttle Subtype to %i", newValue); - accelerator->setSubtype(newValue); - accelerator->saveEEPROM(); + acceleratorConfig->throttleSubType = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } } else if (cmdString == String("T1MN")) { - if ( accelerator != NULL ) { + if (accelerator != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Throttle1 Min to %i", newValue); - accelerator->setMinumumLevel1(newValue); - accelerator->saveEEPROM(); + acceleratorConfig->minimumLevel1 = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } } else if (cmdString == String("T1MX")) { - if ( accelerator != NULL ) { + if (accelerator != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Throttle1 Max to %i", newValue); - accelerator->setMaximumLevel1(newValue); - accelerator->saveEEPROM(); + acceleratorConfig->maximumLevel1 = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } } else if (cmdString == String("T2MN")) { - if ( accelerator != NULL ) { + if (accelerator != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Throttle2 Min to %i", newValue); - accelerator->setMinimumLevel2(newValue); - accelerator->saveEEPROM(); + acceleratorConfig->minimumLevel2 = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } } else if (cmdString == String("T2MX")) { - if ( accelerator != NULL ) { + if (accelerator != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Throttle2 Max to %i", newValue); - accelerator->setMaximumLevel2(newValue); - accelerator->saveEEPROM(); + acceleratorConfig->maximumLevel2 = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } - } else if (cmdString == String("TRGN")) { - if ( accelerator != NULL ) { + } else if (cmdString == String("TRGNMAX")) { + if (accelerator != NULL) { newValue = atoi((char *) (cmdBuffer + i)); - Logger::console("Setting Throttle Regen Start to %i", newValue); - accelerator->setPositionRegenStart(newValue); - accelerator->saveEEPROM(); + Logger::console("Setting Throttle Regen maximum to %i", newValue); + acceleratorConfig->positionRegenMaximum = newValue; + accelerator->saveConfiguration(); + } else { + Logger::console("No acclerator object available"); + } + } else if (cmdString == String("TRGNMIN")) { + if (accelerator != NULL) { + newValue = atoi((char *) (cmdBuffer + i)); + Logger::console("Setting Throttle Regen minimum to %i", newValue); + acceleratorConfig->positionRegenMinimum = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } } else if (cmdString == String("TFWD")) { - if ( accelerator != NULL ) { + if (accelerator != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Throttle Forward Start to %i", newValue); - accelerator->setPositionForwardMotionStart(newValue); - accelerator->saveEEPROM(); + acceleratorConfig->positionForwardMotionStart = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } } else if (cmdString == String("TMAP")) { - if ( accelerator != NULL ) { + if (accelerator != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Throttle MAP Point to %i", newValue); - accelerator->setPositionHalfPower(newValue); - accelerator->saveEEPROM(); + acceleratorConfig->positionHalfPower = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } - } else if (cmdString == String("TMRN")) { - if ( accelerator != NULL ) { + } else if (cmdString == String("TMINRN")) { + if (accelerator != NULL) { newValue = atoi((char *) (cmdBuffer + i)); - Logger::console("Setting Throttle Regen Strength to %i", newValue); - accelerator->setMaximumRegen(newValue); - accelerator->saveEEPROM(); + Logger::console("Setting Throttle Regen Minimum Strength to %i", newValue); + acceleratorConfig->minimumRegen = newValue; + accelerator->saveConfiguration(); + } else { + Logger::console("No acclerator object available"); + } + } else if (cmdString == String("TMAXRN")) { + if (accelerator != NULL) { + newValue = atoi((char *) (cmdBuffer + i)); + Logger::console("Setting Throttle Regen Maximum Strength to %i", newValue); + acceleratorConfig->maximumRegen = newValue; + accelerator->saveConfiguration(); + } else { + Logger::console("No acclerator object available"); + } + } else if (cmdString == String("TCREEP")) { + if (accelerator != NULL) { + newValue = atoi((char *) (cmdBuffer + i)); + Logger::console("Setting Throttle Creep Strength to %i", newValue); + acceleratorConfig->creep = newValue; + accelerator->saveConfiguration(); } else { Logger::console("No acclerator object available"); } } else if (cmdString == String("BMAXR")) { - if ( brake != NULL ) { + if (brake != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Max Brake Regen to %i", newValue); - brake->setMaximumRegen(newValue); - brake->saveEEPROM(); + brakeConfig->maximumRegen = newValue; + brake->saveConfiguration(); } else { Logger::console("No brake object available"); } } else if (cmdString == String("BMINR")) { - if ( brake != NULL ) { + if (brake != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Min Brake Regen to %i", newValue); - brake->setMinimumRegen(newValue); - brake->saveEEPROM(); + brakeConfig->minimumRegen = newValue; + brake->saveConfiguration(); } else { Logger::console("No brake object available"); } } else if (cmdString == String("B1MX")) { - if ( brake != NULL ) { + if (brake != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Brake Max to %i", newValue); - brake->setMaximumLevel1(newValue); - brake->saveEEPROM(); + brakeConfig->maximumLevel1 = newValue; + brake->saveConfiguration(); } else { Logger::console("No brake object available"); } } else if (cmdString == String("B1MN")) { - if ( brake != NULL ) { + if (brake != NULL) { newValue = atoi((char *) (cmdBuffer + i)); Logger::console("Setting Brake Min to %i", newValue); - brake->setMinumumLevel1(newValue); - brake->saveEEPROM(); + brakeConfig->minimumLevel1 = newValue; + brake->saveConfiguration(); } else { Logger::console("No brake object available"); } @@ -457,22 +472,24 @@ void SerialConsole::handleShortCmd() { Logger::console("all outputs: OFF"); break; case 'z': // detect throttle min/max & other details - if ( accelerator != NULL ) { - accelerator->detectThrottle(); + if (accelerator) { + ThrottleDetector *detector = new ThrottleDetector(accelerator); + detector->detect(); } break; case 'Z': // save throttle settings - if ( accelerator != NULL ) { + if (accelerator) { accelerator->saveConfiguration(); } break; case 'b': - if ( brake != NULL ) { - brake->detectThrottle(); + if (brake) { + ThrottleDetector *detector = new ThrottleDetector(brake); + detector->detect(); } break; case 'B': - if ( brake != NULL ) { + if (brake != NULL) { brake->saveConfiguration(); } break; diff --git a/SerialConsole.h b/SerialConsole.h index ea8a4a1..47ff7f7 100644 --- a/SerialConsole.h +++ b/SerialConsole.h @@ -32,10 +32,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "MemCache.h" #include "config.h" #include "sys_io.h" -#include "Throttle.h" +#include "PotThrottle.h" #include "DeviceManager.h" #include "MotorController.h" #include "DmocMotorController.h" //TODO: direct reference to dmoc must be removed +#include "ThrottleDetector.h" class SerialConsole { public: diff --git a/Throttle.cpp b/Throttle.cpp index 389c055..2505878 100644 --- a/Throttle.cpp +++ b/Throttle.cpp @@ -26,195 +26,123 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "Throttle.h" - + +/* + * Constructor + */ Throttle::Throttle() : Device() { prefsHandler = new PrefHandler(EE_THROTTLE_START); - throttleDetector = NULL; level = 0; } -Throttle::~Throttle() { - if ( throttleDetector != NULL ) { - delete throttleDetector; - throttleDetector = NULL; - } -} - +/* + * Controls the main flow of throttle data acquisiton, validation and mapping to + * user defined behaviour. + * + * Get's called by the sub-class which is triggered by the tick handler + */ void Throttle::handleTick() { Device::handleTick(); - if (throttleDetector) - throttleDetector->handleTick(); - - rawValues = acquireData(); // get the raw data from pot's or e.g. send a query to the ECU (can msg) but don't wait for response - if (validate(rawValues)) { // validate the raw data - throttlePosition = calculateThrottlePosition(rawValues); // bring the raw data into a range of 0-1000 (without mapping) - level = mapThrottle(throttlePosition); // apply mapping of the 0-1000 range to the user defined range - } else { + RawSignalData *rawSignals = acquireRawSignal(); // get raw data from the throttle device + if (validateSignal(rawSignals)) { // validate the raw data + uint16_t position = calculatePedalPosition(rawSignals); // bring the raw data into a range of 0-1000 (without mapping) + level = mapPedalPosition(position); // apply mapping of the 0-1000 range to the user defined settings + } else level = 0; - Logger::error("invaild input values, setting throttle to 0"); - } -} - -DeviceType Throttle::getType(){ - return DEVICE_THROTTLE; -} - -int16_t Throttle::getLevel(){ - return level; -} - -bool Throttle::isFaulted() { - return true; -} - -void Throttle::setNumberPotMeters(uint8_t num) { - numberPotMeters = constrain(num, 1, 2); // Currently only valid values are 1 and 2 -} - -uint8_t Throttle::getNumberPotMeters() { - return numberPotMeters; -} - -void Throttle::setSubtype(uint8_t num) { - throttleSubType = constrain(num, 1, 2); // Currently only valid values are 1 and 2 -} - -uint8_t Throttle::getSubtype() { - return throttleSubType; } /* - * Give default versions that return 0. Override in a child class if you implement the throttle - */ -uint16_t Throttle::getRawThrottle1() {return 0;} -uint16_t Throttle::getRawThrottle2() {return 0;} - -/* - * Return the tick interval for this throttle. Override in a child class - * if you use a different tick interval - */ -uint32_t Throttle::getTickInterval() { - return CFG_TICK_INTERVAL_POT_THROTTLE; -} - -/* - * Maps the input throttle position (0-1000 permille) to an output level which is calculated - * based on the throttle mapping parameters. + * Maps the input throttle position (0-1000 permille) to an output level which is + * calculated based on the throttle mapping parameters (free float, regen, acceleration, + * 50% acceleration). + * The output value will be in the range of -1000 to 1000. The value will be used by the + * MotorController class to calculate commanded torque or speed. Positive numbers result in + * acceleration, negative numbers in regeneration. 0 will result in no force applied by + * the motor. + * + * Configuration parameters: + * positionRegenMaximum: The pedal position (0-1000) where maximumRegen will be applied. If not 0, then + * moving the pedal from positionRegenMaximum to 0 will result in linear reduction + * of regen from maximumRegen. + * positionRegenMinimum: The pedal position (0-1000) where minimumRegen will be applied. If not 0, then + * a linear regen force will be applied when moving the pedal from positionRegenMinimum + * to positionRegenMaximum. + * positionForwardMotionStart: The pedal position where the car starts to accelerate. If not equal to + * positionRegenMinimum, then the gap between the two positions will result in no force + * applied (free coasting). + * positionHalfPower: Position of the pedal where 50% of the maximum torque will be applied. To gain more + * fine control in the lower speed range (e.g. when parking) it might make sense to + * set this position higher than the mid point of positionForwardMotionStart and full + * throttle. + * + * Important pre-condition (to be checked when changing parameters) : + * 0 <= positionRegenMaximum <= positionRegenMinimum <= positionForwardMotionStart <= positionHalfPower */ -void Throttle::mapThrottle(int16_t currentPosition) { - int16_t range, value; - - level = 0; //by default we give zero throttle - - if (currentPosition > 0) { - if (positionRegenStart != 0) { - if (currentPosition <= positionRegenStart) { - range = positionRegenStart; - value = range - currentPosition; - level = -10 * maximumRegen * value / range; - } +int16_t Throttle::mapPedalPosition(int16_t pedalPosition) { + int16_t throttleLevel, range, value; + ThrottleConfiguration *config = (ThrottleConfiguration *)getConfiguration(); + + throttleLevel = 0; + + if (pedalPosition <= config->positionRegenMinimum) { + if (pedalPosition >= config->positionRegenMaximum) { + range = config->positionRegenMinimum - config->positionRegenMaximum; + value = pedalPosition - config->positionRegenMinimum; + if (range != 0) // prevent div by zero, should result in 0 throttle if min==max + throttleLevel = -10 * config->minimumRegen + value / range * (config->maximumRegen - config->minimumRegen) * 10; + } else { + range = config->positionRegenMaximum; + value = pedalPosition; + throttleLevel = -10 * config->maximumRegen * value / range; } + } - if (currentPosition >= positionForwardMotionStart) { - if (currentPosition <= positionHalfPower) { //bottom 50% forward - range = positionHalfPower - positionForwardMotionStart; - value = currentPosition - positionForwardMotionStart; - level = 500 * value / range; - } - else { //more than throttleMap - range = 1000 - positionHalfPower; - value = currentPosition - positionHalfPower; - level = 500 + 500 * value / range; - } + if (pedalPosition >= config->positionForwardMotionStart) { + if (pedalPosition <= config->positionHalfPower) { + range = config->positionHalfPower - config->positionForwardMotionStart; + value = pedalPosition - config->positionForwardMotionStart; + if (range != 0) // prevent div by zero, should result in 0 throttle if half==startFwd + throttleLevel = 500 * value / range; + } else { + range = 1000 - config->positionHalfPower; + value = pedalPosition - config->positionHalfPower; + throttleLevel = 500 + 500 * value / range; } } - else { - level = -10 * maximumRegen; - } - //Logger::debug("throttle level: %d", level); + //Logger::debug("throttle level: %d", throttleLevel); + return throttleLevel; } -void Throttle::detectThrottle() { - if ( throttleDetector == NULL ) - throttleDetector = new ThrottleDetector(this); - throttleDetector->detect(); -} - -void Throttle::setMinumumLevel1(uint16_t min) { - minimumLevel1 = min; -} - -uint16_t Throttle::getMinimumLevel1() { - return minimumLevel1; -} - -void Throttle::setMinimumLevel2(uint16_t min) { - minimumLevel2 = min; -} - -uint16_t Throttle::getMinimumLevel2() { - return minimumLevel2; -} - -void Throttle::setMaximumLevel1(uint16_t max) { - maximumLevel1 = max; -} - -uint16_t Throttle::getMaximumLevel1() { - return maximumLevel1; -} - -void Throttle::setMaximumLevel2(uint16_t max) { - maximumLevel2 = max; -} - -uint16_t Throttle::getMaximumLevel2() { - return maximumLevel2; -} - -void Throttle::setPositionRegenStart(uint16_t regen) { - positionRegenStart = regen; -} - -uint16_t Throttle::getPositionRegenStart() { - return positionRegenStart; -} - -void Throttle::setPositionForwardMotionStart(uint16_t fwd) { - positionForwardMotionStart = fwd; -} - -uint16_t Throttle::getPositionForwardMotionStart() { - return positionForwardMotionStart; -} - -void Throttle::setPositionHalfPower(uint16_t map) { - positionHalfPower = map; -} - -uint16_t Throttle::getPositionHalfPower() { - return positionHalfPower; -} - -void Throttle::setMaximumRegen(uint16_t regen) { - maximumRegen = regen; +/* + * Returns the currently calculated/mapped throttle level (from -1000 to 1000). + */ +int16_t Throttle::getLevel(){ + return level; } -uint16_t Throttle::getMaximumRegen() { - return maximumRegen; +/* + * Is the throttle faulted? + */ +bool Throttle::isFaulted() { + return true; } -void Throttle::setMinimumRegen(uint16_t regen) { - minimumRegen = regen; +/* + * Return the device type + */ +DeviceType Throttle::getType(){ + return DEVICE_THROTTLE; } -uint16_t Throttle::getMinimumRegen() { - return minimumRegen; +RawSignalData* Throttle::acquireRawSignal() { + return NULL; } -void Throttle::saveConfiguration() { +bool Throttle::validateSignal(RawSignalData*) { + return false; } -void Throttle::saveEEPROM() { +uint16_t Throttle::calculatePedalPosition(RawSignalData*) { + return 0; } diff --git a/Throttle.h b/Throttle.h index b8eb902..6449722 100644 --- a/Throttle.h +++ b/Throttle.h @@ -35,76 +35,51 @@ #include #include "config.h" #include "Device.h" -#include "ThrottleDetector.h" -class ThrottleDetector; -// avoid circular dependency by declaring it here +/* + * Data structure to hold raw signal(s) of the throttle. + * E.g. for a three pot pedal, all signals could be used. + */ +struct RawSignalData { + int32_t input1; // e.g. pot #1 or the signal from a can bus throttle + int32_t input2; // e.g. pot #2 (optional) + int32_t input3; // e.g. pot #3 (optional) +}; +/* + * A abstract class to hold throttle configuration parameters. + * Can be extended by the subclass. + */ +class ThrottleConfiguration: public DeviceConfiguration { +public: + uint16_t positionRegenMaximum, positionRegenMinimum; // throttle position where regen is highest and lowest + uint16_t positionForwardMotionStart, positionHalfPower; // throttle position where forward motion starts and the mid point of throttle + uint8_t maximumRegen; // percentage of max torque allowable for regen at maximum level + uint8_t minimumRegen; // percentage of max torque allowable for regen at minimum level + uint8_t creep; // percentage of torque used for creep function (imitate creep of automatic transmission, set 0 to disable) +}; + +/* + * Abstract class for all throttle implementations. + */ class Throttle: public Device { public: Throttle(); - ~Throttle(); - + virtual int16_t getLevel(); + virtual void handleTick(); + virtual bool isFaulted(); virtual DeviceType getType(); - virtual void handleTick(); - virtual int16_t getLevel(); - virtual uint16_t getRawThrottle1(); - virtual uint16_t getRawThrottle2(); - - bool isFaulted(); - void detectThrottle(); - void detectThrottleMin(); - void detectThrottleMax(); - - virtual void mapThrottle(int16_t); - virtual void saveConfiguration(); - virtual void saveEEPROM(); - - virtual void setPositionRegenStart(uint16_t regen); - virtual uint16_t getPositionRegenStart(); - virtual void setPositionForwardMotionStart(uint16_t fwd); - virtual uint16_t getPositionForwardMotionStart(); - virtual void setPositionHalfPower(uint16_t map); - virtual uint16_t getPositionHalfPower(); - virtual void setMaximumRegen(uint16_t regen); - virtual uint16_t getMaximumRegen(); - virtual void setMinimumRegen(uint16_t regen); - virtual uint16_t getMinimumRegen(); - virtual void setMinumumLevel1(uint16_t min); - virtual uint16_t getMinimumLevel1(); - virtual void setMinimumLevel2(uint16_t min); - virtual uint16_t getMinimumLevel2(); - virtual void setMaximumLevel1(uint16_t max); - virtual uint16_t getMaximumLevel1(); - virtual void setMaximumLevel2(uint16_t max); - virtual uint16_t getMaximumLevel2(); - virtual void setNumberPotMeters(uint8_t num); - virtual uint8_t getNumberPotMeters(); - virtual void setSubtype(uint8_t num); - virtual uint8_t getSubtype(); + virtual RawSignalData *acquireRawSignal(); protected: - int16_t level; // the final signed throttle level. [-1000, 1000] in permille of maximum - uint16_t positionRegenStart, positionForwardMotionStart, positionHalfPower; // value at which regen starts, forward motion starts, and the mid point of throttle - uint8_t maximumRegen; // percentage of max torque allowable for regen - uint8_t minimumRegen; // percentage of min torque allowable for regen - uint16_t minimumLevel1, maximumLevel1, minimumLevel2, maximumLevel2; // values for when the pedal is at its min and max for each input - uint16_t rawLevel1, rawLevel2; // the raw level of the input potentiometers - uint8_t numberPotMeters; // the number of potentiometers to be used. Should support three as well since some pedals really do have that many - - /* - * Allows subclasses to have sub types for their pedal type - * 0 - unknown type (prefs will return 0 if never set) - * 1 - standard linear potentiometer (low-high). If 2 pots, both are low-high and the 2nd mirrors the 1st. - * 2 - inverse potentiometer (high-low). If 2 pots, then 1st is low-high and 2nd is high-low) - */ - uint8_t throttleSubType; - uint32_t getTickInterval(); - ThrottleDetector *throttleDetector; + virtual bool validateSignal(RawSignalData *); + virtual uint16_t calculatePedalPosition(RawSignalData *); + virtual int16_t mapPedalPosition(int16_t); private: - + int16_t level; // the final signed throttle level. [-1000, 1000] in permille of maximum + }; #endif diff --git a/ThrottleDetector.cpp b/ThrottleDetector.cpp index 2bb0605..36351b2 100644 --- a/ThrottleDetector.cpp +++ b/ThrottleDetector.cpp @@ -35,6 +35,7 @@ */ ThrottleDetector::ThrottleDetector(Throttle *throttle) { this->throttle = throttle; + config = (PotThrottleConfiguration *) throttle->getConfiguration(); state = DoNothing; maxThrottleReadingDeviationPercent = 50; // 5% in 0-1000 scale Logger::debug("ThrottleDetector constructed with throttle %d", throttle); @@ -92,6 +93,8 @@ void ThrottleDetector::detect() { // we wait for 2 seconds so kick this off startTime = millis(); state = DetectMinWait; + + TickHandler::getInstance()->attach(this, CFG_TICK_INTERVAL_POT_THROTTLE); } /* @@ -275,8 +278,22 @@ void ThrottleDetector::detectMaxCalibrate() { SerialUSB.println("========================================"); + // update the throttle's configuration (without storing it yet) + config->minimumLevel1 = getThrottle1Min(); + config->maximumLevel1 = getThrottle1Max(); + config->numberPotMeters = getPotentiometerCount(); + if (config->numberPotMeters > 1) { + config->minimumLevel2 = getThrottle2Min(); + config->maximumLevel2 = getThrottle2Max(); + } else { + config->minimumLevel2 = 0; + config->maximumLevel2 = 0; + } + config->throttleSubType = getSubtype(); + // Done! state = DoNothing; + TickHandler::getInstance()->detach(this); } } @@ -351,7 +368,7 @@ bool ThrottleDetector::isThrottle2Inverse() { * Returns true if a second throttle was provided */ bool ThrottleDetector::throttle2Provided() { - return throttle->getNumberPotMeters() > 1; + return config->numberPotMeters > 1; } /* @@ -376,12 +393,9 @@ void ThrottleDetector::resetValues() { * Reads values from the throttles. */ void ThrottleDetector::readThrottleValues() { - throttle1Value = throttle->getRawThrottle1(); - if (throttle2Provided()) { - throttle2Value = throttle->getRawThrottle2(); - } - throttle1Values[sampleCount] = throttle1Value; - throttle2Values[sampleCount] = throttle2Value; + RawSignalData *rawSignal = throttle->acquireRawSignal(); + throttle1Values[sampleCount] = rawSignal->input1; + throttle2Values[sampleCount] = rawSignal->input2; sampleCount++; // record the minimum sensor value diff --git a/ThrottleDetector.h b/ThrottleDetector.h index c353b10..49d9b68 100644 --- a/ThrottleDetector.h +++ b/ThrottleDetector.h @@ -31,12 +31,12 @@ #ifndef THROTTLE_DETECTOR_H_ #define THROTTLE_DETECTOR_H_ -#include "Throttle.h" +#include "PotThrottle.h" #include "Logger.h" class Throttle; -class ThrottleDetector { +class ThrottleDetector : public Device { public: ThrottleDetector(Throttle *throttle); @@ -74,6 +74,7 @@ class ThrottleDetector { int checkInverse(uint16_t, uint16_t); uint16_t normalize(uint16_t sensorValue, uint16_t sensorMin, uint16_t sensorMax, uint16_t constrainMin, uint16_t constrainMax); Throttle *throttle; + PotThrottleConfiguration *config; DetectionState state; unsigned long startTime; int potentiometerCount; diff --git a/config.h b/config.h index a3b9b41..b876a59 100644 --- a/config.h +++ b/config.h @@ -58,7 +58,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define CFG_ENABLE_DEVICE_POT_BRAKE #define CFG_ENABLE_DEVICE_MOTORCTRL_DMOC_645 //#define CFG_ENABLE_DEVICE_MOTORCTRL_BRUSA_DMC5 -//#define CFG_ENABLE_DEVICE_ICHIP2128_WIFI +#define CFG_ENABLE_DEVICE_ICHIP2128_WIFI //#define CFG_ENABLE_DEVICE_BMS_THINK @@ -98,16 +98,19 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* * HARD CODED PARAMETERS * - * If USE_HARD_CODED is defined, the parameter values defined here are used - * instead of those stored in the EEPROM. + * If USE_HARD_CODED is defined or the checksum of the parameters stored in EEPROM, + * the parameter values defined here are used instead of those stored in the EEPROM. */ //#define USE_HARD_CODED #define ThrottleNumPots 1 //# of pots to use by default #define ThrottleSubtype 1 //subtype 1 is a standard linear pot throttle -#define ThrottleRegenValue 0 //where does Regen stop (1/10 of percent) +#define ThrottleRegenMinValue 0 //where does Regen stop (1/10 of percent) +#define ThrottleRegenMaxValue 0 //where Regen is at maximum (1/10 of percent) #define ThrottleFwdValue 175 //where does forward motion start #define ThrottleMapValue 665 //Where is the 1/2 way point for throttle -#define ThrottleMaxRegenValue 00 //how many percent of full regen to do with accel pedal +#define ThrottleMinRegenValue 0 //how many percent of full power to use at minimal regen +#define ThrottleMaxRegenValue 0 //how many percent of full power to use at maximum regen +#define ThrottleCreepValue 0 //how many percent of full power to use at creep #define ThrottleMaxErrValue 75 //tenths of percentage allowable deviation between pedals #define Throttle1MinValue 180 //Value ADC reads when pedal is up #define Throttle1MaxValue 930 //Value ADC reads when pedal fully depressed @@ -120,7 +123,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define MaxTorqueValue 2000 //in tenths of a Nm -#define MaxRPMValue 6000 //DMOC will ignore this but we can use it ourselves for limiting +#define MaxRPMValue 6000 //DMOC will ignore this but we can use it ourselves for limiting #define PrechargeC 11000 //approximate C of DMOC input - in uF #define PrechargeR 500 //a stupidly high resistance just to make sure we precharge long enough #define NominalVolt 3300 //a reasonable figure for a lithium cell pack driving the DMOC (in tenths of a volt) diff --git a/eeprom_layout.h b/eeprom_layout.h index 3f6d82d..a549e6f 100644 --- a/eeprom_layout.h +++ b/eeprom_layout.h @@ -89,7 +89,7 @@ the end of the stardard data. The below numbers are offsets from the device's ee #define EETH_MAX_ONE 22 //2 bytes - ADC value of maximum value for first channel #define EETH_MIN_TWO 24 //2 bytes - ADC value of minimum value for second channel #define EETH_MAX_TWO 26 //2 bytes - ADC value of maximum value for second channel -#define EETH_REGEN 28 //2 bytes - unsigned int - tenths of a percent (0-1000) of pedal position where regen stops +#define EETH_REGEN_MIN 28 //2 bytes - unsigned int - tenths of a percent (0-1000) of pedal position where regen stops #define EETH_FWD 30 //2 bytes - unsigned int - tenths of a percent (0-1000) of pedal position where forward motion starts #define EETH_MAP 32 //2 bytes - unsigned int - tenths of a percent (0-1000) of pedal position where forward motion is at 50% throttle #define EETH_BRAKE_MIN 34 //2 bytes - ADC value of minimum value for brake input @@ -98,7 +98,10 @@ the end of the stardard data. The below numbers are offsets from the device's ee #define EETH_MAX_BRAKE_REGEN 40 //2 bytes - maximum percentage of throttle to command for braking regen. Starts at min brake regen and works up to here. #define EETH_NUM_THROTTLES 42 //1 byte - How many throttle inputs should we use? (1 or 2) #define EETH_THROTTLE_TYPE 43 //1 byte - Allow for different throttle types. For now 1 = Linear pots, 2 = Inverse relationship between pots. See Throttle.h -#define EETH_MIN_BRAKE_REGEN 44 //2 bytes - the starting point for brake regen as a percentage of throttle +#define EETH_MIN_BRAKE_REGEN 44 //2 bytes - the starting level for brake regen as a percentage of throttle +#define EETH_MIN_ACCEL_REGEN 46 //2 bytes - the starting level for accelerator regen as a percentage of throttle +#define EETH_REGEN_MAX 48 //2 bytes - unsigned int - tenths of a percent (0-1000) of pedal position where regen is at maximum +#define EETH_CREEP 48 //2 bytes - percentage of throttle used to simulate creep //System Data #define EESYS_SYSTEM_TYPE 10 //1 byte - 10 = Macchina/MCP2515, 20 = Arduino Due w/ dual shield diff --git a/ichip_2128.cpp b/ichip_2128.cpp index b3dc6d3..07f771e 100644 --- a/ichip_2128.cpp +++ b/ichip_2128.cpp @@ -239,6 +239,13 @@ void ICHIPWIFI::setParam(String paramName, uint16_t value) { setParam(paramName, buffer); } +/* + * Set a parameter to the given unit16 value + */ +void ICHIPWIFI::setParam(String paramName, uint8_t value) { + sprintf(buffer, "%d", value); + setParam(paramName, buffer); +} /* * Set a parameter to the given float value */ @@ -302,6 +309,16 @@ void ICHIPWIFI::loop() { * by looking for the '=' sign and the leading/trailing '"' have to be ignored. */ void ICHIPWIFI::processParameterChange(char *key) { + PotThrottleConfiguration *acceleratorConfig; + PotThrottleConfiguration *brakeConfig; + Throttle *accelerator = DeviceManager::getInstance()->getAccelerator(); + Throttle *brake = DeviceManager::getInstance()->getBrake(); + + if (accelerator) + acceleratorConfig = (PotThrottleConfiguration *)accelerator->getConfiguration(); + if (brake) + brakeConfig = (PotThrottleConfiguration *)brake->getConfiguration(); + char *value = strchr(key, '='); if (value) { value[0] = 0; // replace the '=' sign with a 0 @@ -311,48 +328,56 @@ void ICHIPWIFI::processParameterChange(char *key) { if (value[strlen(value) - 1] == '"') value[strlen(value) - 1] = 0; // if the value ends with a '"' character, replace it with 0 - if (!strcmp(key, "numThrottlePots")) { - DeviceManager::getInstance()->getAccelerator()->setNumberPotMeters(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "throttleSubType")) { - DeviceManager::getInstance()->getAccelerator()->setSubtype(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "throttleMin1")) { - DeviceManager::getInstance()->getAccelerator()->setMinumumLevel1(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "throttleMin2")) { - DeviceManager::getInstance()->getAccelerator()->setMinimumLevel2(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "throttleMax1")) { - DeviceManager::getInstance()->getAccelerator()->setMaximumLevel1(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "throttleMax2")) { - DeviceManager::getInstance()->getAccelerator()->setMaximumLevel2(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "throttleRegen")) { - DeviceManager::getInstance()->getAccelerator()->setPositionRegenStart(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "throttleFwd")) { - DeviceManager::getInstance()->getAccelerator()->setPositionForwardMotionStart(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "throttleMap")) { - DeviceManager::getInstance()->getAccelerator()->setPositionHalfPower(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "throttleMaxRegen")) { - DeviceManager::getInstance()->getAccelerator()->setMaximumRegen(atol(value)); - DeviceManager::getInstance()->getAccelerator()->saveEEPROM(); - } else if (!strcmp(key, "brakeMin")) { - DeviceManager::getInstance()->getBrake()->setMinumumLevel1(atol(value)); - DeviceManager::getInstance()->getBrake()->saveEEPROM(); - } else if (!strcmp(key, "brakeMax")) { - DeviceManager::getInstance()->getBrake()->setMaximumLevel1(atol(value)); - DeviceManager::getInstance()->getBrake()->saveEEPROM(); - } else if (!strcmp(key, "brakeMinRegen")) { - DeviceManager::getInstance()->getBrake()->setMinimumRegen(atol(value)); - DeviceManager::getInstance()->getBrake()->saveEEPROM(); - } else if (!strcmp(key, "brakeMaxRegen")) { - DeviceManager::getInstance()->getBrake()->setMaximumRegen(atol(value)); - DeviceManager::getInstance()->getBrake()->saveEEPROM(); + if (!strcmp(key, "numThrottlePots") && acceleratorConfig) { + acceleratorConfig->numberPotMeters = atol(value); + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleSubType") && acceleratorConfig) { + acceleratorConfig->throttleSubType = atol(value); + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleMin1") && acceleratorConfig) { + acceleratorConfig->minimumLevel1 = atol(value); + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleMin2") && acceleratorConfig) { + acceleratorConfig->minimumLevel2 = atol(value); + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleMax1") && acceleratorConfig) { + acceleratorConfig->maximumLevel1 = atol(value); + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleMax2") && acceleratorConfig) { + acceleratorConfig->maximumLevel2 = atol(value); + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleRegenMax") && acceleratorConfig) { + acceleratorConfig->positionRegenMaximum = atol(value) * 10; + } else if (!strcmp(key, "throttleRegenMin") && acceleratorConfig) { + acceleratorConfig->positionRegenMinimum = atol(value) * 10; + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleFwd") && acceleratorConfig) { + acceleratorConfig->positionForwardMotionStart = atol(value) * 10; + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleMap") && acceleratorConfig) { + acceleratorConfig->positionHalfPower = atol(value) * 10; + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleMinRegen") && acceleratorConfig) { + acceleratorConfig->minimumRegen = atol(value); + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleMaxRegen") && acceleratorConfig) { + acceleratorConfig->maximumRegen = atol(value); + accelerator->saveConfiguration(); + } else if (!strcmp(key, "throttleCreep") && acceleratorConfig) { + acceleratorConfig->creep = atol(value); + accelerator->saveConfiguration(); + } else if (!strcmp(key, "brakeMin") && brakeConfig) { + brakeConfig->minimumLevel1 = atol(value); + brake->saveConfiguration(); + } else if (!strcmp(key, "brakeMax") && brakeConfig) { + brakeConfig->maximumLevel1 = atol(value); + brake->saveConfiguration(); + } else if (!strcmp(key, "brakeMinRegen") && brakeConfig) { + brakeConfig->minimumRegen = atol(value); + brake->saveConfiguration(); + } else if (!strcmp(key, "brakeMaxRegen") && brakeConfig) { + brakeConfig->maximumRegen = atol(value); + brake->saveConfiguration(); } else if (!strcmp(key, "speedMax")) { DeviceManager::getInstance()->getMotorController()->setSpeedMax(atol(value)); DeviceManager::getInstance()->getMotorController()->saveEEPROM(); @@ -372,21 +397,31 @@ void ICHIPWIFI::loadParameters() { MotorController *motorController = DeviceManager::getInstance()->getMotorController(); Throttle *accelerator = DeviceManager::getInstance()->getAccelerator(); Throttle *brake = DeviceManager::getInstance()->getBrake(); - - setParam("numThrottlePots", (uint16_t)accelerator->getNumberPotMeters()); - setParam("throttleSubType", (uint16_t)accelerator->getSubtype()); - setParam("throttleMin1", accelerator->getMinimumLevel1()); - setParam("throttleMin2", accelerator->getMinimumLevel2()); - setParam("throttleMax1", accelerator->getMaximumLevel1()); - setParam("throttleMax2", accelerator->getMaximumLevel2()); - setParam("throttleRegen", accelerator->getPositionRegenStart()); - setParam("throttleFwd", accelerator->getPositionForwardMotionStart()); - setParam("throttleMap", accelerator->getPositionHalfPower()); - setParam("throttleMaxRegen", accelerator->getMaximumRegen()); - setParam("brakeMin", brake->getMinimumLevel1()); - setParam("brakeMax", brake->getMaximumLevel1()); - setParam("brakeMinRegen", brake->getMinimumRegen()); - setParam("brakeMaxRegen", brake->getMaximumRegen()); + PotThrottleConfiguration *acceleratorConfig; + PotThrottleConfiguration *brakeConfig; + + if (accelerator) + acceleratorConfig = (PotThrottleConfiguration *)accelerator->getConfiguration(); + if (brake) + brakeConfig = (PotThrottleConfiguration *)brake->getConfiguration(); + + setParam("numThrottlePots", acceleratorConfig->numberPotMeters); + setParam("throttleSubType", acceleratorConfig->throttleSubType); + setParam("throttleMin1", acceleratorConfig->minimumLevel1); + setParam("throttleMin2", acceleratorConfig->minimumLevel2); + setParam("throttleMax1", acceleratorConfig->maximumLevel1); + setParam("throttleMax2", acceleratorConfig->maximumLevel2); + setParam("throttleRegenMax", (uint16_t)(acceleratorConfig->positionRegenMaximum / 10)); + setParam("throttleRegenMin", (uint16_t)(acceleratorConfig->positionRegenMinimum / 10)); + setParam("throttleFwd", (uint16_t)(acceleratorConfig->positionForwardMotionStart / 10)); + setParam("throttleMap", (uint16_t)(acceleratorConfig->positionHalfPower / 10)); + setParam("throttleMinRegen", acceleratorConfig->minimumRegen); + setParam("throttleMaxRegen", acceleratorConfig->maximumRegen); + setParam("throttleCreep", acceleratorConfig->creep); + setParam("brakeMin", brakeConfig->minimumLevel1); + setParam("brakeMax", brakeConfig->maximumLevel1); + setParam("brakeMinRegen", brakeConfig->minimumRegen); + setParam("brakeMaxRegen", brakeConfig->maximumRegen); setParam("speedMax", motorController->getSpeedMax()); setParam("torqueMax", (uint16_t)(motorController->getTorqueMax() / 10)); // skip the tenth's } diff --git a/ichip_2128.h b/ichip_2128.h index 24858a0..185ffab 100644 --- a/ichip_2128.h +++ b/ichip_2128.h @@ -79,6 +79,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include "config.h" #include "DeviceManager.h" +#include "PotThrottle.h" #include "Sys_Messages.h" class ICHIPWIFI : public Device { @@ -113,6 +114,7 @@ class ICHIPWIFI : public Device { void setParam(String paramName, int16_t value); void setParam(String paramName, uint32_t value); void setParam(String paramName, uint16_t value); + void setParam(String paramName, uint8_t value); void setParam(String paramName, float value, int precision); void sendCmd(String cmd); void processParameterChange(char *response); diff --git a/website/src/config.htm b/website/src/config.htm index 50ccefc..fe12e8f 100644 --- a/website/src/config.htm +++ b/website/src/config.htm @@ -18,30 +18,44 @@ - Minimum Level Throttle Pot1 + Minimum Signal Level 1 - Pedal Position Regen Start - ‰ + Pedal Position Regen Maximum + % - Maximum Level Throttle Pot1 + Maximum Signal Level 1 - Pedal Position Forward Motion Start - ‰ + Pedal Position Regen Minimum + % - Minimum Level Throttle Pot2 + Minimum Signal Level 2 - Pedal Position 50% throttle - ‰ + Pedal Position Forward Motion Start + % - Maximum Level Throttle Pot2 + Maximum Signal Level 2 + Pedal Position 50% throttle + % + + + Creep Level + % + + Minimum Throttle Regen + % + + + + + Maximum Throttle Regen % @@ -49,14 +63,14 @@

Brake

- Minimum Level Brake + Minimum Signal Level Minimum Brake Regen % - Maximum Level Brake + Maximum Signal Level Maximum Brake Regen diff --git a/website/src/config.xml b/website/src/config.xml index 5ed2ada..9d83b99 100644 --- a/website/src/config.xml +++ b/website/src/config.xml @@ -6,10 +6,13 @@ ~throttleMin2~ ~throttleMax1~ ~throttleMax2~ - ~throttleRegen~ + ~throttleRegenMax~ + ~throttleRegenMin~ ~throttleFwd~ ~throttleMap~ + ~throttleMinRegen~ ~throttleMaxRegen~ + ~throttleCreep~ ~brakeMin~ ~brakeMax~ ~brakeMinRegen~ diff --git a/website/src/control.js b/website/src/control.js index a1eb126..2edfdff 100644 --- a/website/src/control.js +++ b/website/src/control.js @@ -125,12 +125,19 @@ function updateRangeValue(id, source) { var min = getIntValue("throttleMin2"); if (val < min) val = min; - } else if (id == 'throttleRegen') { + } else if (id == 'throttleRegenMax') { + var regen = getIntValue("throttleRegenMin"); + if (val > regen) + val = regen; + } else if (id == 'throttleRegenMin') { + var regen = getIntValue("throttleRegenMax"); var fwd = getIntValue("throttleFwd"); + if (val < regen) + val = regen; if (val > fwd) val = fwd; } else if (id == 'throttleFwd') { - var regen = getIntValue("throttleRegen"); + var regen = getIntValue("throttleRegenMin"); var map = getIntValue("throttleMap"); if (val < regen) val = regen; @@ -140,6 +147,14 @@ function updateRangeValue(id, source) { var map = getIntValue("throttleFwd"); if (val < map) val = map; + } else if (id == 'throttleMinRegen') { + var regen = getIntValue("throttleMaxRegen"); + if (val > regen) + val = regen; + } else if (id == 'throttleMaxRegen') { + var regen = getIntValue("throttleMinRegen"); + if (val < regen) + val = regen; } else if (id == 'brakeMin') { var max = getIntValue("brakeMax"); if (val > max) @@ -150,11 +165,8 @@ function updateRangeValue(id, source) { val = min; } else if (id == 'brakeMinRegen') { var max = getIntValue("brakeMaxRegen"); - var throttleMax = getIntValue("throttleMaxRegen"); if (val > max) val = max; - if (val < throttleMax) - val = throttleMax; } else if (id == 'brakeMaxRegen') { var min = getIntValue("brakeMinRegen"); if (val < min) @@ -177,10 +189,13 @@ function generateRangeControls() { addRangeControl("throttleMin2", 0, 4095); addRangeControl("throttleMax1", 0, 4095); addRangeControl("throttleMax2", 0, 4095); - addRangeControl("throttleRegen", 0, 1000); - addRangeControl("throttleFwd", 0, 1000); - addRangeControl("throttleMap", 0, 1000); + addRangeControl("throttleRegenMax", 0, 100); + addRangeControl("throttleRegenMin", 0, 100); + addRangeControl("throttleFwd", 0, 100); + addRangeControl("throttleMap", 0, 100); + addRangeControl("throttleMinRegen", 0, 100); addRangeControl("throttleMaxRegen", 0, 100); + addRangeControl("throttleCreep", 0, 100); addRangeControl("brakeMin", 0, 4095); addRangeControl("brakeMinRegen", 0, 100); addRangeControl("brakeMax", 0, 4095); diff --git a/website/website.img b/website/website.img index 78c0dcbfbb6aa927e54f53539a3bf5c935292ff6..00ea256e2d51363b8079357c891f53552377dcde 100644 GIT binary patch delta 1156 zcma))O-vI}5XWb?;bZ#&6`D}d&;=DJseAuGM3Wj}7rMpOvOq1+6BzVh zJQ!gPMiK)D(Svv}dsHtbUOdQwn5gljHzSD`MM6xRZnw0Bi<|uN-uu7#&zm>Yt#!bUEYlgL7+bqEO@Z~~)lCM@fz{2O1Du9;v4IKdCbtVL`Hu7cw zz+=A{0~k`yCICJ(?9KtWq0Pi(k{Y9xalmoHLwJc^VvunxfJl>RVv)Ei`Q;LAZ!IqKWVly~F?!CPYFeW{5>%iFiajC0-COiMOo90h=`WM0{KN z8z~P;Nl09cPbLyE`GPzyt3d@t=0vTc>Z|=dipv*z;<7xY-DRY9cB@?5_ExjDL1kQ( zVtk)GE5~@`eK(58rA*9BV70HgIGtTeBHw*_+0T{aMYrH;mlJ;q77X~kHa(ONDr&}D zlr}5q>TawMj14|7L#vU2LzmErN&h))>~jP~p4a0#}4EsxQ%2%RuD)8KzTMA{U!o9%5JW*Ti+O#mQwaLC~j6`h_ z=JUHQCFgxj2PMH~=6w00q8@ZA>LI6sJ3-lF{K9NJ-9hhTPJBkfP$=NZoTT48YtxrG zo^)gGgm&5cmD;WTpxs3GK|ySEQ??p+Cl$>pQNDhb-h#l#Q;*7>(zul1*9F`dIjOgl zTt~=lxMrm|ADxTf%~2=rjFjPHu?{Vw#L}C*F8;P>>f0|`5Z(Ap=4$`zOck#TR~P6F zpkm$FId&AEh?QEuRKwr{sY5FpJI0z*#d!E;58r~K%$FMPPR-HGy>pT5(u1S2YdKnr ZQxOg~Nzj7_1<{KywV}a}fgdyT=gl8x>MZ~H zCs%2Y9Z10Z;-CQp;TTNw7MS=p%>(R7HktsIeweWVRNnNt0fMuiwgV)m3S9sb{o^qJ z$-XuVkjPm_0A4jcDgpT5JI~chYdhxzJ7HuKX(hWjX9W^;NRa|LNiL8p@em)`M%qaaIYhFgNKTV;fs$-T3O4pM7B7r>v@gEhjmPbON)DTwWwoXSYAE%rguawh)Ot6qFrG= zZ%gwQIfU;!#g)0C<3sEpC9%jsysUJX7iJkU#k~CQ(PoTCJh)#8VKy46n-^XG>8?KT zVj$Xt_jcT|{w{1Qsz)`-U3h-yjXyJEciVj{XTuI%GB)p3IJYvU&wqICcVpo|{T*jw z8s_6coQ;3wueyvU`?~Fm*8ABhe45&dES+u;{UL13c=2}n1-{ix#u?4R*~)5>)oQxc zQGA(l;oZzCym)xUMj Date: Sun, 13 Oct 2013 01:19:30 +0200 Subject: [PATCH 4/4] added creep --- PotBrake.h | 2 +- Throttle.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/PotBrake.h b/PotBrake.h index 1fc8322..0612086 100644 --- a/PotBrake.h +++ b/PotBrake.h @@ -29,7 +29,7 @@ #include #include "config.h" -#include "Throttle.h" +#include "PotThrottle.h" #include "sys_io.h" #include "TickHandler.h" #include "Logger.h" diff --git a/Throttle.cpp b/Throttle.cpp index 2505878..a38e0b3 100644 --- a/Throttle.cpp +++ b/Throttle.cpp @@ -85,7 +85,9 @@ int16_t Throttle::mapPedalPosition(int16_t pedalPosition) { throttleLevel = 0; - if (pedalPosition <= config->positionRegenMinimum) { + if (pedalPosition == 0 && config->creep > 0) { + throttleLevel = 10 * config->creep; + } else if (pedalPosition <= config->positionRegenMinimum) { if (pedalPosition >= config->positionRegenMaximum) { range = config->positionRegenMinimum - config->positionRegenMaximum; value = pedalPosition - config->positionRegenMinimum;