[DRIVING] Renault Clio Electrique -97

Tell us about the project you do with the open inverter
Post Reply
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

I don't know how many where built but a lot more than 50. I think over 50 where sold i Sweden. My guess is maybe 1000?
The info on 50 is perhaps from wikipedia? and is regading the second generation clio. Mine is the first generation.

The original NiCd batteries where liquid cooled, hence the radiator in the original car. Motor and motor controller is air cooled, using a fan.
The motor is rarely any cause of trouble. The motor controller and even more so the "UCL" (central computer unit) will cause you trouble.

The Saxo and 106 are similar but not the same as the Clio.

I do have some information material in Swedish and some in French. Please send me a PM with your e-mail address and I will send you more info.
Age
Posts: 19
Joined: Sun Aug 02, 2020 8:53 am
Location: Southern Germany
Has thanked: 6 times
Been thanked: 3 times

Re: Renault Clio Electrique -97

Post by Age »

I owned two of the electric Clios Bexander modified. The car with LiFePo4-Mod was sold about a year ago. The original car was sold just two weeks ago.

As far as I know, there were 250 vehicles built. But I'm not sure if that's true.
The radiator was used for cooling the NiCd battery's.

The Peugeot/ Citroen cars where different regarding the engine and stuff. Though the batteries where similar.

Renault sold some Rapid Electrique cars that used the same components.

The engine and "inverter" (in facts, it's not really an inverter) in the Renault cars where build by ABB.

Nice little cars... Mine had to go because they are too small and unsafe with two kids.
Swing
Posts: 43
Joined: Mon Jan 27, 2020 2:03 pm
Has thanked: 9 times
Been thanked: 6 times

Re: Renault Clio Electrique -97

Post by Swing »

Ok thans for the info. Were they always 2 seaters? It looks like the battery box is at the rear seat place
Age
Posts: 19
Joined: Sun Aug 02, 2020 8:53 am
Location: Southern Germany
Has thanked: 6 times
Been thanked: 3 times

Re: Renault Clio Electrique -97

Post by Age »

The two I owned hat four seats. Afaik the fifth seat was not available because of the heavy batteries.
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

Swing wrote: Wed Aug 31, 2022 6:02 pm Ok thans for the info. Were they always 2 seaters? It looks like the battery box is at the rear seat place
Mine was 4-seats before. I have built my own battery box so that I could fit all my batteries in front of the rear axle.
In a stock car part of the pack is under the rear seat and part is in under the trunk area.
Swing
Posts: 43
Joined: Mon Jan 27, 2020 2:03 pm
Has thanked: 9 times
Been thanked: 6 times

Re: Renault Clio Electrique -97

Post by Swing »

Ah great! Because I thought it was the original battery box, thats why.

There is one for sale that has stood still for 10 years, and it is a bit too far away, would need to arrange transport.
And a bit unsure if it would work out, I would mainly want to put in something like 20kWh or so, no more than 100kg, and not mess to much with the rest of the car.
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

Well, that the same idea I started with when I bought the Clio... :)

Some info in swedish (hopefully some translate service will make this readable for you) regarding battery swap in Clio:
Konvertera till litiumbatterier.odt
(21.61 KiB) Downloaded 60 times
Written for LiFePo4 cells. The motor controller will not start if battery voltage is above 148V. While charging, the system can handle up to 172V.
pm_dawn
Posts: 36
Joined: Mon Jan 07, 2019 7:19 am
Location: Östersund/Sweden
Contact:

Re: Renault Clio Electrique -97

Post by pm_dawn »

bexander wrote: Thu Sep 01, 2022 5:25 pm Well, that the same idea I started with when I bought the Clio... :)

Some info in swedish (hopefully some translate service will make this readable for you) regarding battery swap in Clio:
Konvertera till litiumbatterier.odt
Written for LiFePo4 cells. The motor controller will not start if battery voltage is above 148V. While charging, the system can handle up to 172V.
That information was a short condensation from my old conversion thread on the swedish forum.
I still remember most of the Clio parts, and I also have a Clio in storage that can help me check things.

So if there are any questions regarding the Clio, don't hesitate to PM me.

Regards
/Per
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

I received a question via PM regarding how I implemented the CAN-control of the Leaf inverter so posting it here if someone else is interested.

It is based on work from others, mostly celeron55, http://productions.8dromeda.net/c55-lea ... tocol.html but also Zombiverter code and others.

This is my entire code used in my "Human Machine Interface", however the Leaf parts can be traced from functions readPTCAN() and sendPTCAN().

Code: Select all

// Change log
/*

1_19
* Changed torqueLimit to 125 (100) and powerLimit to 30 (25)
* Changed brakeLightTorqueOn to -15 (-20)

1_18
* Changed torqueLimit to 100 (40) and powerLimit to 25 (20) 

1_17
* Declare powerLimitedAccTorque as int16_t (uint16_t) and changes accordingly in limitedTorqueRequest()
* Changed torqueLimit to 40 (100) and powerLimit to 20 (30) 

1_16
* Changed how powerLimitedAccTorque is calculated in limitedTorqueRequest()
* Set powerLimitedAccTorque active

1_15
* Returned change of id 0x1D4 byte5 and 0x11A byte0 when changing gear, in sendPTCAN()
* Added maxSpeedGearChange constant and not allowing gear change above this speed in requestGear()

1_14
* Changed ID: 1D4, byte 6 0x01 (0x30), in sendPTCAN()

1_13
* Added DCDC_ctrl to work with HMI_10 HW
* Removed unused Dipped beam to free a IO PIN

1_12
* Changes to readPTCAN() to make motor current have the correct representation

1_11
* Changes to readPTCAN() to make motor current and voltage have the correct representation
* Added sendEnd() to reduce number of code lines

1_10
* Changes to sendSerialLCDPageTemp() to work with LCD_8_0
* Changed to checkInputVoltage() to return uin8_t value (boolean)
* Changes to sendSerialLCDPageMain() and sendSerialLCDPageTemp() to work with changed output from checkInputVoltage()
* Changed lowInputVoltageThreshold to 143 (144)

1_9
* Changed levels for regen ramp down and brake light

1_8
* Added regen torque command and other changed to limitedTorqueRequest()
* Moved gear selection to limitedTorqueRequest() from sendPTCAN()
* Added motorDirection to inverterStatus struct
* Added two limiting constants minimumRegenRpm and rampRegenRpm
* Changed how gear is used to singed value ("char")
* Added control of brake light to limitedTorqueRequest()
* Removed brakeLight()

1_7
* Corrected id 0x1D4 byte0 to 0x6E (0x7F) and byte1 to 0x6E (0x07) to be valid for the gen2 inverter

1_6
* Removed change of id 0x1D4 byte5 and 0x11A byte0 when changing gear, only changing sign of requested torque, in sendPTCAN()
* Changed how gear is used to "char" of selected gear in requestGear(), sendSerialLCDPageMain() and sendPTCAN()

1_5
* Removed message 0x1DB from sendPTCAN()
* Sending 0x50B with 100ms (10ms) interval in sendPTCAN()
* Made pedalPosition a global variable and changed how sendSerialLCDPageMain() sends the pedal
* Changed order of CAN frames in sendPTCAN()
* Added changed of id 0x1D4 byte5 when changing gear

1_4
* Changed motor rpm to signed value and then added abs() to get unsigned value in readPTCAN()

1_3
* Changed sign on torque command when in reverse gear in sendPTCAN()
* Removed regen torque and power limited torque from limitedTorqueRequest()
* Simplified calculation of sent torque request in limitedTorqueRequest() and sendPTCAN()
* Added division by 2 for motor RPM in readPTCAN()

1_2
* Made gear a static variable in requestGear()

1_1
* Adjusted requestGear() to start in neutral (0x3E)

1_0
* Initial version based on HMI2_2_10
* Added second CAN-bus to control Leaf inverter

*/

