mirror of
https://github.com/dexterbg/Twizy-Virtual-BMS.git
synced 2024-11-08 12:25:42 +00:00
Version 1.3.0:
- Added support to generate extended info frame 0x700 - New API calls: setInfoBmsType(), setInfoState1(), setInfoState2(), setInfoError(), setInfoBalancing() - Added support for module temperature #8 and cell voltages #15/#16
This commit is contained in:
parent
ce7c6e8311
commit
b8bf0da8ba
6 changed files with 439 additions and 228 deletions
78
API.md
78
API.md
|
@ -66,23 +66,25 @@ Note: all control functions validate their parameters. If you pass any value out
|
|||
- soh: 0 .. 100 (%)
|
||||
|
||||
- `bool setCellVoltage(int cell, float volt)` -- Set battery cell voltage
|
||||
- cell: 1 .. 14 (the original Twizy battery has 14 cells)
|
||||
- cell: 1 .. 16 (the original Twizy battery has 14 cells)
|
||||
- volt: 1.0 .. 5.0
|
||||
- Note: this does no implicit update on the overall pack voltage
|
||||
- Note: cell voltages #15 & #16 will be stored in frame 0x700 (custom protocol extension)
|
||||
|
||||
- `bool setVoltage(float volt, bool deriveCells)` -- Set battery pack voltage
|
||||
- volt: 19.3 .. 69.6 (SEVCON G48 series voltage range)
|
||||
- deriveCells: true = set all cell voltages to volt/14
|
||||
- deriveCells: true = set cell voltages #1-#14 to volt/14
|
||||
|
||||
- `bool setModuleTemperature(int module, int temp)` -- Set battery module temperature
|
||||
- module: 1 .. 7 (the original Twizy battery is organized in 7 modules)
|
||||
- module: 1 .. 8 (the original Twizy battery is organized in 7 modules)
|
||||
- temp: -40 .. 100 (°C)
|
||||
- Note: this does no implicit update on the overall pack temperature
|
||||
- Note: module temperature #8 will be stored in frame 0x554 byte 7, which is unused on the original pack layout
|
||||
|
||||
- `bool setTemperature(int tempMin, int tempMax, bool deriveModules)` -- Set battery pack temperature
|
||||
- tempMin: -40 .. 100 (°C)
|
||||
- tempMax: -40 .. 100 (°C)
|
||||
- deriveModules: true = set all module temperatures to avg(min,max)
|
||||
- deriveModules: true = set module temperatures #1-#7 to avg(min,max)
|
||||
|
||||
- `bool setError(unsigned long error)` -- Set display error/warning indicators
|
||||
- error: 0x000000 .. 0xFFFFFF (0 = no error) or use a bitwise ORed combination of…
|
||||
|
@ -210,3 +212,71 @@ Standard filters will pass IDs `0x423`, `0x597` and `0x599`. Three free CAN filt
|
|||
- this is automatically called every 10 seconds if debug level >= 1
|
||||
|
||||
|
||||
## Extended info frame
|
||||
|
||||
Beginning with version 1.3.0, the VirtualBMS supports sending an extended BMS status information on the CAN bus. The frame layout has been designed by Pascal Ripp and Michael Balzer to create a standard base for CAN bus tools like the Twizplay and the OVMS.
|
||||
|
||||
The extended info frame is sent at CAN ID 0x700 once per second in all states except `Off`. The frame transports these fields:
|
||||
|
||||
- Byte 0: BMS specific state #1 (main state, i.e. twizy.state())
|
||||
- Byte 1: highest 3 bits = BMS type ID (see below), remaining 5 bits = BMS specific error code (see below)
|
||||
- Bytes 2-4: cell voltages #15 & #16 (encoded in 12 bits like #1-#14)
|
||||
- Bytes 5-6: balancing status (bits 15…0 = cells 16…1, 1 = balancing active)
|
||||
- Byte 7: BMS specific state #2 (auxiliary state or data)
|
||||
|
||||
The VirtualBMS will not send frame 0x700 unless you set the BMS type. It will also not insert any data into the fields by itself, to fill in data you need to use the API calls as shown below. This way you can use the VirtualBMS library to implement other BMS types as well.
|
||||
|
||||
|
||||
### BMS types
|
||||
|
||||
The BMS type is meant for CAN tools to be able to identify the BMS and decode the BMS specific states and error info.
|
||||
|
||||
There are currently 7 possible BMS types, defined in `enum TwizyBmsType`:
|
||||
|
||||
- 0 = `bmsType_VirtualBMS` (states and error codes as documented here)
|
||||
- 1 = `bmsType_EdriverBMS` (see Pascal's documentation for details)
|
||||
- 2…6 = reserved
|
||||
- 7 = `bmsType_undefined` (disables frame 0x700)
|
||||
|
||||
Types #2…#6 can be assigned to future BMS types.
|
||||
|
||||
Please contact us if you want to allocate an ID. Keep in mind: any new BMS type needs support in all CAN tools, so try to reuse one of the already defined BMS types as long as possible.
|
||||
|
||||
|
||||
### BMS error codes (for `bmsType_VirtualBMS`)
|
||||
|
||||
Our basic standard proposition covers these error codes (defined in `enum TwizyBmsError`):
|
||||
|
||||
- 0 = `bmsError_None`
|
||||
- 1 = `bmsError_EEPROM`
|
||||
- 2 = `bmsError_SensorFailure`
|
||||
- 3 = `bmsError_VoltageHigh`
|
||||
- 4 = `bmsError_VoltageLow`
|
||||
- 5 = `bmsError_VoltageDiff`
|
||||
- 6 = `bmsError_TemperatureHigh`
|
||||
- 7 = `bmsError_TemperatureLow`
|
||||
- 8 = `bmsError_TemperatureDiff`
|
||||
- 9 = `bmsError_ChargerTemperatureHigh`
|
||||
|
||||
Custom error codes can be added beginning at 128. Please contact us if you'd like to add standard error codes.
|
||||
|
||||
|
||||
### API calls
|
||||
|
||||
- `bool setInfoBmsType(byte bmsType)` -- Set informational BMS type
|
||||
- bmsType: 0 .. 7 (see above / enum TwizyBmsType)
|
||||
- Note: type 7 (bmsType_undefined = default value) deactivates frame 0x700
|
||||
|
||||
- `bool setInfoState1(byte state)` -- Set informational state 1 (main state)
|
||||
- state: 0x00 .. 0xFF (specific by BMS type)
|
||||
|
||||
- `bool setInfoState2(byte state)` -- Set informational state 2 (aux state)
|
||||
- state: 0x00 .. 0xFF (specific by BMS type)
|
||||
|
||||
- `bool setInfoError(byte errorCode)` -- Set informational error code
|
||||
- errorCode: 0x00 .. 0x1F (specific by BMS type)
|
||||
|
||||
- `bool setInfoBalancing(unsigned int flags)` -- Set informational balancing status
|
||||
- flags: 16 bits = 16 cells (#16 = MSB, #1 = LSB), 1 = balancing
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
# History
|
||||
|
||||
## Version 1.3.0 (2017-08-30)
|
||||
|
||||
- Added support to generate extended info frame 0x700
|
||||
- New API calls: setInfoBmsType(), setInfoState1(), setInfoState2(), setInfoError(), setInfoBalancing()
|
||||
- Added support for module temperature #8 and cell voltages #15/#16
|
||||
|
||||
|
||||
## Version 1.2.1 (2017-08-17)
|
||||
|
||||
- New API calls stateName(), stateName(state)
|
||||
|
|
22
keywords.txt
22
keywords.txt
|
@ -28,6 +28,12 @@ setCurrent KEYWORD2
|
|||
setCurrentQA KEYWORD2
|
||||
setError KEYWORD2
|
||||
|
||||
setInfoBmsType KEYWORD2
|
||||
setInfoState1 KEYWORD2
|
||||
setInfoState2 KEYWORD2
|
||||
setInfoError KEYWORD2
|
||||
setInfoBalancing KEYWORD2
|
||||
|
||||
getChargerTemperature KEYWORD2
|
||||
getDCConverterCurrent KEYWORD2
|
||||
isPluggedIn KEYWORD2
|
||||
|
@ -81,3 +87,19 @@ TWIZY_SERV_12V LITERAL1
|
|||
TWIZY_SERV_BATT LITERAL1
|
||||
TWIZY_SERV_TEMP LITERAL1
|
||||
TWIZY_SERV_STOP LITERAL1
|
||||
|
||||
bmsType_VirtualBMS LITERAL1
|
||||
bmsType_EdriverBMS LITERAL1
|
||||
bmsType_undefined LITERAL1
|
||||
|
||||
bmsError_None LITERAL1
|
||||
bmsError_EEPROM LITERAL1
|
||||
bmsError_SensorFailure LITERAL1
|
||||
bmsError_VoltageHigh LITERAL1
|
||||
bmsError_VoltageLow LITERAL1
|
||||
bmsError_VoltageDiff LITERAL1
|
||||
bmsError_TemperatureHigh LITERAL1
|
||||
bmsError_TemperatureLow LITERAL1
|
||||
bmsError_TemperatureDiff LITERAL1
|
||||
bmsError_ChargerTemperatureHigh LITERAL1
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name=Twizy Virtual BMS
|
||||
version=1.2.1
|
||||
version=1.3.0
|
||||
author=Michael Balzer
|
||||
maintainer=Michael Balzer <dexter@dexters-web.de>
|
||||
sentence=Emulation of Renault Twizy BMS (battery management system)
|
||||
|
|
|
@ -38,10 +38,10 @@
|
|||
#ifndef _TwizyVirtualBMS_h
|
||||
#define _TwizyVirtualBMS_h
|
||||
|
||||
#define TWIZY_VBMS_VERSION "V1.2.1 (2017-08-16)"
|
||||
#define TWIZY_VBMS_VERSION "V1.3.0 (2017-08-30)"
|
||||
|
||||
#ifndef TWIZY_TAG
|
||||
#define TWIZY_TAG "twizy."
|
||||
#define TWIZY_TAG "twizy."
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
@ -75,38 +75,68 @@
|
|||
// ==========================================================================
|
||||
|
||||
|
||||
// Standard BMS types for setInfoBmsType():
|
||||
// Note: contact us to allocate a reserved id for your BMS
|
||||
enum TwizyBmsType {
|
||||
bmsType_VirtualBMS,
|
||||
bmsType_EdriverBMS,
|
||||
bmsType_reserved2,
|
||||
bmsType_reserved3,
|
||||
bmsType_reserved4,
|
||||
bmsType_reserved5,
|
||||
bmsType_reserved6,
|
||||
bmsType_undefined // 7 = do not send extended info frame
|
||||
};
|
||||
|
||||
|
||||
// Standard informational error codes for setInfoError():
|
||||
// Note: add custom codes beginning at 128
|
||||
enum TwizyBmsError {
|
||||
bmsError_None,
|
||||
bmsError_EEPROM,
|
||||
bmsError_SensorFailure,
|
||||
bmsError_VoltageHigh,
|
||||
bmsError_VoltageLow,
|
||||
bmsError_VoltageDiff,
|
||||
bmsError_TemperatureHigh,
|
||||
bmsError_TemperatureLow,
|
||||
bmsError_TemperatureDiff,
|
||||
bmsError_ChargerTemperatureHigh
|
||||
};
|
||||
|
||||
|
||||
// Twizy states:
|
||||
enum TwizyState {
|
||||
Off,
|
||||
Init,
|
||||
Error,
|
||||
Ready,
|
||||
StartDrive,
|
||||
Driving,
|
||||
StopDrive,
|
||||
StartCharge,
|
||||
Charging,
|
||||
StopCharge,
|
||||
StartTrickle,
|
||||
Trickle,
|
||||
StopTrickle
|
||||
Off,
|
||||
Init,
|
||||
Error,
|
||||
Ready,
|
||||
StartDrive,
|
||||
Driving,
|
||||
StopDrive,
|
||||
StartCharge,
|
||||
Charging,
|
||||
StopCharge,
|
||||
StartTrickle,
|
||||
Trickle,
|
||||
StopTrickle
|
||||
};
|
||||
|
||||
// Twizy state names:
|
||||
const char twizyStateName[13][13] PROGMEM = {
|
||||
"Off",
|
||||
"Init",
|
||||
"Error",
|
||||
"Ready",
|
||||
"StartDrive",
|
||||
"Driving",
|
||||
"StopDrive",
|
||||
"StartCharge",
|
||||
"Charging",
|
||||
"StopCharge",
|
||||
"StartTrickle",
|
||||
"Trickle",
|
||||
"StopTrickle"
|
||||
"Off",
|
||||
"Init",
|
||||
"Error",
|
||||
"Ready",
|
||||
"StartDrive",
|
||||
"Driving",
|
||||
"StopDrive",
|
||||
"StartCharge",
|
||||
"Charging",
|
||||
"StopCharge",
|
||||
"StartTrickle",
|
||||
"Trickle",
|
||||
"StopTrickle"
|
||||
};
|
||||
|
||||
|
||||
|
@ -134,8 +164,8 @@ typedef void (*TwizyProcessCanMsgCallback)(unsigned long rxId, byte rxLen, byte
|
|||
// ==========================================================================
|
||||
|
||||
// PROGMEM string helpers:
|
||||
#define FLASHSTRING const __FlashStringHelper
|
||||
#define FS(x) (__FlashStringHelper*)(x)
|
||||
#define FLASHSTRING const __FlashStringHelper
|
||||
#define FS(x) (__FlashStringHelper*)(x)
|
||||
|
||||
#if TWIZY_DEBUG_LEVEL >= 1
|
||||
// Parameter boundary check with error output:
|
||||
|
@ -165,14 +195,14 @@ volatile bool twizyCanMsgReceived = false;
|
|||
|
||||
#ifdef TWIZY_CAN_IRQ_PIN
|
||||
void twizyCanISR() {
|
||||
twizyCanMsgReceived = true;
|
||||
twizyCanMsgReceived = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
volatile bool twizyClockTick = false;
|
||||
|
||||
void twizyClockISR() {
|
||||
twizyClockTick = true;
|
||||
twizyClockTick = true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -185,50 +215,58 @@ class TwizyVirtualBMS {
|
|||
|
||||
public:
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Public API
|
||||
//
|
||||
// -----------------------------------------------------
|
||||
// Public API
|
||||
//
|
||||
|
||||
TwizyVirtualBMS();
|
||||
TwizyVirtualBMS();
|
||||
|
||||
void begin(); // to be called in setup()
|
||||
void looper(); // to be called in loop()
|
||||
void begin(); // to be called in setup()
|
||||
void looper(); // to be called in loop()
|
||||
|
||||
// User callback registration:
|
||||
void attachEnterState(TwizyEnterStateCallback fn);
|
||||
void attachCheckState(TwizyCheckStateCallback fn);
|
||||
void attachTicker(TwizyTickerCallback fn);
|
||||
void attachProcessCanMsg(TwizyProcessCanMsgCallback fn);
|
||||
// User callback registration:
|
||||
void attachEnterState(TwizyEnterStateCallback fn);
|
||||
void attachCheckState(TwizyCheckStateCallback fn);
|
||||
void attachTicker(TwizyTickerCallback fn);
|
||||
void attachProcessCanMsg(TwizyProcessCanMsgCallback fn);
|
||||
|
||||
// Model access:
|
||||
bool setChargeCurrent(int amps);
|
||||
bool setCurrent(float amps);
|
||||
bool setCurrentQA(long quarterAmps);
|
||||
bool setSOC(float soc);
|
||||
bool setPowerLimits(unsigned int drive, unsigned int recup);
|
||||
bool setSOH(int soh);
|
||||
bool setCellVoltage(int cell, float volt);
|
||||
bool setVoltage(float volt, bool deriveCells);
|
||||
bool setModuleTemperature(int module, int temp);
|
||||
bool setTemperature(int tempMin, int tempMax, bool deriveModules);
|
||||
bool setError(unsigned long error);
|
||||
// Model access:
|
||||
bool setChargeCurrent(int amps);
|
||||
bool setCurrent(float amps);
|
||||
bool setCurrentQA(long quarterAmps);
|
||||
bool setSOC(float soc);
|
||||
bool setPowerLimits(unsigned int drive, unsigned int recup);
|
||||
bool setSOH(int soh);
|
||||
bool setCellVoltage(int cell, float volt);
|
||||
bool setVoltage(float volt, bool deriveCells);
|
||||
bool setModuleTemperature(int module, int temp);
|
||||
bool setTemperature(int tempMin, int tempMax, bool deriveModules);
|
||||
bool setError(unsigned long error);
|
||||
|
||||
// Extended info frame access:
|
||||
bool setInfoBmsType(byte bmsType);
|
||||
bool setInfoState1(byte state);
|
||||
bool setInfoState2(byte state);
|
||||
bool setInfoError(byte errorCode);
|
||||
bool setInfoBalancing(unsigned int flags);
|
||||
|
||||
// Read access:
|
||||
int getChargerTemperature();
|
||||
float getDCConverterCurrent();
|
||||
bool isPluggedIn();
|
||||
bool isSwitchedOn();
|
||||
|
||||
// State access:
|
||||
TwizyState state() {
|
||||
return twizyState;
|
||||
}
|
||||
FLASHSTRING *stateName(TwizyState state) {
|
||||
return FS(twizyStateName[state]);
|
||||
}
|
||||
FLASHSTRING *stateName() {
|
||||
return FS(twizyStateName[twizyState]);
|
||||
}
|
||||
bool inState(TwizyState state1) {
|
||||
// State access:
|
||||
TwizyState state() {
|
||||
return twizyState;
|
||||
}
|
||||
FLASHSTRING *stateName(TwizyState state) {
|
||||
return FS(twizyStateName[state]);
|
||||
}
|
||||
FLASHSTRING *stateName() {
|
||||
return FS(twizyStateName[twizyState]);
|
||||
}
|
||||
bool inState(TwizyState state1) {
|
||||
return (twizyState==state1);
|
||||
}
|
||||
bool inState(TwizyState state1, TwizyState state2) {
|
||||
|
@ -245,88 +283,91 @@ public:
|
|||
}
|
||||
void enterState(TwizyState newState);
|
||||
|
||||
// CAN interface access:
|
||||
bool sendMsg(INT32U id, INT8U len, INT8U *buf);
|
||||
void setCanFilter(byte filterNum, unsigned int canId);
|
||||
// CAN interface access:
|
||||
bool sendMsg(INT32U id, INT8U len, INT8U *buf);
|
||||
void setCanFilter(byte filterNum, unsigned int canId);
|
||||
|
||||
// Debug utils:
|
||||
void dumpId(FLASHSTRING *name, int len, byte *buf);
|
||||
void debugInfo();
|
||||
// Debug utils:
|
||||
void dumpId(FLASHSTRING *name, int len, byte *buf);
|
||||
void debugInfo();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Twizy CAN model
|
||||
//
|
||||
// -----------------------------------------------------
|
||||
// Twizy CAN model
|
||||
//
|
||||
|
||||
// BMS (controlled by us):
|
||||
byte id155[8] = { 0x07, 0x97, 0xCA, 0x54, 0x52, 0x30, 0x00, 0x6C };
|
||||
byte id424[8] = { 0x11, 0x40, 0x10, 0x20, 0x39, 0x63, 0x00, 0x3A };
|
||||
byte id425[8] = { 0x2A, 0x1F, 0x44, 0xFF, 0xFE, 0x42, 0x01, 0x20 };
|
||||
byte id554[8] = { 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x00 };
|
||||
byte id556[8] = { 0x30, 0x93, 0x09, 0x30, 0x93, 0x09, 0x30, 0x9A };
|
||||
byte id557[8] = { 0x30, 0x93, 0x09, 0x30, 0x93, 0x09, 0x30, 0x90 };
|
||||
byte id55E[8] = { 0x30, 0x93, 0x09, 0x30, 0x93, 0x09, 0x0C, 0xF9 };
|
||||
byte id55F[8] = { 0xFF, 0xFF, 0x73, 0x00, 0x00, 0x21, 0xF2, 0x1F };
|
||||
byte id628[3] = { 0x00, 0x00, 0x00 };
|
||||
byte id659[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
// BMS (controlled by us):
|
||||
byte id155[8] = { 0x07, 0x97, 0xCA, 0x54, 0x52, 0x30, 0x00, 0x6C };
|
||||
byte id424[8] = { 0x11, 0x40, 0x10, 0x20, 0x39, 0x63, 0x00, 0x3A };
|
||||
byte id425[8] = { 0x2A, 0x1F, 0x44, 0xFF, 0xFE, 0x42, 0x01, 0x20 };
|
||||
byte id554[8] = { 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x00 };
|
||||
byte id556[8] = { 0x30, 0x93, 0x09, 0x30, 0x93, 0x09, 0x30, 0x9A };
|
||||
byte id557[8] = { 0x30, 0x93, 0x09, 0x30, 0x93, 0x09, 0x30, 0x90 };
|
||||
byte id55E[8] = { 0x30, 0x93, 0x09, 0x30, 0x93, 0x09, 0x0C, 0xF9 };
|
||||
byte id55F[8] = { 0xFF, 0xFF, 0x73, 0x00, 0x00, 0x21, 0xF2, 0x1F };
|
||||
byte id628[3] = { 0x00, 0x00, 0x00 };
|
||||
byte id659[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
|
||||
// CHARGER:
|
||||
byte id423[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
byte id597[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
// VirtualBMS extended frame (controlled by us):
|
||||
byte id700[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
|
||||
// DISPLAY:
|
||||
byte id599[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
// CHARGER (read only):
|
||||
byte id423[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
byte id597[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
// DISPLAY (read only):
|
||||
byte id599[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Twizy CAN interface
|
||||
//
|
||||
// -----------------------------------------------------
|
||||
// Twizy CAN interface
|
||||
//
|
||||
|
||||
MCP_CAN twizyCAN;
|
||||
MCP_CAN twizyCAN;
|
||||
|
||||
unsigned int sendErrors = 0;
|
||||
unsigned int sendRetries = 0;
|
||||
unsigned int sendErrors = 0;
|
||||
unsigned int sendRetries = 0;
|
||||
|
||||
// RX buffer:
|
||||
unsigned long rxId;
|
||||
byte rxLen;
|
||||
byte rxBuf[8];
|
||||
// RX buffer:
|
||||
unsigned long rxId;
|
||||
byte rxLen;
|
||||
byte rxBuf[8];
|
||||
|
||||
void receiveCanMsgs();
|
||||
void process423();
|
||||
void process597();
|
||||
void process599();
|
||||
void receiveCanMsgs();
|
||||
void process423();
|
||||
void process597();
|
||||
void process599();
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Twizy state machine
|
||||
//
|
||||
// -----------------------------------------------------
|
||||
// Twizy state machine
|
||||
//
|
||||
|
||||
TwizyState twizyState = (TwizyState) -1;
|
||||
TwizyState twizyState = (TwizyState) -1;
|
||||
|
||||
// 3MW pulse tick counter:
|
||||
byte counter3MW = 0;
|
||||
// 3MW pulse tick counter:
|
||||
byte counter3MW = 0;
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Twizy ticker:
|
||||
//
|
||||
// -----------------------------------------------------
|
||||
// Twizy ticker:
|
||||
//
|
||||
|
||||
unsigned int clockCnt = 0;
|
||||
unsigned int clockCnt = 0;
|
||||
|
||||
void ticker();
|
||||
void ticker();
|
||||
|
||||
|
||||
// -----------------------------------------------------
|
||||
// User callbacks hooks
|
||||
//
|
||||
// -----------------------------------------------------
|
||||
// User callbacks hooks
|
||||
//
|
||||
|
||||
TwizyEnterStateCallback bmsEnterState = NULL;
|
||||
TwizyCheckStateCallback bmsCheckState = NULL;
|
||||
TwizyTickerCallback bmsTicker = NULL;
|
||||
TwizyProcessCanMsgCallback bmsProcessCanMsg = NULL;
|
||||
TwizyEnterStateCallback bmsEnterState = NULL;
|
||||
TwizyCheckStateCallback bmsCheckState = NULL;
|
||||
TwizyTickerCallback bmsTicker = NULL;
|
||||
TwizyProcessCanMsgCallback bmsProcessCanMsg = NULL;
|
||||
|
||||
|
||||
};
|
||||
|
@ -337,20 +378,20 @@ private:
|
|||
//
|
||||
|
||||
TwizyVirtualBMS::TwizyVirtualBMS()
|
||||
: twizyCAN(TWIZY_CAN_CS_PIN) {
|
||||
: twizyCAN(TWIZY_CAN_CS_PIN) {
|
||||
}
|
||||
|
||||
void TwizyVirtualBMS::attachEnterState(TwizyEnterStateCallback fn) {
|
||||
bmsEnterState = fn;
|
||||
bmsEnterState = fn;
|
||||
}
|
||||
void TwizyVirtualBMS::attachCheckState(TwizyCheckStateCallback fn) {
|
||||
bmsCheckState = fn;
|
||||
bmsCheckState = fn;
|
||||
}
|
||||
void TwizyVirtualBMS::attachTicker(TwizyTickerCallback fn) {
|
||||
bmsTicker = fn;
|
||||
bmsTicker = fn;
|
||||
}
|
||||
void TwizyVirtualBMS::attachProcessCanMsg(TwizyProcessCanMsgCallback fn) {
|
||||
bmsProcessCanMsg = fn;
|
||||
bmsProcessCanMsg = fn;
|
||||
}
|
||||
|
||||
|
||||
|
@ -384,11 +425,11 @@ bool TwizyVirtualBMS::setCurrent(float amps) {
|
|||
// Set battery pack current level (native 1/4 A resolution)
|
||||
// quarterAmps: -2000 .. +2000 (positive = charge, negative = discharge)
|
||||
bool TwizyVirtualBMS::setCurrentQA(long quarterAmps) {
|
||||
CHECKLIMIT(quarterAmps, -2000L, 2000L);
|
||||
unsigned int level = 2000 + quarterAmps;
|
||||
id155[1] = (id155[1] & 0xf0) | ((level & 0x0f00) >> 8);
|
||||
id155[2] = level & 0x00ff;
|
||||
return true;
|
||||
CHECKLIMIT(quarterAmps, -2000L, 2000L);
|
||||
unsigned int level = 2000 + quarterAmps;
|
||||
id155[1] = (id155[1] & 0xf0) | ((level & 0x0f00) >> 8);
|
||||
id155[2] = level & 0x00ff;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set battery pack SOC
|
||||
|
@ -423,11 +464,11 @@ bool TwizyVirtualBMS::setSOH(int soh) {
|
|||
}
|
||||
|
||||
// Set battery cell voltage level
|
||||
// cell: 1 .. 14
|
||||
// volt: 1.0 .. 5.0
|
||||
// cell: 1 .. 16
|
||||
// volt: 0.0 .. 5.0
|
||||
bool TwizyVirtualBMS::setCellVoltage(int cell, float volt) {
|
||||
CHECKLIMIT(cell, 1, 14);
|
||||
CHECKLIMIT(volt, 1.0, 5.0);
|
||||
CHECKLIMIT(cell, 1, 16);
|
||||
CHECKLIMIT(volt, 0.0, 5.0);
|
||||
|
||||
// cell voltages are packed 12 bit values
|
||||
// determine frame and position:
|
||||
|
@ -442,10 +483,14 @@ bool TwizyVirtualBMS::setCellVoltage(int cell, float volt) {
|
|||
frame = id557;
|
||||
cell -= 6;
|
||||
}
|
||||
else {
|
||||
else if (cell <= 14) {
|
||||
frame = id55E;
|
||||
cell -= 11;
|
||||
}
|
||||
else {
|
||||
frame = id700+2; // [2]-[4] = cells 15+16
|
||||
cell -= 15;
|
||||
}
|
||||
|
||||
int pos = cell * 1.5;
|
||||
unsigned int level = volt * 200;
|
||||
|
@ -498,10 +543,10 @@ bool TwizyVirtualBMS::setVoltage(float volt, bool deriveCells) {
|
|||
}
|
||||
|
||||
// Set battery module temperature
|
||||
// module: 1 .. 7
|
||||
// module: 1 .. 8
|
||||
// temp: -40 .. 100 [°C]
|
||||
bool TwizyVirtualBMS::setModuleTemperature(int module, int temp) {
|
||||
CHECKLIMIT(module, 1, 7);
|
||||
CHECKLIMIT(module, 1, 8);
|
||||
CHECKLIMIT(temp, -40, 100);
|
||||
id554[module-1] = 40 + temp;
|
||||
return true;
|
||||
|
@ -541,6 +586,48 @@ bool TwizyVirtualBMS::setError(unsigned long error) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Set informational BMS type
|
||||
// bmsType: 0 .. 7 (see TwizyBmsType)
|
||||
// 0 = VirtualBMS
|
||||
// 1 = EdriverBMS
|
||||
// 2…6 reserved
|
||||
// 7 = undefined (disables info frame)
|
||||
bool TwizyVirtualBMS::setInfoBmsType(byte bmsType) {
|
||||
CHECKLIMIT(bmsType, 0, 7);
|
||||
id700[1] = (id700[1] & 0x1F) | (bmsType << 5);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set informational state 1
|
||||
// state: 0x00 .. 0xFF (specific by BMS type)
|
||||
bool TwizyVirtualBMS::setInfoState1(byte state) {
|
||||
id700[0] = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set informational state 2
|
||||
// state: 0x00 .. 0xFF (specific by BMS type)
|
||||
bool TwizyVirtualBMS::setInfoState2(byte state) {
|
||||
id700[7] = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set informational error code
|
||||
// errorCode: 0x00 .. 0x1F (specific by BMS type)
|
||||
bool TwizyVirtualBMS::setInfoError(byte errorCode) {
|
||||
CHECKLIMIT(errorCode, 0x00, 0x1F);
|
||||
id700[1] = (id700[1] & 0xE0) | (errorCode);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set informational balancing status
|
||||
// flags: 16 bits = 16 cells (#16 = MSB, #1 = LSB), 1 = balancing
|
||||
bool TwizyVirtualBMS::setInfoBalancing(unsigned int flags) {
|
||||
id700[5] = (flags & 0xFF00) >> 8;
|
||||
id700[6] = (flags & 0x00FF);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Get charger temperature
|
||||
int TwizyVirtualBMS::getChargerTemperature() {
|
||||
|
@ -598,8 +685,8 @@ void TwizyVirtualBMS::receiveCanMsgs() {
|
|||
|
||||
// User space callback:
|
||||
if (bmsProcessCanMsg) {
|
||||
(*bmsProcessCanMsg)(rxId, rxLen, rxBuf);
|
||||
}
|
||||
(*bmsProcessCanMsg)(rxId, rxLen, rxBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -656,10 +743,10 @@ void TwizyVirtualBMS::process597() {
|
|||
// Process DISPLAY frame 599:
|
||||
void TwizyVirtualBMS::process599() {
|
||||
// copy odometer to BMS frame 55E
|
||||
unsigned long odo = ((unsigned long)id599[0] << 24)
|
||||
| ((unsigned long)id599[1] << 16)
|
||||
| ((unsigned long)id599[2] << 8)
|
||||
| (id599[3]);
|
||||
unsigned long odo = ((unsigned long)id599[0] << 24)
|
||||
| ((unsigned long)id599[1] << 16)
|
||||
| ((unsigned long)id599[2] << 8)
|
||||
| (id599[3]);
|
||||
odo /= 10;
|
||||
id55E[6] = odo >> 8;
|
||||
id55E[7] = odo & 0x00ff;
|
||||
|
@ -686,8 +773,8 @@ bool TwizyVirtualBMS::sendMsg(INT32U id, INT8U len, INT8U *buf) {
|
|||
|
||||
#endif //TWIZY_CAN_SEND
|
||||
|
||||
sendErrors++;
|
||||
return false;
|
||||
sendErrors++;
|
||||
return false;
|
||||
|
||||
// Note: MCP_CAN.sendMsgBuf() is not optimized for throughput.
|
||||
// Despite having three send buffers in the MCP, it will wait
|
||||
|
@ -745,6 +832,10 @@ void TwizyVirtualBMS::ticker() {
|
|||
if (ms3000) {
|
||||
sendMsg(0x659, sizeof(id659), id659);
|
||||
}
|
||||
// send frame 0x700 only if BMS type has been set:
|
||||
if (ms1000 && (id700[1] & 0xE0) != 0xE0) {
|
||||
sendMsg(0x700, sizeof(id700), id700);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
@ -823,13 +914,33 @@ void TwizyVirtualBMS::ticker() {
|
|||
} // if ((twizyState != Off) && (twizyState != Error))
|
||||
|
||||
|
||||
else if (twizyState == Error) {
|
||||
|
||||
bool ms1000 = (clockCnt % 100 == 0);
|
||||
bool ms10000 = (clockCnt % 1000 == 0);
|
||||
|
||||
// send frame 0x700 only if BMS type has been set:
|
||||
if (ms1000 && (id700[1] & 0xE0) != 0xE0) {
|
||||
sendMsg(0x700, sizeof(id700), id700);
|
||||
}
|
||||
|
||||
// Debug info every 10 seconds
|
||||
#if TWIZY_DEBUG_LEVEL >= 1
|
||||
if (ms10000) {
|
||||
debugInfo();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // if (twizyState == Error)
|
||||
|
||||
|
||||
//
|
||||
// Callback for BMS ticker code:
|
||||
//
|
||||
|
||||
if (bmsTicker) {
|
||||
(*bmsTicker)(clockCnt);
|
||||
}
|
||||
(*bmsTicker)(clockCnt);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
@ -860,7 +971,7 @@ void TwizyVirtualBMS::debugInfo() {
|
|||
|
||||
#if TWIZY_DEBUG_LEVEL >= 1
|
||||
|
||||
Serial.println(F("\n" TWIZY_TAG "debugInfo:"));
|
||||
Serial.println(F("\n" TWIZY_TAG "debugInfo:"));
|
||||
|
||||
Serial.print(F("- twizyState="));
|
||||
Serial.println(stateName());
|
||||
|
@ -885,20 +996,21 @@ void TwizyVirtualBMS::debugInfo() {
|
|||
|
||||
// CHARGER & DISPLAY:
|
||||
dumpId(F("id423"), sizeof(id423), id423);
|
||||
dumpId(F("id597"), sizeof(id597), id597);
|
||||
dumpId(F("id599"), sizeof(id599), id599);
|
||||
dumpId(F("id597"), sizeof(id597), id597);
|
||||
dumpId(F("id599"), sizeof(id599), id599);
|
||||
|
||||
// BMS:
|
||||
dumpId(F("id155"), sizeof(id155), id155);
|
||||
dumpId(F("id424"), sizeof(id424), id424);
|
||||
dumpId(F("id425"), sizeof(id425), id425);
|
||||
dumpId(F("id554"), sizeof(id554), id554);
|
||||
dumpId(F("id556"), sizeof(id556), id556);
|
||||
dumpId(F("id557"), sizeof(id557), id557);
|
||||
dumpId(F("id55E"), sizeof(id55E), id55E);
|
||||
dumpId(F("id55F"), sizeof(id55F), id55F);
|
||||
dumpId(F("id628"), sizeof(id628), id628);
|
||||
dumpId(F("id659"), sizeof(id659), id659);
|
||||
dumpId(F("id155"), sizeof(id155), id155);
|
||||
dumpId(F("id424"), sizeof(id424), id424);
|
||||
dumpId(F("id425"), sizeof(id425), id425);
|
||||
dumpId(F("id554"), sizeof(id554), id554);
|
||||
dumpId(F("id556"), sizeof(id556), id556);
|
||||
dumpId(F("id557"), sizeof(id557), id557);
|
||||
dumpId(F("id55E"), sizeof(id55E), id55E);
|
||||
dumpId(F("id55F"), sizeof(id55F), id55F);
|
||||
dumpId(F("id628"), sizeof(id628), id628);
|
||||
dumpId(F("id659"), sizeof(id659), id659);
|
||||
dumpId(F("id700"), sizeof(id700), id700);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -983,8 +1095,8 @@ void TwizyVirtualBMS::enterState(TwizyState newState) {
|
|||
|
||||
// call BMS state transition:
|
||||
if (bmsEnterState) {
|
||||
(*bmsEnterState)(twizyState, newState);
|
||||
}
|
||||
(*bmsEnterState)(twizyState, newState);
|
||||
}
|
||||
|
||||
// set new state:
|
||||
twizyState = newState;
|
||||
|
@ -1004,7 +1116,7 @@ void TwizyVirtualBMS::begin() {
|
|||
//
|
||||
|
||||
while (twizyCAN.begin(MCP_STDEXT, CAN_500KBPS, TWIZY_CAN_MCP_FREQ) != CAN_OK) {
|
||||
Serial.println(F(TWIZY_TAG "begin: waiting for CAN connection..."));
|
||||
Serial.println(F(TWIZY_TAG "begin: waiting for CAN connection..."));
|
||||
delay(500);
|
||||
}
|
||||
|
||||
|
@ -1034,31 +1146,31 @@ void TwizyVirtualBMS::begin() {
|
|||
|
||||
pinMode(TWIZY_3MW_CONTROL_PIN, OUTPUT);
|
||||
|
||||
enterState(Off);
|
||||
enterState(Off);
|
||||
|
||||
#if TWIZY_USE_TIMER == 1
|
||||
#if TWIZY_USE_TIMER == 1
|
||||
|
||||
// Use Timer1 (16 bit):
|
||||
Timer1.initialize(TWIZY_CAN_CLOCK_US);
|
||||
Timer1.attachInterrupt(twizyClockISR);
|
||||
// Use Timer1 (16 bit):
|
||||
Timer1.initialize(TWIZY_CAN_CLOCK_US);
|
||||
Timer1.attachInterrupt(twizyClockISR);
|
||||
|
||||
#elif TWIZY_USE_TIMER == 2
|
||||
#elif TWIZY_USE_TIMER == 2
|
||||
|
||||
// Use Timer2 (8 bit): derive count from resolution:
|
||||
FlexiTimer2::set(TWIZY_CAN_CLOCK_US / (1000000UL / TWIZY_TIMER2_RESOLUTION),
|
||||
1.0 / TWIZY_TIMER2_RESOLUTION,
|
||||
twizyClockISR);
|
||||
FlexiTimer2::start();
|
||||
// Use Timer2 (8 bit): derive count from resolution:
|
||||
FlexiTimer2::set(TWIZY_CAN_CLOCK_US / (1000000UL / TWIZY_TIMER2_RESOLUTION),
|
||||
1.0 / TWIZY_TIMER2_RESOLUTION,
|
||||
twizyClockISR);
|
||||
FlexiTimer2::start();
|
||||
|
||||
#else
|
||||
#else
|
||||
|
||||
// Use Timer3 (16 bit):
|
||||
Timer3.initialize(TWIZY_CAN_CLOCK_US);
|
||||
Timer3.attachInterrupt(twizyClockISR);
|
||||
// Use Timer3 (16 bit):
|
||||
Timer3.initialize(TWIZY_CAN_CLOCK_US);
|
||||
Timer3.attachInterrupt(twizyClockISR);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Serial.println(F(TWIZY_TAG "begin: done"));
|
||||
Serial.println(F(TWIZY_TAG "begin: done"));
|
||||
|
||||
}
|
||||
|
||||
|
@ -1074,11 +1186,11 @@ void TwizyVirtualBMS::looper() {
|
|||
|
||||
#ifndef TWIZY_CAN_IRQ_PIN
|
||||
// No IRQ, we need to poll:
|
||||
twizyCanMsgReceived = (twizyCAN.checkReceive() == CAN_MSGAVAIL);
|
||||
twizyCanMsgReceived = (twizyCAN.checkReceive() == CAN_MSGAVAIL);
|
||||
#endif
|
||||
|
||||
if (twizyCanMsgReceived) {
|
||||
twizyCanMsgReceived = false;
|
||||
twizyCanMsgReceived = false;
|
||||
receiveCanMsgs();
|
||||
}
|
||||
|
||||
|
@ -1087,7 +1199,7 @@ void TwizyVirtualBMS::looper() {
|
|||
//
|
||||
|
||||
if (twizyClockTick) {
|
||||
twizyClockTick = false;
|
||||
twizyClockTick = false;
|
||||
ticker();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#define _TwizyVirtualBMS_config_h
|
||||
|
||||
// Serial debug output:
|
||||
// Level 0 = none, only output init & error messages
|
||||
// Level 1 = log state transitions & CAN statistics
|
||||
// Level 0 = none, only output init message
|
||||
// Level 1 = log state transitions, errors & CAN statistics
|
||||
// Level 2 = log CAN frame dumps (10 second interval)
|
||||
#define TWIZY_DEBUG_LEVEL 1
|
||||
|
||||
|
|
Loading…
Reference in a new issue