diff --git a/tasmota/include/tasmota_configurations_ESP32.h b/tasmota/include/tasmota_configurations_ESP32.h index dc827d7e1..6521ab0f1 100644 --- a/tasmota/include/tasmota_configurations_ESP32.h +++ b/tasmota/include/tasmota_configurations_ESP32.h @@ -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_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_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 0262e889e..14edc0fd0 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -553,7 +553,7 @@ const uint16_t kGpioNiceList[] PROGMEM = { #ifdef USE_SDCARD AGPIO(GPIO_SDCARD_CS), // SDCard in SPI mode #endif // USE_SDCARD -#ifdef USE_MCP2515 +#if defined(USE_MCP2515) || defined(USE_CANSNIFFER) AGPIO(GPIO_MCP2515_CS), #endif // USE_MCP2515 #endif // USE_SPI diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index fe52853f7..4c8a0e129 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -738,6 +738,8 @@ // #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_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 diff --git a/tasmota/tasmota_support/support_features.ino b/tasmota/tasmota_support/support_features.ino index a1410007c..ca0cc27c9 100644 --- a/tasmota/tasmota_support/support_features.ino +++ b/tasmota/tasmota_support/support_features.ino @@ -829,9 +829,11 @@ void ResponseAppendFeatures(void) feature9 |= 0x00000001; // xsns_98_sgp40.ino #endif #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 -// feature9 |= 0x00000004; // feature9 |= 0x00000008; // feature9 |= 0x00000010; diff --git a/tasmota/tasmota_xsns_sensor/xsns_87_can_sniffer.ino b/tasmota/tasmota_xsns_sensor/xsns_87_can_sniffer.ino new file mode 100644 index 000000000..6d4f733be --- /dev/null +++ b/tasmota/tasmota_xsns_sensor/xsns_87_can_sniffer.ino @@ -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 . +*/ + +#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;isendMessage(&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 \ No newline at end of file diff --git a/tasmota/tasmota_xsns_sensor/xsns_87_mcp2515.ino b/tasmota/tasmota_xsns_sensor/xsns_87_mcp2515.ino index 629cb9209..ceb3cce07 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_87_mcp2515.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_87_mcp2515.ino @@ -19,6 +19,10 @@ #ifdef USE_SPI #ifdef USE_MCP2515 +#ifdef USE_CANSNIFFER +#undef USE_CANSNIFFER +#warning **** USE_CANSNIFFER disabled in favour of USE_MCP2515 **** +#endif /*********************************************************************************************\ * MCP2515 - Microchip CAN controller * @@ -106,10 +110,13 @@ struct BMS_Struct { #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; + MCP2515 *mcp2515 = nullptr; char c2h(char c) @@ -118,182 +125,182 @@ char c2h(char c) } 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) { - mcp2515 = new MCP2515(5); - if (MCP2515::ERROR_OK != mcp2515->reset()) { - AddLog(LOG_LEVEL_ERROR, PSTR("MCP2515: Failed to reset module")); - mcp2515_init_status = 0; - } + 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: 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 // TODO: Filter CAN bus messages //mcp2515->setFilterMask(); //mcp2515->setFilter(); #endif + } } void MCP2515_Read() { uint8_t nCounter = 0; bool checkRcv; - if (mcp2515_init_status) { - checkRcv = mcp2515->checkReceive(); + checkRcv = mcp2515->checkReceive(); - while (checkRcv && nCounter <= MCP2515_MAX_FRAMES) { - mcp2515->checkReceive(); - nCounter++; - if (mcp2515->readMessage(&canFrame) == MCP2515::ERROR_OK) { - lastFrameRecv = TasmotaGlobal.uptime; + while (checkRcv && nCounter <= MCP2515_MAX_FRAMES) { + mcp2515->checkReceive(); + nCounter++; + if (mcp2515->readMessage(&canFrame) == MCP2515::ERROR_OK) { + Mcp2515.lastFrameRecv = TasmotaGlobal.uptime; #ifdef MCP2515_BMS_CLIENT - #ifdef MCP2515_BMS_FREEDWON - switch (canFrame.can_id) { - // Charge/Discharge parameters - case 0x351: - if (8 == canFrame.can_dlc) { - bms.chargeVoltLimit = (canFrame.data[1] << 8) | canFrame.data[0]; - bms.maxChargeCurrent = (canFrame.data[3] << 8) | canFrame.data[2]; - bms.maxDischargeCurrent = (canFrame.data[5] << 8) | canFrame.data[4]; - 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; - } else { - MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); - } - break; - // State of Charge/Health - case 0x355: - if (6 >= canFrame.can_dlc) { - bms.stateOfCharge = (canFrame.data[1] << 8) | canFrame.data[0]; - bms.stateOfHealth = (canFrame.data[3] << 8) | canFrame.data[2]; - bms.setFields |= BMS_SOC | BMS_SOH; - } else { - MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); - } - break; - // Voltage/Current/Temperature - case 0x356: - if (6 >= canFrame.can_dlc) { - bms.battVoltage = (canFrame.data[1] << 8) | canFrame.data[0]; - 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.setFields |= BMS_VOLT | BMS_AMP | BMS_TEMP; - } else { - MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); - } - break; - // Manufacturer name - case 0x35E: - for (int i = 0; i < canFrame.can_dlc; i++) { - bms.manuf[i] = canFrame.data[i]; - } - bms.setFields |= BMS_MANUFACTURER; - bms.manuf[8] = 0; // Ensure that name is null terminated - break; - // Battery Model / Firmware version - case 0x35F: - if (4 == canFrame.can_dlc) { - bms.model = (canFrame.data[1] << 8) | canFrame.data[0]; - bms.firmwareVer = (canFrame.data[3] << 8) | canFrame.data[2]; - bms.setFields |= BMS_MODEL | BMS_FIRMWARE_VER; - } else { - MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); - } - break; - // Battery / BMS name - case 0x370: - case 0x371: - 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 - 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 - bms.setFields |= BMS_NAME; - } - bms.name[16] = 0; // Ensure that name is null terminated - break; - // Modules status - case 0x372: - // Min/Max cell voltage/temperature - case 0x373: - // Min. cell voltage id string - case 0x374: - // Max. cell voltage id string - case 0x375: - // Min. cell temperature id string - case 0x376: - // Max. cell temperature id string - case 0x377: - break; - // Installed capacity - case 0x379: - if (2 >= canFrame.can_dlc) { - bms.capacityAh = (canFrame.data[1] << 8) | canFrame.data[0]; - bms.setFields |= BMS_CAPACITY; - } else { - MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); - } - break; - // Serial number - case 0x380: - case 0x381: - 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 - 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 - bms.setFields |= BMS_SERIAL; - } - bms.serialNr[16] = 0; // Ensure that serial nr is null terminated - break; - default: - 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_DEBUG, PSTR("MCP2515: Received message 0x%s from ID 0x%x"), canMsg, (uint32_t)canFrame.can_id); - 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; - } +#ifdef MCP2515_BMS_FREEDWON + switch (canFrame.can_id) { + // Charge/Discharge parameters + case 0x351: + if (8 == canFrame.can_dlc) { + bms.chargeVoltLimit = (canFrame.data[1] << 8) | canFrame.data[0]; + bms.maxChargeCurrent = (canFrame.data[3] << 8) | canFrame.data[2]; + bms.maxDischargeCurrent = (canFrame.data[5] << 8) | canFrame.data[4]; + 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; + } else { + MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); + } + break; + // State of Charge/Health + case 0x355: + if (6 >= canFrame.can_dlc) { + bms.stateOfCharge = (canFrame.data[1] << 8) | canFrame.data[0]; + bms.stateOfHealth = (canFrame.data[3] << 8) | canFrame.data[2]; + bms.setFields |= BMS_SOC | BMS_SOH; + } else { + MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); + } + break; + // Voltage/Current/Temperature + case 0x356: + if (6 >= canFrame.can_dlc) { + bms.battVoltage = (canFrame.data[1] << 8) | canFrame.data[0]; + 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.setFields |= BMS_VOLT | BMS_AMP | BMS_TEMP; + } else { + MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); + } + break; + // Manufacturer name + case 0x35E: + for (int i = 0; i < canFrame.can_dlc; i++) { + bms.manuf[i] = canFrame.data[i]; + } + bms.setFields |= BMS_MANUFACTURER; + bms.manuf[8] = 0; // Ensure that name is null terminated + break; + // Battery Model / Firmware version + case 0x35F: + if (4 == canFrame.can_dlc) { + bms.model = (canFrame.data[1] << 8) | canFrame.data[0]; + bms.firmwareVer = (canFrame.data[3] << 8) | canFrame.data[2]; + bms.setFields |= BMS_MODEL | BMS_FIRMWARE_VER; + } else { + MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); + } + break; + // Battery / BMS name + case 0x370: + case 0x371: + 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 + 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 + bms.setFields |= BMS_NAME; + } + bms.name[16] = 0; // Ensure that name is null terminated + break; + // Modules status + case 0x372: + // Min/Max cell voltage/temperature + case 0x373: + // Min. cell voltage id string + case 0x374: + // Max. cell voltage id string + case 0x375: + // Min. cell temperature id string + case 0x376: + // Max. cell temperature id string + case 0x377: + break; + // Installed capacity + case 0x379: + if (2 >= canFrame.can_dlc) { + bms.capacityAh = (canFrame.data[1] << 8) | canFrame.data[0]; + bms.setFields |= BMS_CAPACITY; + } else { + MCP2515_FrameSizeError(canFrame.can_dlc, canFrame.can_id); + } + break; + // Serial number + case 0x380: + case 0x381: + 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 + 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 + bms.setFields |= BMS_SERIAL; + } + bms.serialNr[16] = 0; // Ensure that serial nr is null terminated + break; + default: + 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_DEBUG, PSTR("CAN: Received message 0x%s from ID 0x%x"), canMsg, (uint32_t)canFrame.can_id); + 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 - 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")); - } + 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("CAN: Failed to send keep alive frame")); } -#endif } +#endif } void MCP2515_Show(bool 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 if (bms.setFields & BMS_MANUFACTURER) { bool jsonFirstField = true; @@ -404,11 +411,11 @@ bool Xsns87(uint8_t function) { 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) { - case FUNC_INIT: - MCP2515_Init(); - break; case FUNC_EVERY_50_MSECOND: MCP2515_Read(); break; diff --git a/tools/decode-status.py b/tools/decode-status.py index 8b22a40cb..2d2ce4fa0 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -286,7 +286,7 @@ a_features = [[ "USE_PCF85363","USE_DS3502","USE_IMPROV","USE_FLOWRATEMETER", "USE_BP5758D","USE_HYT","USE_SM2335","USE_DISPLAY_TM1621_SONOFF" ],[ - "USE_SGP40","USE_LUXV30B","","", + "USE_SGP40","USE_LUXV30B","USE_CANSNIFFER","", "","","","", "","","","", "","","","",