diff --git a/Makefile b/Makefile index c0fff9d4..bfd26350 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ OBJSL = $(BINARY).o hwinit.o stm32scheduler.o params.o terminal.o terminal_prj. chademo.o amperaheater.o amperacharger.o subaruvehicle.o iomatrix.o bmw_sbox.o NissanPDM.o teslaCharger.o extCharger.o vag_sbox.o \ daisychainbms.o simpbms.o outlanderCharger.o Can_OBD2.o cansdo.o TeslaDCDC.o BMW_E31.o F30_Lever.o \ CPC.o ElconCharger.o RearOutlanderinverter.o linbus.o VWheater.o JLR_G1.o JLR_G2.o Focci.o digipot.o\ - E65_Lever.o leafbms.o V_Classic.o + OutlanderHeartBeat.o E65_Lever.o leafbms.o V_Classic.o kangoobms.o OutlanderCanHeater.o OBJS = $(patsubst %.o,$(OUT_DIR)/%.o, $(OBJSL)) vpath %.c src/ libopeninv/src/ diff --git a/include/BMW_E65.h b/include/BMW_E65.h index bef1d438..2db060be 100644 --- a/include/BMW_E65.h +++ b/include/BMW_E65.h @@ -25,11 +25,12 @@ class BMW_E65: public Vehicle bool Start() { return terminal15On; } void DashOff(); void handle130(uint32_t data[2]); - void handle192(uint32_t data[2]); + void handle1A0(uint32_t data[2]); void handle2FC(uint32_t data[2]); void handle480(uint32_t data[2]); void SetE90(bool e90) { isE90 = e90; } void Engine_Data(); + void SetFuelGauge(float level); private: void SendAbsDscMessages(bool Brake_In); diff --git a/include/OutlanderCanHeater.h b/include/OutlanderCanHeater.h new file mode 100644 index 00000000..13326928 --- /dev/null +++ b/include/OutlanderCanHeater.h @@ -0,0 +1,44 @@ +/* + * This file is part of the Zombieverter VCU project. + * + * Copyright (C) 2018 Johannes Huebner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef OUTLANDERCANHEATER_H +#define OUTLANDERCANHEATER_H + +//#include +#include + + +class OutlanderCanHeater : public Heater +{ + public: + void SetTargetTemperature(float temp); + void SetCanInterface(CanHardware* c); + void DecodeCAN(int id, uint32_t data[2]); + void SetPower(uint16_t power, bool HeatReq); + void Task100Ms(); + + private: + int8_t currentTemperature; + int8_t desiredTemperature; + + bool shouldHeat; + static void handle398(uint32_t data[2]); + +}; + +#endif // OUTLANDERCANHEATER_H diff --git a/include/OutlanderHeartBeat.h b/include/OutlanderHeartBeat.h new file mode 100644 index 00000000..910bce48 --- /dev/null +++ b/include/OutlanderHeartBeat.h @@ -0,0 +1,43 @@ +/* + * This file is part of the ZombieVerter project. + * + * Copyright (C) 2021-2023 Johannes Huebner + * Damien Maguire + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *Control of the Mitsubishi Outlander PHEV on board charger (OBC) and DCDC Converter. + * + */ +#ifndef OUTLANDERHEARTBEAT_H +#define OUTLANDERHEARTBEAT_H + +#include +#include "params.h" +#include "canhardware.h" + +class OutlanderHeartBeat +{ + +public: +static void Task100Ms(); +static void SetCanInterface(CanHardware* c); +static void SetPullInEVSE(bool pullInEVSE); + + +protected: + +}; + +#endif // OUTLANDERHEARTBEAT_H diff --git a/include/chargerint.h b/include/chargerint.h index 51bb9847..8849c085 100644 --- a/include/chargerint.h +++ b/include/chargerint.h @@ -35,7 +35,6 @@ class Chargerint virtual void DeInit() {} //called when switching to another charger, similar to a destructor virtual void SetCanInterface(CanHardware* c) { can = c; } - protected: CanHardware* can; }; diff --git a/include/heater.h b/include/heater.h index 4b7c9458..ef3dc912 100644 --- a/include/heater.h +++ b/include/heater.h @@ -18,6 +18,7 @@ class Heater virtual void SetPower(uint16_t power, bool HeatReq) = 0; //Must be called cyclically with power in watts virtual void DeInit() {} //called when switching to another heater, similar to a destructor virtual void SetCanInterface(CanHardware* c) { can = c; } + virtual void Task100Ms() {}; protected: CanHardware* can; diff --git a/include/kangoobms.h b/include/kangoobms.h new file mode 100644 index 00000000..056d00a7 --- /dev/null +++ b/include/kangoobms.h @@ -0,0 +1,51 @@ +/* + * This file is part of the ZombieVerter project. + * + * Copyright (C) 2022 Charlie Smurthwaite + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KANGOOBMS_H +#define KANGOOBMS_H +#include + +class KangooBMS: public BMS +{ + public: + virtual void SetCanInterface(CanHardware* c); + void DecodeCAN(int id, uint8_t * data); + float MaxChargeCurrent(); + float GetCurrent(); + void Task100Ms(); + private: + bool BMSDataValid(); + bool ChargeAllowed(); + int messageCounter = 0; + int chargeCurrentLimit = 0; + int timeoutCounter = 0; + uint16_t maxChargeAllowed = 0; + uint8_t maxInput = 0; + uint8_t maxOutput = 0; + uint16_t isolationResistance = 0; + float minCellV = 0; + float maxCellV = 0; + float minTempC = 0; + float maxTempC = 0; + float stateOfCharge = 0; + float current = 0; + float remainingKHW = 0; + float batteryVoltage = 500; //higher than possible so cannot complete precharge until BMS reports battery voltage +}; +#endif // SIMPBMS_H diff --git a/include/param_prj.h b/include/param_prj.h index c1083ec1..893ef9a7 100644 --- a/include/param_prj.h +++ b/include/param_prj.h @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#define VER 2.20.TK +#define VER 2.20.TN /* Entries must be ordered as follows: @@ -25,7 +25,7 @@ 2. Temporary parameters (id = 0) 3. Display values */ -//Next param id (increase when adding new parameter!): 138 +//Next param id (increase when adding new parameter!): 139 /* category name unit min max default id */ #define PARAM_LIST \ PARAM_ENTRY(CAT_SETUP, Inverter, INVMODES, 0, 8, 0, 5 ) \ @@ -34,7 +34,7 @@ PARAM_ENTRY(CAT_SETUP, Transmission, TRNMODES, 0, 1, 0, 78 ) \ PARAM_ENTRY(CAT_SETUP, interface, CHGINT, 0, 4, 0, 39 ) \ PARAM_ENTRY(CAT_SETUP, chargemodes, CHGMODS, 0, 6, 0, 37 ) \ - PARAM_ENTRY(CAT_SETUP, BMS_Mode, BMSMODES, 0, 4, 0, 90 ) \ + PARAM_ENTRY(CAT_SETUP, BMS_Mode, BMSMODES, 0, 5, 0, 90 ) \ PARAM_ENTRY(CAT_SETUP, ShuntType, SHNTYPE, 0, 3, 0, 88 ) \ PARAM_ENTRY(CAT_SETUP, InverterCan, CAN_DEV, 0, 1, 0, 70 ) \ PARAM_ENTRY(CAT_SETUP, VehicleCan, CAN_DEV, 0, 1, 1, 71 ) \ @@ -45,6 +45,7 @@ PARAM_ENTRY(CAT_SETUP, OBD2Can, CAN_DEV, 0, 1, 0, 96 ) \ PARAM_ENTRY(CAT_SETUP, CanMapCan, CAN_DEV, 0, 1, 0, 97 ) \ PARAM_ENTRY(CAT_SETUP, DCDCCan, CAN_DEV, 0, 1, 1, 107 ) \ + PARAM_ENTRY(CAT_SETUP, HeaterCan, CAN_DEV, 0, 1, 1, 138 ) \ PARAM_ENTRY(CAT_SETUP, MotActive, MotorsAct, 0, 2, 0, 129 ) \ PARAM_ENTRY(CAT_THROTTLE, potmin, "dig", 0, 4095, 0, 7 ) \ PARAM_ENTRY(CAT_THROTTLE, potmax, "dig", 0, 4095, 4095, 8 ) \ @@ -102,7 +103,7 @@ PARAM_ENTRY(CAT_BMS, BMS_VmaxLimit, "V", 0, 10, 4.2, 93 ) \ PARAM_ENTRY(CAT_BMS, BMS_TminLimit, "°C", -100, 100, 5, 94 ) \ PARAM_ENTRY(CAT_BMS, BMS_TmaxLimit, "°C", -100, 100, 50, 95 ) \ - PARAM_ENTRY(CAT_HEATER, Heater, HTTYPE, 0, 2, 0, 57 ) \ + PARAM_ENTRY(CAT_HEATER, Heater, HTTYPE, 0, 3, 0, 57 ) \ PARAM_ENTRY(CAT_HEATER, Control, HTCTRL, 0, 2, 0, 58 ) \ PARAM_ENTRY(CAT_HEATER, HeatPwr, "W", 0, 6500, 0, 59 ) \ PARAM_ENTRY(CAT_HEATER, HeatPercnt, "%", 0, 100, 0, 124 ) \ @@ -165,6 +166,10 @@ VALUE_ENTRY(BMS_Tmin, "°C", 2086 ) \ VALUE_ENTRY(BMS_Tmax, "°C", 2087 ) \ VALUE_ENTRY(BMS_ChargeLim, "A", 2088 ) \ + VALUE_ENTRY(BMS_MaxInput, "kW", 2105 ) \ + VALUE_ENTRY(BMS_MaxOutput, "kW", 2106 ) \ + VALUE_ENTRY(BMS_MaxCharge, "W", 2101 ) \ + VALUE_ENTRY(BMS_Isolation, "Ohm", 2104 ) \ VALUE_ENTRY(BMS_IsoMeas, "mV", 2099 ) \ VALUE_ENTRY(speed, "rpm", 2016 ) \ VALUE_ENTRY(Veh_Speed, "kph", 2017 ) \ @@ -257,7 +262,7 @@ #define INVMODES "0=None, 1=Leaf_Gen1, 2=GS450H, 3=UserCAN, 4=OpenI, 5=Prius_Gen3, 6=Outlander, 7=GS300H, 8=RearOutlander" #define PLTMODES "0=Absent, 1=ACStd, 2=ACchg, 3=Error, 4=CCS_Not_Rdy, 5=CCS_Rdy, 6=Static" #define VEHMODES "0=BMW_E46, 1=BMW_E6x+, 2=Classic, 3=None, 5=BMW_E39, 6=VAG, 7=Subaru, 8=BMW_E31" -#define BMSMODES "0=Off, 1=SimpBMS, 2=TiDaisychainSingle, 3=TiDaisychainDual, 4=LeafBms" +#define BMSMODES "0=Off, 1=SimpBMS, 2=TiDaisychainSingle, 3=TiDaisychainDual, 4=LeafBms, 5=RenaultKangoo33" #define OPMODES "0=Off, 1=Run, 2=Precharge, 3=PchFail, 4=Charge" #define DOW "0=Sun, 1=Mon, 2=Tue, 3=Wed, 4=Thu, 5=Fri, 6=Sat" #define CHGTYPS "0=Off, 1=AC, 2=DCFC" @@ -274,7 +279,7 @@ #define ERRLIGHTS "0=Off, 4=EPC, 8=engine" #define CRUISESTATES "0=None, 1=On, 2=Disable, 4=Set, 8=Resume" #define CDMSTAT "1=Charging, 2=Malfunction, 4=ConnLock, 8=BatIncomp, 16=SystemMalfunction, 32=Stop" -#define HTTYPE "0=None, 1=Ampera, 2=VW" +#define HTTYPE "0=None, 1=Ampera, 2=VW, 3=OutlanderCan" #define HTCTRL "0=Disable, 1=Enable, 2=Timer" #define CHGMODS "0=Off, 1=EXT_DIGI, 2=Volt_Ampera, 3=Leaf_PDM, 4=TeslaOI, 5=Out_lander, 6=Elcon" #define CHGCTRL "0=Enable, 1=Disable, 2=Timer" @@ -382,7 +387,8 @@ enum HeatType { Noheater = 0, AmpHeater = 1, - VW = 2 + VW = 2, + OutlanderHeater = 3 }; enum BMSModes @@ -391,7 +397,8 @@ enum BMSModes BMSModeSimpBMS = 1, BMSModeDaisychainSingleBMS = 2, BMSModeDaisychainDualBMS = 3, - BMSModeLeafBMS = 4 + BMSModeLeafBMS = 4, + BMSRenaultKangoo33BMS = 5 }; enum DCDCModes diff --git a/include/stm32_vcu.h b/include/stm32_vcu.h index bf7387ab..8d8a4ad9 100644 --- a/include/stm32_vcu.h +++ b/include/stm32_vcu.h @@ -1,4 +1,3 @@ -/* /* * This file is part of the stm32-vcu project. * @@ -96,6 +95,9 @@ #include "rearoutlanderinverter.h" #include "NoVehicle.h" #include "V_Classic.h" +#include "kangoobms.h" +#include "OutlanderCanHeater.h" +#include "OutlanderHeartBeat.h" #define PRECHARGE_TIMEOUT 5 //5s diff --git a/include/utils.h b/include/utils.h index 2129337c..bf67d866 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,25 +1,10 @@ #ifndef UTILS_H #define UTILS_H - -#include "my_fp.h" -#include "my_math.h" -#include "errormessage.h" -#include "params.h" -#include "digio.h" -#include -#include "canhardware.h" -#include "anain.h" -#include "throttle.h" -#include "isa_shunt.h" -#include "bmw_sbox.h" -#include "vag_sbox.h" #include "vehicle.h" #include "shifter.h" -#include -#include "iomatrix.h" -#include "hwinit.h" - +#include "canhardware.h" +#include "errormessage.h" namespace utils { diff --git a/src/BMW_E65.cpp b/src/BMW_E65.cpp index 1eca43b7..2fb58f7c 100644 --- a/src/BMW_E65.cpp +++ b/src/BMW_E65.cpp @@ -1,6 +1,7 @@ #include #include "stm32_can.h" #include "params.h" +#include "utils.h" uint8_t Gcount; //gear display counter byte uint8_t shiftPos=0xe1; //contains byte to display gear position on dash.default to park @@ -15,7 +16,7 @@ uint8_t A91=0x00;//0x0A9 second counter byte uint8_t BA5=0x4d;//0x0BA first counter byte(byte 5) uint8_t BA6=0x80;//0x0BA second counter byte(byte 6) uint8_t AA1=0x00;//0x0AA First counter byte - +uint8_t engineLights = 0; void BMW_E65::SetCanInterface(CanHardware* c) { @@ -24,6 +25,7 @@ void BMW_E65::SetCanInterface(CanHardware* c) can->RegisterUserMessage(0x130);//E65 CAS can->RegisterUserMessage(0x2FC);//E90 Enclosure status can->RegisterUserMessage(0x480);//Network Management + can->RegisterUserMessage(0x1A0);//Speed } ///////////////////////////////////////////////////////////////////////////////////////////////////// ///////Handle incomming pt can messages from the car here @@ -37,6 +39,10 @@ void BMW_E65::DecodeCAN(int id, uint32_t* data) BMW_E65::handle130(data); break; + case 0x1A0: + BMW_E65::handle1A0(data); + break; + case 0x2FC: BMW_E65::handle2FC(data); break; @@ -101,6 +107,14 @@ void BMW_E65::handle130(uint32_t data[2]) } } +void BMW_E65::handle1A0(uint32_t data[2]) +{ + uint8_t* bytes = (uint8_t*)data; + + float kph = (bytes[0] + uint16_t((bytes[1]&0x0F)<<8)) * 0.1; + Param::SetFloat(Param::Veh_Speed, kph * 0.621371f); +} + void BMW_E65::handle2FC(uint32_t data[2]) { uint8_t* bytes = (uint8_t*)data; @@ -110,11 +124,10 @@ void BMW_E65::handle2FC(uint32_t data[2]) } else if (bytes[0] == 0x81)//Unlocked { - Param::SetInt(Param::VehLockSt,0); + Param::SetInt(Param::VehLockSt,0); } } - void BMW_E65::handle480(uint32_t data[2]) { uint8_t* bytes = (uint8_t*)data; @@ -161,9 +174,11 @@ void BMW_E65::Task100Ms() void BMW_E65::Task200Ms() { + uint8_t bytes[8]; + if(CANWake) { - //update shitPos + //update shitPos over CAN int selectedDir = Param::GetInt(Param::dir); if (selectedDir == 0) @@ -187,8 +202,6 @@ void BMW_E65::Task200Ms() gear_BA = 0x08; shiftPos = 0x78; } - - uint8_t bytes[8]; /////////////////////////////////////////////////////////////////////////////////////////////////// bytes[0]=shiftPos; //e1=P 78=D d2=R b4=N bytes[1]=0x0c; @@ -207,6 +220,33 @@ void BMW_E65::Task200Ms() { Gcount=0x0D; } + + //ERROR lights over CAN//////// + uint8_t errorLightsParam = Param::GetInt(Param::errlights); + if (engineLights != errorLightsParam) + { + bytes[0]=0x40; + bytes[1]=0x22; + bytes[2]=0x00; + bytes[4]=0xFF; + bytes[5]=0xFF; + bytes[6]=0xFF; + bytes[7]=0xFF; + + if (errorLightsParam == 0) + { + bytes[3]=0x30; + } + else if (errorLightsParam == 8) + { + bytes[3]=0x31; + } + engineLights = errorLightsParam; + + can->Send(0x592,bytes,8); //Send on CAN2 + } + + SetFuelGauge(Param::GetFloat(Param::SOC)); } } @@ -337,8 +377,9 @@ void BMW_E65::Engine_Data() if (Param::GetInt(Param::opmode) == MOD_RUN) { EngRun = 0x60; - bytes[4] = 0x9C; - bytes[5] = 0x9E; + uint16_t injectors = 40604; + bytes[4] = injectors; + bytes[5] = injectors >> 8; } else { @@ -346,15 +387,24 @@ void BMW_E65::Engine_Data() bytes[5] = 0x00; } + float Curr = Param::GetFloat(Param::idc) * -1; + Curr = Curr + 100; + float Temp = utils::change(Curr, 0, 500, 50, 150); + Temp = Temp + 48; + if (Curr > 525) + { + Temp = 150; + } + bytes[0] = 0x3C; //Engine Coolant Temp - bytes[1] = 0xFF; //Engine Oil Temp + bytes[1] = Temp; //Engine Oil Temp, use to show current bytes[2] = EngRun | C1D00; //Counter bytes[3] = 0xC3; bytes[6] = 0xCD; bytes[7] = 0x82; //Idle Traget - can->Send(0x1D0,bytes,8); //Send on CAN2 + can->Send(0x1D0,bytes,8); //Send on CAN if (C1D00 == C1D01) { @@ -371,3 +421,43 @@ void BMW_E65::Engine_Data() } +void BMW_E65::SetFuelGauge(float level) +{ + int pot1 = 0; + int pot2 = 0; + const int fuelGaugeMap[20][3] = + { + { 5, 1, 0 }, + { 10, 1, 1 }, + { 15, 2, 1 }, + { 20, 2, 2 }, + { 25, 3, 2 }, + { 30, 4, 3 }, + { 35, 4, 4 }, + { 40, 5, 4 }, + { 45, 5, 5 }, + { 50, 6, 6 }, + { 55, 7, 6 }, + { 60, 8, 7 }, + { 65, 8, 8 }, + { 70, 9, 9 }, + { 75, 10, 10 }, + { 80, 11, 11 }, + { 85, 12, 12 }, + { 90, 14, 14 }, + { 95, 17, 16 }, + { 100, 19, 19 } + }; + + for(int i = 0; i < 20; i++) + { + if (level >= fuelGaugeMap[i][0]) + { + pot1 = fuelGaugeMap[i][1]; + pot2 = fuelGaugeMap[i][2]; + } + } + + Param::SetInt(Param::DigiPot1Step, pot1); + Param::SetInt(Param::DigiPot2Step, pot2); +} diff --git a/src/OutlanderCanHeater.cpp b/src/OutlanderCanHeater.cpp new file mode 100644 index 00000000..e8ba3eb8 --- /dev/null +++ b/src/OutlanderCanHeater.cpp @@ -0,0 +1,105 @@ +/* + * This file is part of the Zombieverter VCU project. + * + * Copyright (C) 2018 Johannes Huebner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "OutlanderHeartBeat.h" + +void OutlanderCanHeater::SetPower(uint16_t power, bool HeatReq) +{ + shouldHeat = HeatReq; + power = power;//mask warning +} + +void OutlanderCanHeater::SetCanInterface(CanHardware* c) +{ + OutlanderHeartBeat::SetCanInterface(c);//set Outlander Heartbeat on same CAN + + can = c; + can->RegisterUserMessage(0x398); +} + +void OutlanderCanHeater::Task100Ms() +{ + if (shouldHeat) + { + uint8_t bytes[8]; + + bytes[0] = 0x03; + bytes[1] = 0x50; + bytes[2] = 0x00; + bytes[3] = 0x4D; + bytes[4] = 0x00; + bytes[5] = 0x00; + bytes[6] = 0x00; + bytes[7] = 0x00; + + if (currentTemperature < desiredTemperature - 5) + { + bytes[2] = 0xA2; + Param::SetInt(Param::powerheater, 3000); + } + else + { + bytes[2] = 0x32; + Param::SetInt(Param::powerheater, 1500); + } + + can->Send(0x188, (uint32_t*)bytes, 8); + } +} + +void OutlanderCanHeater::SetTargetTemperature(float temp) +{ + desiredTemperature = temp; +} + +void OutlanderCanHeater::DecodeCAN(int id, uint32_t data[2]) +{ + switch (id) + { + case 0x398: + OutlanderCanHeater::handle398(data); + break; + } +} + +void OutlanderCanHeater::handle398(uint32_t data[2]) +{ + uint8_t* bytes = (uint8_t*)data;// arrgghhh this converts the two 32bit array into bytes. See comments are useful:) + unsigned int temp1 = bytes[3] - 40; + unsigned int temp2 = bytes[4] - 40; + if (temp2 > temp1) + { + Param::SetInt(Param::tmpheater, temp2); + } + else + { + Param::SetInt(Param::tmpheater, temp1); + } + + if (bytes[6] == 0x09) + { + Param::SetInt(Param::udcheater, 0); + } + else + { + Param::SetInt(Param::udcheater, Param::GetInt(Param::udc)); + } + +} diff --git a/src/OutlanderHeartBeat.cpp b/src/OutlanderHeartBeat.cpp new file mode 100644 index 00000000..dbd247cc --- /dev/null +++ b/src/OutlanderHeartBeat.cpp @@ -0,0 +1,94 @@ +/* + * This file is part of the ZombieVerter project. + * + * Copyright (C) 2021-2023 Johannes Huebner + * Damien Maguire + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *Control of the Mitsubishi Outlander PHEV on board charger (OBC) and DCDC Converter. + * + */ + + +#include + +bool EnableEVSE = false; +bool CanConfigure = false; +bool DualCan = false; + +CanHardware* can1; +CanHardware* can2; + +void OutlanderHeartBeat::SetCanInterface(CanHardware* c) +{ + if(CanConfigure == false) + { + can1 = c; + CanConfigure = true; + } + else if(can1 != c) + { + can2 = c; + DualCan = true; + } +} + +void OutlanderHeartBeat::Task100Ms() +{ + int opmode = Param::GetInt(Param::opmode); + + uint8_t bytes[8]; + bytes[0] = 0x00; + bytes[1] = 0x00; + bytes[2] = 0x00; + bytes[3] = 0x00; + bytes[4] = 0x00; + bytes[5] = 0x00; + bytes[6] = 0x00; + bytes[7] = 0x00; + + if (MOD_CHARGE == opmode) + { + if (EnableEVSE) + { + bytes[2] = 0xB6;//oxb6 in byte 3 enables charger + } + } + else if (MOD_RUN == opmode) + { + + bytes[2] = 0x14; + bytes[3] = 0x39; + bytes[4] = 0x8F; + bytes[5] = 0xFE; + bytes[6] = 0xC; + bytes[7] = 0x10; + } + + if (MOD_CHARGE == opmode || MOD_RUN == opmode) + { + can1->Send(0x285, (uint32_t*)bytes, 8); + + if(DualCan) + { + can2->Send(0x285, (uint32_t*)bytes, 8); + } + } +} + +void OutlanderHeartBeat::SetPullInEVSE(bool pullInEVSE) +{ + EnableEVSE = pullInEVSE; +} diff --git a/src/RearOutlanderinverter.cpp b/src/RearOutlanderinverter.cpp index e6cce3c6..ffdd9123 100644 --- a/src/RearOutlanderinverter.cpp +++ b/src/RearOutlanderinverter.cpp @@ -20,6 +20,7 @@ #include "rearoutlanderinverter.h" #include "my_math.h" #include "params.h" +#include "OutlanderHeartBeat.h" RearOutlanderInverter::RearOutlanderInverter() { @@ -28,6 +29,8 @@ RearOutlanderInverter::RearOutlanderInverter() void RearOutlanderInverter::SetCanInterface(CanHardware* c) { + OutlanderHeartBeat::SetCanInterface(c);//set Outlander Heartbeat on same CAN + can = c; can->RegisterUserMessage(0x289);//Outlander Inv Msg @@ -104,9 +107,11 @@ void RearOutlanderInverter::Task100Ms() can->Send(0x371, data, 8); + /* data[0] = 0x39140000; // inverter enabled B2 0x10 - 0x1F can->Send(0x285, data, 8); + */ data[0] = 0x3D000000; data[1] = 0x00210000; diff --git a/src/V_Classic.cpp b/src/V_Classic.cpp index 0b089067..78b9b02d 100644 --- a/src/V_Classic.cpp +++ b/src/V_Classic.cpp @@ -20,6 +20,7 @@ #include "hwinit.h" #include #include +#include "my_math.h" //We use this as an init function diff --git a/src/kangoobms.cpp b/src/kangoobms.cpp new file mode 100644 index 00000000..812e8410 --- /dev/null +++ b/src/kangoobms.cpp @@ -0,0 +1,152 @@ +/* + * This file is part of the ZombieVerter project. + * + * Copyright (C) 2022 Charlie Smurthwaite + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "stm32_vcu.h" +#include "stm32_can.h" +#include +/* + * This module receives messages from SimpBMS and updates the + * BMS_MinV, BMS_MaxV, BMS_MinT and BMS_MaxT parameters with the + * received values. It also implements a timeout to indicate whether + * the BMS is actively sending data or not. This data can be + * used to safely stop any charging process if the BMS is not + * working correctly. + */ + +void KangooBMS::SetCanInterface(CanHardware* c) +{ + can = c; + can->RegisterUserMessage(0x155); + can->RegisterUserMessage(0x424); + can->RegisterUserMessage(0x425); + can->RegisterUserMessage(0x7BB); + +} + +bool KangooBMS::BMSDataValid() { + // Return false if primary BMS is not sending data. + if(timeoutCounter < 1) return false; + return true; +} + +float KangooBMS::GetCurrent() +{ + return current; +} + +// Return the maximum charge current allowed by the BMS. +float KangooBMS::MaxChargeCurrent() +{ + // if(!ChargeAllowed()) return 0; + // return chargeCurrentLimit / 1000.0; + return maxChargeAllowed / batteryVoltage; +} + +// Process voltage and temperature message from SimpBMS. +void KangooBMS::DecodeCAN(int id, uint8_t *data) +{ + if (id == 0x155) + { + // Reset timeout counter to the full timeout value + timeoutCounter = Param::GetInt(Param::BMS_Timeout) * 10; + stateOfCharge = (float) ((data[4] << 8) + data[5]) * 0.0025; + //maxCharging = data[0] * 300; + + int16_t rawCurrent = ((data[1] << 8) + data[2]) & 0xFFF; + rawCurrent = (float)(rawCurrent * 0.25); + rawCurrent = rawCurrent - 500; + current = rawCurrent; + + batteryVoltage = (float) ((data[6] << 8) + data[7]) / 2; + + maxChargeAllowed = data[0] * 300; + + } else if (id == 0x424) { + + minTempC = (uint8_t)(data[4]) - 40; + maxTempC = (uint8_t)(data[7]) - 40; + + maxInput = data[2] * 0.5; + maxOutput = data[3] * 0.5; + + } else if (id == 0x425) { + minCellV = (((float)(((data[6] & 0x01) << 8) + data[7]) + 100) * 10); + maxCellV = ((float)(((data[4] & 0x03) << 7) + ((data[5] >> 1) + 100)) * 10); + remainingKHW = (float)(data[1] * 0.1); + + isolationResistance = data[4] >> 2; + isolationResistance = isolationResistance + (data[3] << 6); + } +} + +void KangooBMS::Task100Ms() { + // Decrement timeout counter. + if(timeoutCounter > 0) timeoutCounter--; + + if(Param::GetInt(Param::opmode) != MOD_OFF) { + //send can message + uint8_t bytes[8]; + bytes[0]=0x07; + bytes[1]=0x1D; + bytes[2]=0x00; + bytes[3]=0x02; + bytes[4]=0xB2; + bytes[5]=0x80; + bytes[6]=0xB2; + bytes[7]=0xD8; + + if(Param::GetInt(Param::opmode) == MOD_CHARGE) { + bytes[7]=0xD9; + } + + if (messageCounter >=5 ) { + bytes[4]=0x5D; + bytes[6]=0x5D; + } + can->Send(0x423, (uint32_t*)bytes, 8); + + messageCounter++; + if (messageCounter > 10) { + messageCounter = 0; + } + } + + Param::SetFloat(Param::BMS_Vmin, minCellV); + Param::SetFloat(Param::BMS_Vmax, maxCellV); + Param::SetFloat(Param::BMS_Tmin, minTempC); + Param::SetFloat(Param::BMS_Tmax, maxTempC); + Param::SetFloat(Param::KWh, remainingKHW); + Param::SetFloat(Param::SOC, stateOfCharge); + Param::SetFloat(Param::udcsw, batteryVoltage - 30); + Param::SetInt(Param::BMS_MaxCharge, maxChargeAllowed); + Param::SetInt(Param::BMS_MaxInput, maxInput); + Param::SetInt(Param::BMS_MaxOutput, maxOutput); + Param::SetInt(Param::BMS_Isolation, isolationResistance); + Param::SetInt(Param::BMS_ChargeLim, MaxChargeCurrent()); + + //On the Kangoo charging is positive current, discharge is negative + if (BMSDataValid()) { + Param::SetFloat(Param::idc, current); + } else { + Param::SetFloat(Param::idc, 0); + } + + + +} diff --git a/src/leafbms.cpp b/src/leafbms.cpp index 026e4f58..5ab595b4 100644 --- a/src/leafbms.cpp +++ b/src/leafbms.cpp @@ -83,6 +83,9 @@ void LeafBMS::DecodeCAN(int id, uint8_t * data) //Param::SetFixed(Param::dislim, dislimit / 4); Param::SetFloat(Param::BMS_ChargeLim, chargelimit); + + Param::SetInt(Param::BMS_MaxInput, chglimit); + Param::SetInt(Param::BMS_MaxOutput, dislimit); } else if (id == 0x55B) { diff --git a/src/outlanderCharger.cpp b/src/outlanderCharger.cpp index fa562c43..0526dd5b 100644 --- a/src/outlanderCharger.cpp +++ b/src/outlanderCharger.cpp @@ -23,6 +23,7 @@ #include +#include "OutlanderHeartBeat.h" uint8_t outlanderCharger::chgStatus; uint8_t outlanderCharger::evseDuty; @@ -115,6 +116,8 @@ bool outlanderCharger::ControlCharge(bool RunCh, bool ACReq) void outlanderCharger::SetCanInterface(CanHardware* c) { + OutlanderHeartBeat::SetCanInterface(c);//set Outlander Heartbeat on same CAN + can = c; can->RegisterUserMessage(0x377);//dc_dc status can->RegisterUserMessage(0x389);//charger status @@ -144,21 +147,30 @@ void outlanderCharger::Task100Ms() { setVolts=Param::GetInt(Param::Voltspnt)*10; actVolts=Param::GetInt(Param::udc); - - uint8_t bytes[8]; - bytes[0] = 0x00; - bytes[1] = 0x00; - bytes[2] = 0x00; - bytes[3] = 0x00; - bytes[4] = 0x00; - bytes[5] = 0x00; - bytes[6] = 0x00; - bytes[7] = 0x00; + + /* + bytes[0] = 0x00; + bytes[1] = 0x00; + bytes[2] = 0x00; + bytes[3] = 0x00; + bytes[4] = 0x00; + bytes[5] = 0x00; + bytes[6] = 0x00; + bytes[7] = 0x00; + if(clearToStart) + { + bytes[2] = 0xB6;//oxb6 in byte 3 enables charger + can->Send(0x285, (uint32_t*)bytes, 8); + } + */ if(clearToStart) { - bytes[2] = 0xB6;//oxb6 in byte 3 enables charger - can->Send(0x285, (uint32_t*)bytes, 8); + OutlanderHeartBeat::SetPullInEVSE(1); + } + else + { + OutlanderHeartBeat::SetPullInEVSE(0); } diff --git a/src/stm32_vcu.cpp b/src/stm32_vcu.cpp index c132f395..24c3e0d3 100644 --- a/src/stm32_vcu.cpp +++ b/src/stm32_vcu.cpp @@ -44,7 +44,7 @@ static bool StartSig=false; static bool ACrequest=false; static bool initbyStart=false; static bool initbyCharge=false; - +static bool OutlanderCAN=false; static volatile unsigned days=0, @@ -78,6 +78,7 @@ static NoInverterClass NoInverter; static OutlanderInverter outlanderInv; static noHeater Heaternone; static AmperaHeater amperaHeater; +static OutlanderCanHeater outlanderCanHeater; static no_Lever NoGearLever; static F30_Lever F30GearLever; static E65_Lever E65GearLever; @@ -96,6 +97,7 @@ static BMS BMSnone; static SimpBMS BMSsimp; static LeafBMS BMSleaf; static DaisychainBMS BMSdaisychain; +static KangooBMS BMSRenaultKangoo33; static DCDC DCDCnone; static TeslaDCDC DCDCTesla; static BMS* selectedBMS = &BMSnone; @@ -293,8 +295,13 @@ static void Ms100Task(void) selectedBMS->Task100Ms(); selectedDCDC->Task100Ms(); selectedShifter->Task100Ms(); + selectedHeater->Task100Ms(); canMap->SendAll(); + if(OutlanderCAN == true) + { + OutlanderHeartBeat::Task100Ms(); + } if (Param::GetInt(Param::dir) < 0) { @@ -639,12 +646,14 @@ static void UpdateInv() break; case InvModes::Outlander: selectedInverter = &outlanderInv; + OutlanderCAN = true; break; case InvModes::OpenI: selectedInverter = &openInv; break; case InvModes::RearOutlander: selectedInverter = &rearoutlanderInv; + OutlanderCAN = true; break; } //This will call SetCanFilters() via the Clear Callback @@ -711,6 +720,7 @@ static void UpdateCharger() break; case ChargeModes::Out_lander: selectedCharger = &outChg; + OutlanderCAN = true; break; case ChargeModes::Elcon: selectedCharger = &ChargerElcon; @@ -762,6 +772,11 @@ static void UpdateHeater() case HeatType::VW: selectedHeater = &heaterVW; heaterVW.SetLinInterface(lin); + break; + case HeatType::OutlanderHeater: + selectedHeater = &outlanderCanHeater; + OutlanderCAN = true; + break; } //This will call SetCanFilters() via the Clear Callback canInterface[0]->ClearUserMessages(); @@ -783,6 +798,9 @@ static void UpdateBMS() case BMSModes::BMSModeDaisychainDualBMS: selectedBMS = &BMSdaisychain; break; + case BMSModes::BMSRenaultKangoo33BMS: + selectedBMS = &BMSRenaultKangoo33; + break; default: // Default to no BMS selectedBMS = &BMSnone; @@ -865,6 +883,7 @@ static void SetCanFilters() CanHardware* bms_can = canInterface[Param::GetInt(Param::BMSCan)]; CanHardware* obd2_can = canInterface[Param::GetInt(Param::OBD2Can)]; CanHardware* dcdc_can = canInterface[Param::GetInt(Param::DCDCCan)]; + CanHardware* heater_can = canInterface[Param::GetInt(Param::HeaterCan)]; selectedInverter->SetCanInterface(inverter_can); selectedVehicle->SetCanInterface(vehicle_can); @@ -874,6 +893,7 @@ static void SetCanFilters() selectedDCDC->SetCanInterface(dcdc_can); selectedShifter->SetCanInterface(vehicle_can); canOBD2.SetCanInterface(obd2_can); + selectedHeater->SetCanInterface(heater_can); if (Param::GetInt(Param::ShuntType) == 1) ISA::RegisterCanMessages(shunt_can);//select isa shunt if (Param::GetInt(Param::ShuntType) == 2) SBOX::RegisterCanMessages(shunt_can);//select bmw sbox @@ -1021,6 +1041,7 @@ static bool CanCallback(uint32_t id, uint32_t data[2], uint8_t dlc) //This is wh selectedBMS->DecodeCAN(id, (uint8_t*)data); selectedDCDC->DecodeCAN(id, (uint8_t*)data); selectedShifter->DecodeCAN(id,data); + selectedHeater->DecodeCAN(id, data); break; } return false; diff --git a/src/utils.cpp b/src/utils.cpp index f4fef9a4..39b58d2a 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -8,6 +8,7 @@ #include "my_math.h" #include #include +#include "hwinit.h" namespace utils { diff --git a/stm32-vcu.cbp b/stm32-vcu.cbp index 037886a3..bb6c0f8e 100644 --- a/stm32-vcu.cbp +++ b/stm32-vcu.cbp @@ -68,8 +68,11 @@ + + + @@ -89,6 +92,7 @@ + @@ -159,9 +163,12 @@ + + + @@ -172,6 +179,7 @@ +