// Include libraries
#include "mcp_can.h"
#include "SPI.h"
#include "EEPROM.h"

// Constant declaration
// Button state MASK
const uint8_t State_BPA = B00000001;
const uint8_t State_BPD = B00001000;
const uint8_t State_BPR = B00010000;

// statusPBC MASK
const uint8_t statusON					= 0x01;
const uint8_t statusOFF					= 0x02;
const uint8_t statusRunningON 	= 0x04;
const uint8_t statusRunningOFF	= 0x08;

// Limits
const uint16_t pot1Min = 130; // Min limit for pot1
const uint16_t pot1Max = 950;	// Max limit for pot1
const uint16_t pot2Min = 50; // Min limit for pot2
const uint16_t pot2Max = 485; // Max limit for pot2
const uint8_t diffError = 50; // Max diff between pot1 and pot2 to trigger error handling
// Pedal map 
const uint16_t potNomMin = 152; // For calculating pedal map
const uint16_t potNomMax = 927; // For calculating pedal map
const uint16_t potLimp = 475; // 40% of max positive torque (0.4 * 500 + 275)
// Input voltage limit constants
const uint8_t lowInputVoltageThreshold = 143; // Voltage level (10,0V) to trigger odometer save to EEPROM, (10,0 * 1024 * 47 / (5 * (120+47)) - 0.5) / 4
const uint8_t lowInputVoltageWarning = 190; // Voltage level (13,2V) to trigger 12V warning light, (13,2 * 1024 * 47 / (5 * (120+47)) - 0.5) / 4
// Powertrain limits
const uint8_t torqueLimit = 125; // Maximum torque limit (Nm)
const uint8_t powerLimit = 30; // Maximum power limit (kW)
const uint8_t minimumRegenRpm = 40; // No regen torque below this motor speed (rpm)
const uint8_t rampRegenRpm = 240; // Start ramping down regen torque below this motor speed (rpm)
const uint8_t maxSpeedGearChange = 5; // No gear change allowed above this speed
// Brake light settings
const int8_t brakeLightTorqueOn = -15; // Torque to turn on brake light (Nm)
const int8_t brakeLightTorqueOff = -10; // Torque to turn off brake light (Nm)


// Variable declaration
//uint8_t inverter[8];
uint8_t mainBMS[8];
uint8_t highLowBMS[8];
uint8_t tempBMS[8];
uint16_t cellsBMS[42];
boolean newRevsDataFlag = false;
uint8_t statusPBC;
uint16_t pedalPosition = 0;

struct InverterStatus {
	uint16_t voltage = 0;
	int16_t current = 0;
	int8_t motorDirection = 0;
	uint16_t motorSpeed = 0;
	boolean error = false;
	int8_t invTemp = 0;
	int8_t motorTemp = 0;
} inverterStatus;

// I/O-PINS
const uint8_t SPI_CS_PIN = 10; // D10, CS PIN for MCP2515 CAN module
const uint8_t SPI_PTCS_PIN = 17; // A3, CS PIN for MCP2515 CAN module
const uint8_t buttonAPIN = 4; // Stalk button
const uint8_t buttonDPIN = 9; // Drive button
const uint8_t buttonRPIN = 8; // Reverse button
const uint8_t brakeLightPIN = 5; // Brake light relay
const uint8_t brakeFluidLevelPIN = 7; // Brake fluid level switch indicator
const uint8_t parkBrakePIN = 6; // Park brake status indicator
const uint8_t tsRPIN = 2; // Right turn signal indicator
const uint8_t tsLPIN = 3; // Left turn signal indicator
const uint8_t positionLightPIN = 14; // A0, Position light indicator
const uint8_t pot1PIN = 15; // A1, pot1 from accelerator pedal
const uint8_t pot2PIN = 16; // A2, pot2 from accelerator pedal
const uint8_t dcdcCtrlPIN = 19; // A5, To control DCDC turn on 
const uint8_t mainBeamPIN = 18; // A4, Main beam indicator
const uint8_t rawVoltageInputPIN = 6; // A6 for analogRead()
const uint8_t carVoltageInputPIN = 7; // A7 for analogRead()


MCP_CAN CAN(SPI_CS_PIN);	// Set CS pin
MCP_CAN PTCAN(SPI_PTCS_PIN); // Set CS pin


/********
* SETUP *
********/
void setup()
{
	pinMode(buttonAPIN, INPUT_PULLUP);
	pinMode(buttonDPIN, INPUT_PULLUP);
	pinMode(buttonRPIN, INPUT_PULLUP);
	pinMode(brakeFluidLevelPIN, INPUT_PULLUP);
	pinMode(parkBrakePIN, INPUT_PULLUP);
	pinMode(tsRPIN, INPUT_PULLUP);
	pinMode(tsLPIN, INPUT_PULLUP);
	pinMode(positionLightPIN, INPUT_PULLUP);
	pinMode(mainBeamPIN, INPUT_PULLUP);
		
  pinMode(brakeLightPIN, OUTPUT);
  digitalWrite(brakeLightPIN, LOW);
  
  pinMode(dcdcCtrlPIN, OUTPUT);
  digitalWrite(dcdcCtrlPIN, LOW);
  
	Serial.begin(115200);
	
	initCAN();
	
	delay(100);
}


