mirror of https://github.com/arendst/Tasmota.git
189 lines
5.5 KiB
Arduino
189 lines
5.5 KiB
Arduino
|
/*
|
||
|
xsns_89_mcp2515.ino - MCP2515 CAN bus support for Tasmota
|
||
|
|
||
|
Copyright (C) 2021 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_MCP2515
|
||
|
/*********************************************************************************************\
|
||
|
* 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_89 89
|
||
|
|
||
|
#include "mcp2515.h"
|
||
|
|
||
|
// set defaults if not defined
|
||
|
#ifndef MCP2515_BITRATE
|
||
|
#define MCP2515_BITRATE CAN_500KBPS
|
||
|
#endif
|
||
|
|
||
|
#ifndef MCP2515_CLOCK
|
||
|
#define MCP2515_CLOCK MCP_8MHZ
|
||
|
#endif
|
||
|
|
||
|
#ifndef MCP2515_MAX_MSG
|
||
|
#define MCP2515_MAX_MSG 14
|
||
|
#endif
|
||
|
|
||
|
#ifndef MCP2515_BMS_CLIENT
|
||
|
#define MCP2515_BMS_CLIENT
|
||
|
// Look for Freedom Won BMS data in CAN message
|
||
|
#ifndef MCP2515_BMS_FREEDWON
|
||
|
#define MCP2515_BMS_FREEDWON
|
||
|
#endif // MCP2515_BMS_FREEDWON
|
||
|
#endif // MCP2515_BMS_CLIENT
|
||
|
|
||
|
#ifdef MCP2515_BMS_CLIENT
|
||
|
struct BMS_Struct {
|
||
|
uint16_t stateOfCharge;
|
||
|
uint16_t stateOfHealth;
|
||
|
float battVoltage;
|
||
|
float battMilliAmp;
|
||
|
float battTemp;
|
||
|
char name[17];
|
||
|
} bms;
|
||
|
#endif
|
||
|
|
||
|
int8_t mcp2515_init_status = 1;
|
||
|
|
||
|
struct can_frame canMsg;
|
||
|
MCP2515 mcp2515;
|
||
|
|
||
|
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 (MCP2515::ERROR_OK != mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ)) {
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void MCP2515_Read() {
|
||
|
uint8_t nCounter = 0;
|
||
|
while (mcp2515.checkReceive() && nCounter <= MCP2515_MAX_MSG) {
|
||
|
nCounter++;
|
||
|
if (mcp2515.readMessage(&canMsg) == MCP2515::ERROR_OK) {
|
||
|
#ifdef MCP2515_BMS_CLIENT
|
||
|
#ifdef MCP2515_BMS_FREEDWON
|
||
|
switch(canMsg.can_id) {
|
||
|
case 0x355:
|
||
|
bms.stateOfCharge = canMsg.data[1] << 8 + canMsg.data[0];
|
||
|
bms.stateOfHealth = canMsg.data[3] << 8 + canMsg.data[2];
|
||
|
break;
|
||
|
case 0x356:
|
||
|
bms.battVoltage = (canMsg.data[1] << 8 + canMsg.data[0])/100;
|
||
|
bms.battMilliAmp = (canMsg.data[3] << 8 + canMsg.data[2])*100;
|
||
|
bms.battTemp = ConvertTemp((canMsg.data[5] << 8 + canMsg.data[4])/10); // Convert to fahrenheit if SetOpion8 is set
|
||
|
break;
|
||
|
case 0x370:
|
||
|
case 0x371:
|
||
|
for(int i = 0; i < canMsg.can_dlc; i++) {
|
||
|
bms.name[i + (8 * canMsg.can_id & 0x1)] = canMsg.data[i]; // If can_id is 0x371 then fill from byte 8 onwards
|
||
|
}
|
||
|
bms.name[16] = 0; // Ensure that name is null terminated
|
||
|
break;
|
||
|
default:
|
||
|
String canMsg;
|
||
|
for(int i = 0; i < canMsg.can_dlc; i++) {
|
||
|
canMsg += String(canMsg.data[i], HEX);
|
||
|
}
|
||
|
AddLog(LOG_LEVEL_DEBUG, PSTR("MCP2515: Received message 0x%s from ID 0x%X", canMsg, canMsg.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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void MCP2515_Show(bool Json) {
|
||
|
if (Json) {
|
||
|
#ifdef MCP2515_BMS_CLIENT
|
||
|
ResponseAppend_P(PSTR(",\"MCP2515\":{\"SOC\":%d,\"SOH\":%d}"), bms.stateOfCharge, bms.stateOfHealth);
|
||
|
#endif // MCP2515_BMS_CLIENT
|
||
|
|
||
|
#ifdef USE_WEBSERVER
|
||
|
} else {
|
||
|
#ifdef MCP2515_BMS_CLIENT
|
||
|
WSContentSend_PD(HTTP_SNS_SOC, bms.name, bms.stateOfCharge);
|
||
|
WSContentSend_PD(HTTP_SNS_SOH, bms.name, bms.stateOfHealth);
|
||
|
WSContentSend_Voltage(bms.name, bms.battVoltage);
|
||
|
WSContentSend_CurrentMA(bms.name, bms.battMilliAmp);
|
||
|
WSContentSend_Temp(bms.name, bms.battTemp);
|
||
|
#endif // MCP2515_BMS_CLIENT
|
||
|
#endif // USE_WEBSERVER
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************************\
|
||
|
* Interface
|
||
|
\*********************************************************************************************/
|
||
|
|
||
|
bool Xsns89(uint8_t function)
|
||
|
{
|
||
|
bool result = false;
|
||
|
|
||
|
if (PinUsed(GPIO_MCP2515_CS, GPIO_ANY) && TasmotaGlobal.spi_enabled) {
|
||
|
switch (function) {
|
||
|
case FUNC_INIT:
|
||
|
MCP2515_Init();
|
||
|
break;
|
||
|
case FUNC_EVERY_SECOND:
|
||
|
MCP2515_Read();
|
||
|
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_MCP2515
|
||
|
#endif // USE_SPI
|