wattHour feature

This commit is contained in:
doppelhub
2025-06-05 18:28:49 -04:00
parent 4e57dbd90e
commit b802e6f713
9 changed files with 159 additions and 42 deletions

View File

@@ -7,8 +7,8 @@
#define config_h
#include "src/libcm.h"
#define FW_VERSION "0.9.5e"
#define BUILD_DATE "2024NOV27"
#define FW_VERSION "0.9.5f"
#define BUILD_DATE "2025JUN02"
//////////////////////////////////////////////////////////////////
@@ -25,19 +25,19 @@
//choose your battery type:
//#define BATTERY_TYPE_5AhG3 //if you're not sure, you probably have this battery
//#define BATTERY_TYPE_47AhFoMoCo
#define BATTERY_TYPE_47AhFoMoCo
//choose how many cells are in series:
//#define STACK_IS_48S //All 5AhG3 Kits & FoMoCo Kits with QTY4 modules
//#define STACK_IS_60S //FoMoCo Kits with QTY5 modules
#define STACK_IS_60S //FoMoCo Kits with QTY5 modules
//choose which grid charger is installed
//#define GRIDCHARGER_IS_NOT_1500W //All 5AhG3 Kits & 'standard' 47Ah FoMoCo Kits
//#define GRIDCHARGER_IS_1500W //'faster' 47Ah FoMoCo Kits only
#define GRIDCHARGER_IS_1500W //'faster' 47Ah FoMoCo Kits only
//choose ONE of the following
//must match actual "current hack" hardware configuration:
//#define SET_CURRENT_HACK_40 //actually +45.8% //most LiBCM users installed this hardware option
#define SET_CURRENT_HACK_40 //actually +45.8% //most LiBCM users installed this hardware option
//#define SET_CURRENT_HACK_20 //actually +25.0%
//#define SET_CURRENT_HACK_00 //OEM configuration (no current hack installed inside MCM)

View File

