International shipping
kelingyizhi supply professional and honest service.
- items may be subject to customs processing depending on the item's customs value.
Sellers declare the item's customs value and must comply with customs declaration laws.
As the buyer, you should be aware of possible:
' Delays
from customs inspection.
Are you interested in learning more about eltek smartpack controller? Contact us today to secure an expert consultation!
' Import duties
and taxes which buyers must pay.
' Brokerage
fees payable at the point of delivery.
Your country's customs office can offer more details, or visit eBay's page on
international trade.
Code: Select all
Want more information on huawei rectifier module capacity? Feel free to contact us.
// Include libraries
#include <mcp_can.h>
#include <SPI.h>
#include <Wire.h>
enum State_enum
{
START, SELECT, PLOT, INIT, MAIN, DONE, STOP, END
};
// Stated declaration
State_enum state;
// Constant declaration
// newData MASK
const uint8_t statusFPMaster = B;
const uint8_t statusFPSlave1 = B;
const uint8_t statusFPSlave2 = B;
const uint8_t statusBMSMain = B;
const uint8_t statusBMSHiLo = B;
const uint8_t statusBMStemp = B;
const uint8_t statusFPall = B;
const uint8_t statusAllCAN = B;
// stopReason MASK
const uint8_t stopDone = B;
const uint8_t stopButton = B;
const uint8_t stopLowVoltage = B;
const uint8_t stopTemperature = B;
const uint8_t stopBMS = B;
const uint8_t stopWatchdog = B;
// chargerState MASK
const uint8_t chargerStateCurrentLimiting = B;
const uint8_t chargerStateCurrentRegulating = B;
const uint8_t chargerStateEndVoltage = B;
// Timing constants
const uint32_t timePeriod100ms = 100; // Delay 100ms
const uint32_t timePeriod1s = ; // Delay, 1s
const uint32_t timePeriod3s = ; // Delay 3s
const uint32_t timePeriod5s = ; // Delay, 5s
const uint32_t timePeriod30s = ; // Delay 30s
// Configuration
const uint16_t startVoltage = ; // Set start voltage to 43.70 V (divide by 100)
const uint16_t initialLimitVoltage = ; // Set switch voltage to 49.00 V (divide by 100), must be lower than end voltage
const uint16_t endVoltage = ; // Set end voltage to 50.40 V (divide by 100)
const uint16_t overVoltage = ; // Set the overVoltage protection limit at 51.10 V (divide by 100)
const uint16_t maxCurrent1x10AAC = 135; // set charge current to 13.5 A (divide by 10) for max 10AAC at 230V
const uint16_t maxCurrent1x16AAC = 216; // set charge current to 21.6 A (divide by 10) for max 16AAC at 230V
const uint16_t maxCurrent3x16AAC = 600; // set charge current to 60.0 A (divide by 10), no limit, for 3-phase 16A
const uint16_t maxCurrent = 700; // Set slave maximum current to 70.0 A (divide by 10)
const uint16_t endCurrent = 10; // Set endCurrent to 1.0 A (divide by 10)
const uint16_t lowestCellVoltageLimit = ; // Set the lowest voltage acceptable to allow charging
const int16_t maxAllowedTemperature = 70; // Set max allowed BMS card temperature
const int8_t temperatureCorrection[4] = {1, 5, 0, 1};
// Display commands
const uint8_t address = 0x3C;
const uint8_t commands = 0x00;
const uint8_t onecommand = 0x80;
const uint8_t data = 0x40;
const uint8_t onedata = 0xC0;
// Variable declaration
// For FlatPacks
uint8_t mainBMS[8];
uint8_t highLowBMS[8];
uint8_t tempBMS[8];
uint16_t measuredMasterCurrent;
uint8_t newData = 0;
uint8_t stopReason = 0;
uint8_t chargerState = 0;
// Set pin number
const uint8_t SPI_CS_PIN0 = 10; // CAN0, BMS
const uint8_t SPI_CS_PIN1 = 15; // CAN1, FlatPack Master (Current limiting)
const uint8_t SPI_CS_PIN2 = 14; // CAN2, FlatPack Slave 1
const uint8_t SPI_CS_PIN3 = 16; // CAN3, FlatPack Slave 2
const uint8_t threePhasePIN = 17; // Input PIN for 3-phase detection
const uint8_t contactorPIN = 9; // Contactor control and +12V supply to BMS
const uint8_t buttonPIN = 8; // Button to select charge current
// Set CS pin for CAN-buses
MCP_CAN CAN0(SPI_CS_PIN0);
MCP_CAN CAN1(SPI_CS_PIN1);
MCP_CAN CAN2(SPI_CS_PIN2);
MCP_CAN CAN3(SPI_CS_PIN3);
// Function declaration
// For FlatPack
void chargerMain();
void voltageControl();
uint16_t chargeCurrentCalc();
void checkLowestCell();
int16_t checkTemperature();
void updateDisplay();
void plotChargerState();
void stopCharging();
void BMSStopCharging();
void readCAN();
uint16_t readMasterCAN();
uint16_t readSlave1CAN();
uint16_t readSlave2CAN();
void sendCAN();
void sendFlatPackCAN();
void sendMasterCAN();
void sendSlave1CAN();
void sendSlave2CAN();
// HMI
void clearDisplay();
void initDisplay();
void plotChar();
void plotText();
void plotValue();
void moveMarker();
uint16_t selectCurrent();
void buttonStopCharging();
// OLED display
// Character set for text - stored in program memory
const uint8_t CharMap[96][6] PROGMEM = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // 32: "Space"
{ 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00 }, // 33: !
{ 0x00, 0x07, 0x00, 0x07, 0x00, 0x00 }, // 34: "
{ 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00 }, // 35: #
{ 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00 }, // 36: $
{ 0x23, 0x13, 0x08, 0x64, 0x62, 0x00 }, // 37: %
{ 0x36, 0x49, 0x56, 0x20, 0x50, 0x00 }, // 38: &
{ 0x00, 0x08, 0x07, 0x03, 0x00, 0x00 }, // 39: '
{ 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00 }, // 40: (
{ 0x00, 0x41, 0x22, 0x1C, 0x00, 0x00 }, // 41: )
{ 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00 }, // 42: *
{ 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00 }, // 43: +
{ 0x00, 0x80, 0x70, 0x30, 0x00, 0x00 }, // 44: ,
{ 0x08, 0x08, 0x08, 0x08, 0x08, 0x00 }, // 45: -
{ 0x00, 0x00, 0x60, 0x60, 0x00, 0x00 }, // 46: .
{ 0x20, 0x10, 0x08, 0x04, 0x02, 0x00 }, // 47: /
{ 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00 }, // 48: 0
{ 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00 }, // 49: 1
{ 0x72, 0x49, 0x49, 0x49, 0x46, 0x00 }, // 50: 2
{ 0x21, 0x41, 0x49, 0x4D, 0x33, 0x00 }, // 51: 3
{ 0x18, 0x14, 0x12, 0x7F, 0x10, 0x00 }, // 52: 4
{ 0x27, 0x45, 0x45, 0x45, 0x39, 0x00 }, // 53: 5
{ 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00 }, // 54: 6
{ 0x41, 0x21, 0x11, 0x09, 0x07, 0x00 }, // 55: 7
{ 0x36, 0x49, 0x49, 0x49, 0x36, 0x00 }, // 56: 8
{ 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00 }, // 57: 9
{ 0x00, 0x00, 0x14, 0x00, 0x00, 0x00 }, // 58: :
{ 0x00, 0x40, 0x34, 0x00, 0x00, 0x00 }, // 59: ;
{ 0x00, 0x08, 0x14, 0x22, 0x41, 0x00 }, // 60: <
{ 0x14, 0x14, 0x14, 0x14, 0x14, 0x00 }, // 61: =
{ 0x00, 0x41, 0x22, 0x14, 0x08, 0x00 }, // 62: >
{ 0x02, 0x01, 0x59, 0x09, 0x06, 0x00 }, // 63: ?
{ 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x00 }, // 64: @
{ 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00 }, // 65: A
{ 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00 }, // 66: B
{ 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00 }, // 67: C
{ 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00 }, // 68: D
{ 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00 }, // 69: E
{ 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00 }, // 70: F
{ 0x3E, 0x41, 0x41, 0x51, 0x73, 0x00 }, // 71: G
{ 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00 }, // 72: H
{ 0x00, 0x41, 0x7F, 0x41, 0x00, 0x00 }, // 73: I
{ 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00 }, // 74: J
{ 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00 }, // 75: K
{ 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00 }, // 76: L
{ 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x00 }, // 77: M
{ 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00 }, // 78: N
{ 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00 }, // 79: O
{ 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00 }, // 80: P
{ 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00 }, // 81: Q
{ 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00 }, // 82: R
{ 0x26, 0x49, 0x49, 0x49, 0x32, 0x00 }, // 83: S
{ 0x03, 0x01, 0x7F, 0x01, 0x03, 0x00 }, // 84: T
{ 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00 }, // 85: U
{ 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00 }, // 86: V
{ 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00 }, // 87: W
{ 0x63, 0x14, 0x08, 0x14, 0x63, 0x00 }, // 88: X
{ 0x03, 0x04, 0x78, 0x04, 0x03, 0x00 }, // 89: Y
{ 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00 }, // 90: Z
{ 0x00, 0x7F, 0x41, 0x41, 0x41, 0x00 }, // 91: [
{ 0x02, 0x04, 0x08, 0x10, 0x20, 0x00 }, // 92: backslash
{ 0x00, 0x41, 0x41, 0x41, 0x7F, 0x00 }, // 93: ]
{ 0x04, 0x02, 0x01, 0x02, 0x04, 0x00 }, // 94: ^
{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00 }, // 95: _
{ 0x00, 0x03, 0x07, 0x08, 0x00, 0x00 }, // 96: `
{ 0x20, 0x54, 0x54, 0x78, 0x40, 0x00 }, // 97: a
{ 0x7F, 0x28, 0x44, 0x44, 0x38, 0x00 }, // 98: b
{ 0x38, 0x44, 0x44, 0x44, 0x28, 0x00 }, // 99: c
{ 0x38, 0x44, 0x44, 0x28, 0x7F, 0x00 }, // 100: d
{ 0x38, 0x54, 0x54, 0x54, 0x18, 0x00 }, // 101: e
{ 0x00, 0x08, 0x7E, 0x09, 0x02, 0x00 }, // 102: f
{ 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x00 }, // 103: g
{ 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00 }, // 104: h
{ 0x00, 0x44, 0x7D, 0x40, 0x00, 0x00 }, // 105: i
{ 0x20, 0x40, 0x40, 0x3D, 0x00, 0x00 }, // 106: j
{ 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00 }, // 107: k
{ 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00 }, // 108: l
{ 0x7C, 0x04, 0x78, 0x04, 0x78, 0x00 }, // 109: m
{ 0x7C, 0x08, 0x04, 0x04, 0x78, 0x00 }, // 110: n
{ 0x38, 0x44, 0x44, 0x44, 0x38, 0x00 }, // 111: o
{ 0xFC, 0x18, 0x24, 0x24, 0x18, 0x00 }, // 112: p
{ 0x18, 0x24, 0x24, 0x18, 0xFC, 0x00 }, // 113: q
{ 0x7C, 0x08, 0x04, 0x04, 0x08, 0x00 }, // 114: r
{ 0x48, 0x54, 0x54, 0x54, 0x24, 0x00 }, // 115: s
{ 0x04, 0x04, 0x3F, 0x44, 0x24, 0x00 }, // 116: t
{ 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00 }, // 117: u
{ 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00 }, // 118: v
{ 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00 }, // 119: w
{ 0x44, 0x28, 0x10, 0x28, 0x44, 0x00 }, // 120: x
{ 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00 }, // 121: y
{ 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00 }, // 122: z
{ 0x00, 0x08, 0x36, 0x41, 0x00, 0x00 }, // 123: {
{ 0x00, 0x00, 0x77, 0x00, 0x00, 0x00 }, // 124: |
{ 0x00, 0x41, 0x36, 0x08, 0x00, 0x00 }, // 125: }
{ 0x00, 0x06, 0x09, 0x06, 0x00, 0x00 }, // 126: degree symbol = '~'
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 } // 127: DEL
};
/********
* SETUP *
********/
void setup()
{
pinMode(buttonPIN, INPUT_PULLUP);
pinMode(contactorPIN, OUTPUT);
digitalWrite(contactorPIN, LOW);
Wire.begin();
initDisplay();
clearDisplay();
//Serial.begin();
while (CAN_OK != CAN0.begin(MCP_STDEXT, CAN_250KBPS, MCP_8MHZ)) // Init car CAN bus : baudrate = 250k
{ delay(100); }
CAN0.init_Mask(0,0,0x07FF); // Init first mask
CAN0.init_Filt(0,0,0x); // Init first filter
CAN0.init_Filt(1,0,0x); // Init second filter
CAN0.init_Mask(1,0,0x07FF); // Init second mask
CAN0.init_Filt(2,0,0x); // Init third filter
CAN0.init_Filt(3,0,0x); // Init fouth filter
CAN0.init_Filt(4,0,0x); // Init fifth filter
CAN0.init_Filt(5,0,0x); // Init sixth filter
CAN0.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP sends acks to received data.
while (CAN_OK != CAN1.begin(MCP_STDEXT, CAN_125KBPS, MCP_8MHZ)) // Init FlatPack Master CAN bus : baudrate = 125k
{ delay(100); }
// These are the status messages ( is not current-limiting, is current limiting = busy with walkin, C in input voltage low)
CAN1.init_Mask(0,1,0x1FFFFFFF); // Init first mask
CAN1.init_Filt(0,1,0x); // Init first filter
CAN1.init_Filt(1,1,0x); // Init second filter
CAN1.init_Mask(1,1,0x1FFFFFFF); // Init second mask
CAN1.init_Filt(2,1,0x); // Init third filter
CAN1.init_Filt(3,1,0x); // Init fouth filter
CAN1.init_Filt(4,1,0x); // Init fifth filter
CAN1.init_Filt(5,1,0x); // Init sixth filter
CAN1.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP sends acks to received data.
while (CAN_OK != CAN2.begin(MCP_STDEXT, CAN_125KBPS, MCP_8MHZ)) // Init FlatPack Slave 1 CAN bus : baudrate = 125k
{ delay(100); }
// These are the status messages ( is not current-limiting, is current limiting = busy with walkin, C in input voltage low)
CAN2.init_Mask(0,1,0x1FFFFFFF); // Init first mask
CAN2.init_Filt(0,1,0x); // Init first filter
CAN2.init_Filt(1,1,0x); // Init second filter
CAN2.init_Mask(1,1,0x1FFFFFFF); // Init second mask
CAN2.init_Filt(2,1,0x); // Init third filter
CAN2.init_Filt(3,1,0x); // Init fouth filter
CAN2.init_Filt(4,1,0x); // Init fifth filter
CAN2.init_Filt(5,1,0x); // Init sixth filter
CAN2.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP sends acks to received data.
while (CAN_OK != CAN3.begin(MCP_STDEXT, CAN_125KBPS, MCP_8MHZ)) // Init FlatPack Slave 2 CAN bus : baudrate = 125k
{ delay(100); }
// These are the status messages ( is not current-limiting, is current limiting = busy with walkin, C in input voltage low)
CAN3.init_Mask(0,1,0x1FFFFFFF); // Init first mask
CAN3.init_Filt(0,1,0x); // Init first filter
CAN3.init_Filt(1,1,0x); // Init second filter
CAN3.init_Mask(1,1,0x1FFFFFFF); // Init second mask
CAN3.init_Filt(2,1,0x); // Init third filter
CAN3.init_Filt(3,1,0x); // Init fouth filter
CAN3.init_Filt(4,1,0x); // Init fifth filter
CAN3.init_Filt(5,1,0x); // Init sixth filter
CAN3.setMode(MCP_NORMAL); // Set operation mode to normal so the MCP sends acks to received data.
state = START;
}
/*******
* LOOP *
*******/
void loop()
{
readCAN();
uint16_t measuredMasterVoltage = readMasterCAN();
uint16_t measuredSlave1Voltage = readSlave1CAN();
uint16_t measuredSlave2Voltage = readSlave2CAN();
chargerMain(measuredMasterVoltage, measuredSlave1Voltage, measuredSlave2Voltage);
}
/************************************************************************
* Sets the output both master and slave. Checks if charging is complete *
************************************************************************/
void chargerMain(uint16_t measuredMasterVoltage, uint16_t measuredSlave1Voltage, uint16_t measuredSlave2Voltage)
{
static uint16_t startCapacity;
static uint16_t selectedMaxCurrent = 0;
uint16_t endCapacity;
switch (state)
{
case START:
plotText("U:000.0V T:00", 0, 0);
plotChar(126, 13, 0); // Degree symbol
plotChar('C', 14, 0);
plotText("Is:00.0A I:00.0A", 0, 1);
plotText("Cs:00.0Ah C:00.0Ah", 0, 2);
plotText("Status:", 0, 3);
plotChar('*', 0, 4);
plotText("IAC=1x10A", 2, 4);
plotText("IAC=1x16A", 2, 5);
plotText("IAC=3x16A", 2, 6);
state = SELECT;
break;
case SELECT:
static uint32_t lastMillisTimer = millis(); // Reset timer
selectedMaxCurrent = selectCurrent();
if(selectedMaxCurrent != 0)
{
state = PLOT;
}
if((millis() - lastMillisTimer) > timePeriod30s)
{
selectedMaxCurrent = maxCurrent1x10AAC;
state = PLOT;
}
break;
case PLOT:
plotText(" ", 0, 4);
plotText(" ", 0, 5);
plotText(" ", 0, 6);
plotValue(selectedMaxCurrent, 10.0, 1, 3, 1);
state = INIT;
break;
case INIT:
digitalWrite(contactorPIN, HIGH); // Turn on contactor and +12V supply to BMS
sendFlatPackCAN(startVoltage, startVoltage, selectedMaxCurrent); // Send data
if((newData & statusAllCAN) == statusAllCAN) // All FlatPacks and BMS are responding
{
startCapacity = (mainBMS[4] << 8) + mainBMS[5]; // Battery pack capacity
plotValue(startCapacity, 10.0, 1, 3, 2);
state = MAIN;
}
break;
case MAIN:
if((newData & statusAllCAN) == statusAllCAN) // All FlatPacks and BMS are responding
{
static uint32_t lastMillisDo = millis();
if(millis() - lastMillisDo >= timePeriod1s)
{
lastMillisDo = millis(); // Reset timer
voltageControl(selectedMaxCurrent, measuredMasterVoltage);
checkLowestCell();
int16_t maxTemperature = checkTemperature();
updateDisplay(maxTemperature);
}
if(((measuredMasterVoltage + measuredSlave1Voltage + measuredSlave2Voltage) >= (endVoltage * 3)) && (measuredMasterCurrent <= endCurrent)) // Check if charging is complete
{
stopReason = stopDone;
state = STOP;
}
}
buttonStopCharging();
BMSStopCharging();
break;
case STOP:
sendFlatPackCAN(startVoltage, startVoltage, endCurrent); // Send data to stop FlatPack power output
delay();
endCapacity = (mainBMS[4] << 8) + mainBMS[5]; // Battery pack capacity
if(stopReason & stopDone) // Check if battery capacity needs reset
{
while(!(mainBMS[6] & B)) // Do until battery capacity is reset
{
sendCAN(); // Reset BMS battery capacity
delay(100);
readCAN(); // Readback to check reset
}
}
stopCharging(startCapacity, endCapacity);
state = END;
break;
case END:
break;
default:
// Error
break;
}
}
/******************************************
* Sets the FP output voltages and current *
******************************************/
void voltageControl(uint16_t selectedMaxCurrent, uint16_t measuredMasterVoltage)
{
static uint16_t setMasterVoltage = startVoltage;
static uint16_t setSlaveVoltage = startVoltage;
static uint16_t limitVoltage = initialLimitVoltage;
((setMasterVoltage + 10) < endVoltage) ? setMasterVoltage += 10 : setMasterVoltage = endVoltage; // Increases setMasterVoltage with 100mV
if(measuredMasterVoltage >= limitVoltage)
{
((setSlaveVoltage + 10) < endVoltage) ? setSlaveVoltage += 10 : setSlaveVoltage = endVoltage; // Increases setSlaveVoltage with 100mV
}
if(setSlaveVoltage > limitVoltage)
{
((limitVoltage + 10) < endVoltage) ? limitVoltage += 10 : limitVoltage = endVoltage; // Increases limitVoltage with 100mV
}
uint16_t chargeCurrent = chargeCurrentCalc(selectedMaxCurrent);
sendFlatPackCAN(setMasterVoltage, setSlaveVoltage, chargeCurrent); // Send new data
newData &= ~statusAllCAN; // Reset FlatPack and BMS bits to check if new data is received
if(limitVoltage == endVoltage) // Check and set charger state constant voltage
{
chargerState |= chargerStateEndVoltage;
}
}
/************************************************************************************
* Calculates the max allowed charging current based on highest battery cell voltage *
************************************************************************************/
uint16_t chargeCurrentCalc(uint16_t selectedMaxCurrent)
{
uint16_t chargeCurrent;
uint16_t highestCellVoltage = (highLowBMS[1] << 8) + highLowBMS[2]; // Highest cell voltage
const uint16_t highVoltageLowerLimit = ; // Set highest cell lower voltage for start of current limiting, in mV
const uint16_t highVoltageUpperLimit = ; // Set highest cell upper voltage for max current limiting, in mV
if(highestCellVoltage < highVoltageLowerLimit)
{
chargeCurrent = selectedMaxCurrent;
}
else if((highestCellVoltage >= highVoltageLowerLimit) && (highestCellVoltage <= highVoltageUpperLimit))
{
//chargeCurrent = selectedMaxCurrent * (highVoltageUpperLimit - highestCellVoltage) / (highVoltageUpperLimit - highVoltageLowerLimit);
chargeCurrent = (11L * highestCellVoltage * highestCellVoltage - L * highestCellVoltage + L) / 49;
if (chargeCurrent > selectedMaxCurrent)
{
chargeCurrent = selectedMaxCurrent;
}
}
else
{
chargeCurrent = 0;
}
(chargeCurrent < selectedMaxCurrent) ? chargerState |= chargerStateCurrentRegulating : chargerState &= ~chargerStateCurrentRegulating; // Check and set charger state current regulation
return chargeCurrent;
}
/**********************************************************************
* Checks that lowest battery cell is above limit, else stops charging *
**********************************************************************/
void checkLowestCell()
{
uint16_t lowestCellVoltage = (highLowBMS[4] << 8) + highLowBMS[5]; // Lowest cell voltage
if(lowestCellVoltage < lowestCellVoltageLimit)
{
state = STOP; // Abort charging
stopReason = stopLowVoltage;
}
}
/*****************************************************************************************************************
* Finds max temperature after board temperatures have been corrected, checks if below limit, else stops charging *
*****************************************************************************************************************/
int16_t checkTemperature()
{
int16_t maxTemperature = 0;
int16_t tempModule;
for(int i=0; i<4; i++)
{
tempModule = (tempBMS[i*2] << 8) + tempBMS[(i*2)+1]; // BMS module card temperature in degree C
tempModule = tempModule - temperatureCorrection[i]; // Correction so all boards show the same temperature for the same ambient temperature
if(maxTemperature < tempModule)
{
maxTemperature = tempModule;
}
}
if(maxTemperature > maxAllowedTemperature)
{
state = STOP; // Abort charging
stopReason = stopTemperature;
}
return maxTemperature;
}
/***********************************
* Updates display with latest data *
***********************************/
void updateDisplay(int16_t maxTemperature)
{
uint16_t capacity = (mainBMS[4] << 8) + mainBMS[5]; // Battery pack remaining capacity
uint16_t totalBatteryVoltage = (mainBMS[2] << 8) + mainBMS[3]; // Battery pack total voltage
plotValue(totalBatteryVoltage, 10.0, 1, 2, 0);
plotValue(maxTemperature, 1, 0, 11, 0);
plotValue(measuredMasterCurrent, 10.0, 1, 11, 1);
plotValue(capacity, 10.0, 1, 12, 2);
plotChargerState();
}
/**********************************
* Blinks charger state on display *
**********************************/
void plotChargerState()
{
static uint8_t flash = 0;
if(flash == 0)
{
if(!chargerState)
{
plotText("RV", 7, 3);
}
else if((chargerState & chargerStateCurrentLimiting) && !(chargerState & chargerStateCurrentRegulating))
{
plotText("CC", 7, 3);
}
else if((chargerState & chargerStateCurrentLimiting) && (chargerState & chargerStateCurrentRegulating))
{
plotText("CR", 7, 3);
}
else if(!(chargerState & chargerStateCurrentLimiting) && (chargerState & chargerStateEndVoltage))
{
plotText("CV", 7, 3);
}
else
{
plotText("EE", 7, 3);
}
flash = 1;
}
else
{
plotText(" ", 7, 3);
flash = 0;
}
}
/**********************************************
* Stops charging and prints reason on display *
**********************************************/
void stopCharging(uint16_t startCapacity, uint16_t endCapacity)
{
digitalWrite(contactorPIN, LOW); // Turn off contactor and +12V supply to BMS
if(stopReason & stopDone)
{
plotText("Done! ", 0, 5);
}
else if (stopReason & stopButton)
{
plotText("Stop! ", 0, 5);
}
else if (stopReason & stopLowVoltage)
{
plotText("LowV! ", 0, 5);
}
else if (stopReason & stopTemperature)
{
plotText("Temp! ", 0, 5);
}
else if (stopReason & stopBMS)
{
plotText("BMS! ", 0, 5);
}
else if (stopReason & stopWatchdog)
{
plotText("Wdog! ", 0, 5);
}
else
{
plotText("Err! ", 0, 5);
}
plotValue((endCapacity - startCapacity), 10.0, 1, 6, 5);
plotText("Ah", 10, 5);
}
/***************************************************
* Stops charging if BMS sends charging not allowed *
***************************************************/
void BMSStopCharging()
{
if(!(mainBMS[6] & B))
{
state = STOP;
stopReason = stopBMS;
}
}
/*********************************************************************************************
* Checks BMS CAN-bus buffer and if new data is available stores it in appropriate data array *
*********************************************************************************************/
void readCAN()
{
uint32_t rxId;
uint8_t len;
uint8_t rxBuf[8];
if(CAN_MSGAVAIL == CAN0.checkReceive()) // Check if data is coming
{
CAN0.readMsgBuf(&rxId, &len, rxBuf); // read data, rxId: message ID, ext: flag extended ID, len: data length, rxBuf: data buf
rxId &= 0x7FF; // Remove bits outside of the 11-bits ID
if(rxId == 0x180) // Main data from BMS
{
newData |= statusBMSMain;
for(int i=0; i<8; i++)
{
mainBMS[i] = rxBuf[i];
}
}
if(rxId == 0x181) // Highest and lowest cell data from BMS
{
newData |= statusBMSHiLo;
for(int i=0; i<8; i++)
{
highLowBMS[i] = rxBuf[i];
}
}
if(rxId == 0x182) // Temperature data from BMS
{
newData |= statusBMStemp;
for(int i=0; i<8; i++)
{
tempBMS[i] = rxBuf[i];
}
}
}
}
/*****************************************************************************************************************************
* Checks FlatPack Master CAN-bus buffer and if new data is available stores it in appropriate variable. Also resets watchdog *
*****************************************************************************************************************************/
uint16_t readMasterCAN()
{
static uint32_t lastMillisWatchDog = millis();
uint32_t rxId;
uint8_t len;
uint8_t rxBuf[8];
uint16_t measuredMasterVoltage;
if(CAN_MSGAVAIL == CAN1.checkReceive()) // Check if data is coming
{
CAN1.readMsgBuf(&rxId, &len, rxBuf); // Read data, rxId: message ID, ext: flag extended ID, len: data length, rxBuf: data buf
rxId &= 0x1FFFFFFF; // Remove bits outside of the 29-bits ID
lastMillisWatchDog = millis(); // Reset WatchDog
newData |= statusFPMaster;
measuredMasterVoltage = (rxBuf[4] << 8) + rxBuf[3]; // The voltage measured at the output in centiVolt
measuredMasterCurrent = (rxBuf[2] << 8) + rxBuf[1]; // The current measured at the output in deciAmpere
(rxId == 0x) ? chargerState |= chargerStateCurrentLimiting : chargerState &= ~chargerStateCurrentLimiting; // Check and set charger state current limiting
}
if((newData & statusFPMaster) && (millis() - lastMillisWatchDog >= timePeriod5s) && (state != END))
{
lastMillisWatchDog = millis(); // Reset WatchDog
newData &= ~statusFPMaster;
state = STOP; // Abort charging
stopReason = stopWatchdog;
//state = START;
}
return measuredMasterVoltage;
}
/********************************************************************************************************
* Checks FlatPack Slave 1 CAN-bus buffer and if new data is available stores it in appropriate variable *
********************************************************************************************************/
uint16_t readSlave1CAN()
{
uint32_t rxId;
uint8_t len;
uint8_t rxBuf[8];
uint16_t measuredSlave1Voltage;
if(CAN_MSGAVAIL == CAN2.checkReceive()) // check if data is coming
{
CAN2.readMsgBuf(&rxId, &len, rxBuf); // read data, rxId: message ID, ext: flag extended ID, len: data length, rxBuf: data buf
newData |= statusFPSlave1;
measuredSlave1Voltage = (rxBuf[4] << 8) + rxBuf[3]; // The voltage measured at the output in centiVolt
}
return measuredSlave1Voltage;
}
/********************************************************************************************************
* Checks FlatPack Slave 2 CAN-bus buffer and if new data is available stores it in appropriate variable *
********************************************************************************************************/
uint16_t readSlave2CAN()
{
uint32_t rxId;
uint8_t len;
uint8_t rxBuf[8];
uint16_t measuredSlave2Voltage;
if(CAN_MSGAVAIL == CAN3.checkReceive()) // check if data is coming
{
CAN3.readMsgBuf(&rxId, &len, rxBuf); // read data, rxId: message ID, ext: flag extended ID, len: data length, rxBuf: data buf
newData |= statusFPSlave2;
measuredSlave2Voltage = (rxBuf[4] << 8) + rxBuf[3]; // The voltage measured at the output in centiVolt
}
return measuredSlave2Voltage;
}
/******************************************
* Sends reset capacity to BMS via CAN-bus *
******************************************/
void sendCAN()
{
uint8_t reset[1] = {'Z'};
CAN0.sendMsgBuf(0x170, 0, 1, reset);
}
/***********************************************************
* Sends setVoltage and setCurrent to FlatPacks via CAN-bus *
***********************************************************/
void sendFlatPackCAN(uint16_t setMasterVoltage, uint16_t setSlaveVoltage, uint16_t setCurrent)
{
sendMasterCAN(setMasterVoltage, setCurrent);
sendSlave1CAN(setSlaveVoltage);
sendSlave2CAN(setSlaveVoltage);
}
/*****************************************************************
* Sends setVoltage and setCurrent to FlatPack Master via CAN-bus *
*****************************************************************/
void sendMasterCAN(uint16_t setVoltage, uint16_t setCurrent)
{
uint8_t login[8] = {0x14, 0x43, 0x71, 0x03, 0x97, 0x57, 0x00, 0x00}; //this is the serial number of the flatpack followed by two 00 bytes
CAN1.sendMsgBuf(0x, 1, 8, login); //send message to log in
uint8_t config[8] = {lowByte(setCurrent), highByte(setCurrent), lowByte(setVoltage), highByte(setVoltage), lowByte(setVoltage), highByte(setVoltage), lowByte(overVoltage), highByte(overVoltage)}; // set rectifiers maxCurrent, outputVoltage and OVP
CAN1.sendMsgBuf(0x05FF, 1, 8, config); // short walk-in , for long walk-in set
//Serial.print(setVoltage);
//Serial.print(" ");
//Serial.println(setCurrent);
}
/***************************************************
* Sends setVoltage to FlatPack Slave 1 via CAN-bus *
***************************************************/
void sendSlave1CAN(uint16_t setVoltage)
{
uint8_t login[8] = {0x15, 0x10, 0x72, 0x00, 0x77, 0x78, 0x00, 0x00}; //this is the serial number of the flatpack followed by two 00 bytes
CAN2.sendMsgBuf(0x, 1, 8, login); //send message to log in
uint8_t config[8] = {lowByte(maxCurrent), highByte(maxCurrent), lowByte(setVoltage), highByte(setVoltage), lowByte(setVoltage), highByte(setVoltage), lowByte(overVoltage), highByte(overVoltage)}; // set rectifiers maxCurrent, outputVoltage and OVP
CAN2.sendMsgBuf(0x05FF, 1, 8, config); // short walk-in , for long walk-in set
//Serial.println(setVoltage);
}
/***************************************************
* Sends setVoltage to FlatPack Slave 2 via CAN-bus *
***************************************************/
void sendSlave2CAN(uint16_t setVoltage)
{
uint8_t login[8] = {0x14, 0x22, 0x71, 0x14, 0x90, 0x69, 0x00, 0x00}; //this is the serial number of the flatpack followed by two 00 bytes
CAN3.sendMsgBuf(0x, 1, 8, login); //send message to log in
uint8_t config[8] = {lowByte(maxCurrent), highByte(maxCurrent), lowByte(setVoltage), highByte(setVoltage), lowByte(setVoltage), highByte(setVoltage), lowByte(overVoltage), highByte(overVoltage)}; // set rectifiers maxCurrent, outputVoltage and OVP
CAN3.sendMsgBuf(0x05FF, 1, 8, config); // short walk-in , for long walk-in set
//Serial.println(setVoltage);
}
/***************
* Initiate LCD *
***************/
void initDisplay()
{
Wire.beginTransmission(address);
Wire.write(commands);
Wire.write(0xA0); // No flip
Wire.write(0xAF); // Display on
Wire.write(0x40); // Display start at bottom line
Wire.write(0x81); // Set contrast
Wire.write(0x00);
Wire.endTransmission();
}
/****************
* Clear the LCD *
****************/
void clearDisplay()
{
for (int p = 0 ; p < 8; p++)
{
Wire.beginTransmission(address);
Wire.write(commands);
Wire.write(0xB0 + p); // Page
Wire.write(0x02); // Column low nibble
Wire.write(0x10); // Column high nibble
Wire.endTransmission();
for (int q = 0 ; q < 5; q++)
{
Wire.beginTransmission(address);
Wire.write(data);
for (int i = 0 ; i < 26; i++)
{
Wire.write(0x00);
}
Wire.endTransmission();
}
}
}
/*****************************************************************
* Plot an ASCII character with bottom left corner at x and row y *
*****************************************************************/
void plotChar (uint8_t c, uint8_t x, uint8_t y)
{
if(x > 20 || y > 7) return;
x = (x * 6) + 1;
Wire.beginTransmission(address);
Wire.write(commands);
Wire.write(0xB0 + y); // Page
Wire.write(0x00 + ((x+2) & 0x0F)); // Column low nibble
Wire.write(0x10 + ((x+2)>>4)); // Column high nibble
Wire.endTransmission();
Wire.beginTransmission(address);
Wire.write(data);
for (int col=0; col<6; col++)
{
uint8_t bits = pgm_read_byte(&CharMap[c-32][col]);
Wire.write(bits);
}
Wire.endTransmission();
}
/****************************************************
* Plot text starting with bottom left corner at x,y *
****************************************************/
void plotText(String textToPlot, uint8_t x, uint8_t y)
{
int8_t lengthOfString = textToPlot.length();
int8_t characters[lengthOfString + 1];
textToPlot.toCharArray(characters, (lengthOfString + 1));
for (int i=0; i<lengthOfString; i++)
{
plotChar(characters[i], x, y);
x++;
}
}
/*******************************************************
* Plot a value starting with bottom left corner at x,y *
*******************************************************/
void plotValue(uint16_t valueToPlot, float division, uint8_t decimal, uint8_t x, uint8_t y)
{
String textToPlot = String(valueToPlot / division, decimal);
plotText(textToPlot, x, y);
}
/**************************************************************
* Reads button and moves marker or selects max charge current *
**************************************************************/
uint16_t selectCurrent()
{
static uint32_t lastMillisButton = millis();
static uint8_t buttonPressed = 0;
static uint8_t row = 4;
uint16_t selectedMaxCurrent = 0;
if(digitalRead(buttonPIN) == HIGH) // Button not pressed
{
lastMillisButton = millis(); // reset timer
buttonPressed &= B110;
}
else if((millis() - lastMillisButton) > timePeriod100ms) // Button pressed for 100ms
{
buttonPressed |= B011;
}
if((buttonPressed == B011) && (millis() - lastMillisButton > timePeriod3s)) // Button pressed for 3s -> select current and stop further marker movment
{
buttonPressed |= B100;
plotChar('-', 1, row);
if(row == 4) selectedMaxCurrent = maxCurrent1x10AAC;
else if (row == 5) selectedMaxCurrent = maxCurrent1x16AAC;
else if (row == 6) selectedMaxCurrent = maxCurrent3x16AAC;
}
if(buttonPressed == B010) // Button released, before timeout, after having been depressed -> move marker
{
plotChar(' ', 0, row);
row = (row>=6) ? 4 : row + 1;
plotChar('*', 0, row);
buttonPressed &= B101;
}
return selectedMaxCurrent;
}
/******************************************
* Stops charging if button is held for 3s *
******************************************/
void buttonStopCharging()
{
static uint32_t lastMillisButton = millis();
if(digitalRead(buttonPIN) == HIGH) // Button not pressed
{
lastMillisButton = millis(); // reset timer
}
else if((millis() - lastMillisButton) > timePeriod3s) // Button pressed for 3s
{
state = STOP;
stopReason = stopButton;
}
}