// https://controllerstech.com/eeprom-and-stm32/
#include <string.h>

#include "eeprom.h"
#include "stdio.h"
#include "modbus.h"

#define CONCAT(a, b) CONCAT_INNER(a, b)											// These three macros
#define CONCAT_INNER(a, b) a ## b												// generate unique variables
#define UNIQUE_NAME(base) CONCAT(base, __COUNTER__)								// according to template "baseX", like "base1", "base2" and etc

// Define the I2C
extern I2C_HandleTypeDef hi2c3;
#define EEPROM_I2C			&hi2c3

// EEPROM ADDRESS (8bits)
#define EEPROM_ADDR			0xA0

// Define the Page Size and number of pages
#define PAGE_SIZE			64		// in Bytes
#define PAGE_NUM			4096	// number of pages
	
	
#define PADDRPOSITION 6 //MUST BE calculated  for every eeprom type --//int paddrposition = log(PAGE_SIZE)/log(2);

/********************************************************/

// Eeprom state related defines

/*****************EEPROM_EMULATOR************************/

typedef struct
{
  // Schnittstellenparameter
  uint32_t	  baudrate;
  uint16_t	  parityMode;
  uint16_t	  stopBits;
  uint16_t	  slave_adress;
  uint16_t	  ibn_day;
  uint16_t	  ibn_month;
  uint16_t	  ibn_year;
  uint16_t	  user_id;

  // Offset und Gain
  int16_t	  batteryCurrentOffsetRefTemperatureShunt;
  int16_t	  batteryCurrentOffsetRefTemperatureChip;
  int16_t	  batteryCurrentGainRefTempShunt;
  int16_t	  batteryCurrentGainRefTempChip;
  int16_t	  batteryCurrentOffsetTemperatureCalibrationTemperature;
  int16_t	  batteryCurrentGainTemperatureCalibrationShuntTemperature;
  int16_t	  batteryCurrentGainTemperatureCalibrationChipTemperature;
  int32_t	  batteryCurrentOffsetRefshuntVoltage;
  int32_t	  batteryCurrentOffsetCommonModeCalibrationVoltage;
  int32_t	  batteryCurrentOffsetCommonModeCompensationFactor;
  int32_t	  batteryCurrentOffsetTemperatureCompensationFactor;
  int32_t	  batteryCurrentGainRefCurrent;
  int32_t	  batteryCurrentGainTemperatureCompensationShuntFactor;
  int32_t	  batteryCurrentGainTemperatureCompensationChipFactor;

  int32_t	  currentOffset;
  uint32_t	  currentGain;

  int32_t	  currentOffsetFast;
  uint32_t	  currentGainFast;

  int64_t	  mAsCounter;
  int32_t	  detectedCapacity;
  int32_t	  detectedEnergy;
  int64_t	  mAs_AutoMode;								// 160-163	Helps to restore current SoC after Reset or Shutdown
  int64_t	  mWs_AutoMode;								// 164-167	Helps to restore current SoC after Reset or Shutdown

  // battery parameter
  uint16_t	  cef;
  uint16_t	  peukert;
  uint32_t	  cellCapacity;
  uint32_t	  cellEnergy;
  uint16_t	  iBatFull;
  uint16_t	  tBatFull;
  uint16_t	  uBatFull;
  uint16_t	  uBatEmpty;
  uint16_t	  socCalcMode;
  uint16_t	  cellRatedDischargeTime;

  // Schaltausgänge
  uint16_t	  lvpStart;									// Spannung ab der die LOW Voltage Protection aktiv wird in mV
  uint16_t	  lvpStop;									// Spannung ab der die LOW Voltage Protection wieder inaktiv wird
  uint16_t	  ovpStart;									// Spannung ab der die OVER Voltage Protection aktiv wird in mV
  uint16_t	  ovpStop;									// Spannung ab der die OVER Voltage Protection wieder inaktiv wird
  int16_t	  loadCurrentLimit;							// maximaler Laststrom in A wenn der Strom größer ist als der eingestelle Wert dann wird die Laststrom Protection aktiv
  int16_t	  chargeCurrentLimit;						// maximaler Ladestrom in A wenn der Strom größer ist als der eingestelle Wert dann wird die Ladestrom Protection aktiv
  int16_t	  chargeStopHighTemperatureStart;			// Abschalttemperatur Ladung wegen zu hoher Temperatur
  int16_t	  chargeStopHighTemperatureStop;			// Wiedereinschalttemperatur
  int16_t	  chargeStopLowTemperatureStart;			// Abschalttemperatur Ladung wegen zu niedriger Temperatur
  int16_t	  chargeStopLowTemperatureStop;				// Wiedereinschalttemperatur
  int16_t	  dischargeStopHighTemperatureStart;		// Abschalttemperatur Entladung wegen zu hoher Temperatur
  int16_t	  dischargeStopHighTemperatureStop;			// Wiedereinschalttemperatur
  int16_t	  dischargeStopLowTemperatureStart;			// Abschalttemperatur EntLadung wegen zu niedriger Temperatur
  int16_t	  dischargeStopLowTemperatureStop;			// Wiedereinschalttemperatur

  int16_t	  uBatEmptyCompStartTemp;					// We start calculating uBatEmpty compensations only when cell temperature is lower than this value
  int16_t	  uBatEmptyCompStopTemp;					// We stop calculating uBatEmpty compensations when cell temperature is lower than this value
  uint16_t	  uBatEmptyCompStopVolt;					// uBatEmpty Voltage at temperatures lower than lvpCompStopTemp
  int16_t	  extraDischargeStrom_mA;					// For example, current that consumes LiPro itself
  uint16_t	  cefW;
  int16_t	  batteryEmptyDetectionMode;		
  uint16_t	  auxOutputMode;
  uint16_t	  auxOutputSetpointOn;
  uint16_t	  auxOutputSetpointOff;
  uint16_t	  auxOutputInverted;   

} eeprom_data_t;



// Substitute for #if sizeof(some_type) == sizeof(another_type) functionality
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

typedef struct
{
  // Geräteinformation
  uint32_t    SN;
  uint8_t     deviceInfoWritten;
  uint8_t     UNIQUE_NAME(reserved)[3];
}device_info_t;