@@ -43,6 +43,7 @@ void loop()
LiDisplay_handler();
batteryHistory_handler();
cellBalance_handler();
energy_handler();
if (key_getSampledState() == KEYSTATE_ON)
{

View File

@@ -17,7 +17,7 @@ void serialUSB_waitForEmptyBuffer(void)
void serialUSB_waitForAnyUserInput(void)
{
const uint32_t maxTestPeriod_ms = 60000; //prevent overcharging modules if user walks off mid-test
const uint32_t maxTestPeriod_ms = 120000; //prevent overcharging modules if user walks off mid-test
uint32_t timestamp_testStartTime_ms = millis();
static bool hasTooMuchTimePassed = false;

View File

@@ -6,8 +6,8 @@
uint8_t adc_packVoltage_VpinIn(void);
int16_t adc_getLatestBatteryCurrent_amps (void);
int16_t adc_getLatestBatteryCurrent_deciAmps(void);
int16_t adc_getLatestBatteryCurrent_amps (void); //assist is positive
int16_t adc_getLatestBatteryCurrent_deciAmps(void); //regen is negative
int16_t adc_getLatestSpoofedCurrent_amps(void);
int16_t adc_getLatestSpoofedCurrent_deciAmps(void);

View File

@@ -19,15 +19,18 @@ const uint16_t EEPROM_LAST_USABLE_ADDRESS = 0xF9F; //atmega2560 has 4kB
//EEPROM address map:
const uint16_t EEPROM_ADDRESS_COMPILE_DATE = 0x000; //EEPROM range is 0x000:0x00B (12B)
const uint16_t EEPROM_ADDRESS_HOURS_SINCE_UPDATE = 0x00C; //EEPROM range is 0x00C:0x00D ( 2B)
const uint16_t EEPROM_ADDRESS_FIRMWARE_STATUS = 0x00E; //EEPROM range is 0x00E:0x00E ( 1B)
const uint16_t EEPROM_ADDRESS_BATTSCI_REGEN = 0x00F; //EEPROM range is 0x00F:0x00F ( 1B)
const uint16_t EEPROM_ADDRESS_BATTSCI_ASSIST = 0x010; //EEPROM range is 0x010:0x010 ( 1B)
const uint16_t EEPROM_ADDRESS_KEYON_DELAY = 0x011; //EEPROM range is 0x011:0x011 ( 1B)
const uint16_t EEPROM_ADDRESS_unused = 0x012; //EEPROM range is 0x012:0x012 ( 1B)
const uint16_t EEPROM_ADDRESS_FIRMWARE_STATUS = 0x00E; //EEPROM range is 0x00E ( 1B)
const uint16_t EEPROM_ADDRESS_BATTSCI_REGEN = 0x00F; //EEPROM range is 0x00F ( 1B)
const uint16_t EEPROM_ADDRESS_BATTSCI_ASSIST = 0x010; //EEPROM range is 0x010 ( 1B)
const uint16_t EEPROM_ADDRESS_KEYON_DELAY = 0x011; //EEPROM range is 0x011 ( 1B)
const uint16_t EEPROM_ADDRESS_NEXT_Wh_RECORD = 0x012; //EEPROM range is 0x012 ( 1B)
const uint16_t EEPROM_ADDRESS_COMPILE_TIME = 0x013; //EEPROM range is 0x013:0x01B ( 9B)
//this EEPROM space still available
const uint16_t EEPROM_ADDRESS_BATT_HISTORY = EEPROM_LAST_USABLE_ADDRESS - NUM_BYTES_BATTERY_HISTORY; //stored last
const uint16_t EEPROM_ADDRESS_BATT_HISTORY_UNINITIALIZED = EEPROM_ADDRESS_BATT_HISTORY - 1; //0xFF if updating from old version
//The following addresses start from end of EEPROM space and work backwards to beginning
const uint16_t EEPROM_ADDRESS_BATT_HISTORY = EEPROM_LAST_USABLE_ADDRESS - NUM_BYTES_BATTERY_HISTORY; //0xA57:0xF9F (1536B)
const uint16_t EEPROM_ADDRESS_BATT_HISTORY_UNINIT = EEPROM_ADDRESS_BATT_HISTORY - 1; //0xA56 ( 1B)
const uint16_t EEPROM_ADDRESS_Wh_RECORDS = EEPROM_ADDRESS_BATT_HISTORY_UNINIT - 1 - NUM_BYTES_Wh_HISTORY; //0x455:0xA55 (1536B)
const uint16_t EEPROM_ADDRESS_Wh_RECORDS_UNINIT = EEPROM_ADDRESS_Wh_RECORDS - 1; //0x454 ( 1B)
//compile date & time stored in EEPROM the last time the firmware was updated
uint8_t compileDateEEPROM[BYTES_IN_DATE] = {}; //JTS2doLater: Move these into single function (to save RAM)
@@ -262,26 +265,67 @@ void eeprom_batteryHistory_incrementValue(uint8_t indexTemperature, uint8_t inde
/////////////////////////////////////////////////////////////////////////////////////////
void eeprom_batteryHistory_reset(void)
void eeprom_eraseRange(uint16_t minAddress, uint16_t maxAddress)
{
uint16_t minAddress = EEPROM_ADDRESS_BATT_HISTORY;
uint16_t maxAddress = EEPROM_ADDRESS_BATT_HISTORY + NUM_BYTES_BATTERY_HISTORY;
Serial.print(F("\nInitializing battery history"));
Serial.print('\n');
for (uint16_t address=minAddress; address<=maxAddress; address++)
{
EEPROM.update(address, EEPROM_ADDRESS_FORMATTED_VALUE);
Serial.print('.');
if ((address & 0b1111111) == 0) //divisible by 128
if ((address & 0b01111111) == 0) //if divisible by 128
{
wdt_reset();
Serial.print('\n');
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
void eeprom_batteryHistory_reset(void)
{
uint16_t minAddress = EEPROM_ADDRESS_BATT_HISTORY;
uint16_t maxAddress = EEPROM_ADDRESS_BATT_HISTORY + NUM_BYTES_BATTERY_HISTORY;
Serial.print(F("\nInitializing battery history"));
eeprom_eraseRange(minAddress, maxAddress);
Serial.print(F("\nDone"));
}
/////////////////////////////////////////////////////////////////////////////////////////
void eeprom_wattHourHistory_storeSession(uint16_t assist_Wh, uint16_t regen_Wh, uint16_t time_s)
{
uint8_t recordNumber = EEPROM.read(EEPROM_ADDRESS_NEXT_Wh_RECORD);
uint16_t baseAddress = EEPROM_ADDRESS_Wh_RECORDS + recordNumber * NUM_BYTES_PER_Wh_RECORD;
writeToEEPROM_uint16(baseAddress + 0, assist_Wh);
writeToEEPROM_uint16(baseAddress + 2, regen_Wh );
writeToEEPROM_uint16(baseAddress + 4, time_s );
EEPROM.update(EEPROM_ADDRESS_NEXT_Wh_RECORD, ++recordNumber);
}
/////////////////////////////////////////////////////////////////////////////////////////
void eeprom_wattHourHistory_printSession(uint8_t sessionNumber)
{
//JTS2doNow: add print history here
}
/////////////////////////////////////////////////////////////////////////////////////////
void eeprom_wattHourHistory_reset(void)
{
uint16_t minAddress = EEPROM_ADDRESS_Wh_RECORDS;
uint16_t maxAddress = EEPROM_ADDRESS_Wh_RECORDS + NUM_BYTES_BATTERY_HISTORY;
Serial.print(F("\nInitializing energy history"));
eeprom_eraseRange(minAddress, maxAddress);
Serial.print(F("\nDone"));
}
@@ -332,12 +376,18 @@ void eeprom_begin(void)
{
eeprom_verifyDataValid();
if (EEPROM.read(EEPROM_ADDRESS_BATT_HISTORY_UNINITIALIZED) == EEPROM_ADDRESS_FACTORY_DEFAULT_VALUE)
if (EEPROM.read(EEPROM_ADDRESS_BATT_HISTORY_UNINIT) == EEPROM_ADDRESS_FACTORY_DEFAULT_VALUE)
{
eeprom_batteryHistory_reset();
EEPROM.update(EEPROM_ADDRESS_BATT_HISTORY_UNINITIALIZED, EEPROM_ADDRESS_FORMATTED_VALUE);
EEPROM.update(EEPROM_ADDRESS_BATT_HISTORY_UNINIT, EEPROM_ADDRESS_FORMATTED_VALUE);
}
if (EEPROM.read(EEPROM_ADDRESS_Wh_RECORDS_UNINIT) == EEPROM_ADDRESS_FACTORY_DEFAULT_VALUE)
{
eeprom_wattHourHistory_reset();
EEPROM.update(EEPROM_ADDRESS_Wh_RECORDS_UNINIT, EEPROM_ADDRESS_FORMATTED_VALUE);
}
}
/////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -5,24 +5,84 @@
#include "libcm.h"
uint16_t wattHours_assist = 0;
uint16_t wattHours_regen = 0;
/////////////////////////////////////////////////////////////////////////////////////////
void energy_integrate_centiJoules(void)
uint16_t energy_getAssist_Wh(void) { return wattHours_assist; }
uint16_t energy_getRegen_Wh (void) { return wattHours_regen; }
/////////////////////////////////////////////////////////////////////////////////////////
int32_t energySinceLastCall_uWh(void)
{
static uint32_t timestamp_lastCall_ms = 0;
uint32_t milliseconds_now = millis();
uint8_t periodBetweenCalls_ms = (uint8_t)(milliseconds_now - timestamp_lastCall_ms);
uint8_t period_ms = (uint8_t)(milliseconds_now - timestamp_lastCall_ms);
timestamp_lastCall_ms = milliseconds_now;
if (period_ms > (time_loopPeriod_ms_get() << 2)) { return 0; } //ignore keyOn, chargeOn
int32_t power_deciWatts = (int32_t)adc_getLatestBatteryCurrent_deciAmps() * LTC68042result_packVoltage_get();
int32_t uWh_sinceLastUpdate = (power_deciWatts * period_ms * 114) >> 12; //divide by 36
uint32_t power_deciWatts = LTC68042result_packVoltage_get() * (uint32_t)adc_getLatestBatteryCurrent_deciAmps();
// centiJoules_sinceLastUpdate = (power_deciWatts / 10) * (loopPeriod_ms / 1000) * (100 cJ/J)
// centiJoules_sinceLastUpdate = (power_deciWatts * loopPeriod_ms ) / 100
// centiJoules_sinceLastUpdate = (power_deciWatts * loopPeriod_ms ) * 0.01
// centiJoules_sinceLastUpdate = ((power_deciWatts * loopPeriod_ms ) * 41) >> 12
uint16_t centiJoules_sinceLastUpdate = (uint16_t)((power_deciWatts * periodBetweenCalls_ms ) * 41) >> 12;
return uWh_sinceLastUpdate;
}
/////////////////////////////////////////////////////////////////////////////////////////
void energy_keyOn(void)
{
wattHours_assist = 0;
wattHours_regen = 0;
}
/////////////////////////////////////////////////////////////////////////////////////////
void accumulate_uWh_to_Wh(void)
{
static uint32_t uWh_remainder_assist = 0;
static uint32_t uWh_remainder_regen = 0;
int32_t uWh_thisLoop = energySinceLastCall_uWh();
if (uWh_thisLoop < 0)
{
//regen
uint32_t uWh_helper = uWh_remainder_regen - uWh_thisLoop; //adding, uWh_thisLoop is negative
while (uWh_helper >= 1000000)
{
uWh_helper -= 1000000;
if (wattHours_regen < 32767) { wattHours_regen++; } //MSb stores charge source: grid or regen
}
uWh_remainder_regen = uWh_helper;
}
else
{
//assist
uint32_t uWh_helper = uWh_remainder_assist + uWh_thisLoop;
while (uWh_helper >= 1000000)
{
uWh_helper -= 1000000;
if (wattHours_assist < 65535) { wattHours_assist++; }
}
uWh_remainder_assist = uWh_helper;
}
}
/////////////////////////////////////////////////////////////////////////////////////////
void energy_handler(void)
{
if ((key_getSampledState() == KEYSTATE_ON) ||
(gpio_isGridChargerChargingNow() == YES ) )
{
accumulate_uWh_to_Wh();
}
}
/////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -4,6 +4,14 @@
#ifndef wattHours_h
#define wattHours_h
void energy_integrate_centiJoules(void);
#define NUM_BYTES_PER_Wh_RECORD 6
#define NUM_Wh_RECORDS 256
#define NUM_BYTES_Wh_HISTORY (NUM_BYTES_PER_Wh_RECORD * NUM_Wh_RECORDS)
uint16_t energy_getAssist_Wh(void);
uint16_t energy_getRegen_Wh (void);
void energy_keyOn(void);
void energy_handler(void);
#endif

View File

@@ -45,6 +45,7 @@ void key_handleKeyEvent_on(void)
LTC68042configure_programVolatileDefaults(); //turn discharge resistors off, set ADC LPF, etc.
LTC68042configure_handleKeyStateChange();
vPackSpoof_handleKeyON();
energy_keyOn();
LED(1,HIGH);
time_latestKeyOn_ms_set(millis()); //MUST RUN LAST!

View File

@@ -163,12 +163,9 @@ bool lcd_printWattHours(void)
lcd2.setCursor(12,3);
uint16_t wattHoursAssist = 12345; //JTS2doNow: Add total watt hour math function
uint16_t wattHoursRegen = 5; //JTS2doNow: Add total watt hour math function
uint16_t wattHours_new = 0;
if (cycleFrameNumber == CYCLEFRAME_A) { wattHours_new = wattHoursAssist; }
else if (cycleFrameNumber == CYCLEFRAME_B) { wattHours_new = wattHoursRegen; }
if (cycleFrameNumber == CYCLEFRAME_A) { wattHours_new = energy_getAssist_Wh(); }
else if (cycleFrameNumber == CYCLEFRAME_B) { wattHours_new = energy_getRegen_Wh(); }
if (wattHours_new != wattHours_onScreen)
@@ -444,7 +441,7 @@ bool lcd_printCurrent(void)
else { lcd2.print(' '); }
#elif defined DISPLAY_POSITIVE_SIGN_DURING_ASSIST
if (deciAmps > 0) { lcd2.print('+'); } //When discharging battery (i.e. assist), we display '+' symbol
else if (deciAmps < 0) { lcd2.print('-'); } //When charging battery (i.e. regen ), we display '+' symbol
else if (deciAmps < 0) { lcd2.print('-'); } //When charging battery (i.e. regen ), we display '-' symbol
else { lcd2.print(' '); }
#endif