Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions include/CONSTANTS.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ namespace constants {
constexpr uint32_t kModuleScanIntervalMs = 2000;

constexpr uint32_t kCanBitRate = 250000;
constexpr uint32_t kCanStatusIntervalMs = 500;
constexpr uint32_t kCanStatusMessageId = 0x070;
constexpr uint32_t kCanStatusIntervalMs = 500; // Rate at which data CAN status information is sent
constexpr uint32_t kCanChargerIntervalMs = 1000; // Rate to check if the charger sent a CAN message to toggle on charging mode
constexpr uint32_t kCanChargerTimeOutMs = 2000; // If the charger stops sending CAN messages then toggle off charging mode
constexpr uint32_t kCanChargerControlIntervalMs = 1000; // Rate at which the charger needs a CAN control message
constexpr uint32_t kChargerStatusUpdateIntervalMs = 250;
constexpr uint8_t kCanStatusPayloadLength = 8;

constexpr uint8_t kConnectDebounce = 2;
Expand All @@ -50,4 +53,12 @@ namespace constants {

constexpr std::size_t kLedCount = 13;
constexpr uint8_t kLedBrightness = 32;
} // namespace constants

// TODO constants for charging
constexpr float kSocChargingLimit = 1.0f; // SOC percentage charge limit
constexpr float kVoltageChargerMaxPackV = 445.0f; // Charger will shutoff if limit is over-reached and BMS or wiring fails; this parameter is sent to the charger via CAN in charger mode
constexpr uint16_t kStartBalancingMv = 3900; // when any cell reaches this value then balancing will start
constexpr float kMaxChargerPowerOutputW = 6550.0f; // ELCON charger specification
constexpr float kStartChargeA = 18.0f; // 1.0C begining charging amperage

} // namespace constants
68 changes: 68 additions & 0 deletions include/SOCLookUpTable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once

#include <cstddef>
#include <cstdint>

// SOC : State of charge
// Human readable state of the accumulator because the charging logic
// uses only the min cell voltage or the max cell voltage in the pack

namespace soclookuptable {
Comment thread
potter4500 marked this conversation as resolved.
constexpr uint8_t kNumLookUpPoints = 100;

constexpr float kVoltageTable[kNumLookUpPoints] = {
3150.0f, 3159.6f, 3169.2f, 3178.8f, 3188.4f, 3198.0f, 3207.6f, 3217.2f,
3226.8f, 3236.4f, 3246.0f, 3255.6f, 3265.2f, 3274.7f, 3284.3f, 3293.9f,
3303.5f, 3313.1f, 3322.7f, 3332.3f, 3341.9f, 3351.5f, 3361.1f, 3370.7f,
3380.3f, 3389.9f, 3399.5f, 3409.1f, 3418.7f, 3428.3f, 3437.9f, 3447.5f,
3457.1f, 3466.7f, 3476.3f, 3485.9f, 3495.5f, 3505.1f, 3514.6f, 3524.2f,
3533.8f, 3543.4f, 3553.0f, 3562.6f, 3572.2f, 3581.8f, 3591.4f, 3601.0f,
3610.6f, 3620.2f, 3629.8f, 3639.4f, 3649.0f, 3658.6f, 3668.2f, 3677.8f,
3687.4f, 3697.0f, 3706.6f, 3716.2f, 3725.8f, 3735.4f, 3744.9f, 3754.5f,
3764.1f, 3773.7f, 3783.3f, 3792.9f, 3802.5f, 3812.1f, 3821.7f, 3831.3f,
3840.9f, 3850.5f, 3860.1f, 3869.7f, 3879.3f, 3888.9f, 3898.5f, 3908.1f,
3917.7f, 3927.3f, 3936.9f, 3946.5f, 3956.1f, 3965.7f, 3975.3f, 3984.8f,
3994.4f, 4004.0f, 4013.6f, 4023.2f, 4032.8f, 4042.4f, 4052.0f, 4061.6f,
4071.2f, 4080.8f, 4090.4f, 4100.0f
};

constexpr float kSocTable[kNumLookUpPoints] = {
0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.8f, 0.9f, 1.1f,
1.3f, 1.5f, 1.7f, 1.9f, 2.1f, 2.4f, 2.7f, 2.9f, 3.3f, 3.6f,
4.0f, 4.4f, 4.8f, 5.3f, 5.8f, 6.3f, 6.8f, 7.5f, 8.1f, 8.8f,
9.5f, 10.3f, 11.2f, 12.0f, 13.0f, 14.0f, 15.0f, 16.2f, 17.3f, 18.6f,
19.9f, 21.3f, 22.7f, 24.2f, 25.8f, 27.4f, 29.1f, 30.8f, 32.6f, 34.5f,
36.4f, 38.3f, 40.3f, 42.3f, 44.4f, 46.4f, 48.5f, 50.6f, 52.7f, 54.8f,
56.9f, 58.9f, 61.0f, 63.0f, 64.9f, 66.9f, 68.7f, 70.6f, 72.4f, 74.1f,
75.8f, 77.4f, 78.9f, 80.4f, 81.8f, 83.1f, 84.4f, 85.6f, 86.8f, 87.9f,
88.9f, 89.9f, 90.8f, 91.7f, 92.5f, 93.3f, 94.0f, 94.7f, 95.3f, 95.9f,
96.5f, 97.0f, 97.5f, 97.9f, 98.3f, 98.7f, 99.1f, 99.4f, 99.7f, 100.0f,
};

} // State of charge lookup table