typedef struct
{
  // Eeprom Status Infos
   uint8_t     firstStartId;
   uint8_t     UNIQUE_NAME(reserved)[3];
  uint16_t     structureSize;
  uint16_t     revisionInfo;
  uint32_t     writeCounter;
}eeprom_state_t;

// fasse zu einer Struktur zusammen um nachher einfach darauf zugreifen zu können
typedef struct
{
  eeprom_state_t eepromState;
  device_info_t  deviceInfo;
  eeprom_data_t  changedData;
}eeprom_stored_data_t;


// Data to store reated defines
//#define SIZEOF_DEFAULT_EEPROM_DATA				(sizeof(eeprom_new_data_t))
//#define SIZEOF_CHANGED_EEPROM_DATA                (sizeof(eeprom_data_t))
//#define SIZEOF_DEVICE_INFO                        (sizeof(device_info_t))
//#define SIZEOF_EEPROM_STATE                       (sizeof(eeprom_state_t))

//#define SIZE_OF_DATA_TO_STORE                     (SIZEOF_CHANGED_EEPROM_DATA + SIZEOF_DEVICE_INFO + SIZEOF_EEPROM_STATE)


// Adress related defines
#define EEPROM_ADRESS_FIRST_START_ID              (0)
#define FIRST_START_ID                            (0xFF)  // See datasheet (Chapter "Initial delivery state")
#define CONFIG_ID								  (01)	  // Increment by 1 to make compartible update, more than 1 - incompartible
#if CONFIG_ID == FIRST_START_ID
#error "CONFIG_ID must not be equal to FIRST_START_ID!!! Calibration data will be erased!!!"
#endif



static uint32_t GetPage(uint32_t Address);
static HAL_StatusTypeDef getEEPROMData(uint32_t address, uint8_t * data, uint32_t len);
void EEPROM_Read (uint16_t page, uint16_t offset, uint8_t *data, uint16_t size);
void EEPROM_Write (uint16_t page, uint16_t offset, uint8_t *data, uint16_t size);


static eeprom_stored_data_t eepromData;


static FLASH_EraseInitTypeDef EraseInitStruct = {0};