/*******
* LOOP *
*******/
void loop()
{
  static uint32_t lastMillisSerialLCD = millis(); // For timing
  static uint8_t page = 0;
  static uint32_t savedOdometer = 0;
  static uint16_t savedTripmeter = 0;
  static uint16_t resetTripmeter = 0;
  static bool readFlag = false;
  
  if(!readFlag)
  {
		readFlag = true;
    
		for(int i=0; i<4; i++)
		{
			uint32_t odo = EEPROM.read(i);
			savedOdometer |= (odo << (i*8));
		}

    for(int i=0; i<2; i++)
    {
      uint16_t trip = EEPROM.read(i+4);
      savedTripmeter |= (trip << (i*8));
    }
    
	}
    
  readCAN();
  readPTCAN();
	uint8_t buttonState = readButtons();
	uint8_t carSpeed = revsToSpeed();
	int8_t gear = requestGear(buttonState, carSpeed);
	sendPTCAN(gear);
	sendCAN();
	turnSignal();
	
	uint16_t tripmeter = speedToTrip(carSpeed);
	uint16_t tripmeter2 = savedTripmeter + tripmeter - resetTripmeter;
	uint32_t odometer = savedOdometer + tripmeter;
	
	saveOdometerToEEPROM(odometer, tripmeter2);
	
	// Reset tripmeter before 1000km is reached
	(tripmeter2 >= 10000) ? (resetTripmeter = savedTripmeter + tripmeter) : 0;
		
	uint8_t newPage = changePageOrReset(page, buttonState);
	
	// Check if reset is requested, if not update page
	(newPage == 0xFF) ? (resetTripmeter = savedTripmeter + tripmeter) : page = newPage;
		
	if (millis() - lastMillisSerialLCD >= 15) // Test if the 15ms period has elapsed
  {
		lastMillisSerialLCD = millis();  // IMPORTANT to save the start time
		sendSerialLCD(page, carSpeed, tripmeter, tripmeter2, odometer, gear);
	}
}


/*****************************************************************************************
* Checks 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];
  static boolean bmsAlive = false;

  if(CAN_MSGAVAIL == CAN.checkReceive()) // Check if data is coming
  {
    CAN.readMsgBuf(&rxId, &len, rxBuf); // read data, rxId: message ID, ext: flag extended ID, len: data length, rxBuf: data buf
    
    if(rxId == 0x100) // Main data from BMS
    {
      for(int i=0; i<8; i++)
      {
				mainBMS[i] = rxBuf[i];
			}
			if(!bmsAlive)
			{
				bmsAlive = true;
				digitalWrite(dcdcCtrlPIN, HIGH);
			}
    }
		if(rxId == 0x101) // Highest and lowest cell data from BMS
    {
      for(int i=0; i<8; i++)
      {
				highLowBMS[i] = rxBuf[i];
			}
    }
		if(rxId == 0x102) // Temperature data from BMS
    {
      for(int i=0; i<8; i++)
      {
				tempBMS[i] = rxBuf[i];
			}
    }
    if(rxId == 0x091) // PBC status
    {
			statusPBC = rxBuf[0];
		}
  }
}


/*********************************************
* Send park brake request to PBC via CAN-bus *
*********************************************/
void sendCAN()
{
	static uint32_t lastMillisCAN = millis();
	
	if (millis() - lastMillisCAN >= 100) // Test if the 100ms period has elapsed
  {
    lastMillisCAN = millis(); // reset timer
    
    uint8_t parkBrakeRequest[1];
    if(!digitalRead(parkBrakePIN)) // Pulled up
    {
			parkBrakeRequest[0] = 0x01; // requestON
		}
		else
		{
			parkBrakeRequest[0] = 0x02; // requestOFF
		}
		
		CAN.sendMsgBuf(0x090, 0, 1, parkBrakeRequest); // Send data to PBC
	}
}


/*****************************************************
* Reads pot data, check for errors and set potReturn *
*****************************************************/
int16_t readPot()
{
	uint16_t pot1 = analogRead(pot1PIN); // Read accelerator pot1
	uint16_t pot2 = analogRead(pot2PIN); // Read accelerator pot2
	
  int16_t potReturn;
  static bool potGood = false;
  
  potGood = potChecker(pot1, pot2);
  
	if (potGood) // Pot:s good, set output from pot1
	{
		potReturn = pedalMap(pot1, false); // Set output value from pot1	
	}
	else // Pot:s bad, check whats causing the error and possibly allow limp home
	{
		if ((pot1 < pot1Min || pot1 > pot1Max) && (pot2 > pot2Min && pot2 < pot2Max)) // Check if pot1 is causing error
		{
			potReturn = pedalMap(pot2*2, true); // Set output value from pot2
		}
		else if ((pot2 < pot2Min || pot2 > pot2Max) && (pot1 > pot1Min && pot1 < pot1Max)) // Check if pot2 is causing error
		{
			potReturn = pedalMap(pot1, true); // Set output value from pot1
		}
		else if ((pot1 < pot1Min || pot1 > pot1Max) && (pot2 < pot2Min || pot2 > pot2Max)) // Check if both pot:s are outside nominal range
		{
			potReturn = 0; // Stand still
		}
		else // Some other fault, could be a loose wire or a faulty potentiometer causing values to float around
		{
			uint16_t pot = min(pot1, (pot2*2));
			potReturn = pedalMap(pot, true); // Set output from min of pot1 and pot2
		}
	}
	
	return potReturn;
}


/****************************************************************
* Checks if pot:s are OK to use and starts error counter if not *
****************************************************************/
bool potChecker(uint16_t pot1, uint16_t pot2)
{
	static uint8_t errorCounter = 0;
	static uint8_t worksCounter = 5; // Assume pot:s are good at start
	static bool potGood = false;
	int16_t diff = pot1 - (2*pot2);
	
	bool potError = abs(diff) > diffError || pot1 < pot1Min || pot1 > pot1Max || pot2 < pot2Min || pot2 > pot2Max; // Checks if pot:s agree and are within limits, if not set error
	
	if (potError)
  {
    worksCounter = 0;
    if (errorCounter < 5)
    {
      errorCounter++;
    }
    else
    {
      potGood = false;
    }
  }
  else
  {
    errorCounter = 0;
    if (worksCounter < 5)
    {
      worksCounter++;
    }
    else
    {
      potGood = true;
    }
  }

  return potGood;	
}


/*************************************************
* Maps output to controller from pedal pot input *
*************************************************/
int16_t pedalMap(uint16_t potInput, bool limpHome)
{
	int16_t potReturn; // (0 - 775)
	static bool limpHomeTriggered = false;
	
	if (potInput < potNomMin) // To avoid values outside nominal values
  {
    potInput = potNomMin;
  }
	if (potInput > potNomMax) // To avoid values outside nominal values
  {
    potInput = potNomMax;
  }
  
  potReturn = potInput - potNomMin;
  
  if (limpHome || limpHomeTriggered) // Check if limpHome is triggered
	{
		limpHomeTriggered = true;
		if (potReturn > potLimp)
		{
			potReturn = potLimp;
		}
	}
	
	return potReturn;
}


/*************************************************************
* Checks if any buttons are pressed and returns button state *
*************************************************************/
uint8_t readButtons()
{
	static uint32_t lastMillisButton;
	static uint8_t lastButtonState;
	static uint8_t buttonState;
	uint8_t buttonReading;
	
	buttonReading = (digitalRead(buttonAPIN) == LOW) ? State_BPA : 0;
	buttonReading |= (digitalRead(buttonDPIN) == LOW) ? State_BPD : 0;
	buttonReading |= (digitalRead(buttonRPIN) == LOW) ? State_BPR : 0;
		
	(buttonReading != lastButtonState) ? (lastMillisButton = millis()) : 0;	// reset timer
	
	(millis() - lastMillisButton > 100) ? (buttonState = buttonReading) : 0; // 100ms
	
	lastButtonState = buttonReading;

	return buttonState;
}	