// Maps the first table (voltages) to the second table (percentage)
float ReadBMS::lookUpSOC(uint16_t cellMv) {
// check if value is out of range
if (cellMv <= soclookuptable::kVoltageTable[0]) {
return soclookuptable::kSocTable[0];
}
if (cellMv >= soclookuptable::kVoltageTable[soclookuptable::kNumLookUpPoints - 1]) {
return soclookuptable::kSocTable[soclookuptable::kNumLookUpPoints - 1];
}

// linear interpolation to map between the two tables
for (size_t i=0; i < soclookuptable::kNumLookUpPoints - 1; i++) {
if (cellMv >= soclookuptable::kVoltageTable[i] && cellMv <= soclookuptable::kVoltageTable[i+1]) {
float v1 = soclookuptable::kVoltageTable[i];
float v2 = soclookuptable::kVoltageTable[i + 1];
float soc1 = soclookuptable::kSocTable[i];
float soc2 = soclookuptable::kSocTable[i + 1];

return static_cast<float>((soc1 * (v1 - cellMv) + soc2 * (cellMv - v2)) / (v1 - v2));
}
}

// fallback
return 0.0;
}
8 changes: 0 additions & 8 deletions include/SystemStatus.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,7 @@ inline StatusMode evaluateVoltageStatus(const ModuleReadings& module) {
if (cellMv == adbms6830::BMSInterface::kInvalidCellValue) {
return StatusMode::BAD_DATA;
}
#if defined(DANGEROUS_MODE)
if (cellMv >= constants::kBalanceMaxCellMv) {
continue;
}
if (cellMv < constants::kCellVoltageErrorMinMv ||
(cellMv > constants::kCellVoltageErrorMaxMv && cellMv < constants::kBalanceMaxCellMv)) {
#else
if (cellMv < constants::kCellVoltageErrorMinMv || cellMv > constants::kCellVoltageErrorMaxMv) {
#endif
return StatusMode::ERROR;
}
if (cellMv < constants::kCellVoltageExhaustedMinMv || cellMv > constants::kCellVoltageWarningMaxMv) {
Expand Down
13 changes: 7 additions & 6 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
monitor_speed = 115200
upload_protocol = picotool
build_flags =
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
fastled/FastLED@^3.10.3
pierremolinaro/acan2517
pierremolinaro/acan2517FD
build_flags =
-DARDUINO_USB_CDC_ON_BOOT=1
lib_deps =
fastled/FastLED@^3.10.3
pierremolinaro/acan2517
pierremolinaro/acan2517FD
throwtheswitch/Unity@^2.6.1
43 changes: 43 additions & 0 deletions src/BMSControl.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "BMSControl.h"
#include "SOCLookUpTable.h"

const SPISettings ReadBMS::kBmsSpiSettings(1000000, MSBFIRST, SPI_MODE0);

Expand Down Expand Up @@ -691,3 +692,45 @@ void ReadBMS::copyModuleReadings(ModuleReadings& destination,
destination.cellVoltages = source.cellVoltages;
destination.thermistorTempsC = source.thermistorTempsC;
}

// Update State of charge struct data
ReadBMS::StateOfCharge ReadBMS::pollSOC() {
uint16_t lowestCellMv = UINT16_MAX;
uint16_t highestCellMv = 0;
float minStateOfCharge = 0.0f;
float maxStateOfCharge = 0.0f;

// get lastest module readings
updatePollData();

for (const ReadBMS::ModuleReadings &module : pollData_.modules)
{
if (!module.connected || !module.cellDataValid) {
continue;
}
Comment on lines +708 to +710

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentional to ignore bad cells? Might want to add a comment explaining why it's okay to ignore.


for (uint16_t cellMv : module.cellVoltages) {
if (cellMv == adbms6830::BMSInterface::kInvalidCellValue) {
continue;
}

if (cellMv < lowestCellMv) {
lowestCellMv = cellMv;
}

if (cellMv > highestCellMv) {
highestCellMv = cellMv;
}
}
}

minStateOfCharge = lookUpSOC(lowestCellMv);
maxStateOfCharge = lookUpSOC(highestCellMv);

StateOfCharge soc{};
soc.minSOC = minStateOfCharge;
soc.maxSOC = maxStateOfCharge;
soc.minCellMv = lowestCellMv;
soc.maxCellMv = highestCellMv;
return soc;
}
11 changes: 11 additions & 0 deletions src/BMSControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,21 @@ class ReadBMS {
std::array<adbms6830::BMSInterface::SiliconIdReadback, constants::kModuleCount> moduleSiliconIds{};
};

// To be used for charging logic and CAN msg data sent to the dashboard
struct StateOfCharge {
Comment thread
potter4500 marked this conversation as resolved.
float minSOC = 0.0f;
float maxSOC = 0.0f;
uint16_t minCellMv = 0;
uint16_t maxCellMv = 0;
};

ReadBMS();

void begin();
void pollBMS();
void updateBalancing(bool enabled);
const PollData& data() const;
StateOfCharge pollSOC(); // to pull SOC data for charging
LogSnapshot captureLogSnapshot() const;
static void logBalancingState(const LogSnapshot& snapshot, Stream& stream);
static void logConnectedModules(const LogSnapshot& snapshot, Stream& stream);
Expand Down Expand Up @@ -115,6 +124,8 @@ class ReadBMS {
const adbms6830::BMSInterface::ModuleData& source,
bool connected) const;

float lookUpSOC(uint16_t cellMv);

static const SPISettings kBmsSpiSettings;

adbms6830::ADBMS6830Driver mainBmsDriver_;
Expand Down
20 changes: 20 additions & 0 deletions src/MCP2517Can.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ class MCP2517Can {
Osc40MHzDiv2,
};

enum CanMsgId : uint32_t {
MotorControlCommand = 0x0C0, // Motor control command, BMS will switch to drive ready mode when this msg is recieved

BmsStatus = 0x070, // BMS status message
StateOfCharge = 0x072, // BMS state of charge

ChargerControl = 0x1806E5F4, // ELCON CAN 3865 charger control message id
ChargerStatus = 0x18FF50E5, // ELCON CAN 3865 charger status broadcast message
};

enum ChargerControl : bool {
ChargerStart = 0,
ChargerClose = 1,
};

enum ChargingMode : bool {
ChargingMode = 0,
HeatingMode = 1,
};

struct Message {
uint32_t id = 0;
bool extended = false;
Expand Down
Loading