mirror of https://github.com/arendst/Tasmota.git
parent
5fe2554e6a
commit
ec964d1c9e
|
@ -629,6 +629,8 @@
|
||||||
|
|
||||||
#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
|
#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC)
|
||||||
//#define USE_RC522 // Add support for MFRC522 13.56Mhz Rfid reader (+6k code)
|
//#define USE_RC522 // Add support for MFRC522 13.56Mhz Rfid reader (+6k code)
|
||||||
|
//#define USE_MCP2515 // Add support for can bus using MCP2515 (+7k code)
|
||||||
|
//#define USE_CANSNIFFER // Add support for can bus sniffer using MCP2515 (+5k code)
|
||||||
|
|
||||||
#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
|
#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code)
|
||||||
#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
|
#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
|
||||||
|
|
|
@ -553,7 +553,7 @@ const uint16_t kGpioNiceList[] PROGMEM = {
|
||||||
#ifdef USE_SDCARD
|
#ifdef USE_SDCARD
|
||||||
AGPIO(GPIO_SDCARD_CS), // SDCard in SPI mode
|
AGPIO(GPIO_SDCARD_CS), // SDCard in SPI mode
|
||||||
#endif // USE_SDCARD
|
#endif // USE_SDCARD
|
||||||
#ifdef USE_MCP2515
|
#if defined(USE_MCP2515) || defined(USE_CANSNIFFER)
|
||||||
AGPIO(GPIO_MCP2515_CS),
|
AGPIO(GPIO_MCP2515_CS),
|
||||||
#endif // USE_MCP2515
|
#endif // USE_MCP2515
|
||||||
#endif // USE_SPI
|
#endif // USE_SPI
|
||||||
|
|
|
@ -738,6 +738,8 @@
|
||||||
// #define USE_RC522 // Add support for MFRC522 13.56Mhz Rfid reader (+6k code)
|
// #define USE_RC522 // Add support for MFRC522 13.56Mhz Rfid reader (+6k code)
|
||||||
// #define USE_RC522_DATA_FUNCTION // Add support for reading data block content (+0k4 code)
|
// #define USE_RC522_DATA_FUNCTION // Add support for reading data block content (+0k4 code)
|
||||||
// #define USE_RC522_TYPE_INFORMATION // Add support for showing card type (+0k4 code)
|
// #define USE_RC522_TYPE_INFORMATION // Add support for showing card type (+0k4 code)
|
||||||
|
// #define USE_MCP2515 // Add support for can bus using MCP2515 (+7k code)
|
||||||
|
// #define USE_CANSNIFFER // Add support for can bus sniffer using MCP2515 (+5k code)
|
||||||
|
|
||||||
#endif // USE_SPI
|
#endif // USE_SPI
|
||||||
|
|
||||||
|
|
|
@ -829,9 +829,11 @@ void ResponseAppendFeatures(void)
|
||||||
feature9 |= 0x00000001; // xsns_98_sgp40.ino
|
feature9 |= 0x00000001; // xsns_98_sgp40.ino
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_I2C) && defined(USE_LUXV30B)
|
#if defined(USE_I2C) && defined(USE_LUXV30B)
|
||||||
feature9 |= 0x00000002;
|
feature9 |= 0x00000002; // xsns_99_luxv30b.ino
|
||||||
|
#endif
|
||||||
|
#if defined(USE_SPI) && defined(USE_CANSNIFFER)
|
||||||
|
feature9 |= 0x00000004; // xsns_87_can_sniffer.ino
|
||||||
#endif
|
#endif
|
||||||
// feature9 |= 0x00000004;
|
|
||||||
// feature9 |= 0x00000008;
|
// feature9 |= 0x00000008;
|
||||||
|
|
||||||
// feature9 |= 0x00000010;
|
// feature9 |= 0x00000010;
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
xsns_87_can_sniffer.ino - MCP2515 CAN bus support for Tasmota
|
||||||
|
|
||||||
|
Copyright (C) 2022 kwiatek6324 and Marius Bezuidenhout
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef USE_SPI
|
||||||
|
#ifdef USE_CANSNIFFER
|
||||||
|
#ifdef USE_MCP2515
|
||||||
|
#undef USE_MCP2515
|
||||||
|
#warning **** USE_MCP2515 disabled in favour of USE_CANSNIFFER ****
|
||||||
|
#endif
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* CAN sniffer using MCP2515 - Microchip CAN controller
|
||||||
|
*
|
||||||
|
* Connections:
|
||||||
|
* MCP2515 ESP8266 Tasmota
|
||||||
|
* ------- -------------- ----------
|
||||||
|
* INT not used
|
||||||
|
* SCK GPIO14 SPI CLK
|
||||||
|
* SI GPIO13 SPI MOSI
|
||||||
|
* SO GPIO12 SPI MISO
|
||||||
|
* CS GPIO0..5,15,16 MCP2515
|
||||||
|
* Gnd Gnd
|
||||||
|
* VCC Vin/5V
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
#define XSNS_87 87
|
||||||
|
|
||||||
|
#ifndef MCP2515_BITRATE
|
||||||
|
#define MCP2515_BITRATE CAN_100KBPS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MCP2515_CLOCK
|
||||||
|
#define MCP2515_CLOCK MCP_8MHZ
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MCP2515_MAX_FRAMES
|
||||||
|
#define MCP2515_MAX_FRAMES 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CAN_KEEP_ALIVE_SECS
|
||||||
|
#define CAN_KEEP_ALIVE_SECS 300
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MCP2515_TIMEOUT
|
||||||
|
#define MCP2515_TIMEOUT 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define D_PRFX_CAN "Can"
|
||||||
|
#define D_CMND_CANSEND "Send"
|
||||||
|
|
||||||
|
const char kCanCommands[] PROGMEM = D_PRFX_CAN "|" "|" D_CMND_CANSEND ;
|
||||||
|
|
||||||
|
void (* const CanCommand[])(void) PROGMEM = { &CmndCan, &CmndCanSend};
|
||||||
|
|
||||||
|
#include "mcp2515.h"
|
||||||
|
|
||||||
|
struct MCP2515_Struct {
|
||||||
|
uint32_t lastFrameRecv = 0;
|
||||||
|
int8_t init_status = 0;
|
||||||
|
} Mcp2515;
|
||||||
|
|
||||||
|
struct can_frame canFrame;
|
||||||
|
|
||||||
|
MCP2515 *mcp2515 = nullptr;
|
||||||
|
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* Commands
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
void CmndCan(void) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Can (%d) [%s][%s]"), XdrvMailbox.index, XdrvMailbox.topic, XdrvMailbox.data);
|
||||||
|
ResponseCmndChar_P(PSTR("OK"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CmndCanSend(void) {
|
||||||
|
JsonParser parser(XdrvMailbox.data);
|
||||||
|
JsonParserObject root = parser.getRootObject();
|
||||||
|
|
||||||
|
uint16_t id = root.getUInt(PSTR("ID"), 0); // case insensitive
|
||||||
|
uint16_t len = root.getUInt(PSTR("LEN"), 0); // case insensitive
|
||||||
|
JsonParserArray data = root[PSTR("DATA")];
|
||||||
|
|
||||||
|
struct can_frame canMsg;
|
||||||
|
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: CanSend (%d)->%d"), id,len);
|
||||||
|
canMsg.can_id =id;
|
||||||
|
canMsg.can_dlc=len;
|
||||||
|
for (uint8_t i=0;i<len;i++) {
|
||||||
|
canMsg.data[i]=data[i].getUInt();
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: CanSend data[%d]=%d"),i,data[i].getUInt());
|
||||||
|
}
|
||||||
|
mcp2515->sendMessage(&canMsg);
|
||||||
|
// delay(100);
|
||||||
|
ResponseCmndChar_P(PSTR("OK"));
|
||||||
|
}
|
||||||
|
|
||||||
|
char c2h(char c) {
|
||||||
|
return "0123456789ABCDEF"[0x0F & (unsigned char)c];
|
||||||
|
}
|
||||||
|
|
||||||
|
void MCP2515_FrameSizeError(uint8_t len, uint32_t id) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Unexpected length (%d) for ID 0x%x"), len, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MCP2515_Init(void) {
|
||||||
|
if (PinUsed(GPIO_MCP2515_CS, GPIO_ANY) && TasmotaGlobal.spi_enabled) {
|
||||||
|
mcp2515 = new MCP2515(5);
|
||||||
|
if (MCP2515::ERROR_OK != mcp2515->reset()) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Failed to reset module"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MCP2515::ERROR_OK != mcp2515->setBitrate(MCP2515_BITRATE, MCP2515_CLOCK)) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Failed to set module bitrate"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MCP2515::ERROR_OK != mcp2515->setNormalMode()) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Failed to set normal mode"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Sniffer Initialized"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MCP2515_Read() {
|
||||||
|
uint8_t nCounter = 0;
|
||||||
|
bool checkRcv;
|
||||||
|
char mqtt_data[128];
|
||||||
|
|
||||||
|
checkRcv = mcp2515->checkReceive();
|
||||||
|
|
||||||
|
while (checkRcv && nCounter <= MCP2515_MAX_FRAMES) {
|
||||||
|
mcp2515->checkReceive();
|
||||||
|
nCounter++;
|
||||||
|
if (mcp2515->readMessage(&canFrame) == MCP2515::ERROR_OK) {
|
||||||
|
Mcp2515.lastFrameRecv = TasmotaGlobal.uptime;
|
||||||
|
|
||||||
|
char canMsg[17];
|
||||||
|
canMsg[0] = 0;
|
||||||
|
for (int i = 0; i < canFrame.can_dlc; i++) {
|
||||||
|
canMsg[i*2] = c2h(canFrame.data[i]>>4);
|
||||||
|
canMsg[i*2+1] = c2h(canFrame.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canFrame.can_dlc > 0) {
|
||||||
|
canMsg[(canFrame.can_dlc - 1) * 2 + 2] = 0;
|
||||||
|
}
|
||||||
|
// AddLog(LOG_LEVEL_INFO, PSTR("CAN: Received message 0x%s from ID 0x%x"), canMsg, (uint32_t)canFrame.can_id);
|
||||||
|
|
||||||
|
// AddLog(LOG_LEVEL_INFO, PSTR("CAN: Received: ID: %d"), (uint32_t)canFrame.can_id);
|
||||||
|
// AddLog(LOG_LEVEL_INFO, PSTR("CAN: Received: LEN: %d"), (uint32_t)canFrame.can_dlc);
|
||||||
|
// for (int i = 0; i < canFrame.can_dlc; i++) {
|
||||||
|
// AddLog(LOG_LEVEL_INFO, PSTR("CAN: Received: DATA[%d]: %d"), i,canFrame.data[i]);
|
||||||
|
// }
|
||||||
|
Response_P(PSTR("{\"%s\":%d,\"%s\":%d"),
|
||||||
|
"ID",(uint32_t)canFrame.can_id,
|
||||||
|
"LEN",(uint32_t)canFrame.can_dlc
|
||||||
|
);
|
||||||
|
for (int i = 0; i < canFrame.can_dlc; i++) { ResponseAppend_P(PSTR(",\"D%d\":%d"),i,canFrame.data[i]); }
|
||||||
|
ResponseJsonEnd();
|
||||||
|
|
||||||
|
|
||||||
|
MqttPublishPrefixTopic_P(STAT, "CAN");
|
||||||
|
ResponseClear();
|
||||||
|
|
||||||
|
|
||||||
|
} else if (mcp2515->checkError()) {
|
||||||
|
uint8_t errFlags = mcp2515->getErrorFlags();
|
||||||
|
mcp2515->clearRXnOVRFlags();
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Received error %d"), errFlags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************************************\
|
||||||
|
* Interface
|
||||||
|
\*********************************************************************************************/
|
||||||
|
|
||||||
|
bool Xsns87(uint8_t function) {
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
if (FUNC_INIT == function) {
|
||||||
|
MCP2515_Init();
|
||||||
|
}
|
||||||
|
else if (Mcp2515.init_status) {
|
||||||
|
switch (function) {
|
||||||
|
case FUNC_EVERY_50_MSECOND:
|
||||||
|
MCP2515_Read();
|
||||||
|
break;
|
||||||
|
case FUNC_COMMAND:
|
||||||
|
result = DecodeCommand(kCanCommands, CanCommand);
|
||||||
|
break;
|
||||||
|
case FUNC_JSON_APPEND:
|
||||||
|
// MCP2515_Show(1);
|
||||||
|
break;
|
||||||
|
#ifdef USE_WEBSERVER
|
||||||
|
case FUNC_WEB_SENSOR:
|
||||||
|
// MCP2515_Show(0);
|
||||||
|
break;
|
||||||
|
#endif // USE_WEBSERVER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_CANSNIFFER
|
||||||
|
#endif // USE_SPI
|
|
@ -19,6 +19,10 @@
|
||||||
|
|
||||||
#ifdef USE_SPI
|
#ifdef USE_SPI
|
||||||
#ifdef USE_MCP2515
|
#ifdef USE_MCP2515
|
||||||
|
#ifdef USE_CANSNIFFER
|
||||||
|
#undef USE_CANSNIFFER
|
||||||
|
#warning **** USE_CANSNIFFER disabled in favour of USE_MCP2515 ****
|
||||||
|
#endif
|
||||||
/*********************************************************************************************\
|
/*********************************************************************************************\
|
||||||
* MCP2515 - Microchip CAN controller
|
* MCP2515 - Microchip CAN controller
|
||||||
*
|
*
|
||||||
|
@ -106,10 +110,13 @@ struct BMS_Struct {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int8_t mcp2515_init_status = 1;
|
struct MCP2515_Struct {
|
||||||
|
uint32_t lastFrameRecv = 0;
|
||||||
|
int8_t init_status = 0;
|
||||||
|
} Mcp2515;
|
||||||
|
|
||||||
uint32_t lastFrameRecv = 0;
|
|
||||||
struct can_frame canFrame;
|
struct can_frame canFrame;
|
||||||
|
|
||||||
MCP2515 *mcp2515 = nullptr;
|
MCP2515 *mcp2515 = nullptr;
|
||||||
|
|
||||||
char c2h(char c)
|
char c2h(char c)
|
||||||
|
@ -118,182 +125,182 @@ char c2h(char c)
|
||||||
}
|
}
|
||||||
|
|
||||||
void MCP2515_FrameSizeError(uint8_t len, uint32_t id) {
|
void MCP2515_FrameSizeError(uint8_t len, uint32_t id) {
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Unexpected length (%d) for ID 0x%x"), len, id);
|
AddLog(LOG_LEVEL_DEBUG, PSTR("CAN: Unexpected length (%d) for ID 0x%x"), len, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MCP2515_Init(void) {
|
void MCP2515_Init(void) {
|
||||||
mcp2515 = new MCP2515(5);
|
if (PinUsed(GPIO_MCP2515_CS, GPIO_ANY) && TasmotaGlobal.spi_enabled) {
|
||||||
if (MCP2515::ERROR_OK != mcp2515->reset()) {
|
mcp2515 = new MCP2515(5);
|
||||||
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to reset module"));
|
if (MCP2515::ERROR_OK != mcp2515->reset()) {
|
||||||
mcp2515_init_status = 0;
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Failed to reset module"));
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
if (MCP2515::ERROR_OK != mcp2515->setBitrate(MCP2515_BITRATE, MCP2515_CLOCK)) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Failed to set module bitrate"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MCP2515::ERROR_OK != mcp2515->setNormalMode()) {
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Failed to set normal mode"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AddLog(LOG_LEVEL_INFO, PSTR("CAN: Initialized"));
|
||||||
|
|
||||||
if (MCP2515::ERROR_OK != mcp2515->setBitrate(MCP2515_BITRATE, MCP2515_CLOCK)) {
|
|
||||||
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to set module bitrate"));
|
|
||||||
mcp2515_init_status = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mcp2515_init_status && MCP2515::ERROR_OK != mcp2515->setNormalMode()) {
|
|
||||||
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to set normal mode"));
|
|
||||||
mcp2515_init_status = 0;
|
|
||||||
}
|
|
||||||
#ifdef MCP2515_BMS_FREEDWON
|
#ifdef MCP2515_BMS_FREEDWON
|
||||||
// TODO: Filter CAN bus messages
|
// TODO: Filter CAN bus messages
|
||||||
//mcp2515->setFilterMask();
|
//mcp2515->setFilterMask();
|
||||||
//mcp2515->setFilter();
|
//mcp2515->setFilter();
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MCP2515_Read() {
|
void MCP2515_Read() {
|
||||||
uint8_t nCounter = 0;
|
uint8_t nCounter = 0;
|
||||||
bool checkRcv;
|
bool checkRcv;
|
||||||
if (mcp2515_init_status) {
|
|
||||||
|
|
||||||
checkRcv = mcp2515->checkReceive();
|
checkRcv = mcp2515->checkReceive();
|
||||||
|
|
||||||
while (checkRcv && nCounter <= MCP2515_MAX_FRAMES) {
|
while (checkRcv && nCounter <= MCP2515_MAX_FRAMES) {
|
||||||
mcp2515->checkReceive();
|
mcp2515->checkReceive();
|
||||||
nCounter++;
|
nCounter++;
|
||||||
if (mcp2515->readMessage(&canFrame) == MCP2515::ERROR_OK) {
|
if (mcp2515->readMessage(&canFrame) == MCP2515::ERROR_OK) {
|
||||||
lastFrameRecv = TasmotaGlobal.uptime;
|
Mcp2515.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:
|
||||||
if (8 == canFrame.can_dlc) {
|
if (8 == canFrame.can_dlc) {
|
||||||
bms.chargeVoltLimit = (canFrame.data[1] << 8) | canFrame.data[0];
|
bms.chargeVoltLimit = (canFrame.data[1] << 8) | canFrame.data[0];
|
||||||
bms.maxChargeCurrent = (canFrame.data[3] << 8) | canFrame.data[2];
|
bms.maxChargeCurrent = (canFrame.data[3] << 8) | canFrame.data[2];
|
||||||
bms.maxDischargeCurrent = (canFrame.data[5] << 8) | canFrame.data[4];
|
bms.maxDischargeCurrent = (canFrame.data[5] << 8) | canFrame.data[4];
|
||||||
bms.dischargeVolt = (canFrame.data[7] << 8) | canFrame.data[6];
|
bms.dischargeVolt = (canFrame.data[7] << 8) | canFrame.data[6];
|
||||||
bms.setFields |= BMS_CHARGE_VOLT_MAX | BMS_CHARGE_VOLT_MIN | BMS_CHARGE_AMP_MAX | BMS_DISCHARGE_AMP_MAX;
|
bms.setFields |= BMS_CHARGE_VOLT_MAX | BMS_CHARGE_VOLT_MIN | BMS_CHARGE_AMP_MAX | BMS_DISCHARGE_AMP_MAX;
|
||||||
} else {
|
} else {
|
||||||
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
||||||
}
|
}
|
||||||
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];
|
||||||
bms.setFields |= BMS_SOC | BMS_SOH;
|
bms.setFields |= BMS_SOC | BMS_SOH;
|
||||||
} else {
|
} else {
|
||||||
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// Voltage/Current/Temperature
|
// Voltage/Current/Temperature
|
||||||
case 0x356:
|
case 0x356:
|
||||||
if (6 >= canFrame.can_dlc) {
|
if (6 >= canFrame.can_dlc) {
|
||||||
bms.battVoltage = (canFrame.data[1] << 8) | canFrame.data[0];
|
bms.battVoltage = (canFrame.data[1] << 8) | canFrame.data[0];
|
||||||
bms.battAmp = (canFrame.data[3] << 8) | canFrame.data[2];
|
bms.battAmp = (canFrame.data[3] << 8) | canFrame.data[2];
|
||||||
bms.battTemp = (canFrame.data[5] << 8) | canFrame.data[4]; // Convert to fahrenheit if SetOpion8 is set
|
bms.battTemp = (canFrame.data[5] << 8) | canFrame.data[4]; // Convert to fahrenheit if SetOpion8 is set
|
||||||
bms.setFields |= BMS_VOLT | BMS_AMP | BMS_TEMP;
|
bms.setFields |= BMS_VOLT | BMS_AMP | BMS_TEMP;
|
||||||
} else {
|
} else {
|
||||||
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// Manufacturer name
|
// Manufacturer name
|
||||||
case 0x35E:
|
case 0x35E:
|
||||||
for (int i = 0; i < canFrame.can_dlc; i++) {
|
for (int i = 0; i < canFrame.can_dlc; i++) {
|
||||||
bms.manuf[i] = canFrame.data[i];
|
bms.manuf[i] = canFrame.data[i];
|
||||||
}
|
}
|
||||||
bms.setFields |= BMS_MANUFACTURER;
|
bms.setFields |= BMS_MANUFACTURER;
|
||||||
bms.manuf[8] = 0; // Ensure that name is null terminated
|
bms.manuf[8] = 0; // Ensure that name is null terminated
|
||||||
break;
|
break;
|
||||||
// Battery Model / Firmware version
|
// Battery Model / Firmware version
|
||||||
case 0x35F:
|
case 0x35F:
|
||||||
if (4 == canFrame.can_dlc) {
|
if (4 == canFrame.can_dlc) {
|
||||||
bms.model = (canFrame.data[1] << 8) | canFrame.data[0];
|
bms.model = (canFrame.data[1] << 8) | canFrame.data[0];
|
||||||
bms.firmwareVer = (canFrame.data[3] << 8) | canFrame.data[2];
|
bms.firmwareVer = (canFrame.data[3] << 8) | canFrame.data[2];
|
||||||
bms.setFields |= BMS_MODEL | BMS_FIRMWARE_VER;
|
bms.setFields |= BMS_MODEL | BMS_FIRMWARE_VER;
|
||||||
} else {
|
} else {
|
||||||
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// Battery / BMS name
|
// Battery / BMS name
|
||||||
case 0x370:
|
case 0x370:
|
||||||
case 0x371:
|
case 0x371:
|
||||||
for (int i = 0; i < canFrame.can_dlc; i++) {
|
for (int i = 0; i < canFrame.can_dlc; i++) {
|
||||||
uint8_t nameStrPos = i + ((canFrame.can_id & 0x1) * 8); // If can_id is 0x371 then fill from byte 8 onwards
|
uint8_t nameStrPos = i + ((canFrame.can_id & 0x1) * 8); // If can_id is 0x371 then fill from byte 8 onwards
|
||||||
bms.name[nameStrPos] = canFrame.data[i];
|
bms.name[nameStrPos] = canFrame.data[i];
|
||||||
}
|
}
|
||||||
if ((canFrame.can_id & 0x1) && (bms.name[0] > 0)) { // Upper and lower part of name has been set now
|
if ((canFrame.can_id & 0x1) && (bms.name[0] > 0)) { // Upper and lower part of name has been set now
|
||||||
bms.setFields |= BMS_NAME;
|
bms.setFields |= BMS_NAME;
|
||||||
}
|
}
|
||||||
bms.name[16] = 0; // Ensure that name is null terminated
|
bms.name[16] = 0; // Ensure that name is null terminated
|
||||||
break;
|
break;
|
||||||
// Modules status
|
// Modules status
|
||||||
case 0x372:
|
case 0x372:
|
||||||
// Min/Max cell voltage/temperature
|
// Min/Max cell voltage/temperature
|
||||||
case 0x373:
|
case 0x373:
|
||||||
// Min. cell voltage id string
|
// Min. cell voltage id string
|
||||||
case 0x374:
|
case 0x374:
|
||||||
// Max. cell voltage id string
|
// Max. cell voltage id string
|
||||||
case 0x375:
|
case 0x375:
|
||||||
// Min. cell temperature id string
|
// Min. cell temperature id string
|
||||||
case 0x376:
|
case 0x376:
|
||||||
// Max. cell temperature id string
|
// Max. cell temperature id string
|
||||||
case 0x377:
|
case 0x377:
|
||||||
break;
|
break;
|
||||||
// Installed capacity
|
// Installed capacity
|
||||||
case 0x379:
|
case 0x379:
|
||||||
if (2 >= canFrame.can_dlc) {
|
if (2 >= canFrame.can_dlc) {
|
||||||
bms.capacityAh = (canFrame.data[1] << 8) | canFrame.data[0];
|
bms.capacityAh = (canFrame.data[1] << 8) | canFrame.data[0];
|
||||||
bms.setFields |= BMS_CAPACITY;
|
bms.setFields |= BMS_CAPACITY;
|
||||||
} else {
|
} else {
|
||||||
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// Serial number
|
// Serial number
|
||||||
case 0x380:
|
case 0x380:
|
||||||
case 0x381:
|
case 0x381:
|
||||||
for (int i = 0; i < canFrame.can_dlc; i++) {
|
for (int i = 0; i < canFrame.can_dlc; i++) {
|
||||||
uint8_t serialNrStrPos = i + ((canFrame.can_id & 0x1) * 8); // If can_id is 0x381 then fill from byte 8 onwards
|
uint8_t serialNrStrPos = i + ((canFrame.can_id & 0x1) * 8); // If can_id is 0x381 then fill from byte 8 onwards
|
||||||
bms.serialNr[serialNrStrPos] = canFrame.data[i];
|
bms.serialNr[serialNrStrPos] = canFrame.data[i];
|
||||||
}
|
}
|
||||||
if ((canFrame.can_id & 0x1) && (bms.serialNr[0] > 0)) { // Upper and lower part of serial number has been set now
|
if ((canFrame.can_id & 0x1) && (bms.serialNr[0] > 0)) { // Upper and lower part of serial number has been set now
|
||||||
bms.setFields |= BMS_SERIAL;
|
bms.setFields |= BMS_SERIAL;
|
||||||
}
|
}
|
||||||
bms.serialNr[16] = 0; // Ensure that serial nr is null terminated
|
bms.serialNr[16] = 0; // Ensure that serial nr is null terminated
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
char canMsg[17];
|
char canMsg[17];
|
||||||
canMsg[0] = 0;
|
canMsg[0] = 0;
|
||||||
for (int i = 0; i < canFrame.can_dlc; i++) {
|
for (int i = 0; i < canFrame.can_dlc; i++) {
|
||||||
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("CAN: Received message 0x%s from ID 0x%x"), canMsg, (uint32_t)canFrame.can_id);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
#endif // MCP2515_BMS_FREEDWON
|
|
||||||
#endif // MCP2515_BMS_CLIENT
|
|
||||||
} else if (mcp2515->checkError()) {
|
|
||||||
uint8_t errFlags = mcp2515->getErrorFlags();
|
|
||||||
mcp2515->clearRXnOVRFlags();
|
|
||||||
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Received error %d"), errFlags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif // MCP2515_BMS_FREEDWON
|
||||||
|
#endif // MCP2515_BMS_CLIENT
|
||||||
|
} else if (mcp2515->checkError()) {
|
||||||
|
uint8_t errFlags = mcp2515->getErrorFlags();
|
||||||
|
mcp2515->clearRXnOVRFlags();
|
||||||
|
AddLog(LOG_LEVEL_DEBUG, PSTR("CAN: Received error %d"), errFlags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MCP2515_BMS_FREEDWON
|
#ifdef MCP2515_BMS_FREEDWON
|
||||||
if (!(TasmotaGlobal.uptime%CAN_KEEP_ALIVE_SECS) && TasmotaGlobal.uptime>60) {
|
if (!(TasmotaGlobal.uptime%CAN_KEEP_ALIVE_SECS) && TasmotaGlobal.uptime>60) {
|
||||||
canFrame.can_id = 0x305;
|
canFrame.can_id = 0x305;
|
||||||
canFrame.can_dlc = 0;
|
canFrame.can_dlc = 0;
|
||||||
if (MCP2515::ERROR_OK != mcp2515->sendMessage(&canFrame)) {
|
if (MCP2515::ERROR_OK != mcp2515->sendMessage(&canFrame)) {
|
||||||
AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to send keep alive frame"));
|
AddLog(LOG_LEVEL_ERROR, PSTR("CAN: Failed to send keep alive frame"));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void MCP2515_Show(bool Json) {
|
void MCP2515_Show(bool Json) {
|
||||||
if (Json) {
|
if (Json) {
|
||||||
if (lastFrameRecv > 0 && TasmotaGlobal.uptime - lastFrameRecv <= MCP2515_TIMEOUT) {
|
if (Mcp2515.lastFrameRecv > 0 && TasmotaGlobal.uptime - Mcp2515.lastFrameRecv <= MCP2515_TIMEOUT) {
|
||||||
#ifdef MCP2515_BMS_CLIENT
|
#ifdef MCP2515_BMS_CLIENT
|
||||||
if (bms.setFields & BMS_MANUFACTURER) {
|
if (bms.setFields & BMS_MANUFACTURER) {
|
||||||
bool jsonFirstField = true;
|
bool jsonFirstField = true;
|
||||||
|
@ -404,11 +411,11 @@ bool Xsns87(uint8_t function)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
if (PinUsed(GPIO_MCP2515_CS, GPIO_ANY) && TasmotaGlobal.spi_enabled) {
|
if (FUNC_INIT == function) {
|
||||||
|
MCP2515_Init();
|
||||||
|
}
|
||||||
|
else if (Mcp2515.init_status) {
|
||||||
switch (function) {
|
switch (function) {
|
||||||
case FUNC_INIT:
|
|
||||||
MCP2515_Init();
|
|
||||||
break;
|
|
||||||
case FUNC_EVERY_50_MSECOND:
|
case FUNC_EVERY_50_MSECOND:
|
||||||
MCP2515_Read();
|
MCP2515_Read();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -286,7 +286,7 @@ a_features = [[
|
||||||
"USE_PCF85363","USE_DS3502","USE_IMPROV","USE_FLOWRATEMETER",
|
"USE_PCF85363","USE_DS3502","USE_IMPROV","USE_FLOWRATEMETER",
|
||||||
"USE_BP5758D","USE_HYT","USE_SM2335","USE_DISPLAY_TM1621_SONOFF"
|
"USE_BP5758D","USE_HYT","USE_SM2335","USE_DISPLAY_TM1621_SONOFF"
|
||||||
],[
|
],[
|
||||||
"USE_SGP40","USE_LUXV30B","","",
|
"USE_SGP40","USE_LUXV30B","USE_CANSNIFFER","",
|
||||||
"","","","",
|
"","","","",
|
||||||
"","","","",
|
"","","","",
|
||||||
"","","","",
|
"","","","",
|
||||||
|
|
Loading…
Reference in New Issue