Skip to content

Commit

Permalink
Bumped version to 3.2.0 - Keep accumulated analytics data at reset.
Browse files Browse the repository at this point in the history
  • Loading branch information
Armin committed Jun 23, 2024
1 parent 63cc0e0 commit 250d65d
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 114 deletions.
4 changes: 2 additions & 2 deletions JK-BMSToPylontechCAN/JK-BMS.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void computeUpTimeString();
void printJKStaticInfo();
void printJKDynamicInfo();
void detectAndPrintAlarmInfo();
#if !defined(DISABLE_MONITORING)
#if defined(ENABLE_MONITORING)
void printCSVLine(char aLeadingChar = '\0');
#endif

Expand Down Expand Up @@ -190,7 +190,7 @@ struct JKComputedDataStruct {
int16_t Battery10MilliAmpere; // Charging is positive discharging is negative
float BatteryLoadCurrentFloat; // Ampere
int16_t BatteryLoadPower; // Watt Computed value, Charging is positive discharging is negative
int32_t BatteryCapacityAccumulator10MilliAmpere; // 500 Ah = 180,000,000 10MilliAmpereSeconds
int32_t BatteryCapacityAsAccumulator10MilliAmpere; // 500 Ah = 180,000,000 10MilliAmpereSeconds. Pre-computed capacity to compare with accumulator value.
bool BMSIsStarting; // True if SOC and Cycles are both 0, for around 16 seconds during JK-BMS startup.
};
extern struct JKComputedDataStruct JKComputedData; // All derived converted and computed data useful for display
Expand Down
25 changes: 14 additions & 11 deletions JK-BMSToPylontechCAN/JK-BMS.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ void printJKCellStatisticsInfo() {

void initializeComputedData() {
// Initialize capacity accumulator with sensible value
JKComputedData.BatteryCapacityAccumulator10MilliAmpere = (AMPERE_HOUR_AS_ACCUMULATOR_10_MILLIAMPERE / 100)
JKComputedData.BatteryCapacityAsAccumulator10MilliAmpere = (AMPERE_HOUR_AS_ACCUMULATOR_10_MILLIAMPERE / 100)
* sJKFAllReplyPointer->SOCPercent * JKComputedData.TotalCapacityAmpereHour;
}

Expand Down Expand Up @@ -592,12 +592,12 @@ void fillJKComputedData() {

JKComputedData.Battery10MilliAmpere = getCurrent(sJKFAllReplyPointer->Battery10MilliAmpere);
JKComputedData.BatteryLoadCurrentFloat = JKComputedData.Battery10MilliAmpere / 100.0;
JKComputedData.BatteryCapacityAccumulator10MilliAmpere += JKComputedData.Battery10MilliAmpere;
JKComputedData.BatteryCapacityAsAccumulator10MilliAmpere += JKComputedData.Battery10MilliAmpere;
if (lastJKReply.SOCPercent == 0 && sJKFAllReplyPointer->SOCPercent == 1) {
JK_INFO_PRINTLN(F("Reset capacity to 1%"));
// Reset capacity at transition from 0 to 1
JKComputedData.BatteryCapacityAccumulator10MilliAmpere = getOnePercentCapacityAsAccumulator10Milliampere();
JKLastPrintedData.BatteryCapacityAccumulator10MilliAmpere = JKComputedData.BatteryCapacityAccumulator10MilliAmpere;
JKComputedData.BatteryCapacityAsAccumulator10MilliAmpere = getOnePercentCapacityAsAccumulator10Milliampere();
JKLastPrintedData.BatteryCapacityAccumulator10MilliAmpere = JKComputedData.BatteryCapacityAsAccumulator10MilliAmpere;
}

// Serial.print("Battery10MilliAmpere=0x");
Expand Down Expand Up @@ -871,6 +871,9 @@ void printActiveState(bool aIsActive) {
Serial.print(F(" active"));
}

/*
* Called exclusively once by processJK_BMSStatusFrame()
*/
void printJKStaticInfo() {

Serial.println(F("*** BMS INFO ***"));
Expand Down Expand Up @@ -918,7 +921,7 @@ extern const char sCSVCaption[] PROGMEM;
void printJKDynamicInfo() {
JKReplyStruct *tJKFAllReplyPointer = sJKFAllReplyPointer;

#if !defined(DISABLE_MONITORING)
#if defined(ENABLE_MONITORING)
# if defined(MONOTORING_PERIOD_FAST)
// Print every dataset, every 2 seconds, and caption every minute
printCSVLine();
Expand All @@ -942,8 +945,8 @@ void printJKDynamicInfo() {
// Print +CSV line every percent of nominal battery capacity (TotalCapacityAmpereHour) for capacity to voltage graph
if (abs(
JKLastPrintedData.BatteryCapacityAccumulator10MilliAmpere
- JKComputedData.BatteryCapacityAccumulator10MilliAmpere) > getOnePercentCapacityAsAccumulator10Milliampere()) {
JKLastPrintedData.BatteryCapacityAccumulator10MilliAmpere = JKComputedData.BatteryCapacityAccumulator10MilliAmpere;
- JKComputedData.BatteryCapacityAsAccumulator10MilliAmpere) > getOnePercentCapacityAsAccumulator10Milliampere()) {
JKLastPrintedData.BatteryCapacityAccumulator10MilliAmpere = JKComputedData.BatteryCapacityAsAccumulator10MilliAmpere;
printCSVLine('+');
}

Expand Down Expand Up @@ -1003,7 +1006,7 @@ void printJKDynamicInfo() {
Serial.println(F("There is less than 10% capacity below 3.0V and 20% capacity below 3.2V."));
}
#endif
#if !defined(DISABLE_MONITORING) && !defined(MONOTORING_PERIOD_FAST)
#if defined(ENABLE_MONITORING) && !defined(MONOTORING_PERIOD_FAST)
/*
* Print CSV caption every 10 minute
*/
Expand Down Expand Up @@ -1097,7 +1100,7 @@ void printJKDynamicInfo() {
}
}

#if !defined(DISABLE_MONITORING)
#if defined(ENABLE_MONITORING)
const char sCSVCaption[] PROGMEM
= "Uptime[min];Cell_1;Cell_2;Cell_3;Cell_4;Cell_5;Cell_6;Cell_7;Cell_8;Cell_9;Cell_10;Cell_11;Cell_12;Cell_13;Cell_14;Cell_15;Cell_16;Voltage[mV];Current[A];Capacity[100mAh];SOC[%]";

Expand Down Expand Up @@ -1140,7 +1143,7 @@ void setCSVString() {
dtostrf(JKComputedData.BatteryLoadCurrentFloat, 4, 2, &tCurrentAsFloatString[0]);
sprintf_P(&sStringBuffer[tBufferIndex], PSTR("%u;%s;%ld;%d"), JKComputedData.BatteryVoltage10Millivolt * 10,
tCurrentAsFloatString,
JKComputedData.BatteryCapacityAccumulator10MilliAmpere / (AMPERE_HOUR_AS_ACCUMULATOR_10_MILLIAMPERE / 10), /* 100mAh units*/
JKComputedData.BatteryCapacityAsAccumulator10MilliAmpere / (AMPERE_HOUR_AS_ACCUMULATOR_10_MILLIAMPERE / 10), /* 100mAh units*/
sJKFAllReplyPointer->SOCPercent);
}
}
Expand All @@ -1153,7 +1156,7 @@ void printCSVLine(char aLeadingChar) {
setCSVString();
Serial.println(sStringBuffer);
}
#endif // !defined(DISABLE_MONITORING)
#endif // defined(ENABLE_MONITORING)

#include "LocalDebugLevelEnd.h"
#endif // _JK_BMS_HPP
129 changes: 71 additions & 58 deletions JK-BMSToPylontechCAN/JK-BMSToPylontechCAN.ino
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,10 @@
* --------------- GND
*/

/*
* Ideas:
* Balancing time per day / week / month etc.
*/
#include <Arduino.h>

#define VERSION_EXAMPLE "3.2.0"
// For full revision history see https://github.com/ArminJo/JK-BMSToPylontechCAN?tab=readme-ov-file#revision-history
#define MILLIS_IN_ONE_SECOND 1000L
#define VERSION_EXAMPLE "3.2.0"

/*
* If battery SOC is below this value, the inverter is forced to charge the battery from any available power source regardless of inverter settings.
Expand All @@ -124,10 +119,6 @@
*/
//#define MAX_CURRENT_MODIFICATION_LOWER_SOC_THRESHOLD_PERCENT 80 // Start SOC for linear reducing maximum current. Default 80
//#define MAX_CURRENT_MODIFICATION_MIN_CURRENT_TENTHS_OF_AMPERE 50 // Value of current at 100 % SOC. Units are 100 mA! Default 50
#if !defined(DO_NOT_SHOW_SHORT_CELL_VOLTAGES)
#define SHOW_SHORT_CELL_VOLTAGES // Print 3 digits cell voltage (value - 3.0 V) on Cell Info page. Enables display of up to 20 voltages or additional information.
#endif

//#define DEBUG // This enables debug output for all files - only for development
//#define STANDALONE_TEST // If activated, fixed BMS data is sent to CAN bus and displayed on LCD.
#if defined(STANDALONE_TEST)
Expand All @@ -137,35 +128,54 @@
# endif
#endif

#if defined(__AVR_ATmega644P__)
#define USE_LAYOUT_FOR_644_BOARD
#endif

/*
* Options to reduce program size
* Options to reduce program size / add optional features
*/
//#define ENABLE_MONITORING // Requires additional 846 bytes program space
#if defined(ENABLE_MONITORING)
# if !defined(MONOTORING_PERIOD_SECONDS)
#if FLASHEND > 0x7FFF // for more than 32k
#define ENABLE_MONITORING // Requires additional 858 bytes program space
#define SERIAL_INFO_PRINT // Requires additional 1684 bytes program space
#endif
#define KEEP_ANALYTICS_ACCUMULATED_DATA_AT_RESET // Requires additional 80 bytes program space

//#define DO_NOT_SHOW_SHORT_CELL_VOLTAGES // Saves 470 bytes program space
#if !defined(DO_NOT_SHOW_SHORT_CELL_VOLTAGES)
#define SHOW_SHORT_CELL_VOLTAGES // Print 3 digits cell voltage (value - 3.0 V) on Cell Info page. Enables display of up to 20 voltages or additional information.
#endif

#if defined(ENABLE_MONITORING) && !defined(MONOTORING_PERIOD_SECONDS)
//#define MONOTORING_PERIOD_FAST // If active, then print CSV line every 2 seconds, else every minute
#define MONOTORING_PERIOD_SLOW // If active, then print CSV line every hour, and CSV line every 10 minutes
# endif
#elif FLASHEND <= 0x7FFF // for 32k or less
#define DISABLE_MONITORING // Disables writing cell and current values CSV data to serial output. Saves 846 bytes program space. - currently activated to save program space.
#endif

#if !defined(SERIAL_INFO_PRINT) && !defined(STANDALONE_TEST) && FLASHEND <= 0x7FFF
#define NO_SERIAL_INFO_PRINT // Disables writing some info to serial output. Saves 974 bytes program space. - currently activated to save program space.
#define NO_SERIAL_INFO_PRINT // Disables writing some info to serial output. Saves 974 bytes program space.
#endif

#if !defined(ENABLE_LIFEPO4_PLAUSI_WARNING)
#define SUPPRESS_LIFEPO4_PLAUSI_WARNING // Disables warning on Serial out about using LiFePO4 beyond 3.0 v to 3.45 V.
#if defined(NO_SERIAL_INFO_PRINT)
#define JK_INFO_PRINT(...) void();
#define JK_INFO_PRINTLN(...) void();
#else
#define JK_INFO_PRINT(...) Serial.print(__VA_ARGS__);
#define JK_INFO_PRINTLN(...) Serial.println(__VA_ARGS__);
#endif
//#define NO_CAPACITY_35F_EXTENSIONS // Disables generating of frame 0x35F for total capacity. This additional frame is no problem for Deye inverters.
//#define NO_CAPACITY_379_EXTENSIONS // Disables generating of frame 0x379 for total capacity. This additional frame is no problem for Deye inverters.
//#define NO_BYD_LIMITS_373_EXTENSIONS // Disables generating of frame 0x373 for cell limits as sent by BYD battery. See https://github.com/dfch/BydCanProtocol/tree/main. This additional frame is no problem for Deye inverters.

//#define NO_CAPACITY_35F_EXTENSIONS // Disables generating of frame 0x35F for total capacity. This additional frame is no problem for Deye inverters. Saves 56 bytes program space.
//#define NO_CAPACITY_379_EXTENSIONS // Disables generating of frame 0x379 for total capacity. This additional frame is no problem for Deye inverters. Saves 24 bytes program space.
//#define NO_BYD_LIMITS_373_EXTENSIONS // Disables generating of frame 0x373 for cell limits as sent by BYD battery. See https://github.com/dfch/BydCanProtocol/tree/main. This additional frame is no problem for Deye inverters. Saves 200 bytes program space.
//#define NO_CELL_STATISTICS // Disables generating and display of cell balancing statistics. Saves 1628 bytes program space.
//#define NO_ANALYTICS // Disables generating, storing and display of SOC graph for Arduino Serial Plotter. Saves 3882 bytes program space.
//#define NO_ANALYTICS // Disables generating, storing and display of SOC graph for Arduino Serial Plotter. Saves 3856 bytes program space.
//#define USE_NO_LCD // Disables the code for the LCD display. Saves 25% program space on a Nano.

//#define USE_NO_COMMUNICATION_STATUS_LEDS // The code for the BMS and CAN communication status LED is deactivated and the pins are not switched to output

#if !defined(ENABLE_LIFEPO4_PLAUSI_WARNING)
#define SUPPRESS_LIFEPO4_PLAUSI_WARNING // Disables warning on Serial out about using LiFePO4 beyond 3.0 v to 3.45 V.
#endif

#if !defined(USE_NO_LCD)
#define USE_SERIAL_2004_LCD // Parallel or 1604 LCD not yet supported
#define LCD_MESSAGE_PERSIST_TIME_MILLIS 2000
Expand All @@ -188,8 +198,8 @@
# endif
#endif

// sStringBuffer is defined in JK-BMS_LCD.hpp if DISABLE_MONITORING and NO_ANALYTICS are defined
#if !defined(DISABLE_MONITORING)
// sStringBuffer is defined in JK-BMS_LCD.hpp if not ENABLE_MONITORING and NO_ANALYTICS are defined
#if defined(ENABLE_MONITORING)
char sStringBuffer[100]; // For cvs lines, "Store computed capacity" line and LCD rows
#elif !defined(NO_ANALYTICS)
char sStringBuffer[40]; // for "Store computed capacity" line, printComputedCapacity() and LCD rows
Expand All @@ -198,13 +208,12 @@ char sStringBuffer[40]; // for "Store computed capacity" line, p
/*
* Pin layout, may be adapted to your requirements
*/
//#define ANDRES_644_BOARD
#define BUZZER_PIN A2 // To signal errors
#define PAGE_SWITCH_DEBUG_BUTTON_PIN_FOR_INFO 2 // Button at INT0 / D2 for switching LCD pages - definition is not used in program, only for documentation.
// The standard RX of the Arduino is used for the JK_BMS connection.
#define JK_BMS_RX_PIN_FOR_INFO 0 // We use the Serial RX pin - definition is not used in program, only for documentation.
#if !defined(JK_BMS_TX_PIN) // Allow override by global symbol
# if defined(ANDRES_644_BOARD)
# if defined(USE_LAYOUT_FOR_644_BOARD)
#define JK_BMS_TX_PIN 12
# else
#define JK_BMS_TX_PIN 4
Expand All @@ -219,7 +228,7 @@ char sStringBuffer[40]; // for "Store computed capacity" line, p
#else
// BMS and CAN communication status LEDs
# if !defined(BMS_COMMUNICATION_STATUS_LED_PIN)
# if defined(ANDRES_644_BOARD)
# if defined(USE_LAYOUT_FOR_644_BOARD)
#define BMS_COMMUNICATION_STATUS_LED_PIN 14
#define CAN_COMMUNICATION_STATUS_LED_PIN 15
# else
Expand All @@ -239,7 +248,7 @@ char sStringBuffer[40]; // for "Store computed capacity" line, p

//#define TIMING_TEST
#if defined(TIMING_TEST)
# if defined(ANDRES_644_BOARD)
# if defined(USE_LAYOUT_FOR_644_BOARD)
#define TIMING_TEST_PIN 13
# else
#define TIMING_TEST_PIN 10 // is SS pin for SPI and must be used as OUTPUT (set by SPI.init())!
Expand All @@ -252,7 +261,7 @@ char sStringBuffer[40]; // for "Store computed capacity" line, p
* SPI: MOSI - 11, MISO - 12, SCK - 13. CS cannot be replaced by constant ground.
* I2C: SDA - A4, SCL - A5.
*/
#if defined(ANDRES_644_BOARD)
#if defined(USE_LAYOUT_FOR_644_BOARD)
#define SPI_CS_PIN 4 // !SS Must be specified before #include "MCP2515_TX.hpp"
#define SPI_MOSI_PIN_FOR_INFO 5 // Definition is not used in program, only for documentation.
#define SPI_MISO_PIN_FOR_INFO 6 // Definition is not used in program, only for documentation.
Expand All @@ -265,7 +274,7 @@ char sStringBuffer[40]; // for "Store computed capacity" line, p
#define SPI_MISO_PIN_FOR_INFO 12 // Definition is not used in program, only for documentation.
#define SPI_SCK_PIN_FOR_INFO 13 // Definition is not used in program, only for documentation.
# endif
#endif // defined(ANDRES_644_BOARD)
#endif // defined(USE_LAYOUT_FOR_644_BOARD)

/*
* Program timing, may be adapted to your requirements
Expand Down Expand Up @@ -302,6 +311,8 @@ uint8_t sBeepTimeoutCounter = 0;
#error NO_BEEP_ON_ERROR and ONE_BEEP_ON_ERROR are both defined, which makes no sense!
#endif

#define MILLIS_IN_ONE_SECOND 1000L

/*
* Page button stuff
*
Expand All @@ -326,14 +337,6 @@ bool readJK_BMSStatusFrame();
void processJK_BMSStatusFrame();
void handleFrameReceiveTimeout();

#if defined(NO_SERIAL_INFO_PRINT)
#define JK_INFO_PRINT(...) void();
#define JK_INFO_PRINTLN(...) void();
#else
#define JK_INFO_PRINT(...) Serial.print(__VA_ARGS__);
#define JK_INFO_PRINTLN(...) Serial.println(__VA_ARGS__);
#endif

#include "HexDump.hpp"
#include "digitalWriteFast.h"

Expand Down Expand Up @@ -367,13 +370,13 @@ bool sTimeoutJustdetected = false; // Is set to true at first detection
* CAN stuff
*/
#if !defined(NO_CAPACITY_35F_EXTENSIONS) // SMA Sunny Island inverters
//#define CAPACITY_35F_EXTENSIONS // Add frame 0x35F for total capacity for SMA Sunny Island inverters, which is no problem for Deye inverters.
#define CAPACITY_35F_EXTENSIONS // Add frame 0x35F for total capacity for SMA Sunny Island inverters, which is no problem for Deye inverters.
#endif
#if !defined(NO_CAPACITY_379_EXTENSIONS) // Luxpower SNA inverters
//#define CAPACITY_379_EXTENSIONS // Add frame 0x379 for total capacity for Luxpower SNA inverters, which is no problem for Deye inverters.
#define CAPACITY_379_EXTENSIONS // Add frame 0x379 for total capacity for Luxpower SNA inverters, which is no problem for Deye inverters.
#endif
#if !defined(NO_BYD_LIMITS_373_EXTENSIONS) // BYD
//#define BYD_LIMITS_373_EXTENSIONS // Add frame 0x373 for cell limits as sent by BYD battery, which is no problem for Deye inverters.
#define BYD_LIMITS_373_EXTENSIONS // Add frame 0x373 for cell limits as sent by BYD battery, which is no problem for Deye inverters.
#endif
#include "Pylontech_CAN.hpp" // Must be before #include "MCP2515_TX.hpp"
#define CAN_BAUDRATE 500000 // 500 kB
Expand Down Expand Up @@ -409,7 +412,7 @@ bool sBMSFrameProcessingComplete = false; // True if one status frame was receiv
#if TIMEOUT_MILLIS_FOR_FRAME_REPLY > MILLISECONDS_BETWEEN_JK_DATA_FRAME_REQUESTS
#error "TIMEOUT_MILLIS_FOR_FRAME_REPLY must be smaller than MILLISECONDS_BETWEEN_JK_DATA_FRAME_REQUESTS to detect timeouts"
#endif
bool sStaticInfoWasSent = false; // Flag to send static info only once after reset.
bool sInitialActionsPerformed = false; // Flag to send static info only once after reset.

void processReceivedData();
void printReceivedData();
Expand Down Expand Up @@ -492,14 +495,14 @@ delay(4000); // To be able to connect Serial monitor after reset or power up and
// Just to know which program is running on my Arduino
Serial.println(F("START " __FILE__ "\r\nVersion " VERSION_EXAMPLE " from " __DATE__));

#if defined(ANDRES_644_BOARD)
#if defined(USE_LAYOUT_FOR_644_BOARD)
JK_INFO_PRINTLN(F("Settings are for Andres 644 board"));
#endif

#if defined(DISABLE_MONITORING)
JK_INFO_PRINTLN(F("Monitoring disabled"));
#else
#if defined(ENABLE_MONITORING)
JK_INFO_PRINTLN(F("Monitoring enabled"));
#else
JK_INFO_PRINTLN(F("Monitoring disabled"));
#endif

#if defined(NO_CELL_STATISTICS)
Expand All @@ -517,7 +520,7 @@ delay(4000); // To be able to connect Serial monitor after reset or power up and
JK_INFO_PRINTLN(sBatteryESRMilliohm);

findFirstSOCDataPointIndex();
#if defined(ANDRES_644_BOARD)
#if defined(USE_LAYOUT_FOR_644_BOARD)
JK_INFO_PRINT(F("EEPROM SOC data start index="));
JK_INFO_PRINT(SOCDataPointsInfo.ArrayStartIndex);
JK_INFO_PRINT(F(" length="));
Expand Down Expand Up @@ -738,6 +741,7 @@ void loop() {
sResponseFrameBytesAreExpected = false; // No response!
sBMSFrameProcessingComplete = true; // for LCD timeout etc.
processReceivedData(); // for statistics
writeSOCData(); // for analytics tests
printBMSDataOnLCD(); // for switching between MAX and MIN display
delay(MILLISECONDS_BETWEEN_JK_DATA_FRAME_REQUESTS); // do it simple :-)

Expand Down Expand Up @@ -912,6 +916,21 @@ void processJK_BMSStatusFrame() {
JK_INFO_PRINTLN(F("Successfully receiving first BMS status frame after BMS communication timeout"));
}
processReceivedData();

if (!sInitialActionsPerformed) {
/*
* Do initialization once here
*/
sInitialActionsPerformed = true;
initializeComputedData();
Serial.println();
printJKStaticInfo();
#if !defined(NO_ANALYTICS)
initializeAnalytics();
JK_INFO_PRINTLN();
#endif
}

printReceivedData();
#if !defined(NO_ANALYTICS)
writeSOCData();
Expand Down Expand Up @@ -1013,16 +1032,10 @@ void processReceivedData() {
sCANDataIsInitialized = true; // One time flag
}

/*
* Called exclusively by processJK_BMSStatusFrame()
*/
void printReceivedData() {
if (!sStaticInfoWasSent) {
// Send static info only once after reset
sStaticInfoWasSent = true;
initializeComputedData();
#if !defined(NO_ANALYTICS)
initializeAnalytics();
#endif
printJKStaticInfo();
}
#if defined(USE_SERIAL_2004_LCD)
if (sLCDDisplayPageNumber != JK_BMS_PAGE_CAPACITY_INFO) {
// Do not interfere with plotter output
Expand Down
Loading

0 comments on commit 250d65d

Please sign in to comment.