Login

Everything You Need To Know To Find The Best eltek flatpack2 voltage adjustment

Author: Ada

Oct. 28, 2024

23

0

Communicate and control Eltek Flatpack 2 HE

Code: Select all

If you are looking for more details, kindly visit kelingyizhi.

For more eltek flatpack2 voltage adjustmentinformation, please contact us. We will provide professional answers.

// 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;
  }
}

ELTEK Flatpack2 48/ (eaton alternative) - Endless Sphere



Searching for a adaptto charger i've came across with this power supply very similar to the Eaton APR48-3G, but a little better IMO.
I made a little comparison with the two power supplies / module rectifiers:

Operating Voltage range: 85-300V AC (ELTEK) --------- 90-300V AC (EATON)

Efficiency (max): 92.5% (ELTEK) ---------- 92.0% (EATON)

Power output: W @ 185-275Vac (ELTEK) ----------- W @ 208-240Vac (EATON)
850W @ 85Vac (ELTEK) ----------------- 900W @ 100Vac (EATON)

Default output voltage: 53.5V (ELTEK) ---------------- 54.5V (EATON)

Adjustable Voltage range: 43.5-57.6V (ELTEK) -------------- 43.0-57.5V (EATON)

Weight: 1.9kg / 4.2lbs (ELTEK) ----------------- 1.7kg / 3.7lbs (EATON)

Dimensions: 109x41.5x327mm / 4.25x1.69x13" (ELTEK) ------------ 130x42x266mm / 5.1x1.6x10.5" (EATON)

here is the datasheet:
They have also a W version:
and a 48v-60v version:

I bought it new for less than 100EUR with shipping from here:

Pictures:

View attachment 6




Pinout:


Type of connectors:


I've flattened some female bullet connectors to power on and to make a charge test:


I didn't try to push more than W from the ELTEk (i've limited the maximum current from power supply in adaptto to 37.4A).
I will try later to see if i can push more power.

According with the datasheet the ELTEK can adjust the voltage output but i don't know how to adjust it (for now). I know that it is possible with a Module Controller that connects via CAN but without it i don't know how.I really appreciate if someone could help with the voltage adjustment.

Here some user guides and tests that i found about the ELTEK:

Best Regards.

Hello guys,Searching for a adaptto charger i've came across with this power supply very similar to the Eaton APR48-3G, but a little better IMO.I made a little comparison with the two power supplies / module rectifiers:Operating Voltage range:--------- 90-300V AC (EATON)Efficiency (max):---------- 92.0% (EATON)Power output:----------- W @ 208-240Vac (EATON)----------------- 900W @ 100Vac (EATON)Default output voltage:---------------- 54.5V (EATON)Adjustable Voltage range:-------------- 43.0-57.5V (EATON)Weight:----------------- 1.7kg / 3.7lbs (EATON)Dimensions:------------ 130x42x266mm / 5.1x1.6x10.5" (EATON)here is the datasheet: View attachment Datasheet Flatpack2 48-.pdf They have also a W version: View attachment Datasheet - Flatpack2 HE Front End Rectifier.pdf and a 48v-60v version: View attachment Datasheet Flatpack2 48-60- HE.pdf from here: http://www.ebay.com/itm/ Pictures:Pinout:Type of connectors:I've flattened some female bullet connectors to power on and to make a charge test:I didn't try to push more than W from the ELTEk (i've limited the maximum current from power supply in adaptto to 37.4A).I will try later to see if i can push more power.According with the datasheet the ELTEK can adjust the voltage output but i don't know how to adjust it (for now). I know that it is possible with a Module Controller that connects via CAN but without it i don't know how.I really appreciate if someone could help with the voltage adjustment.Here some user guides and tests that i found about the ELTEK: https://www.dropbox.com/sh/m9y36nhvzhc3a66/AADRlqkEZEV8RQ81F7SiAED9a?dl=0 Best Regards.

Are you interested in learning more about eltek always on? Contact us today to secure an expert consultation!

Comments

0

0/2000

Guest Posts

If you are interested in sending in a Guest Blogger Submission,welcome to write for us!

Your Name: (required)

Your Email: (required)

Subject:

Your Message: (required)

Join Us