/*****************************************************************
* Reads button state and returns requested page or reset request *
*****************************************************************/
uint8_t changePageOrReset(uint8_t page, uint8_t buttonState)
{
	static uint32_t lastMillisButtonA;
	static uint8_t buttonAPressedFlag = 0;

  if((buttonState & State_BPA) && (buttonAPressedFlag == 0)) // Button A depressed and has not been depressed before
  {
    lastMillisButtonA = millis(); // Reset the timer
    buttonAPressedFlag = 1;
  }
  if((buttonState & State_BPA) && (buttonAPressedFlag == 1) && (millis() - lastMillisButtonA > 3000)) // 3s
  {
		buttonAPressedFlag = 2;
		page = 0xFF; // Request reset
	}
  if(((buttonState & State_BPA) == 0) && (buttonAPressedFlag != 0)) // Button A released after having been depressed
  {
		if(buttonAPressedFlag == 1)
    {
      (page >= 1) ? page = 0 : page++; 
    }
		
    buttonAPressedFlag = 0;
  }
  
	return page;
}


/***********************************************
* Reads button state and returns selected gear *
***********************************************/
int8_t requestGear(uint8_t buttonState, uint8_t carSpeed)
{
	static int8_t gear = 0;
	
	if((buttonState & State_BPD) && !(buttonState & State_BPR) && (carSpeed < maxSpeedGearChange))
	{
		gear = 1; // Drive
	}
	if((buttonState & State_BPR) && !(buttonState & State_BPD) && (carSpeed < maxSpeedGearChange))
	{
		gear = -1; // Reverse
	}
	if((buttonState & State_BPD) && (buttonState & State_BPR))
	{
		gear = 0; // Neutral
	}

	return gear;
}


/*****************************************************************************************************************
* Measures car 12V-system voltage, calculates average over 5 values and turns on or off 12V system warning light *
*****************************************************************************************************************/
uint8_t checkInputVoltage()
{
	static uint8_t inputVoltageArray[5] = {0, 0, 0, 0, 0};
	static uint8_t readIndex = 0;
	uint16_t sumOfReadings = 0;
	
	uint8_t voltage = analogRead(carVoltageInputPIN) >> 2; // Division by 4
	inputVoltageArray[readIndex] = voltage;
	readIndex++;
	
	if(readIndex >= 5)
	{
		readIndex = 0;
	}
		
	for(int i=0; i<5; i++)
	{
		sumOfReadings += inputVoltageArray[i];
	}
	
	uint8_t inputVoltage = sumOfReadings / 5;
	
	//boolean status12VError = inputVoltage < lowInputVoltageWarning; // Set 12V warning light if triggered
	
	return inputVoltage;
}


/****************************************************************************************
* Reads arduino raw input voltage and saves odometer to EEPROM if below threshold value *
****************************************************************************************/
void saveOdometerToEEPROM(uint32_t odometerToSave, uint16_t tripmeterToSave)
{
	static boolean savedFlag = false;
	static uint32_t lastMillisSave = millis();
	
	uint8_t inputVoltage = analogRead(rawVoltageInputPIN) >> 2; // Division by 4
  
	if((inputVoltage < lowInputVoltageThreshold) && !savedFlag) // Save odometer if input voltage is below threshold and not saved before
	{
		savedFlag = true;
		lastMillisSave = millis(); // Reset timer
		
		for(int i=0; i<4; i++)
		{
			uint8_t odo = (odometerToSave >> (i*8)) & 0xFF;
			EEPROM.update(i, odo);
		}
		
		EEPROM.update(4, lowByte(tripmeterToSave));
		EEPROM.update(5, highByte(tripmeterToSave));
	}
	
	if ((millis() - lastMillisSave >= 10000) && savedFlag) // Test if the 10s period has elapsed
  {
    lastMillisSave = millis(); // Reset timer
    savedFlag = false; // Reset flag
	}
}


/***************************************************************************************************************************************
* Checks if new motor revolution data is available, calculates average over 5 values, translates it to car speed and returns car speed *
***************************************************************************************************************************************/
uint8_t revsToSpeed()
{
	uint8_t carSpeed;
	static uint16_t motorRevsInput[5] = {0, 0, 0, 0, 0};
	static uint8_t readIndex = 0;
	uint16_t sumOfReadings = 0;
			
	if(newRevsDataFlag)
	{
		newRevsDataFlag = false;
		
		motorRevsInput[readIndex] = inverterStatus.motorSpeed;
		readIndex++;
	
		if(readIndex >= 5)
		{
			readIndex = 0;
		}
	}
		
	for(int i=0; i<5; i++)
	{
		sumOfReadings += motorRevsInput[i];
	}
	
	carSpeed = sumOfReadings / 385; // Car speed in km/h (77 * 5 = 385)
	
	return carSpeed;
}


/*******************************************************************************************************
* Calculates the distance traveled by integrating the car speed, returns distance with 100m resolution *
*******************************************************************************************************/
uint32_t speedToTrip(uint8_t carSpeed)
{
	static uint32_t distanceIn100Meter = 0;
	static uint32_t distanceInMeter3600 = 0;
	static uint32_t lastMillisTrip = millis(); // For timing
	uint16_t timeSinceLast;
		
	if (millis() - lastMillisTrip >= 100) // Test if the 100ms period has elapsed
  {
		timeSinceLast = millis() - lastMillisTrip;
		lastMillisTrip = millis(); // Reset timer
    
    distanceInMeter3600 += timeSinceLast * carSpeed;
    
    if(distanceInMeter3600 >= 360000) // Translate m*3600 to 100m
    {
			distanceIn100Meter++;
			distanceInMeter3600 -= 360000; 
		}
	}
	
	return distanceIn100Meter;
}


/*********************************************************************
* Calculates the average battery current over the last five readings *
*********************************************************************/
int16_t averageBatteryCurrent()
{
	static int16_t batteryCurrentArray[5] = {0, 0, 0, 0, 0};
	static uint8_t readIndex = 0;
	int16_t sumOfReadings = 0;
	
	int16_t current = (mainBMS[0] << 8) + mainBMS[1]; // Battery pack current
	batteryCurrentArray[readIndex] = current;
	readIndex++;
	
	if(readIndex >= 5)
	{
		readIndex = 0;
	}
		
	for(int i=0; i<5; i++)
	{
		sumOfReadings += batteryCurrentArray[i];
	}
	
	int16_t averageBatteryCurrent = sumOfReadings / 5;
	
	return averageBatteryCurrent;
}


/****************************************
* Sends end transmission command to LCD *
****************************************/
void sendEnd()
{
	Serial.write(0xFF);
	Serial.write(0xFF);
	Serial.write(0xFF);
}


/*************************************************************************************
* Determines which page to display on LCD and calls corresponding data send function *
*************************************************************************************/
void sendSerialLCD(uint8_t page, uint8_t carSpeed, uint16_t tripmeter, uint16_t tripmeter2, uint32_t odometer, int8_t gear)
{
	static uint8_t pageOld = 1; // Set to !=0 to force page change at startup
	uint8_t inputVoltage = checkInputVoltage();	
	
	if(page == 0)
	{
		if(page != pageOld)
		{
			sendEnd();
			
			Serial.print("page m");
			sendEnd();
      
			pageOld = page;
		}
		else
		{
			sendSerialLCDPageMain(carSpeed, tripmeter2, gear, inputVoltage);
		}
	}
	if(page == 1)
	{
		if(page != pageOld)
		{
      sendEnd();
      
      Serial.print("page t");
			sendEnd();
      
			pageOld = page;
		}
		else
		{
			sendSerialLCDPageTemp(tripmeter, tripmeter2, odometer, inputVoltage);
		}
	}
}


/**********************************************************
* Page Main, calculates and sends LCD data via serial bus *
**********************************************************/
void sendSerialLCDPageMain(uint8_t carSpeed, uint16_t tripmeter2, int8_t gear, uint8_t inputVoltage)
{
	static uint8_t counter = 0;
	
	sendEnd();
	
	uint16_t pedalColour = 65535; // White	
	uint8_t pedalValue;
	
	if(pedalPosition < 250) // Regen
	{
		pedalValue = map(pedalPosition, 0, 250, 0, 30);
		pedalColour = 31; // Blue (regen)
	}
	else if(pedalPosition > 275) // Throttle
	{
		pedalValue = map(pedalPosition, 275, 775, 30, 100);
		pedalColour = 2016; // Green (throttle)
	}
	else // Coast
	{
		pedalValue = 30;
		pedalColour = 65535; // White
	}
	
	Serial.print("m.pd.val=");
	Serial.print(pedalValue);
	sendEnd();
	
	Serial.print("m.pd.pco=");
	Serial.print(pedalColour);
	sendEnd();
	
	
	if(counter == 0)
	{
		Serial.print("m.sp.val=");
		Serial.print(carSpeed);
		sendEnd();
		
		if(digitalRead(positionLightPIN))
    {
      Serial.print("vis po,0");
      sendEnd();
    }
    else
    {
      Serial.print("vis po,1"); // Position light
      sendEnd();
    }
		
		counter++;
	}
	else if(counter == 1)
	{
		uint16_t capacity = (mainBMS[4] << 8) + mainBMS[5]; // Battery pack remaining capacity
		uint8_t valCapacity;
	
		if(capacity >= 50)
		{
			valCapacity = ((capacity - 50) * 100) / 198; // Battery capacity in % of 19.8Ah (24.8Ah * 80%)
		}
		else
		{
			valCapacity = 0;
		}
	
		Serial.print("m.ca.val=");
		Serial.print(valCapacity);
		sendEnd();
		
		if(digitalRead(mainBeamPIN))
    {
      Serial.print("vis mb,0");
      sendEnd();
    }
    else
    {
      Serial.print("vis mb,1"); // Main beam
      sendEnd();
    }
		
		counter++;
	}
	else if(counter == 2)
	{
		Serial.print("m.ta.val=");
		Serial.print(tripmeter2);
		sendEnd();
		
		static uint8_t pa;
		if(statusPBC == statusOFF)
    {
      pa = 0;
    }
    if(statusPBC == statusON)
    {
      pa = 1;
    }
    if(statusPBC == statusRunningON || statusPBC == statusRunningOFF)
    {
			static uint8_t blinkCounter = 0;
			blinkCounter++;
			if(blinkCounter > 3)
			{
				blinkCounter = 0;
				pa = (pa == 0) ? 1 : 0;
			}
		}
		
		Serial.print("vis pa,");
    Serial.print(pa);
    sendEnd();
				
		counter++;
	}
	else if(counter == 3)
	{
		uint8_t gearSet;
		if(gear == 0) // Neutral
		{
			gearSet = 'N';
		}
		else if(gear == -1) // Reverse
		{
			gearSet = 'R';
		}
		else if(gear == 1) // Drive
		{
			gearSet = 'D';
		}
		else
		{
			gearSet = 'P'; // error (P)
		}
	
		Serial.print("m.ge.txt=");
		Serial.write(0x22);
		Serial.write(gearSet);
		Serial.write(0x22);
		sendEnd();
		
		if(digitalRead(brakeFluidLevelPIN))
    {
      Serial.print("vis bf,0");
      sendEnd();
    }
    else
    {
      Serial.print("vis bf,1"); // Brake fluid level
      sendEnd();
    }
		
		counter++;
	}
	else if(counter == 4)
	{
		uint16_t totalVoltage = (mainBMS[2] << 8) + mainBMS[3]; // Battery pack total voltage
		
		Serial.print("m.vo.val=");
		Serial.print(totalVoltage);
		sendEnd();
		
		if(inputVoltage < lowInputVoltageWarning)
    {
      Serial.print("vis bl,1"); // Low 12V battery voltage
      sendEnd();
    }
    else
    {
      Serial.print("vis bl,0");
      sendEnd();
    }
		
		counter++;
	}
	else if(counter == 5)
	{
		int16_t batteryCurrent = averageBatteryCurrent();
		String stringCurrent;
		stringCurrent = ((batteryCurrent < 100) && (batteryCurrent > -100)) ? String(batteryCurrent / 10.0, 1) : String(batteryCurrent / 10);
			
		Serial.print("m.cu.txt=");
		Serial.write(0x22);
		Serial.print(stringCurrent);
		Serial.write(0x22);
		sendEnd();
		
		if(inverterStatus.invTemp > 60 || inverterStatus.motorTemp > 60)
    {
      Serial.print("vis ot,1"); // Over temperature
      sendEnd();
    }
    else
    {
      Serial.print("vis ot,0");
      sendEnd();
    }
		
		counter++;
	}
	else if(counter == 6)
	{
		if(mainBMS[6] & 0x02)
    {
      Serial.print("vis bh,1"); // Low main battery voltage
      sendEnd();
    }
    else
    {
      Serial.print("vis bh,0");
      sendEnd();
    }
		
		
		if((mainBMS[6] & 0x04) || inverterStatus.error)
		{
      Serial.print("vis mw,1"); // Master warning
      sendEnd();
    }
    else
    {
      Serial.print("vis mw,0");
      sendEnd();
    }
		
		counter = 0;
	}
	else // error handler
	{
		counter = 0;
	}
}


/**********************************************************
* Page Temp, calculates and sends LCD data via serial bus *
**********************************************************/
void sendSerialLCDPageTemp(uint16_t tripmeter, uint16_t tripmeter2, uint32_t odometer, uint8_t inputVoltage)
{
	static uint8_t counter = 0;
	
	sendEnd();
	
	if(counter == 0)
	{
		Serial.print("t.mv.val=");
		Serial.print(inverterStatus.voltage);
		sendEnd();
		
		Serial.print("t.mc.val=");
		Serial.print(inverterStatus.current);
		sendEnd();
		
		uint8_t batteryVoltage = inputVoltage * 0.694; // 5 * (120 + 47) * 4 * 10 / (1024 * 47)
		
		Serial.print("t.bv.val=");
		Serial.print(batteryVoltage);
		sendEnd();
		
		counter++;
	}
	else if(counter == 1)
	{
		Serial.print("t.od.val=");
		Serial.print(odometer / 10);
		sendEnd();
	
		Serial.print("t.ta.val=");
		Serial.print(tripmeter2);
		sendEnd();
	
		Serial.print("t.tb.val=");
		Serial.print(tripmeter);
		sendEnd();
		
		counter++;
	}
	else if(counter == 2)
	{
		int8_t tempModule = tempBMS[0]; // Lowest cell module temperature in degree C
        
    Serial.print("t.t0.val=");
    Serial.print(tempModule);
    sendEnd();

    tempModule = tempBMS[1]; // Highest cell module temperature in degree C
        
    Serial.print("t.t1.val=");
    Serial.print(tempModule);
    sendEnd();

    tempModule = tempBMS[2]; // Battery box internal temperature in degree C
        
    Serial.print("t.t2.val=");
    Serial.print(tempModule);
    sendEnd();

    tempModule = tempBMS[3]; // BMS slave module highest die temperature in degree C
        
    Serial.print("t.t3.val=");
    Serial.print(tempModule);
    sendEnd();
		
		counter++;
	}
	else if(counter == 3)
	{
		uint8_t highCell = highLowBMS[0]; // Highest cell number
		uint16_t highVoltage = (highLowBMS[1] << 8) + highLowBMS[2]; // Highest cell voltage
	
		Serial.print("t.hc.val=");
		Serial.print(highCell);
		sendEnd();
	
		Serial.print("t.hv.val=");
		Serial.print(highVoltage);
		sendEnd();
	
		uint8_t lowCell = highLowBMS[3]; // Lowest cell number
		uint16_t lowVoltage = (highLowBMS[4] << 8) + highLowBMS[5]; // Lowest cell voltage
	
		Serial.print("t.lc.val=");
		Serial.print(lowCell);
		sendEnd();
	
		Serial.print("t.lv.val=");
		Serial.print(lowVoltage);
		sendEnd();
		
		counter++;
	}
	else if(counter == 4)
	{
		Serial.print("t.s0.val=");
		Serial.print(mainBMS[6]);
		sendEnd();
		
		Serial.print("t.tm.val=");
		Serial.print(inverterStatus.motorTemp);
		sendEnd();
		
		Serial.print("t.ti.val=");
		Serial.print(inverterStatus.invTemp);
		sendEnd();
			
		uint16_t capacity = (mainBMS[4] << 8) + mainBMS[5]; // Battery pack remaining capacity
		
		Serial.print("t.ca.val=");
		Serial.print(capacity);
		sendEnd();
		
		counter = 0;
	}
	else // error handler
	{
		counter = 0;
	}	
}


/*****************************************************
* Sends turn signal indication to LCD via serial bus *
*****************************************************/
void turnSignal()
{
	static boolean turnSignalRightOld = false;
	static boolean turnSignalLeftOld = false;
	
	boolean turnSignalLeft = !digitalRead(tsLPIN);
	boolean turnSignalRight = !digitalRead(tsRPIN);
	
	if(turnSignalRight != turnSignalRightOld)
	{
		sendEnd();
		
		if(turnSignalRight)
		{
			turnSignalRightOld = true;
			
			Serial.print("vis r,1");
			sendEnd();
		}
		else
		{
			turnSignalRightOld = false;
			
			Serial.print("vis r,0");
			sendEnd();
		}
	}
	
	if(turnSignalLeft != turnSignalLeftOld)
	{
		sendEnd();
		
		if(turnSignalLeft)
		{
			turnSignalLeftOld = true;
			
			Serial.print("vis l,1");
			sendEnd();
		}
		else
		{
			turnSignalLeftOld = false;
			
			Serial.print("vis l,0");
			sendEnd();
		}
	}
}


/*********************************
* Reads PTCAN data from inverter *
*********************************/
void readPTCAN()
{
	uint32_t rxId;
  uint8_t len;
  uint8_t rxBuf[8];  
        
  if(CAN_MSGAVAIL == PTCAN.checkReceive()) // Check if data is coming
  {
    PTCAN.readMsgBuf(&rxId, &len, rxBuf); // read data, rxId: message ID, ext: flag extended ID, len: data length, rxBuf: data buf
    
    if(rxId == 0x1DA) // Status from Inverter
    {
			uint16_t parsedVoltage = (rxBuf[0] << 2) | (rxBuf[1] >> 6); // Inverter voltage (2 x V)
			inverterStatus.voltage = parsedVoltage / 2; // Inverter voltage (V)
			uint16_t parsedCurrent = ((rxBuf[2] & 0x07) << 8) | rxBuf[3]; // Inverter current (A), 11-bit
			inverterStatus.current = (int16_t)(parsedCurrent << 5) >> 5; // Inverter current (A), 16-bit signed
			int16_t parsedSpeed = (rxBuf[4] << 8) | rxBuf[5];
			int16_t speed = (parsedSpeed == 0x7FFF ? 0 : parsedSpeed); // Motor speed (2 x rpm)
			inverterStatus.motorDirection = speed < 0 ? -1 : 1; // Motor rotation direction (1 is forward, -1 is reverse)
			inverterStatus.motorSpeed = abs(speed) / 2; // Absolute value of motor speed (rpm)
			inverterStatus.error = (rxBuf[6] & 0xB0) != 0x00; // Inverter error if true
			newRevsDataFlag = true;
    }
    
    if(rxId == 0x55A) // Temperature from Inverter
    {
			inverterStatus.invTemp = fahrenheitToCelsius(rxBuf[2]); // Inverter temperature (degree C)
			inverterStatus.motorTemp = fahrenheitToCelsius(rxBuf[1]); // Motor temperature (degree C)
    } 
  }  
}


/***************************************************
* Sends control commands to inverter via PTCAN-bus *
***************************************************/
void sendPTCAN(int8_t gear)
{
	static uint32_t lastMillisPTCAN = millis();	
	
	if (millis() - lastMillisPTCAN >= 10) // 10ms
  {
    lastMillisPTCAN = millis(); // reset timer
    static uint8_t counter = 0;
    static uint8_t counter50B = 0;
    counter50B++;
    
    uint8_t byte1D4B5;
    uint8_t byte11AB0;
		if(gear == 0) // Neutral
		{
			byte1D4B5 = 0x43;
			byte11AB0 = 0x3E;
		}
		else if(gear == -1) // Reverse
		{
			byte1D4B5 = 0x42;
			byte11AB0 = 0x2E;
		}
		else if(gear == 1) // Drive
		{
			byte1D4B5 = 0x44;
			byte11AB0 = 0x4E;
		}
		else
		{
			byte1D4B5 = 0x00;
			byte11AB0 = 0x00;
		}
    
    // 1D4
    uint8_t bytes1D4[8] = {0x6E, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00};
    
    int16_t torqueRequest = limitedTorqueRequest(gear);		
    bytes1D4[2] = highByte(torqueRequest);
    bytes1D4[3] = lowByte(torqueRequest) & 0xF0;
    
    bytes1D4[4] = 0x07 | (counter << 6);
    bytes1D4[5] = byte1D4B5;
    nissanCRC(bytes1D4);
    
    PTCAN.sendMsgBuf(0x1D4, 0, 8, bytes1D4);
    
    // 50B
    if(counter50B > 9) // every 100ms
		{
			counter50B = 0; // reset counter
			uint8_t bytes50B[7] = {0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x00};
    
			PTCAN.sendMsgBuf(0x50B, 0, 7, bytes50B);
		}
    
		// 11A
    uint8_t bytes11A[8] = {0x00, 0x40, 0x00, 0xAA, 0XC0, 0x00, 0x00, 0x00};
    
    bytes11A[0] = byte11AB0;
    bytes11A[6] = counter;
    nissanCRC(bytes11A);
    
    PTCAN.sendMsgBuf(0x11A, 0, 8, bytes11A);
    
    // Counter
    counter++;
    if(counter > 3)
    {
      counter = 0;
    }
	}
}


/******************************************************
* Calculates what torque to request from the inverter *
******************************************************/
int16_t limitedTorqueRequest(int8_t gear)
{
	uint16_t potRequest = readPot(); // (0 - 775)
	pedalPosition = potRequest; // For LCD presentation of pedal position
	int16_t torqueRequest = 0;
	int16_t accTorqueRequestLimit = torqueLimit * 72; // 250Nm * 72 = 18000 
	int16_t regenTorqueRequestLimit = -(accTorqueRequestLimit / 2); // Acc torque / 2
	
  int16_t powerLimitedAccTorque = 0x7FFF;
  if(inverterStatus.motorSpeed != 0)
  {
		powerLimitedAccTorque = min(((int32_t)powerLimit * 687549 / inverterStatus.motorSpeed), (int32_t)0x7FFF); // 1000 x 60 x 72 / (2 x pi)
	}
  int16_t powerLimitedRegenTorque = -(powerLimitedAccTorque / 2); // Acc torque / 2
  
  boolean gearSelected = abs(gear) == 1; // Drive or reverse selected
  static boolean brakeLightOn = false;
  	
	if(gearSelected && ((potRequest >= 275) && (potRequest <= 775))) // Acceleration torque requested
	{
		torqueRequest = map(potRequest, 275, 775, 0, accTorqueRequestLimit);
		
		if(torqueRequest > powerLimitedAccTorque)
		{
			torqueRequest = powerLimitedAccTorque;
		}
		
		brakeLightOn = false;
	} 
	else if(gearSelected && ((potRequest <= 250) && (potRequest >= 0)) && (inverterStatus.motorSpeed > minimumRegenRpm)) // Regeneration torque requested and above minimum regen speed
	{
		// Note: negative torque values for regen!
		torqueRequest = map(potRequest, 0, 250, regenTorqueRequestLimit, 0);
		
		if(inverterStatus.motorSpeed < rampRegenRpm) // Ramp down regen torque for low motor speeds
		{
			int16_t speedLimitedTorqueRequest = map(inverterStatus.motorSpeed, minimumRegenRpm, rampRegenRpm, 0, regenTorqueRequestLimit);
			if(torqueRequest < speedLimitedTorqueRequest)
			{
				torqueRequest = speedLimitedTorqueRequest;
			}
		}
		
		if(torqueRequest < powerLimitedRegenTorque)
		{
			torqueRequest = powerLimitedRegenTorque;
		}
		
		if(torqueRequest < (brakeLightTorqueOn * 72))
		{
			brakeLightOn = true;
		}
		if(torqueRequest > (brakeLightTorqueOff * 72))
		{
			brakeLightOn = false;
		}
		
		int8_t expectedDirection = gear * inverterStatus.motorDirection; // Handle when selected gear does not match motor rotation direction
		
		torqueRequest *= expectedDirection;
	}	
	else 
	{
		torqueRequest = 0;
		brakeLightOn = false;
	}
	
	torqueRequest *= gear; // Sign of torque from selected gear
	
	digitalWrite(brakeLightPIN, brakeLightOn ? HIGH : LOW);
	
	return torqueRequest;
}


/**********************************
* Farenheit to celsius conversion *
**********************************/
int8_t fahrenheitToCelsius(uint8_t fahrenheit)
{
  //int8_t result = ((int16_t)fahrenheit - 32) * 5 / 9;
  int8_t result = ((int16_t)(fahrenheit - 32) * 9) >> 4;
  
  return result;
}


/*************************
* Nissan CRC calculation *
*************************/
static void nissanCRC(uint8_t *data)
{
	data[7] = 0;
	uint8_t crc = 0;
		
	for (int b=0; b<8; b++) {
		for (int i=7; i>=0; i--) {
			uint8_t bit = ((data[b] & (1 << i)) > 0) ? 1 : 0;
			if(crc >= 0x80) {
				crc = (byte)(((crc << 1) + bit) ^ 0x85);
			} else {
				crc = (byte)((crc << 1) + bit);
			}
		}
	}
	
	data[7] = crc;
}


/****************
* Init CAN-bus *
****************/
void initCAN()
{
	// CAN
	for(int i=0; i<20; i++)
	{
		if(CAN_OK == CAN.begin(MCP_STDEXT, CAN_250KBPS, MCP_8MHZ))    // Init car CAN bus : baudrate = 250k
		{
			CAN.setMode(MCP_NORMAL);  // Set operation mode to normal so the MCP2515 sends acks to received data.
					
			CAN.init_Mask(0,0,0x07FF0000);                // Init first mask
			CAN.init_Filt(0,0,0x01320000);                // Init first filter
			CAN.init_Filt(1,0,0x00910000);                // Init second filter
  
			CAN.init_Mask(1,0,0x07FF0000);                // Init second mask 
			CAN.init_Filt(2,0,0x01320000);                // Init third filter
			CAN.init_Filt(3,0,0x01000000);                // Init fouth filter
			CAN.init_Filt(4,0,0x01010000);                // Init fifth filter
			CAN.init_Filt(5,0,0x01020000);                // Init sixth filter
			
			Serial.println("CAN: MCP2515 init ok");
			break;
		}
		else
		{
			Serial.println("CAN: MCP2515 init failed");
		}
		delay(100);
	}
	
	// PTCAN   
	for(int i=0; i<20; i++)
	{
		if(CAN_OK == PTCAN.begin(MCP_STDEXT, CAN_500KBPS, MCP_8MHZ))    // Init car CAN bus : baudrate = 500k
		{
			PTCAN.setMode(MCP_NORMAL);  // Set operation mode to normal so the MCP2515 sends acks to received data.
					
			PTCAN.init_Mask(0,0,0x07FF0000);                // Init first mask
			PTCAN.init_Filt(0,0,0x01DA0000);                // Init first filter
			PTCAN.init_Filt(1,0,0x055A0000);                // Init second filter
  
			PTCAN.init_Mask(1,0,0x07FF0000);                // Init second mask 
			PTCAN.init_Filt(2,0,0x01DA0000);                // Init third filter
			PTCAN.init_Filt(3,0,0x055A0000);                // Init fouth filter
			PTCAN.init_Filt(4,0,0x055A0000);                // Init fifth filter
			PTCAN.init_Filt(5,0,0x055A0000);                // Init sixth filter
			
			Serial.println("PTCAN: MCP2515 init ok");
			break;
		}
		else
		{
			Serial.println("PTCAN: MCP2515 init failed");
		}
		delay(100);
	}
}
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

johu wrote: Thu Jun 30, 2022 8:39 pm Like you could put the MGR back in there :)
Would it make sense to try the MGR once more in the Clio? To my understanding there have been a lot of progress made with the SW lately and it is likely that the MGR will work with this latest release (5.24)?
User avatar
johu
Site Admin
Posts: 5684
Joined: Thu Nov 08, 2018 10:52 pm
Location: Kassel/Germany
Has thanked: 154 times
Been thanked: 960 times
Contact:

Re: Renault Clio Electrique -97

Post by johu »

Yes I expect it to perform well now, so if you have the time that would be very interesting.

Greetings from Jämtland and sorry we didn't manage to stop by this time
Support R/D and forum on Patreon: https://patreon.com/openinverter - Subscribe on odysee: https://odysee.com/@openinverter:9
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

I'm a bit unmotivated to work on the C30 at the moment so I might have a go at it. Not promising anything but have started going through my old documentation and thinking about what needs to be done in order to make it work.

Yes, I understand you are enjoying the Swedish winter up north, all while I dream about winters in Spain or similar place... :)
Always a pleasure when you stop by but I don't expect visits every time you pass Gothenburg.
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

This idea of a working MGR has really started to give my bad ideas... :)
I have revived my Prius inverter and the old logic board is really hacked up but will be ok for testing. Will try and upload the latest SW before I do anything to the car. Installed th DC/DC from one of Johannes old blown-up inverter as the other one is installed on top of the Leaf inverter. Hopefully it is just the fuse that was bad but time will tell.
Backup option is to use my Yaris inverter and feed battery power on the motor side and buck voltage down for the DC/DC.
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

More than the fuse that is bad so can't get the DC/DC to work in the Prius inverter.
Looked quickly at plan B, the Yaris inverter and relized the MG2 output looks diffenrent so my MGR motor cable won't fit. :(
Lost the motivation and left the garage...
User avatar
johu
Site Admin
Posts: 5684
Joined: Thu Nov 08, 2018 10:52 pm
Location: Kassel/Germany
Has thanked: 154 times
Been thanked: 960 times
Contact:

Re: Renault Clio Electrique -97

Post by johu »

Yes, the fuse blew for a reason. I got that far when I left off. Sorry, could have reminded you.
How hard would it be to swap in the power PCB from another DC/DC?
Support R/D and forum on Patreon: https://patreon.com/openinverter - Subscribe on odysee: https://odysee.com/@openinverter:9
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

johu wrote: Wed Dec 28, 2022 2:17 pm Yes, the fuse blew for a reason. I got that far when I left off. Sorry, could have reminded you.
How hard would it be to swap in the power PCB from another DC/DC?
It is ok to swap the power PCB, it just takes some time to desolder it. Problem is to get hold of a new one. Proabably easier to just get a new inverter...
Anyway, will do another attempt with the Yaris inverter and temporary motor cables that will be ok for testing. Would maybe be possible to steel the capacitors from the broken Prius DC/DC power board to the Yaris DC/DC, to make it run at 400V (Yaris has only 300V caps... ).
Must test with MGR first, then if it works, do fancy time consuming installation/modificatios... :)
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

The Yaris inverter is fully up and running on the bench, including buck for DC/DC.
These are my old parameters with adjustments for the 5.24 SW.
Yaris_MGR_FW5_24_2022-12-29.json
(1.45 KiB) Downloaded 35 times
Will be my starting point for test with motor in car.

Now the C30 need to be moved out of the garage to make room for Clio and actual motor swap.
User avatar
Ev8
Posts: 801
Joined: Sat Jan 30, 2021 11:05 am
Has thanked: 41 times
Been thanked: 149 times

Re: Renault Clio Electrique -97

Post by Ev8 »

Brilliant that you are giving the mgr another go
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

Well the MGR is such a neat package for a small vehicle that I have to give it another go.

Leaf powertrain is out and MGR is mounted.
IMG_2201_scale.jpg
Next is to mount inverter and inverter cooling system.
User avatar
Ev8
Posts: 801
Joined: Sat Jan 30, 2021 11:05 am
Has thanked: 41 times
Been thanked: 149 times

Re: Renault Clio Electrique -97

Post by Ev8 »

It does look at home in there, if it’s any inspiration with the latest firmware I have full control over mg1 and mg2 of the rx400h transaxle motors at 320v hitting 5krpm (limited by mg2 motors are 1:1) making about 40kw mg1 60kw mg2 which is about half the Lexus rated power at half the rated voltage… so there is hope for the mgr I’m sure
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

More progress, inverter is in the car. Yes, the mount for it is temorary, will be replaced if MGR behaves.
IMG_2204_scale.jpg
Next, cooling system for inverter.
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

No picture today but cooling system is in. Just a tank and a pump, no radiator at this point.
LV wiring is also done.
Next, HV wiring and then testing...
User avatar
johu
Site Admin
Posts: 5684
Joined: Thu Nov 08, 2018 10:52 pm
Location: Kassel/Germany
Has thanked: 154 times
Been thanked: 960 times
Contact:

Re: Renault Clio Electrique -97

Post by johu »

Getting close.. very excited :)
Support R/D and forum on Patreon: https://patreon.com/openinverter - Subscribe on odysee: https://odysee.com/@openinverter:9
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

Indeed, very exciting.
Everything ready for test.
IMG_2206_scale.jpg
Have moved the car with its own power in the garage. Unfortunately it is raining outside and the road is wet so I will wait for better weather as my HV wiring is far from watertight. Hopefully road test tomorrow.
Latest parameters.
Yaris_MGR_FW5_24_2023-01-04.json
(1.45 KiB) Downloaded 46 times
At first the motor would not spin but draw current. I had swaped the phase cables... :( Glad nothing bad happend and it was an easy fix once I realized what was wrong.
User avatar
bexander
Posts: 834
Joined: Tue Jun 16, 2020 6:00 pm
Location: Gothenburg, Sweden
Has thanked: 63 times
Been thanked: 89 times

Re: Renault Clio Electrique -97

Post by bexander »

Ok, first test drive done.
It needs tuning for shure. I get oscillation when speed goes above approx 40km/h. Can this be due to my high lqminusld at 4mH or should I start somewhere else with the tuning?
2023-01-05_12-15-23_oscillation_above_40.png
It also feels a bit "raw" or "cogging" at 0-2km/h. This is minor and not important right now.
Post Reply