static const eeprom_data_t defaultEepromData =
{
	// Schnittstellenparameter

	/* baudrate		*/														19200,			// uint32_t   baudrate;
	/* parityMode	*/														MODBUS_UART_PARITY_EVEN,  // uint16_t   parityMode;
	/* stopBits		*/														1,				// uint16_t   stopBits;
	/* slave_adress	*/														1,				// uint16_t   slave_adress;
	/* ibn_day		*/														0,				// ibm_day
	/* ibn_month	*/														0,				// ibm_month
	/* ibn_year		*/														0,				// ibm_year
	/* user_id		*/														0,				// user id

	// Offset und Gain

	/* batteryCurrentOffsetRefTemperatureShunt					*/			0,				// int16_t   batteryCurrentOffsetRefTemperatureShunt;
	/* batteryCurrentOffsetRefTemperatureChip					*/			0,				// int16_t   batteryCurrentOffsetRefTemperatureChip
	/* batteryCurrentGainRefTempShunt							*/			0,				// int16_t   batteryCurrentGainRefTempShunt;
	/* batteryCurrentGainRefTempChip							*/			0,				// int16_t   batteryCurrentGainRefTempShip
	/* batteryCurrentOffsetTemperatureCalibrationTemperature	*/			0,				// int16_t   batteryCurrentOffsetTemperatureCalibrationTemperature;
	/* batteryCurrentGainTemperatureCalibrationShuntTemperature	*/			0,				// int16_t   batteryCurrentGainTemperatureCalibrationShuntTemperature;
	/* batteryCurrentGainTemperatureCalibrationChipTemperature	*/			0,				// int16_t   batteryCurrentGainTemperatureCalibrationChipTemperature;
	/* batteryCurrentOffsetRefshuntVoltage						*/			0,				// int32_t   batteryCurrentOffsetRefshuntVoltage;
	/* batteryCurrentOffsetCommonModeCalibrationVoltage			*/			0,				// int32_t   batteryCurrentOffsetCommonModeCalibrationVoltage;
	/* batteryCurrentOffsetTemperatureCompensationFactor		*/			0,				// int32_t   batteryCurrentOffsetTemperatureCompensationFactor;
	/* batteryCurrentOffsetCommonModeCompensationFactor			*/			0,				// int32_t   batteryCurrentOffsetCommonModeCompensationFactor;
	/* batteryCurrentGainRefCurrent								*/			250000,			// int32_t   batteryCurrentGainRefCurrent;
	/* batteryCurrentGainTemperatureCompensationShuntFactor		*/			0,				// int32_t   batteryCurrentGainTemperatureCompensationShuntFactor;
	/* batteryCurrentGainTemperatureCompensationChipFactor		*/			0,				// int32_t   batteryCurrentGainTemperatureCompensationChipFactor;

	/* currentOffset  */													0,				//int32_t   currentOffset;
	/* currentGain	  */													1000000,		//uint32_t  currentGain;

	/* currentOffsetFast  */												0,				//int32_t   currentOffset;
	/* currentGainFast  */													1000000,		//uint32_t  currentGain;

	/* mAsCounter		*/													0,				// mAsCounter
	/* detectedCapacity	*/													-1,				// detectedCapacity
	/* detectedEnergy	*/													-1,				// detectedEnergy
	/* mAs_AutoMode		*/													(-100000LL*3600LL),	// mAs_AutoMode = cellCapacity*3600,
	/* mWs_AutoMode		*/													(-2640000LL*3600LL),// mWs_AutoMode = cellEnergy*3600,

	// battery parameter

	/* cef						*/											990,			// cef %*10
	/* peukert					*/											105,			// peukert
	/* cellCapacity				*/											160000,			// cell Capacity in mAh
	/* cellEnergy				*/											2048000,		// cell energy in mWh
	/* iBatFull					*/											10,				// I-batt full 10%, 10A bei 100Ah akku
	/* tBatFull					*/											2,				// t-batt full 2 Sekunden
#if defined SYSTEM_VOLTAGE_12V
	/* uBatFull					*/											14000,				// 14V olt Ubatt full, Neu: Bei 0: Erkung von Lipro LVP als 0% 
	/* uBatEmpty				*/											12400,				// 11,312V Ubatt Empty
#elif defined SYSTEM_VOLTAGE_24V
	/* uBatFull					*/											28000,				// 14V olt Ubatt full, Neu: Bei 0: Erkung von Lipro LVP als 0% 
	/* uBatEmpty				*/											24800,				// 11,312V Ubatt Empty
#elif defined SYSTEM_VOLTAGE_48V
	/* uBatFull					*/											56000,				// 14V olt Ubatt full, Neu: Bei 0: Erkung von Lipro LVP als 0% 
	/* uBatEmpty				*/											49600,				// 11,312V Ubatt Empty
#else
  #error "System voltage not defined"
#endif


	/* socCalcMode				*/											1,				// SoC calculation mode: 0(default)
	/* cellRatedDischargeTime	*/											2,				// cell rated current discharge time [C/x]. For example, if 40Ah cell is rated as 0.5c, then rated discharge time is 2
#if defined SYSTEM_VOLTAGE_12V
	/* lvpStart	*/															12000,			// uint16_t lvpStart; Spannung ab der die LOW Voltage Protection aktiv wird in mV
	/* lvpStop	*/															12500,			// uint16_t lvpStop; Spannung ab der die LOW Voltage Protection wieder inaktiv wird
	/* ovpStart	*/															14800,			// uint16_t  ovpStart; Spannung ab der die OVER Voltage Protection aktiv wird in mV
	/* ovpStop	*/															14000,			// uint16_t  ovpStop; Spannung ab der die OVER Voltage Protection wieder inaktiv wird
#elif defined SYSTEM_VOLTAGE_24V
	/* lvpStart	*/															24000,			// uint16_t lvpStart; Spannung ab der die LOW Voltage Protection aktiv wird in mV
	/* lvpStop	*/															25000,			// uint16_t lvpStop; Spannung ab der die LOW Voltage Protection wieder inaktiv wird
	/* ovpStart	*/															29600,			// uint16_t  ovpStart; Spannung ab der die OVER Voltage Protection aktiv wird in mV
	/* ovpStop	*/															28000,			// uint16_t  ovpStop; Spannung ab der die OVER Voltage Protection wieder inaktiv wird

#elif defined SYSTEM_VOLTAGE_48V
	/* lvpStart	*/															48000,			// uint16_t lvpStart; Spannung ab der die LOW Voltage Protection aktiv wird in mV
	/* lvpStop	*/															50000,			// uint16_t lvpStop; Spannung ab der die LOW Voltage Protection wieder inaktiv wird
	/* ovpStart	*/															59200,			// uint16_t  ovpStart; Spannung ab der die OVER Voltage Protection aktiv wird in mV
	/* ovpStop	*/															56000,			// uint16_t  ovpStop; Spannung ab der die OVER Voltage Protection wieder inaktiv wird
#else
  #error "System voltage not defined"
#endif



#if (DEVICETYPE == 500)
	/* loadCurrentLimit	  */												-500,			// uint16_t loadCurrentLimit; maximaler Laststrom in A wenn der Strom größer ist als der eingestelle Wert dann wird die Laststrom Protection aktiv
	/* chargeCurrentLimit */												500,			// uint16_t chargeCurrentLimit;	maximaler Ladestrom in A wenn der Strom größer ist als der eingestelle Wert dann wird die Ladestrom Protection aktiv
#elif (DEVICETYPE == 250)
	/* loadCurrentLimit	  */												-250,			// uint16_t loadCurrentLimit; maximaler Laststrom in A wenn der Strom größer ist als der eingestelle Wert dann wird die Laststrom Protection aktiv
	/* chargeCurrentLimit */												250,			// uint16_t chargeCurrentLimit maximaler Ladestrom in A wenn der Strom größer ist als der eingestelle Wert dann wird die Ladestrom Protection aktiv
#elif (DEVICETYPE == 125)
	/* loadCurrentLimit	  */												-125,			// uint16_t loadCurrentLimit; maximaler Laststrom in A wenn der Strom größer ist als der eingestelle Wert dann wird die Laststrom Protection aktiv
	/* chargeCurrentLimit */												125,			// uint16_t chargeCurrentLimit; maximaler Ladestrom in A wenn der Strom größer ist als der eingestelle Wert dann wird die Ladestrom Protection aktiv
#else
#error No valid device type
#endif
	/* chargeStopHighTemperatureStart	*/									6000,			// 80°C int16_t chargeStopHighTemperatureStart;	Abschalttemperatur Ladung wegen zu hoher Temperatur
	/* chargeStopHighTemperatureStop	*/									5500,			// 75°C int16_t chargeStopHighTemperatureStop;	Wiedereinschalttemperatur
	/* chargeStopLowTemperatureStart	*/									-2500,			// -35°C int16_t chargeStopLowTemperatureStart;	Abschalttemperatur Ladung wegen zu niedriger Temperatur
	/* chargeStopLowTemperatureStop		*/									-2000,			// -30°C int16_t chargeStopLowTemperatureStop; Wiedereinschalttemperatur
	/* dischargeStopHighTemperatureStart*/									6000,			// 80°C int16_t dischargeStopHighTemperatureStart; Abschalttemperatur Entladung wegen zu hoher Temperatur
	/* dischargeStopHighTemperatureStop	*/									5500,			// 75°C int16_t dischargeStopHighTemperatureStop; Wiedereinschalttemperatur
	/* dischargeStopLowTemperatureStart	*/									-3500,			// -35°C int16_t dischargeStopLowTemperatureStart; Abschalttemperatur EntLadung wegen zu niedriger Temperatur
	/* dischargeStopLowTemperatureStop	*/									-3000,			// -30°C int16_t dischargeStopLowTemperatureStop; Wiedereinschalttemperatur

	/* uBatEmptyCompStartTemp	*/											50,				// 5°C We start calculating uBatEmpty compensations only when cell temperature is lower than this value
	/* uBatEmptyCompStopTemp	*/											-200,			// -20°C We stop calculating uBatEmpty compensations when cell temperature is lower than this value
#if defined SYSTEM_VOLTAGE_12V
	/* uBatEmptyCompStopVolt	*/											10000,			// 10V uBatEmpty voltage at temperatures lower than -20°C
#elif defined SYSTEM_VOLTAGE_24V
/* uBatEmptyCompStopVolt	*/												20000,			// 10V uBatEmpty voltage at temperatures lower than -20°C
#elif defined SYSTEM_VOLTAGE_48V
/* uBatEmptyCompStopVolt	*/												40000,			// 10V uBatEmpty voltage at temperatures lower than -20°C
#else
  #error "System voltage not defined"
#endif

	/* extraDischargeStrom_mA	*/											7,				// mA, current that LiPro consumes itself
	/* cefW		*/															900,			// 90% cef for Wh calculations [%*10]
	/* Battery Empty Detection Mode*/										1,				// Auto, from BMS
	/* AUX MODE */															AUX_MODE_HEATER,// Heizung
#if defined SYSTEM_VOLTAGE_12V
	/* AUX SETPOINT ON */													13600,			// Erkennung Ladegerät ab 13,6V
#elif defined SYSTEM_VOLTAGE_24V
	/* AUX SETPOINT ON */													27200,			// Erkennung Ladegerät ab 13,6V
#elif defined SYSTEM_VOLTAGE_48V
	/* AUX SETPOINT ON */													54400,			// Erkennung Ladegerät ab 13,6V
#else
#error No valid device type
#endif
	/* AUX SETPOINT OFF */													100,			// Batterie Entladung wird erkannt bei -100mA
	/* AUX Inverted */														0,				// Nicht invertiert
};

/**
  * @brief  Gets the page of a given address
  * @param  Addr: Address of the FLASH Memory
  * @retval The page of a given address
  */
static uint32_t GetPage(uint32_t Addr)
{
  return (Addr - FLASH_BASE) / FLASH_PAGE_SIZE;
}


startType_t EEPROM_isFirstStart(void)
{

  uint8_t firstStartCatcher;

  EEPROM_Read(0, EEPROM_ADRESS_FIRST_START_ID, &firstStartCatcher, 1);

  if (firstStartCatcher == FIRST_START_ID)
  {
	  printf ("First start detected!\n");
	  return FIRST_START_AFTER_ERASE;
  }
  else if (firstStartCatcher == CONFIG_ID)
  {
	  printf ("Normal start without EEPROM changes detected\n");
	  return FIRST_START_AFTER_COMPARTIBLE_UPDATE;
  }
  else if (firstStartCatcher == CONFIG_ID - 1)
  {
	  printf ("EEPROM was changed! We need to preserve calibration and settings data!\n");
	  return FIRST_START_AFTER_INCOMPARTIBLE_UPDATE;
  }
  else return FIRST_START_ERROR;
}

//------------------------------------------------------------------------------

HAL_StatusTypeDef EEPROM_fullRestore(sys_data_t* data)
{
  printf("EEPROM FULL RESTORE!\n");

  /**************** LESE_DEFAULT_WERTE ************************/

  memcpy(&eepromData.changedData, &defaultEepromData, sizeof(eeprom_data_t));
  
  // Eeprom Status Infos
  eepromData.eepromState.writeCounter++;
  eepromData.eepromState.structureSize = sizeof(eeprom_stored_data_t);
  eepromData.eepromState.revisionInfo = 0;
  eepromData.eepromState.firstStartId = CONFIG_ID;

  /**************** EEPROM Speichern ********************/

  EEPROM_Write(0, 0, (uint8_t*)&eepromData, sizeof(eeprom_stored_data_t));

  /**************** AUSLESEN_UND_PRÜFEN ********************/

  return EEPROM_readConfig(data);
}

//------------------------------------------------------------------------------

//Werkeinstellung ohne Kalibrierwert Überschreibung
HAL_StatusTypeDef EEPROM_factoryRestore(sys_data_t* data, int resetToDefault)
{
  printf("EEPROM FACTORY RESTORE/UPDATE!\n");
 
  //eeprom_stored_data_t* dataToStore = &eepromData;

  EEPROM_readConfig(data);															// Restoring calibration data

  // Offset und Gain
  
  eepromData.changedData.batteryCurrentOffsetRefTemperatureShunt					= data->s.parameter.batteryCurrentOffsetRefTemperatureShunt;
  eepromData.changedData.batteryCurrentOffsetRefTemperatureChip						= data->s.parameter.batteryCurrentOffsetRefTemperatureChip;
  eepromData.changedData.batteryCurrentOffsetRefshuntVoltage						= data->s.parameter.batteryCurrentOffsetRefshuntVoltage;
  eepromData.changedData.batteryCurrentOffsetCommonModeCalibrationVoltage			= data->s.parameter.batteryCurrentOffsetCommonModeCalibrationVoltage;
  eepromData.changedData.batteryCurrentOffsetCommonModeCompensationFactor			= data->s.parameter.batteryCurrentOffsetCommonModeCompensationFactor;
  eepromData.changedData.batteryCurrentOffsetTemperatureCalibrationTemperature		= data->s.parameter.batteryCurrentOffsetTemperatureCalibrationTemperature;
  eepromData.changedData.batteryCurrentOffsetTemperatureCompensationFactor			= data->s.parameter.batteryCurrentOffsetTemperatureCompensationFactor;
  eepromData.changedData.currentOffset												= data->s.parameter.batteryCurrentOffset;
  eepromData.changedData.currentGain												= data->s.parameter.batteryCurrentGainCorrectionFaktor;
  eepromData.changedData.currentOffsetFast											= data->s.parameter.batteryCurrentOffsetFast;
  eepromData.changedData.currentGainFast											= data->s.parameter.batteryCurrentGainCorrectionFaktorFast;
  
  eepromData.changedData.batteryCurrentGainRefTempShunt								= data->s.parameter.batteryCurrentGainRefTempShunt;
  eepromData.changedData.batteryCurrentGainRefTempChip								= data->s.parameter.batteryCurrentGainRefTempChip;
  eepromData.changedData.batteryCurrentGainRefCurrent								= data->s.parameter.batteryCurrentGainRefCurrent;
  eepromData.changedData.batteryCurrentGainTemperatureCalibrationShuntTemperature	= data->s.parameter.batteryCurrentGainTemperatureCalibrationShuntTemperature;
  eepromData.changedData.batteryCurrentGainTemperatureCompensationShuntFactor		= data->s.parameter.batteryCurrentGainTemperatureCompensationShuntFactor;
  eepromData.changedData.batteryCurrentGainTemperatureCalibrationChipTemperature	= data->s.parameter.batteryCurrentGainTemperatureCalibrationChipTemperature;
  eepromData.changedData.batteryCurrentGainTemperatureCompensationChipFactor		= data->s.parameter.batteryCurrentGainTemperatureCompensationChipFactor;

  // Schnittstellenparameter
  eepromData.changedData.baudrate													= defaultEepromData.baudrate;
  eepromData.changedData.parityMode													= defaultEepromData.parityMode;
  eepromData.changedData.stopBits													= defaultEepromData.stopBits;
  eepromData.changedData.slave_adress												= defaultEepromData.slave_adress;
  eepromData.changedData.ibn_day													= defaultEepromData.ibn_day;
  eepromData.changedData.ibn_month													= defaultEepromData.ibn_month;
  eepromData.changedData.ibn_year													= defaultEepromData.ibn_year;
  eepromData.changedData.user_id													= defaultEepromData.user_id;

  //Ah counter
  eepromData.changedData.cef														= defaultEepromData.cef;
  eepromData.changedData.cellCapacity												= defaultEepromData.cellCapacity;
  eepromData.changedData.cellEnergy													= defaultEepromData.cellEnergy;
  eepromData.changedData.iBatFull													= defaultEepromData.iBatFull;
  eepromData.changedData.peukert													= defaultEepromData.peukert;
  eepromData.changedData.tBatFull													= defaultEepromData.tBatFull;
  eepromData.changedData.uBatFull													= defaultEepromData.uBatFull;
  eepromData.changedData.uBatEmpty													= defaultEepromData.uBatEmpty;
  eepromData.changedData.socCalcMode												= defaultEepromData.socCalcMode;
  eepromData.changedData.cellRatedDischargeTime										= defaultEepromData.cellRatedDischargeTime;

  // Schaltausgänge
  eepromData.changedData.lvpStart													= defaultEepromData.lvpStart;
  eepromData.changedData.lvpStop													= defaultEepromData.lvpStop;
  eepromData.changedData.ovpStart													= defaultEepromData.ovpStart;
  eepromData.changedData.ovpStop													= defaultEepromData.ovpStop;
  eepromData.changedData.loadCurrentLimit											= defaultEepromData.loadCurrentLimit;
  eepromData.changedData.chargeCurrentLimit											= defaultEepromData.chargeCurrentLimit;
  eepromData.changedData.chargeStopHighTemperatureStart								= defaultEepromData.chargeStopHighTemperatureStart;
  eepromData.changedData.chargeStopHighTemperatureStop								= defaultEepromData.chargeStopHighTemperatureStop;
  eepromData.changedData.chargeStopLowTemperatureStart								= defaultEepromData.chargeStopLowTemperatureStart;
  eepromData.changedData.chargeStopLowTemperatureStop								= defaultEepromData.chargeStopLowTemperatureStop;
  eepromData.changedData.dischargeStopHighTemperatureStart							= defaultEepromData.dischargeStopHighTemperatureStart;
  eepromData.changedData.dischargeStopHighTemperatureStop							= defaultEepromData.dischargeStopHighTemperatureStop;
  eepromData.changedData.dischargeStopLowTemperatureStart							= defaultEepromData.dischargeStopLowTemperatureStart;
  eepromData.changedData.dischargeStopLowTemperatureStop							= defaultEepromData.dischargeStopLowTemperatureStop;

  eepromData.changedData.uBatEmptyCompStartTemp										= defaultEepromData.uBatEmptyCompStartTemp;
  eepromData.changedData.uBatEmptyCompStopTemp										= defaultEepromData.uBatEmptyCompStopTemp;
  eepromData.changedData.uBatEmptyCompStopVolt										= defaultEepromData.uBatEmptyCompStopVolt;

  eepromData.changedData.extraDischargeStrom_mA										= defaultEepromData.extraDischargeStrom_mA;
  eepromData.changedData.cefW														= defaultEepromData.cefW;
  eepromData.changedData.batteryEmptyDetectionMode									= defaultEepromData.batteryEmptyDetectionMode;
  eepromData.changedData.auxOutputMode												= defaultEepromData.auxOutputMode;
  eepromData.changedData.auxOutputSetpointOn										= defaultEepromData.auxOutputSetpointOn;
  eepromData.changedData.auxOutputSetpointOff										= defaultEepromData.auxOutputSetpointOff;
  eepromData.changedData.auxOutputInverted											= defaultEepromData.auxOutputInverted;
 

  eepromData.eepromState.writeCounter		= eepromData.eepromState.writeCounter++;
  eepromData.eepromState.structureSize		= sizeof(eeprom_stored_data_t);
  eepromData.eepromState.revisionInfo		= 0;
  eepromData.eepromState.firstStartId		= CONFIG_ID;

  eepromData.deviceInfo.deviceInfoWritten	= 1;
  eepromData.deviceInfo.SN					= data->s.parameter.sn;

  EEPROM_Write(0,0, (uint8_t*)&eepromData, sizeof(eeprom_stored_data_t));

  return EEPROM_readConfig(data);
}

HAL_StatusTypeDef EEPROM_storeConfig(sys_data_t* data, uint8_t withSN)
{
  eeprom_stored_data_t* dataToStore;
  /****************LESE_WERTE_AUS_SYSDATA*********************/
  printf("EEPROM STORE CONFIG!\n");
  dataToStore = (eeprom_stored_data_t*)&eepromData;

  // Schnittstellenparameter
  dataToStore->changedData.baudrate = data->s.parameter.baudrate;
  dataToStore->changedData.parityMode =	data->s.parameter.parityMode;
  dataToStore->changedData.stopBits = data->s.parameter.stopBit;
  dataToStore->changedData.slave_adress = data->s.parameter.slave_address;
  dataToStore->changedData.ibn_day = data->s.parameter.ibn_day;
  dataToStore->changedData.ibn_month = data->s.parameter.ibn_month;
  dataToStore->changedData.ibn_year = data->s.parameter.ibn_year;
  dataToStore->changedData.user_id = data->s.parameter.user_id;

  // Offset und Gain
  dataToStore->changedData.batteryCurrentOffsetRefTemperatureChip = data->s.parameter.batteryCurrentOffsetRefTemperatureChip;
  dataToStore->changedData.batteryCurrentOffsetRefTemperatureShunt = data->s.parameter.batteryCurrentOffsetRefTemperatureShunt;

  dataToStore->changedData.batteryCurrentGainRefTempShunt = data->s.parameter.batteryCurrentGainRefTempShunt;
  dataToStore->changedData.batteryCurrentGainRefTempChip = data->s.parameter.batteryCurrentGainRefTempChip;
  dataToStore->changedData.batteryCurrentOffsetTemperatureCalibrationTemperature = data->s.parameter.batteryCurrentOffsetTemperatureCalibrationTemperature;
  dataToStore->changedData.batteryCurrentGainTemperatureCalibrationShuntTemperature = data->s.parameter.batteryCurrentGainTemperatureCalibrationShuntTemperature;
  dataToStore->changedData.batteryCurrentGainTemperatureCalibrationChipTemperature = data->s.parameter.batteryCurrentGainTemperatureCalibrationChipTemperature;
  dataToStore->changedData.batteryCurrentOffsetRefshuntVoltage = data->s.parameter.batteryCurrentOffsetRefshuntVoltage;

  dataToStore->changedData.batteryCurrentOffsetCommonModeCalibrationVoltage = data->s.parameter.batteryCurrentOffsetCommonModeCalibrationVoltage;
  dataToStore->changedData.batteryCurrentOffsetCommonModeCompensationFactor = data->s.parameter.batteryCurrentOffsetCommonModeCompensationFactor;
  dataToStore->changedData.batteryCurrentOffsetTemperatureCompensationFactor = data->s.parameter.batteryCurrentOffsetTemperatureCompensationFactor;
  dataToStore->changedData.batteryCurrentGainRefCurrent = data->s.parameter.batteryCurrentGainRefCurrent;
  dataToStore->changedData.batteryCurrentGainTemperatureCompensationShuntFactor = data->s.parameter.batteryCurrentGainTemperatureCompensationShuntFactor;
  dataToStore->changedData.batteryCurrentGainTemperatureCompensationChipFactor = data->s.parameter.batteryCurrentGainTemperatureCompensationChipFactor;

  dataToStore->changedData.currentOffset = data->s.parameter.batteryCurrentOffset;
  dataToStore->changedData.currentGain = data->s.parameter.batteryCurrentGainCorrectionFaktor;

  dataToStore->changedData.currentOffsetFast = data->s.parameter.batteryCurrentOffsetFast;
  dataToStore->changedData.currentGainFast = data->s.parameter.batteryCurrentGainCorrectionFaktorFast;


  // AH COUNTER Einstellungen
  dataToStore->changedData.cef = data->s.parameter.cef;
  dataToStore->changedData.peukert = data->s.parameter.peukert;
  dataToStore->changedData.cellCapacity = data->s.parameter.cellCapacity;
  dataToStore->changedData.cellEnergy =	data->s.parameter.battEnergy;
  dataToStore->changedData.iBatFull = data->s.parameter.iBatFull;
  dataToStore->changedData.tBatFull = data->s.parameter.tBatFull;
  dataToStore->changedData.uBatFull = data->s.parameter.uBatFull;
  dataToStore->changedData.uBatEmpty = data->s.parameter.uBatEmpty;
  dataToStore->changedData.socCalcMode = data->s.parameter.socCalcMode;

  dataToStore->changedData.cellRatedDischargeTime = data->s.parameter.cellRatedDischargeTime;
  // Schaltausgänge
  dataToStore->changedData.lvpStart = data->s.parameter.lvpStart;
  dataToStore->changedData.lvpStop = data->s.parameter.lvpStop;
  dataToStore->changedData.ovpStart = data->s.parameter.ovpStart;
  dataToStore->changedData.ovpStop = data->s.parameter.ovpStop;
  dataToStore->changedData.loadCurrentLimit = data->s.parameter.loadCurrentLimit;
  dataToStore->changedData.chargeCurrentLimit =	data->s.parameter.chargeCurrentLimit;
  dataToStore->changedData.chargeStopHighTemperatureStart = data->s.parameter.chargeStopHighTemperatureStart;
  dataToStore->changedData.chargeStopHighTemperatureStop = data->s.parameter.chargeStopHighTemperatureStop;
  dataToStore->changedData.chargeStopLowTemperatureStart = data->s.parameter.chargeStopLowTemperatureStart;
  dataToStore->changedData.chargeStopLowTemperatureStop = data->s.parameter.chargeStopLowTemperatureStop;
  dataToStore->changedData.dischargeStopHighTemperatureStart = data->s.parameter.dischargeStopHighTemperatureStart;
  dataToStore->changedData.dischargeStopHighTemperatureStop = data->s.parameter.dischargeStopHighTemperatureStop;
  dataToStore->changedData.dischargeStopLowTemperatureStart = data->s.parameter.dischargeStopLowTemperatureStart;
  dataToStore->changedData.dischargeStopLowTemperatureStop = data->s.parameter.dischargeStopLowTemperatureStop;

  // Neue Parameter für SOC
  dataToStore->changedData.uBatEmptyCompStartTemp = data->s.parameter.uBatEmptyCompStartTemp;
  dataToStore->changedData.uBatEmptyCompStopTemp = data->s.parameter.uBatEmptyCompStopTemp;
  dataToStore->changedData.uBatEmptyCompStopVolt = data->s.parameter.uBatEmptyCompStopVolt;
  dataToStore->changedData.extraDischargeStrom_mA = data->s.parameter.extraDischargeStrom_mA;
  dataToStore->changedData.cefW = data->s.parameter.cefW;
  dataToStore->changedData.batteryEmptyDetectionMode = data->s.parameter.batteryEmptyDetectionMode;

  dataToStore->changedData.auxOutputMode	= data->s.parameter.auxOutputMode;
  dataToStore->changedData.auxOutputSetpointOn = data->s.parameter.auxOutputSetpointOn;
  dataToStore->changedData.auxOutputSetpointOff = data->s.parameter.auxOutputSetpointOff;
  dataToStore->changedData.auxOutputInverted	= data->s.parameter.auxOutputInverted;

  // Eeprom Status Infos
  dataToStore->eepromState.writeCounter++;
  dataToStore->eepromState.structureSize = sizeof(eeprom_stored_data_t);
  dataToStore->eepromState.revisionInfo = 0;
  dataToStore->eepromState.firstStartId = CONFIG_ID;

  if (withSN)
  {
    printf("Writing SN!\n");
    dataToStore->deviceInfo.SN = data->s.parameter.sn;
  }


  EEPROM_Write(0,0, (uint8_t*)dataToStore, sizeof(eeprom_stored_data_t));

  return EEPROM_readConfig(data);
}

//------------------------------------------------------------------------------

HAL_StatusTypeDef EEPROM_readConfig(sys_data_t* data)
{
  eeprom_stored_data_t* dataToStore;

  /****************WERTE_AUS_EEPROM_LESEN********************/


  EEPROM_Read(0, 0, (uint8_t*)&eepromData,  sizeof(eepromData));


  dataToStore = (eeprom_stored_data_t*)&eepromData;

  // Schnittstellenparameter
  data->s.parameter.baudrate													= dataToStore->changedData.baudrate;
  data->s.parameter.parityMode													= dataToStore->changedData.parityMode;
  data->s.parameter.stopBit														= dataToStore->changedData.stopBits;
  data->s.parameter.slave_address												= dataToStore->changedData.slave_adress;
  data->s.parameter.ibn_day														= dataToStore->changedData.ibn_day;
  data->s.parameter.ibn_month													= dataToStore->changedData.ibn_month;
  data->s.parameter.ibn_year													= dataToStore->changedData.ibn_year;
  data->s.parameter.user_id														= dataToStore->changedData.user_id;

  // Offset und Gain
  data->s.parameter.batteryCurrentOffsetRefTemperatureShunt						= dataToStore->changedData.batteryCurrentOffsetRefTemperatureShunt;
  data->s.parameter.batteryCurrentOffsetRefTemperatureChip						= dataToStore->changedData.batteryCurrentOffsetRefTemperatureChip;
  data->s.parameter.batteryCurrentGainRefTempShunt								= dataToStore->changedData.batteryCurrentGainRefTempShunt;
  data->s.parameter.batteryCurrentGainRefTempChip								= dataToStore->changedData.batteryCurrentGainRefTempChip;
  data->s.parameter.batteryCurrentOffsetTemperatureCalibrationTemperature		= dataToStore->changedData.batteryCurrentOffsetTemperatureCalibrationTemperature;
  data->s.parameter.batteryCurrentGainTemperatureCalibrationShuntTemperature	= dataToStore->changedData.batteryCurrentGainTemperatureCalibrationShuntTemperature;
  data->s.parameter.batteryCurrentGainTemperatureCalibrationChipTemperature		= dataToStore->changedData.batteryCurrentGainTemperatureCalibrationChipTemperature;
  data->s.parameter.batteryCurrentOffsetRefshuntVoltage							= dataToStore ->changedData.batteryCurrentOffsetRefshuntVoltage;
  data->s.parameter.batteryCurrentOffsetCommonModeCalibrationVoltage			= dataToStore->changedData.batteryCurrentOffsetCommonModeCalibrationVoltage;
  data->s.parameter.batteryCurrentOffsetCommonModeCompensationFactor			= dataToStore->changedData.batteryCurrentOffsetCommonModeCompensationFactor;
  data->s.parameter.batteryCurrentOffsetTemperatureCompensationFactor			= dataToStore->changedData.batteryCurrentOffsetTemperatureCompensationFactor;
  data->s.parameter.batteryCurrentGainRefCurrent								= dataToStore->changedData.batteryCurrentGainRefCurrent;
  data->s.parameter.batteryCurrentGainTemperatureCompensationShuntFactor		= dataToStore->changedData.batteryCurrentGainTemperatureCompensationShuntFactor;
  data->s.parameter.batteryCurrentGainTemperatureCompensationChipFactor			= dataToStore->changedData.batteryCurrentGainTemperatureCompensationChipFactor;
  data->s.parameter.batteryCurrentOffset										= dataToStore->changedData.currentOffset;
  data->s.parameter.batteryCurrentGainCorrectionFaktor							= dataToStore->changedData.currentGain;

  data->s.parameter.batteryCurrentOffsetFast									= dataToStore->changedData.currentOffsetFast;
  data->s.parameter.batteryCurrentGainCorrectionFaktorFast						= dataToStore->changedData.currentGainFast;

  //Einstellungenm für AH counter
  data->s.parameter.cef															= dataToStore ->changedData.cef;
  data->s.parameter.peukert														= dataToStore ->changedData.peukert;
  data->s.parameter.cellCapacity												= dataToStore ->changedData.cellCapacity;
  data->s.parameter.battEnergy													= dataToStore ->changedData.cellEnergy;
  data->s.parameter.iBatFull													= dataToStore ->changedData.iBatFull;
  data->s.parameter.tBatFull													= dataToStore->changedData.tBatFull;
  data->s.parameter.uBatFull													= dataToStore->changedData.uBatFull;
  data->s.parameter.uBatEmpty													= dataToStore->changedData.uBatEmpty;
  data->s.parameter.socCalcMode													= dataToStore->changedData.socCalcMode;
  data->s.parameter.cellRatedDischargeTime										= dataToStore->changedData.cellRatedDischargeTime;

  // New EU directive says that cell Energy in Wh must be somwhere on the
  // visible part of the cell as well as capacity in Ah


  // Schaltausgänge
  data->s.parameter.lvpStart													= dataToStore->changedData.lvpStart;
  data->s.parameter.lvpStop														= dataToStore->changedData.lvpStop;
  data->s.parameter.ovpStart													= dataToStore->changedData.ovpStart;
  data->s.parameter.ovpStop														= dataToStore->changedData.ovpStop;
  data->s.parameter.loadCurrentLimit											= dataToStore->changedData.loadCurrentLimit;
  data->s.parameter.chargeCurrentLimit											= dataToStore->changedData.chargeCurrentLimit;
  data->s.parameter.chargeStopHighTemperatureStart								= dataToStore->changedData.chargeStopHighTemperatureStart;
  data->s.parameter.chargeStopHighTemperatureStop								= dataToStore->changedData.chargeStopHighTemperatureStop;
  data->s.parameter.chargeStopLowTemperatureStart								= dataToStore->changedData.chargeStopLowTemperatureStart;
  data->s.parameter.chargeStopLowTemperatureStop								= dataToStore->changedData.chargeStopLowTemperatureStop;
  data->s.parameter.dischargeStopHighTemperatureStart							= dataToStore->changedData.dischargeStopHighTemperatureStart;
  data->s.parameter.dischargeStopHighTemperatureStop							= dataToStore->changedData.dischargeStopHighTemperatureStop;
  data->s.parameter.dischargeStopLowTemperatureStart							= dataToStore->changedData.dischargeStopLowTemperatureStart;
  data->s.parameter.dischargeStopLowTemperatureStop								= dataToStore->changedData.dischargeStopLowTemperatureStop;

  data->s.parameter.uBatEmptyCompStartTemp										= dataToStore->changedData.uBatEmptyCompStartTemp;
  data->s.parameter.uBatEmptyCompStopTemp										= dataToStore->changedData.uBatEmptyCompStopTemp;
  data->s.parameter.uBatEmptyCompStopVolt										= dataToStore->changedData.uBatEmptyCompStopVolt;

  data->s.parameter.extraDischargeStrom_mA										= dataToStore->changedData.extraDischargeStrom_mA;
  data->s.parameter.cefW														= dataToStore->changedData.cefW;
  data->s.parameter.batteryEmptyDetectionMode									= dataToStore->changedData.batteryEmptyDetectionMode;

  data->s.parameter.auxOutputMode												= dataToStore->changedData.auxOutputMode;
  data->s.parameter.auxOutputSetpointOn											= dataToStore->changedData.auxOutputSetpointOn;
  data->s.parameter.auxOutputSetpointOff										= dataToStore->changedData.auxOutputSetpointOff;
  data->s.parameter.auxOutputInverted											= dataToStore->changedData.auxOutputInverted;

	

  //  Geräteinformation
  data->s.parameter.sn															= dataToStore->deviceInfo.SN;

  // prüfe Eeprom Status Infos
  //dataToStore->eepromState.writeCounter ++ ;
  if (dataToStore->eepromState.structureSize != sizeof(eeprom_stored_data_t)) return HAL_ERROR;
  if (dataToStore->eepromState.revisionInfo != 0) return HAL_ERROR;
  if (dataToStore->eepromState.firstStartId != CONFIG_ID) return HAL_ERROR;


  return HAL_OK;
}

/*Entfernte EEPROM Werte

  data->s.values.dischargeTotalAh												= dataToStore->changedData.dischargeTotalAh;
  data->s.values.chargeTotalAh													= dataToStore->changedData.chargeTotalAh;
  data->s.values.dischargeTotalWh												= dataToStore->changedData.dischargeTotalWh;
  data->s.values.chargeTotalWh													= dataToStore->changedData.chargeTotalWh;
  data->s.values.onTime															= dataToStore->changedData.onTime;
  data->s.values.mAsCounter														= dataToStore->changedData.mAsCounter;
  data->s.values.detectedCapacity												= dataToStore->changedData.detectedCapacity;
  data->s.values.detectedEnergy													= dataToStore->changedData.detectedEnergy;
  data->s.values.mAs_AutoMode													= dataToStore->changedData.mAs_AutoMode;
  data->s.values.mWs_AutoMode													= dataToStore->changedData.mWs_AutoMode;
*/


//--- NEW Functions
// function to determine the remaining bytes
uint16_t bytestowrite (uint16_t size, uint16_t offset)
{
	if ((size+offset)<PAGE_SIZE) return size;
	else return PAGE_SIZE-offset;
}

//-----------------------------------------------------------------------------

void EEPROM_Read (uint16_t page, uint16_t offset, uint8_t *data, uint16_t size)
{
	//int paddrposition = log(PAGE_SIZE)/log(2);
	int paddrposition = PADDRPOSITION;

	uint16_t startPage = page;
	uint16_t endPage = page + ((size+offset)/PAGE_SIZE);

	uint16_t numofpages = (endPage-startPage) + 1;
	uint16_t pos=0;

	for (int i=0; i<numofpages; i++)
	{
		uint16_t MemAddress = startPage<<paddrposition | offset;
		uint16_t bytesremaining = bytestowrite(size, offset);
		HAL_StatusTypeDef res = HAL_I2C_Mem_Read(EEPROM_I2C, EEPROM_ADDR, MemAddress, 2, &data[pos], bytesremaining, 1000);
#ifdef DEBUG
		//printf("MemAddress = 0x%04X, data ptr = 0x%08X, bytes_remaining = %u, res = %u\n", MemAddress, (uint32_t)&data[pos], bytesremaining, res);
#endif
		startPage += 1;
		offset=0;
		size = size-bytesremaining;
		pos += bytesremaining;
	}
}

//-----------------------------------------------------------------------------

void EEPROM_Write (uint16_t page, uint16_t offset, uint8_t *data, uint16_t size)
{
	// Find out the number of bit, where the page addressing starts
	int paddrposition = PADDRPOSITION;

	// calculate the start page and the end page
	uint16_t startPage = page;
	uint16_t endPage = page + ((size+offset)/PAGE_SIZE);

	// number of pages to be written
	uint16_t numofpages = (endPage-startPage) + 1;
	uint16_t pos=0;

	// write the data
	for (int i=0; i<numofpages; i++)
	{
		/* calculate the address of the memory location
		 * Here we add the page address with the byte address
		 */
		uint16_t MemAddress = startPage<<paddrposition | offset;
		uint16_t bytesremaining = bytestowrite(size, offset);  // calculate the remaining bytes to be written

		HAL_StatusTypeDef res = HAL_I2C_Mem_Write(EEPROM_I2C, EEPROM_ADDR, MemAddress, 2, &data[pos], bytesremaining, 1000);  // write the data to the EEPROM
#ifdef DEBUG
		//printf("MemAddress = 0x%04X, data ptr = 0x%08X, bytes_remaining = %u, res = %u\n", MemAddress, (uint32_t)&data[pos], bytesremaining, res);
#endif
		startPage += 1;  // increment the page, so that a new page address can be selected for further write
		offset=0;   // since we will be writing to a new page, so offset will be 0
		size = size-bytesremaining;  // reduce the size of the bytes
		pos += bytesremaining;  // update the position for the data buffer

		HAL_Delay (5);  // Write cycle delay (5ms)
	}
}
