Skip to content

Instantly share code, notes, and snippets.

@Corteil
Created May 20, 2023 08:20
Show Gist options
  • Save Corteil/f83ee4708a0838859fcd7e00524226ac to your computer and use it in GitHub Desktop.
Save Corteil/f83ee4708a0838859fcd7e00524226ac to your computer and use it in GitHub Desktop.
added monitoring function and created a simple menu.
// CAN readout of VW type 5 modules
// ------- Programa adaptado por Alfonso para comunicar las celdas SDI de VW a 15-04-2022
// ------- Funciona con un Arduino nano, y el MCP2515, la interrupción del integrado interrumpe algunos comandos, pero en general va respondiendo con 1 módulo solo conectado.
//
// ------- Program adapted by Alfonso to communicate VW SDI cells to 04-15-2022
// ------- It works with an Arduino nano, and the MCP2515, the IC interrupt interrupts some commands, but in general it responds with only 1 module connected.
//
//
//
// Link información muy útil: https://openinverter.org/wiki/VW_Hybrid_Battery_Packs
//
// Revisión programa 0.1: Balanceo para 1 módulo solo, no se ha probado con más módulos.
// La equivalencia con la tabla balanceBMS no coincide con el cmcID , por lo que he ajustado a ojo.
// Hay que hacer pruebas con todos los módulos que tengo de stock, para ver si todos los CMC responden bien a los comandos de balanceo.
//
//
// Balancing for 1 module only, has not been tested with more modules.
// The equivalence with the balanceBMS table does not match the cmcID , so I have adjusted it carefully.
// You have to do tests with all the modules that I have in stock, to see if all the CMCs respond well to the balancing commands.
// Brian's add ons.
// added simple menu enter 'b' for blancing mode or 'm' to monitor battery cell voltages.
//
// totaled cell voltages for battery voltage.
//
#include <mcp_can.h>
#include <SPI.h>
long unsigned int rxId;
unsigned char len = 0;
unsigned char rxBuf[8];
char msgString[128]; // Array to store serial string
unsigned long looptime = 0;
#define CAN0_INT 8 // Set INT to pin 2
MCP_CAN CAN0(10); // Set CS to pin 9 de[ending on shield used
int controlid = 0x0BA;
int controlid2 = 0x0BB;
long cmcID[] = {0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc};
long controlbms1[] = {0x1A55540A, 0x1A55540C, 0x1A555410, 0x1A555412, 0x1A555414, 0x1A555416, 0x1A555418 };
long balanceBMS[8][2] = {0x1A55540A, 0x1A55540B, 0x1A55540C, 0x1A55540D, 0x1A555410, 0x1A555411, 0x1A555412, 0x1A555413, 0x1A555414, 0x1A555415, 0x1A555416, 0x1A555417, 0x1A555418, 0x1A555419, 0x1A55541A, 0x1A55541B};
long moduleidstart, CMC;
char mes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
char mes2[8], mes3[8];
int minimoVoltage, inicioVoltage; // min voltage, max voltage
long mesBalanceo[14];
boolean necesitaBalanceo; //needs Balancing
bool monitorFlag = true;
uint16_t voltage[30][14];
int modulespresent = 0;
int sent = 0;
int debug = 0;
float totalVoltage = 0;
void setup()
{
Serial.begin(115200);
// Can0.begin(500000);
Serial.println("Program Start ...");
// Initialize MCP2515 running at 8MHz with a baudrate of 500kb/s and the masks and filters disabled.
if (CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_8MHZ) == CAN_OK)
Serial.println("MCP2515 Initialized Successfully!");
else
Serial.println("Error Initializing MCP2515...");
CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP2515 sends acks to received data.
pinMode(CAN0_INT, INPUT); // Configuring pin for /INT input
//Serial.println("Time Stamp,ID,Extended,Bus,LEN,D1,D2,D3,D4,D5,D6,D7,D8");
//Serial.println("Vcell 1, Vcell 2, Vcell 3, Vcell 4, Vcell 5, Vcell 6, Vcell 7, Vcell 8, Vcell 9, Vcell 10, Vcell 11, Vcell 12");
}
void loop()
{
if (CAN0.checkReceive() == 3){ // If CAN0_INT pin is low, read receive buffer
// Serial.print(CAN0.checkReceive()); // If it reads 3 it means that it has received a message, decode to see if it is relevant or not.
candecode();
}
if (Serial.available() > 0){
menu(); // A menu, which doesn't work, but it's in the code.
}
if (millis() > looptime + 1000){
Serial.println();
if (monitorFlag) {
Serial.println();
Serial.println("Monitor Mode On.");
}
else {-
Serial.println();
Serial.println("Balance Mode On.");
}
looptime = millis();
for(int i=0;i<8;i++){
if(CMC == cmcID[i]){
Serial.println();
Serial.print("CMC = ");
CMC = i;
Serial.println(CMC+1);
//Serial.println(" Looking for balance board ...");
}
}
for (int y = 0; y < modulespresent; y++){
//Serial.println();
Serial.print("Module ");
Serial.print(y+1);
Serial.print(" Voltages : ");
Serial.print("");
minimoVoltage = voltage[y][1]; // OK, save the minimum
// Serial.print( inicioVoltage);
totalVoltage = 0;
for (int i = 1; i < 13; i++){
//Serial.print("");
if(voltage[y][i] <= minimoVoltage){ // Find the minimum voltage
minimoVoltage = voltage[y][i]; // Save the minimum value
//Serial.print("_Min_");
}
Serial.print(voltage[y][i]);
Serial.print("mV ");
totalVoltage += voltage[y][i];
if( i<12 ){ Serial.print(","); }
}
Serial.println();
Serial.print("Cell blance status: ");
for (int i = 1; i < 13; i++){
if(voltage[y][i] > minimoVoltage + 4){ // Differential of 4mV to stop equalizing with respect to the minimum value
mesBalanceo[i-1] = 0x01;
necesitaBalanceo = 1; // needs balance
Serial.print("O");
}else{
mesBalanceo[i-1] = 0x00;
Serial.print(".");
}
}
Serial.println();
Serial.print("Battery Voltage: ");
Serial.print(totalVoltage/1000);
Serial.println("V.");
}
//Serial.print();
//Serial.print(" Minimum cell voltage = ");
//Serial.print(minimoVoltage);
//Serial.print(" Swinging: ");
//Serial.println(necesitaBalanceo);
if(necesitaBalanceo && !monitorFlag){
//Serial.println("Balance commard sent");
sendcommandbalance();
}
sendcommand();
}
}
void menu()
{
byte incomingByte = Serial.read(); // read the incoming byte:
switch (incomingByte)
{
case 's': //
sendcommand();
break;
case 'm': // monitor only mode
Serial.println("*** changed mode to montior only. ***");
monitorFlag = true;
break;
case 'b': // balance mode
Serial.println("*** changed mode to balance. ***");
monitorFlag = false;
break;
}
}
void sendcommandbalance(){
mes[0] = mesBalanceo[0];
mes[1] = mesBalanceo[1];
mes[2] = mesBalanceo[2];
mes[3] = mesBalanceo[3];
mes[4] = mesBalanceo[4];
mes[5] = mesBalanceo[5];
mes[6] = mesBalanceo[6];
mes[7] = mesBalanceo[7];
CAN0.sendMsgBuf(balanceBMS[CMC-1][0], 1, 8, mes);
mes[0] = mesBalanceo[8];
mes[1] = mesBalanceo[9];
mes[2] = mesBalanceo[10];
mes[3] = mesBalanceo[11];
mes[4] = 0x00;
mes[5] = 0x00;
mes[6] = 0x00;
mes[7] = 0x00;
CAN0.sendMsgBuf(balanceBMS[CMC-1][1], 1, 8, mes);
}
void sendcommand()
{
mes[0] = 0x00;
mes[1] = 0x00;
mes[2] = 0x00;
mes[3] = 0x00;
mes[4] = 0x00;
mes[5] = 0x00;
mes[6] = 0x00;
mes[7] = 0x00;
CAN0.sendMsgBuf(controlid, 0, 8, mes);
mes[0] = 0x45;
mes[1] = 0x01;
mes[2] = 0x28;
mes[3] = 0x00;
mes[4] = 0x00;
mes[5] = 0x00;
mes[6] = 0x00;
mes[7] = 0x30;
CAN0.sendMsgBuf(controlid, 0, 8, mes);
sent = 1;
// Serial.println();
// Serial.print("Command Sent");
// Serial.print(" Present Modules: ");
// Serial.print(modulespresent);
// Serial.println();
modulespresent = 0;
}
void candecode()
{
CAN0.readMsgBuf(&rxId, &len, rxBuf); // Read data: len = data length, buf = data byte(s)
//Serial.print(rxId, HEX);
//Serial.print("=");
//Serial.print(rxId);
//Serial.print(" / ");
if (sent == 1){
moduleidstart = rxId;
sent = 0;
debug = 0; // Activa depuración, para ver las tramas que recibe
}
if (rxId < 1024){
//Serial.print(" ");
sent = 0;
int ID = rxId - moduleidstart;
//Serial.print(ID);
//Serial.print(",");
CMC = moduleidstart;
switch (ID){
case 0:
voltage[modulespresent][1] = uint16_t(rxBuf[1] >> 4) + uint16_t(rxBuf[2] << 4) + 1000;
voltage[modulespresent][3] = uint16_t(rxBuf[5] << 4) + uint16_t(rxBuf[4] >> 4) + 1000;
voltage[modulespresent][2] = rxBuf[3] + uint16_t((rxBuf[4] & 0x0F) << 8) + 1000;
voltage[modulespresent][4] = rxBuf[6] + uint16_t((rxBuf[7] & 0x0F) << 8) + 1000;
break;
case 1:
voltage[modulespresent][5] = uint16_t(rxBuf[1] >> 4) + uint16_t(rxBuf[2] << 4) + 1000;
voltage[modulespresent][7] = uint16_t(rxBuf[5] << 4) + uint16_t(rxBuf[4] >> 4) + 1000;
voltage[modulespresent][6] = rxBuf[3] + uint16_t((rxBuf[4] & 0x0F) << 8) + 1000;
voltage[modulespresent][8] = rxBuf[6] + uint16_t((rxBuf[7] & 0x0F) << 8) + 1000;
break;
case 2:
voltage[modulespresent][9] = uint16_t(rxBuf[1] >> 4) + uint16_t(rxBuf[2] << 4) + 1000;
voltage[modulespresent][11] = uint16_t(rxBuf[5] << 4) + uint16_t(rxBuf[4] >> 4) + 1000;
voltage[modulespresent][10] = rxBuf[3] + uint16_t((rxBuf[4] & 0x0F) << 8) + 1000;
voltage[modulespresent][12] = rxBuf[6] + uint16_t((rxBuf[7] & 0x0F) << 8) + 1000;
modulespresent++;
sent =1;
break;
}
}
if (debug == 1) // If CAN0_INT pin is low, read receive buffer
{
Serial.print(millis());
if ((rxId & 0x80000000) == 0x80000000) // Determine if ID is standard (11 bits) or extended (29 bits)
sprintf(msgString, " Extended ID: 0x%.8lX DLC: %1d Data:", (rxId & 0x1FFFFFFF), len);
else
sprintf(msgString, ",0x%.3lX,false,%1d", rxId, len);
Serial.print(msgString);
if ((rxId & 0x40000000) == 0x40000000) { // Determine if message is a remote request frame.
sprintf(msgString, " REMOTE REQUEST FRAME");
Serial.print(msgString);
} else {
for (byte i = 0; i < len; i++) {
sprintf(msgString, " , 0x%.2X", rxBuf[i]);
Serial.print(msgString);
}
}
Serial.println();
}
}
/*********************************************************************************************************
END FILE
*********************************************************************************************************/
@Corteil
Copy link
Author

Corteil commented Aug 4, 2023

Updated access to public.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment