mirror of https://github.com/arendst/Tasmota.git
First working version
This commit is contained in:
parent
f14f86f6fe
commit
ad8b557bec
|
@ -51,6 +51,14 @@
|
||||||
#define MCP2515_MAX_FRAMES 14
|
#define MCP2515_MAX_FRAMES 14
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CAN_KEEP_ALIVE_SECS
|
||||||
|
#define CAN_KEEP_ALIVE_SECS 300
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MCP2515_TIMEOUT
|
||||||
|
#define MCP2515_TIMEOUT 10
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef MCP2515_BMS_CLIENT
|
#ifndef MCP2515_BMS_CLIENT
|
||||||
#define MCP2515_BMS_CLIENT
|
#define MCP2515_BMS_CLIENT
|
||||||
// Look for Freedom Won BMS data in CAN message
|
// Look for Freedom Won BMS data in CAN message
|
||||||
|
@ -63,15 +71,16 @@
|
||||||
struct BMS_Struct {
|
struct BMS_Struct {
|
||||||
uint16_t stateOfCharge;
|
uint16_t stateOfCharge;
|
||||||
uint16_t stateOfHealth;
|
uint16_t stateOfHealth;
|
||||||
float battVoltage;
|
uint16_t battVoltage; // Div 100
|
||||||
float battAmp = 0;
|
int16_t battAmp; // Div 10
|
||||||
float battTemp;
|
int16_t battTemp; // Div 10
|
||||||
char name[17];
|
char name[17];
|
||||||
} bms;
|
} bms;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int8_t mcp2515_init_status = 1;
|
int8_t mcp2515_init_status = 1;
|
||||||
|
|
||||||
|
uint32_t lastFrameRecv = 0;
|
||||||
struct can_frame canFrame;
|
struct can_frame canFrame;
|
||||||
MCP2515 *mcp2515 = nullptr;
|
MCP2515 *mcp2515 = nullptr;
|
||||||
|
|
||||||
|
@ -96,12 +105,17 @@ void MCP2515_Init(void) {
|
||||||
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to set normal mode"));
|
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to set normal mode"));
|
||||||
mcp2515_init_status = 0;
|
mcp2515_init_status = 0;
|
||||||
}
|
}
|
||||||
|
#ifdef MCP2515_BMS_FREEDWON
|
||||||
|
// TODO: Filter CAN bus messages
|
||||||
|
//mcp2515->setFilterMask();
|
||||||
|
//mcp2515->setFilter();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void MCP2515_Read() {
|
void MCP2515_Read() {
|
||||||
uint8_t nCounter = 0;
|
uint8_t nCounter = 0;
|
||||||
bool checkRcv;
|
bool checkRcv;
|
||||||
if(mcp2515_init_status) {
|
if (mcp2515_init_status) {
|
||||||
|
|
||||||
checkRcv = mcp2515->checkReceive();
|
checkRcv = mcp2515->checkReceive();
|
||||||
|
|
||||||
|
@ -109,31 +123,37 @@ void MCP2515_Read() {
|
||||||
mcp2515->checkReceive();
|
mcp2515->checkReceive();
|
||||||
nCounter++;
|
nCounter++;
|
||||||
if (mcp2515->readMessage(&canFrame) == MCP2515::ERROR_OK) {
|
if (mcp2515->readMessage(&canFrame) == MCP2515::ERROR_OK) {
|
||||||
|
lastFrameRecv = TasmotaGlobal.uptime;
|
||||||
#ifdef MCP2515_BMS_CLIENT
|
#ifdef MCP2515_BMS_CLIENT
|
||||||
#ifdef MCP2515_BMS_FREEDWON
|
#ifdef MCP2515_BMS_FREEDWON
|
||||||
switch(canFrame.can_id) {
|
switch (canFrame.can_id) {
|
||||||
// Charge/Discharge parameters
|
// Charge/Discharge parameters
|
||||||
case 0x351:
|
case 0x351:
|
||||||
break;
|
break;
|
||||||
// State of Charge/Health
|
// State of Charge/Health
|
||||||
case 0x355:
|
case 0x355:
|
||||||
if(6 >= canFrame.can_dlc) {
|
if (6 >= canFrame.can_dlc) {
|
||||||
bms.stateOfCharge = (canFrame.data[1] << 8) + canFrame.data[0];
|
bms.stateOfCharge = (canFrame.data[1] << 8) | canFrame.data[0];
|
||||||
bms.stateOfHealth = (canFrame.data[3] << 8) + canFrame.data[2];
|
bms.stateOfHealth = (canFrame.data[3] << 8) | canFrame.data[2];
|
||||||
} else {
|
} else {
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Unexpected length (%d) for ID 0x355"), canFrame.can_dlc);
|
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Unexpected length (%d) for ID 0x355"), canFrame.can_dlc);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// Voltage/Current/Temperature
|
// Voltage/Current/Temperature
|
||||||
case 0x356:
|
case 0x356:
|
||||||
if(6 >= canFrame.can_dlc) {
|
if (6 >= canFrame.can_dlc) {
|
||||||
bms.battVoltage = (float)((canFrame.data[1] << 8) + canFrame.data[0])/100;
|
bms.battVoltage = (canFrame.data[1] << 8) | canFrame.data[0];
|
||||||
bms.battAmp = (float)((canFrame.data[3] << 8) + canFrame.data[2])/10;
|
bms.battAmp = (canFrame.data[3] << 8) | canFrame.data[2];
|
||||||
bms.battTemp = ConvertTemp((float)((canFrame.data[5] << 8) + canFrame.data[4])/10); // Convert to fahrenheit if SetOpion8 is set
|
bms.battTemp = (canFrame.data[5] << 8) | canFrame.data[4]; // Convert to fahrenheit if SetOpion8 is set
|
||||||
} else {
|
} else {
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Unexpected length (%d) for ID 0x356"), canFrame.can_dlc);
|
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Unexpected length (%d) for ID 0x356"), canFrame.can_dlc);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
// Manufacturer name
|
||||||
|
case 0x35E:
|
||||||
|
// Battery Model / Firmware version
|
||||||
|
case 0x35F:
|
||||||
|
break;
|
||||||
// Battery / BMS name
|
// Battery / BMS name
|
||||||
case 0x370:
|
case 0x370:
|
||||||
case 0x371:
|
case 0x371:
|
||||||
|
@ -145,10 +165,16 @@ void MCP2515_Read() {
|
||||||
break;
|
break;
|
||||||
// Modules status
|
// Modules status
|
||||||
case 0x372:
|
case 0x372:
|
||||||
|
// Min/Max cell voltage/temperature
|
||||||
|
case 0x373:
|
||||||
// Min. cell voltage id string
|
// Min. cell voltage id string
|
||||||
case 0x374:
|
case 0x374:
|
||||||
|
// Max. cell voltage id string
|
||||||
|
case 0x375:
|
||||||
// Min. cell temperature id string
|
// Min. cell temperature id string
|
||||||
case 0x376:
|
case 0x376:
|
||||||
|
// Max. cell temperature id string
|
||||||
|
case 0x377:
|
||||||
// Installed capacity
|
// Installed capacity
|
||||||
case 0x379:
|
case 0x379:
|
||||||
// Serial number
|
// Serial number
|
||||||
|
@ -162,7 +188,7 @@ void MCP2515_Read() {
|
||||||
canMsg[i*2] = c2h(canFrame.data[i]>>4);
|
canMsg[i*2] = c2h(canFrame.data[i]>>4);
|
||||||
canMsg[i*2+1] = c2h(canFrame.data[i]);
|
canMsg[i*2+1] = c2h(canFrame.data[i]);
|
||||||
}
|
}
|
||||||
if(canFrame.can_dlc > 0) {
|
if (canFrame.can_dlc > 0) {
|
||||||
canMsg[(canFrame.can_dlc - 1) * 2 + 2] = 0;
|
canMsg[(canFrame.can_dlc - 1) * 2 + 2] = 0;
|
||||||
}
|
}
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Received message 0x%s from ID 0x%X"), canMsg, (uint32_t)canFrame.can_id);
|
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Received message 0x%s from ID 0x%X"), canMsg, (uint32_t)canFrame.can_id);
|
||||||
|
@ -170,32 +196,46 @@ void MCP2515_Read() {
|
||||||
}
|
}
|
||||||
#endif // MCP2515_BMS_FREEDWON
|
#endif // MCP2515_BMS_FREEDWON
|
||||||
#endif // MCP2515_BMS_CLIENT
|
#endif // MCP2515_BMS_CLIENT
|
||||||
} else if(mcp2515->checkError()) {
|
} else if (mcp2515->checkError()) {
|
||||||
uint8_t errFlags = mcp2515->getErrorFlags();
|
uint8_t errFlags = mcp2515->getErrorFlags();
|
||||||
mcp2515->clearRXnOVRFlags();
|
mcp2515->clearRXnOVRFlags();
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Received error %d"), errFlags);
|
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Received error %d"), errFlags);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef MCP2515_BMS_FREEDWON
|
||||||
|
if (!(TasmotaGlobal.uptime%CAN_KEEP_ALIVE_SECS) && TasmotaGlobal.uptime>60) {
|
||||||
|
canFrame.can_id = 0x305;
|
||||||
|
canFrame.can_dlc = 0;
|
||||||
|
if (MCP2515::ERROR_OK != mcp2515->sendMessage(&canFrame)) {
|
||||||
|
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to send keep alive frame"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MCP2515_Show(bool Json) {
|
void MCP2515_Show(bool Json) {
|
||||||
if (Json) {
|
if (Json) {
|
||||||
|
if (lastFrameRecv > 0 && TasmotaGlobal.uptime - lastFrameRecv <= MCP2515_TIMEOUT ) {
|
||||||
#ifdef MCP2515_BMS_CLIENT
|
#ifdef MCP2515_BMS_CLIENT
|
||||||
ResponseAppend_P(PSTR(",\"MCP2515\":{\"SOC\":%d,\"SOH\":%d}"), bms.stateOfCharge, bms.stateOfHealth);
|
ResponseAppend_P(PSTR(",\"MCP2515\":{\"SOC\":%d,\"SOH\":%d}"), \
|
||||||
|
bms.stateOfCharge,
|
||||||
|
bms.stateOfHealth
|
||||||
|
);
|
||||||
#endif // MCP2515_BMS_CLIENT
|
#endif // MCP2515_BMS_CLIENT
|
||||||
|
}
|
||||||
#ifdef USE_WEBSERVER
|
#ifdef USE_WEBSERVER
|
||||||
} else {
|
} else {
|
||||||
#ifdef MCP2515_BMS_CLIENT
|
#ifdef MCP2515_BMS_CLIENT
|
||||||
char ampStr[6];
|
char ampStr[6];
|
||||||
dtostrf(bms.battAmp, 5, 1, ampStr);
|
dtostrf((float(bms.battAmp) / 10), 5, 1, ampStr);
|
||||||
WSContentSend_PD(HTTP_SNS_SOC, bms.name, bms.stateOfCharge);
|
WSContentSend_PD(HTTP_SNS_SOC, bms.name, bms.stateOfCharge);
|
||||||
WSContentSend_PD(HTTP_SNS_SOH, bms.name, bms.stateOfHealth);
|
WSContentSend_PD(HTTP_SNS_SOH, bms.name, bms.stateOfHealth);
|
||||||
WSContentSend_Voltage(bms.name, bms.battVoltage);
|
WSContentSend_Voltage(bms.name, (float(bms.battVoltage) / 100));
|
||||||
WSContentSend_PD(HTTP_SNS_CURRENT, ampStr);
|
WSContentSend_PD(HTTP_SNS_CURRENT, ampStr);
|
||||||
WSContentSend_Temp(bms.name, bms.battTemp);
|
WSContentSend_Temp(bms.name, ConvertTemp(float(bms.battTemp) / 10));
|
||||||
#endif // MCP2515_BMS_CLIENT
|
#endif // MCP2515_BMS_CLIENT
|
||||||
#endif // USE_WEBSERVER
|
#endif // USE_WEBSERVER
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue