Leaf Gen 1 Inverter Board
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Just in case anyone out there is using a Leaf Gen1 VCU and would like to use the online Arduino IDE to make adjustments or updates I figured out today how to upload the libraries to the cloud. Several of them did not have a library.properties file so they would not upload. I added the file to each library and posted them in my github. You can find them here. This is also a convenient source for the required libraries if using IDE 1.8 or 2.
https://github.com/Alibro53/Nissan-Leaf ... r/Software
Apologies to those of you who are experts at github, I find it very confusing but hopefully think this will work. One or two of the libraries are already in Arduino but you need to upload the others.
The libraries to download are
ISA.zip
Metro.zip
Wire_EEPROM.zip
due_can.zip
due_wire.zip
DueTimer.zip
I know this was not necessary if using the Arduino IDE App but I use two laptops plus a desktop PC and was loosing track of versions for several sketches so I want to try to stick with the online IDE from now on.
https://github.com/Alibro53/Nissan-Leaf ... r/Software
Apologies to those of you who are experts at github, I find it very confusing but hopefully think this will work. One or two of the libraries are already in Arduino but you need to upload the others.
The libraries to download are
ISA.zip
Metro.zip
Wire_EEPROM.zip
due_can.zip
due_wire.zip
DueTimer.zip
I know this was not necessary if using the Arduino IDE App but I use two laptops plus a desktop PC and was loosing track of versions for several sketches so I want to try to stick with the online IDE from now on.
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Here is a question for those of you familiar with the IVT shunt and better at coding than me.
I've had my car driving for a few months but have only gotten around to working on the shunt now.
Version 1.5 of the Leaf code includes the ISA shunt but as standard it does not work, as in it does not show voltage and current in the WiFi display. As a temporary solution I made a change to the code so that the voltage being broadcast by the inverter would show in the display and this is great for telling me the state of charge but can't see current so I want to use the shunt.
I spent most of yesterday (thanks to a dodgy Arduino Due) working to get the shunt initialised and it is now broadcasting voltage and current in the test code so I know it is working.
Some time ago james@N52E01 shared his code which I have tried but when I use it I can't drive the car.
With James code I can see voltage and current but the VCU will not trigger the positive contactor.
I went through the changes James made and the line of code (which I think was copied from the ISA Shunt test example) which causes the issue is below.
Sensor.begin(port,datarate); //Start ISA object on CAN 0 at 500 kbps
With this line of code present the inverter voltage is not showing in the serial monitor so it looks like the VCU cannot see the precharge has completed therefore is not turning on the positive contactor..
1.5 version of the code can be found here.
https://github.com/damienmaguire/Nissan ... r/Software
I'm assuming the best option is to use the voltage from the shunt rather than the inverter so I'll have a look to see if I can do that but I'm not good at coding so not confident.
Thanks
I've had my car driving for a few months but have only gotten around to working on the shunt now.
Version 1.5 of the Leaf code includes the ISA shunt but as standard it does not work, as in it does not show voltage and current in the WiFi display. As a temporary solution I made a change to the code so that the voltage being broadcast by the inverter would show in the display and this is great for telling me the state of charge but can't see current so I want to use the shunt.
I spent most of yesterday (thanks to a dodgy Arduino Due) working to get the shunt initialised and it is now broadcasting voltage and current in the test code so I know it is working.
Some time ago james@N52E01 shared his code which I have tried but when I use it I can't drive the car.
With James code I can see voltage and current but the VCU will not trigger the positive contactor.
I went through the changes James made and the line of code (which I think was copied from the ISA Shunt test example) which causes the issue is below.
Sensor.begin(port,datarate); //Start ISA object on CAN 0 at 500 kbps
With this line of code present the inverter voltage is not showing in the serial monitor so it looks like the VCU cannot see the precharge has completed therefore is not turning on the positive contactor..
1.5 version of the code can be found here.
https://github.com/damienmaguire/Nissan ... r/Software
I'm assuming the best option is to use the voltage from the shunt rather than the inverter so I'll have a look to see if I can do that but I'm not good at coding so not confident.
Thanks
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
OK so I figured out I can get the VCU to turn on the inverter with this code by checking for the voltage from the shunt rather than the inverter so that bit is working but the motor temp, inverter temp and motor rpm no longer show in the display and the dash so the line of code I mentioned above seems to be preventing the VCU from seeing all CAN messages from the Inverter.
This is the code that works but kills the data coming from the inverter, some of the changes came from James and some myself but I've claimed them all here.
This is the code that works but kills the data coming from the inverter, some of the changes came from James and some myself but I've claimed them all here.
Code: Select all
/*
Leaf Gen1 Inverter driver. Alpha software for testing.
Runs on the Arduino Due SAM3X8E MCU. V1 Leaf open source vcu.
Enter torque request on serial window.
As of now only responds to negative torque requests. e.g. -10
Positive torque requests trigger the inverter pwm but do not rotate the motor.
V5 incorporates ISA can shunt on CAN0. Let's hope the leaf inverter doesnt mind the isa messages and vice versa:)
WiFi on Serial2.
Precharge control : out1 = precharge , out2= main contactor
Copyright 2019
Perttu Ahola (all the hard work!)
http://productions.8dromeda.net/c55-leaf-inverter-protocol.html
Damien Maguire (copy and paste).
OpenSource VCU hardware design available on Github :
https://github.com/damienmaguire/Nissan-Leaf-Inverter-Controller
2011 Nisan Leaf Gen 1 EV CAN logs on Github:
https://github.com/damienmaguire/LeafLogs
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <Metro.h>
#include <due_can.h>
#include <due_wire.h>
#include <DueTimer.h>
#include <Wire_EEPROM.h>
#include <ISA.h> //isa can shunt library
#define Serial SerialUSB
template<class T> inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; }
CAN_FRAME outFrame; //A structured variable according to due_can library for transmitting CAN data.
CAN_FRAME inFrame; //structure to keep inbound inFrames
//////timers//////////////////////////////
Metro timer_Frames10 = Metro(10);
Metro timer_Frames100 = Metro(100);
Metro timer_wifi = Metro(1100);
Metro timer_hv = Metro(1000);
int inv_volts_local;
int16_t final_torque_request = 0;
#define INVERTER_BITS_PER_VOLT 2
#define INVERTER_BITS_PER_RPM 2
////////////////////Pin Map////////////////////////////////////////////////////
int led = 13; //onboard led for diagnosis
#define Throttle1 A0 //Analog throttle channel 1
#define Throttle2 A1 //Analog throttle channel 2
#define Brake 61 //Brake pedal switch. High = brake on
#define IN1 6 //General purpose digital input 1. High =12v
#define IN2 7 //General purpose digital input 2. High =12v
#define OUT1 48 //Low side switched general purpose digital output 1. high = on.
#define OUT2 49 //Low side switched general purpose digital output 2. high = on.
#define OUT3 50 //Low side switched general purpose digital output 3. high = on.
/////////////////////////////////////////////////////////////////////////////////
#define HVPreset 340 //voltage at which to enable main contactor
uint16_t ThrotVal =0; //analog value of throttle position.
uint16_t outRPM; //calculated value of rpm for e46 ike
static int16_t MaxTrq=2000; //max torque request
struct InverterStatus {
uint16_t voltage = 0;
int16_t speed = 0;
int8_t inverter_temperature = 0;
int8_t motor_temperature = 0;
bool error_state = false;
} inverter_status;
String readString;
byte ABSMsg=0x11;
uint8_t tempValue; //value to send to e46 temp gauge.
bool T15Status; //flag to keep status of Terminal 15 from In1
bool can_status; //flag for turning off and on can sending.
bool Pch_Flag; //precharge status flag
bool HV_Flag; //hv on flag
uint port=0; //Alibro Added
uint16_t datarate=500; //Alibro Added
ISA Sensor; //Instantiate ISA Module Sensor object to measure current and voltage
void setup()
{
Sensor.begin(port,datarate); //Start ISA object on CAN 0 at 500 kbps //Alibro Added
Can0.begin(CAN_BPS_500K); // Inverter CAN
Can1.begin(CAN_BPS_500K); // Vehicle CAN
//Can0.watchFor(0x1da); //set message filter for inverter can. Note sure if I can use two seperate values here. it might just pick 1!
Can0.watchFor();
Can1.watchFor(0x1ff); //just a blank message to block receive from e46 messages.
Serial.begin(115200); //Initialize our USB port which will always be redefined as SerialUSB to use the Native USB port tied directly to the SAM3X processor.
Serial2.begin(19200); //Serial comms with ESP32 WiFi module on serial 2
// Timer3.attachInterrupt(Msgs10ms).start(10000); // 10ms CAN Message Timer
// Timer4.attachInterrupt(Msgs100ms).start(100000); //100ms CAN Message Timer
pinMode(led, OUTPUT);
pinMode(Brake, INPUT);
pinMode(IN1, INPUT); //T15 input from ign on switch
pinMode(IN2, INPUT);
pinMode(OUT1, OUTPUT);
pinMode(OUT2, OUTPUT);
pinMode(OUT3, OUTPUT);
//digitalWrite(led, HIGH);
digitalWrite(OUT1, LOW); //precharge
digitalWrite(OUT2, LOW); //main contactor
digitalWrite(OUT3, LOW); //inverter power
}
void loop()
{
Check_T15(); //is the ignition on?
if (timer_hv.check()) HV_Con(); //control hv system
Msgs100ms(); //fire the 100ms can messages
Msgs10ms(); //fire the 10ms can messages
readPedals(); //read throttle and brake pedal status.
SendTorqueRequest(); //send torque request to inverter.
ProcessRPM(); //send rpm and temp to e46 instrument cluster
CheckCAN(); //check for incoming can
handle_wifi(); //send wifi data
}
void Check_T15()
{
if (digitalRead(IN1))
{
T15Status=true;
can_status=true;
}
else
{
T15Status=false;
can_status=false;
Pch_Flag=false;
HV_Flag=false;
inv_volts_local==0;
}
}
void HV_Con()
{
inv_volts_local=(inverter_status.voltage / INVERTER_BITS_PER_VOLT);
if (T15Status && !Pch_Flag) //if terminal 15 is on and precharge not enabled
{
digitalWrite(OUT3, HIGH); //inverter power on
if(Sensor.Voltage<200) //Alibro changed inv_volts_local to Sensor.Voltage
{
digitalWrite(OUT1, HIGH); //precharge on
Pch_Flag=true;
}
}
if (T15Status && !HV_Flag && Pch_Flag) //using inverter measured hv for initial tests. Will use ISA derived voltage in final version.
{
if (Sensor.Voltage>340) //Alibro changed inv_volts_local to Sensor.Voltage
{
digitalWrite(OUT2, HIGH); //main contactor on
HV_Flag=true; //hv on flag
}
}
if (!T15Status)
{
digitalWrite(OUT1, LOW); //precharge off
digitalWrite(OUT2, LOW); //main contactor off
digitalWrite(OUT3, LOW); //inverter power off
}
}
void handle_wifi(){
if (timer_wifi.check())
{
/*
*
* Routine to send data to wifi on serial 2
The information will be provided over serial to the esp8266 at 19200 baud 8n1 in the form :
vxxx,ixxx,pxxx,mxxxx,oxxx,rxxx* where :
v=pack voltage (0-700Volts)
i=current (0-1000Amps)
p=power (0-300kw)
m=motor rpm (0-10000rpm)
o=motor temp (-20 to 120C)
r=inverter temp (-20 to 120C)
*=end of string
xxx=three digit integer for each parameter eg p100 = 100kw.
updates will be every 1100ms approx.
v100,i200,p35,m3000,o20,r100*
*/
//Serial2.print("v100,i200,p35,m3000,o20,r100*"); //test string
digitalWrite(13,!digitalRead(13));//blink led every time we fire this interrrupt.
Serial.print(inv_volts_local);
Serial.print(F(" Volts"));
Serial.println();
Serial.println(HV_Flag);
Serial2.print("v");//dc bus voltage
Serial2.print(Sensor.Voltage);//voltage derived from ISA shunt //To show voltage from inverter change Sensor.Voltage to inv_volts_local
Serial2.print(",i");//dc current
Serial2.print(Sensor.Amperes);//current derived from ISA shunt
Serial2.print(",p");//total motor power
Serial2.print(Sensor.KW);//Power value derived from ISA Shunt
Serial2.print(",m");//motor rpm
Serial2.print(inverter_status.speed);
Serial2.print(",o");//motor temp
Serial2.print(inverter_status.motor_temperature);
Serial2.print(",r");//inverter temp
Serial2.print(inverter_status.inverter_temperature);
Serial2.print("*");// end of data indicator
}
}
void Msgs10ms() //10ms messages here
{
if(timer_Frames10.check())
{
if(can_status)
{
static uint8_t counter_11a_d6 = 0;
static uint8_t counter_1d4 = 0;
static uint8_t counter_1db = 0;
static uint8_t counter_329 = 0;
static uint8_t counter_100ms = 0;
// Send VCM gear selection signal (gets rid of P3197)
outFrame.id = 0x11a; // Set our transmission address ID
outFrame.length = 8; // Data payload 3 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
// CAN_FRAME inFrame;
// inFrame.id = 0x11a;
// inFrame.length = 8;
// Data taken from a gen1 inFrame where the car is starting to
// move at about 10% throttle: 4E400055 0000017D
// All possible gen1 values: 00 01 0D 11 1D 2D 2E 3D 3E 4D 4E
// MSB nibble: Selected gear (gen1/LeafLogs)
// 0: some kind of non-gear before driving
// 1: some kind of non-gear after driving
// 2: R
// 3: N
// 4: D
// LSB nibble: ? (LeafLogs)
// 0: sometimes at startup, not always; never when the
// inverted is powered on (0.06%)
// 1: this is the usual value (55% of the time in LeafLogs)
// D: seems to occur for ~90ms when changing gears (0.2%)
// E: this also is a usual value, but never occurs with the
// non-gears 0 and 1 (44% of the time in LeafLogs)
outFrame.data.bytes[0] = 0x4E;
//outFrame.data.bytes[0] = 0x01;
// 0x40 when car is ON, 0x80 when OFF, 0x50 when ECO
outFrame.data.bytes[1] = 0x40;
// Usually 0x00, sometimes 0x80 (LeafLogs), 0x04 seen by canmsgs
outFrame.data.bytes[2] = 0x00;
// Weird value at D3:4 that goes along with the counter
// NOTE: Not actually needed, you can just send constant AA C0
const static uint8_t weird_d34_values[4][2] = {
{0xaa, 0xc0},
{0x55, 0x00},
{0x55, 0x40},
{0xaa, 0x80},
};
outFrame.data.bytes[3] = weird_d34_values[counter_11a_d6][0];
outFrame.data.bytes[4] = weird_d34_values[counter_11a_d6][1];
// Always 0x00 (LeafLogs, canmsgs)
outFrame.data.bytes[5] = 0x00;
// A 2-bit counter
outFrame.data.bytes[6] = counter_11a_d6;
counter_11a_d6++;
if(counter_11a_d6 >= 4)
counter_11a_d6 = 0;
// Extra CRC
nissan_crc(outFrame.data.bytes, 0x85);
/*Serial.print(F("Sending "));
print_fancy_inFrame(inFrame);
Serial.println();*/
Can0.sendFrame(outFrame);
// Send target motor torque signal
outFrame.id = 0x1d4; // Set our transmission address ID
outFrame.length = 8; // Data payload 3 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
// Data taken from a gen1 inFrame where the car is starting to
// move at about 10% throttle: F70700E0C74430D4
// Usually F7, but can have values between 9A...F7 (gen1)
outFrame.data.bytes[0] = 0xF7;
// 2016: 6E
// outFrame.data.bytes[0] = 0x6E;
// Usually 07, but can have values between 07...70 (gen1)
outFrame.data.bytes[1] = 0x07;
// 2016: 6E
//outFrame.data.bytes[1] = 0x6E;
// Requested torque (signed 12-bit value + always 0x0 in low nibble)
static int16_t last_logged_final_torque_request = 0;
if(final_torque_request != last_logged_final_torque_request){
last_logged_final_torque_request = final_torque_request;
//log_print_timestamp();
//Serial.print(F("Sending torque request "));
//Serial.print(final_torque_request);
//Serial.print(F(" (speed: "));
//Serial.print(inverter_status.speed / INVERTER_BITS_PER_RPM);
//Serial.print(F(" rpm)"));
//Serial.print(inverter_status.voltage / INVERTER_BITS_PER_VOLT);
//Serial.print(F(" Volts)"));
//Serial.println();
}
if(final_torque_request >= -2048 && final_torque_request <= 2047){
outFrame.data.bytes[2] = ((final_torque_request < 0) ? 0x80 : 0) |
((final_torque_request >> 4) & 0x7f);
outFrame.data.bytes[3] = (final_torque_request << 4) & 0xf0;
} else {
outFrame.data.bytes[2] = 0x00;
outFrame.data.bytes[3] = 0x00;
}
// MSB nibble: Runs through the sequence 0, 4, 8, C
// LSB nibble: Precharge report (precedes actual precharge
// control)
// 0: Discharging (5%)
// 2: Precharge not started (1.4%)
// 3: Precharging (0.4%)
// 5: Starting discharge (3x10ms) (2.0%)
// 7: Precharged (93%)
outFrame.data.bytes[4] = 0x07 | (counter_1d4 << 6);
//outFrame.data.bytes[4] = 0x02 | (counter_1d4 << 6);
counter_1d4++;
if(counter_1d4 >= 4)
counter_1d4 = 0;
// MSB nibble:
// 0: 35-40ms at startup when gear is 0, then at shutdown 40ms
// after the car has been shut off (6% total)
// 4: Otherwise (94%)
// LSB nibble:
// 0: ~100ms when changing gear, along with 11A D0 b3:0 value
// D (0.3%)
// 2: Reverse gear related (13%)
// 4: Forward gear related (21%)
// 6: Occurs always when gear 11A D0 is 01 or 11 (66%)
//outFrame.data.bytes[5] = 0x44;
//outFrame.data.bytes[5] = 0x46;
// 2016 drive cycle: 06, 46, precharge, 44, drive, 46, discharge, 06
// 0x46 requires ~25 torque to start
//outFrame.data.bytes[5] = 0x46;
// 0x44 requires ~8 torque to start
outFrame.data.bytes[5] = 0x44;
// MSB nibble:
// In a drive cycle, this slowly changes between values (gen1):
// leaf_on_off.txt:
// 5 7 3 2 0 1 3 7
// leaf_on_rev_off.txt:
// 5 7 3 2 0 6
// leaf_on_Dx3.txt:
// 5 7 3 2 0 2 3 2 0 2 3 2 0 2 3 7
// leaf_on_stat_DRDRDR.txt:
// 0 1 3 7
// leaf_on_Driveincircle_off.txt:
// 5 3 2 0 8 B 3 2 0 8 A B 3 2 0 8 A B A 8 0 2 3 7
// leaf_on_wotind_off.txt:
// 3 2 0 8 A B 3 7
// leaf_on_wotinr_off.txt:
// 5 7 3 2 0 8 A B 3 7
// leaf_ac_charge.txt:
// 4 6 E 6
// Possibly some kind of control flags, try to figure out
// using:
// grep 000001D4 leaf_on_wotind_off.txt | cut -d' ' -f10 | uniq | ~/projects/leaf_tools/util/hex_to_ascii_binary.py
// 2016:
// Has different values!
// LSB nibble:
// 0: Always (gen1)
// 1: (2016)
// 2016 drive cycle:
// E0: to 0.15s
// E1: 2 messages
// 61: to 2.06s (inverter is powered up and precharge
// starts and completes during this)
// 21: to 13.9s
// 01: to 17.9s
// 81: to 19.5s
// A1: to 26.8s
// 21: to 31.0s
// 01: to 33.9s
// 81: to 48.8s
// A1: to 53.0s
// 21: to 55.5s
// 61: 2 messages
// 60: to 55.9s
// E0: to end of capture (discharge starts during this)
// This value has been chosen at the end of the hardest
// acceleration in the wide-open-throttle pull, with full-ish
// torque still being requested, in
// LeafLogs/leaf_on_wotind_off.txt
//outFrame.data.bytes[6] = 0x00;
// This value has been chosen for being seen most of the time
// when, and before, applying throttle in the wide-open-throttle
// pull, in
// LeafLogs/leaf_on_wotind_off.txt
outFrame.data.bytes[6] = 0x30; //brake applied heavilly.
// Value chosen from a 2016 log
//outFrame.data.bytes[6] = 0x61;
// Value chosen from a 2016 log
// 2016-24kWh-ev-on-drive-park-off.pcap #12101 / 15.63s
// outFrame.data.bytes[6] = 0x01;
//byte 6 brake signal
// Extra CRC
nissan_crc(outFrame.data.bytes, 0x85);
/*Serial.print(F("Sending "));
print_fancy_inFrame(inFrame);
Serial.println();*/
Can0.sendFrame(outFrame);
//We need to send 0x1db here with voltage measured by inverter
outFrame.id = 0x1db; // Set our transmission address ID
outFrame.length = 8; // Data payload 3 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
outFrame.data.bytes[0]=0x00;
outFrame.data.bytes[1]=0x00;
outFrame.data.bytes[2]=0x00;
outFrame.data.bytes[3]=0x00;
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00;
outFrame.data.bytes[6]=counter_1db;
outFrame.data.bytes[7]=0x00;
counter_1db++;
if(counter_1db >= 4)
counter_1db = 0;
Can0.sendFrame(outFrame);
///////////////Originally sent as 100ms messages.///////////////////////////////////////////
outFrame.id = 0x50b; // Set our transmission address ID
outFrame.length = 7; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
// Statistics from 2016 capture:
// 10 00000000000000
// 21 000002c0000000
// 122 000000c0000000
// 513 000006c0000000
// Let's just send the most common one all the time
// FIXME: This is a very sloppy implementation
// hex_to_data(outFrame.data.bytes, "00,00,06,c0,00,00,00");
outFrame.data.bytes[0]=0x00;
outFrame.data.bytes[1]=0x00;
outFrame.data.bytes[2]=0x06;
outFrame.data.bytes[3]=0xc0;
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00;
outFrame.data.bytes[6]=0x00;
/*CONSOLE.print(F("Sending "));
print_fancy_inFrame(inFrame);
CONSOLE.println();*/
Can0.sendFrame(outFrame);
/////////////////////////////////////////////////////////////////////////////////////////////////////
//these messages go out on vehicle can and are specific to driving the E46 instrument cluster etc.
//////////////////////DME Messages //////////////////////////////////////////////////////////
outFrame.id = 0x316; // Set our transmission address ID
outFrame.length = 8; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
outFrame.data.bytes[0]=0x05;
outFrame.data.bytes[1]=0x00;
outFrame.data.bytes[2]=lowByte(outRPM); //RPM LSB
outFrame.data.bytes[3]=highByte(outRPM); //RPM MSB [RPM=(hex2dec("byte3"&"byte2"))/6.4] 0x12c0 should be 750rpm on tach
// outFrame.data.bytes[2]=0xc0; //RPM LSB
// outFrame.data.bytes[3]=0x12; //RPM MSB [RPM=(hex2dec("byte3"&"byte2"))/6.4] 0x12c0 should be 750rpm on tach
// outFrame.data.bytes[2]=0xff; //RPM LSB
// outFrame.data.bytes[3]=0x4f; //RPM MSB [RPM=(hex2dec("byte3"&"byte2"))/6.4] 0x4fff gives 3200rpm on tach
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00;
outFrame.data.bytes[6]=0x00;
outFrame.data.bytes[7]=0x00;
Can1.sendFrame(outFrame);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//********************temp sense *******************************
// tempValue=analogRead(tempIN); //read Analog pin voltage
// The sensor and gauge are not linear. So if the sensed
// Voltage is less than the mid point the Map function
// is used to map the input values to output values For the gauge
// output values (in decimal):
// 86 = First visible movment of needle starts
// 93 = Begining of Blue section
// 128 = End of Blue Section
// 169 = Begin Straight up
// 193 = Midpoint of needle straight up values
// 219 = Needle begins to move from center
// 230 = Beginning of Red section
// 254 = needle pegged to the right
// MAP program statement: map(value, fromLow, fromHigh, toLow, toHigh)
// if(tempValue < 964){ //if pin voltage < mid point value
tempValue= inverter_status.inverter_temperature; //read temp from leaf inverter can.
tempValue= map(tempValue,15,80,88,254); //Map to e46 temp gauge
// }
// else {
// tempValue= map(tempValue,964,1014,219,254); //Map upper half of range
// }
//Can bus data packet values to be sent
outFrame.id = 0x329; // Set our transmission address ID
outFrame.length = 8; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
outFrame.data.bytes[0]=ABSMsg; //needs to cycle 11,86,d9
outFrame.data.bytes[1]=tempValue; //temp bit tdata
outFrame.data.bytes[2]=0xc5;
outFrame.data.bytes[3]=0x00;
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00; //Throttle position currently just fixed value
outFrame.data.bytes[6]=0x00;
outFrame.data.bytes[7]=0x00;
Can1.sendFrame(outFrame);
counter_329++;
if(counter_329 >= 22) counter_329 = 0;
if(counter_329==0) ABSMsg=0x11;
if(counter_329==8) ABSMsg=0x86;
if(counter_329==15) ABSMsg=0xd9;
//From ECU, MPG, MIL, overheat light, Cruise
// ErrorState variable controls:
//Check engine(binary 10), Cruise (1000), EML (10000)
//Temp light Variable controls temp light. Hex 08 is on zero off
//**************** set Error Lights & cruise light ******
// hex 08 = Overheat light on
// // hex 08 = Overheat light on
//Set check engine. Binary 0010 (hex 02)
// No error light (zero)
// int z = 0x60; // + y; higher value lower MPG
//Can bus data packet values to be sent
outFrame.id = 0x545; // Set our transmission address ID
outFrame.length = 8; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
outFrame.data.bytes[0]=0x00; //2=check ewwngine on , 0=check engine off
outFrame.data.bytes[1]=0x00; //LSB fuel comp
outFrame.data.bytes[2]=0x00; //MSB fuel comp
outFrame.data.bytes[3]=0x00; // hex 08 = Overheat light on
outFrame.data.bytes[4]=0x7E;
outFrame.data.bytes[5]=0x10;
outFrame.data.bytes[6]=0x00;
outFrame.data.bytes[7]=0x18;
Can1.sendFrame(outFrame);
}
}
}
void Msgs100ms() ////100ms messages here
{
if(timer_Frames100.check())
{
if(can_status)
{
// digitalWrite(led, !digitalRead(led)); //toggle led everytime we fire the 100ms messages.
outFrame.id = 0x50b; // Set our transmission address ID
outFrame.length = 7; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
// Statistics from 2016 capture:
// 10 00000000000000
// 21 000002c0000000
// 122 000000c0000000
// 513 000006c0000000
// Let's just send the most common one all the time
// FIXME: This is a very sloppy implementation
// hex_to_data(outFrame.data.bytes, "00,00,06,c0,00,00,00");
outFrame.data.bytes[0]=0x00;
outFrame.data.bytes[1]=0x00;
outFrame.data.bytes[2]=0x06;
outFrame.data.bytes[3]=0xc0;
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00;
outFrame.data.bytes[6]=0x00;
/*CONSOLE.print(F("Sending "));
print_fancy_inFrame(inFrame);
CONSOLE.println();*/
Can0.sendFrame(outFrame);
}
}
}
void readPedals()
{
ThrotVal = analogRead(Throttle1); //read throttle channel 1 directly
ThrotVal = constrain(ThrotVal, 145, 620);
ThrotVal = map(ThrotVal, 145, 620, 0, MaxTrq); //will need to work here for cal.
if(ThrotVal<0) ThrotVal=0; //no negative numbers for now.
if(digitalRead(Brake)) ThrotVal=0; //if brake is pressed we zero the throttle value.
//Serial.println(ThrotVal); //print for calibration.
}
void SendTorqueRequest()
{
final_torque_request = ThrotVal; //send mapped throttle signal to inverter.
}
void ProcessRPM() //here we convert motor rpm values received from the leaf inverter into BMW E46 RPM can message.
{
outRPM = (inverter_status.speed)*6.4;
if(outRPM<4800) outRPM=4800; //set lowest rpm to 750 displayed on tach to keep car alive thinking engine is running.
if(outRPM>44800) outRPM=44800; //DONT READ MORE THAN 7000RPM!
//Serial.println(outRPM);
}
static int8_t fahrenheit_to_celsius(uint16_t fahrenheit)
{
int16_t result = ((int16_t)fahrenheit - 32) * 5 / 9;
if(result < -128)
return -128;
if(result > 127)
return 127;
return result;
}
void CheckCAN()
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//read incomming data from inverter//////////////////
//////////////////////////////////////////////////////
if(Can0.available())
{
Can0.read(inFrame);
//Serial.println(inFrame.id, HEX);
if(inFrame.id == 0x1da && inFrame.length == 8){
// last_received_from_inverter_timestamp = millis();
inverter_status.voltage = ((uint16_t)inFrame.data.bytes[0] << 2) |
(inFrame.data.bytes[1] >> 6);
int16_t parsed_speed = ((uint16_t)inFrame.data.bytes[4] << 8) |
(uint16_t)inFrame.data.bytes[5];
inverter_status.speed = (parsed_speed == 0x7fff ? 0 : parsed_speed);
inverter_status.error_state = (inFrame.data.bytes[6] & 0xb0) != 0x00;
}
if(inFrame.id == 0x55a && inFrame.length == 8){
// last_received_from_inverter_timestamp = millis();
inverter_status.inverter_temperature = fahrenheit_to_celsius(inFrame.data.bytes[2]);
inverter_status.motor_temperature = fahrenheit_to_celsius(inFrame.data.bytes[1]);
//Serial.println(inverter_status.inverter_temperature);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
}
static void nissan_crc(uint8_t *data, uint8_t polynomial)
{
// We want to process 8 bytes with the 8th byte being zero
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) ^ polynomial);
else
crc = (byte)((crc << 1) + bit);
}
}
data[7] = crc;
}
-
- Posts: 250
- Joined: Sun Feb 20, 2022 4:23 am
- Location: N. Wiltshire. UK
- Has thanked: 21 times
- Been thanked: 121 times
Re: Leaf Gen 1 Inverter Board
Seting up the ISA shunt sets a filter on can0 that blocks the inverter can ids.
Adding the line in bold after Sensor.begin may fix it.
Sensor.begin(port,datarate);
port->setRXFilter(0, 0x100 0x300, false); // this allows ids 0x100 - 0x1FF and 0x500 - 0x5FF to pass
Adding the line in bold after Sensor.begin may fix it.
Sensor.begin(port,datarate);
port->setRXFilter(0, 0x100 0x300, false); // this allows ids 0x100 - 0x1FF and 0x500 - 0x5FF to pass
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Sorry I though I had already thanked you for this but checking back I hadn't
I tried this line of code but still having issues.
I've tried multiple ways to sort it but I'm not even sure what it means.
Sorry I may have mentioned before I'm hopeless at programming.
I tried this line of code but still having issues.
Would you mind having another look at this please? With this added I get the error "base operand of '->' is not a pointer"royhen99 wrote: ↑Sun Apr 14, 2024 8:55 pm Seting up the ISA shunt sets a filter on can0 that blocks the inverter can ids.
Adding the line in bold after Sensor.begin may fix it.
Sensor.begin(port,datarate);
port->setRXFilter(0, 0x100 0x300, false); // this allows ids 0x100 - 0x1FF and 0x500 - 0x5FF to pass
I've tried multiple ways to sort it but I'm not even sure what it means.
Sorry I may have mentioned before I'm hopeless at programming.
- crasbe
- Posts: 282
- Joined: Mon Jul 08, 2019 5:18 pm
- Location: Germany
- Has thanked: 47 times
- Been thanked: 138 times
Re: Leaf Gen 1 Inverter Board
The error you are getting is caused by the "port->". Port is just a number with the type "uint8_t". It is not the object of the CAN Due library, therefore the compiler complains about the fact that 1) port is not a pointer and 2) it would complain about port not having a function with the name "setRXfilter", because uint8_t does not know anything about CAN. It's just a number
The (more) correct line would be
Code: Select all
Can0.setRXFilter(0, 0x100 0x300, false);
Code: Select all
Can0.setRXFilter(0, 0x100, 0x300, false);
I can not assess if this code does what you want it to do, but it should compile
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Thank you for this, I'm pretty sure at some stage I tried using Can0 instead but couldn't figure out the correct syntax.
It is no longer giving an error so I'll pop down to the car and see what it does for my display.
It is no longer giving an error so I'll pop down to the car and see what it does for my display.
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
So after a lot of messing about I'm no further on.
With the line below in place I get voltage, current and power from the ISA shunt but the Inverter CAN is blocked.
Sensor.begin(port,datarate);
If I try setting filters to 'false' it either stops everything or makes no difference.
With the line below in place I get voltage, current and power from the ISA shunt but the Inverter CAN is blocked.
Sensor.begin(port,datarate);
If I try setting filters to 'false' it either stops everything or makes no difference.
-
- Posts: 250
- Joined: Sun Feb 20, 2022 4:23 am
- Location: N. Wiltshire. UK
- Has thanked: 21 times
- Been thanked: 121 times
Re: Leaf Gen 1 Inverter Board
I think I have fixed it. The isa library has is own interrupt service routine which takes priority over the routine in the main code. The modifications below fix this by calling the isa interrupt routine from the main code. I have checked with appropriate ids and data generated in Savvycan.
1) in ISA.h move
void gotFrame(CAN_FRAME *frame, int mailbox); // CAN interrupt service routine
from private to public
2) In Leaf_Gen1_5
a) comment out ( or delete ) Sensor.begin(port, baudrate);
b) after can setup add/ modify
Can0.watchFor(); // This allows all ids to pass
c) In void CheckCAN() add affter the closing bracket of the motor temperature if statement
if(inFrame.id >= 0x521 && inFrame.id <= 0x528 && inFrame.length == 8){
Sensor.ISA::gotFrame(&inFrame, 0);
}
1) in ISA.h move
void gotFrame(CAN_FRAME *frame, int mailbox); // CAN interrupt service routine
from private to public
2) In Leaf_Gen1_5
a) comment out ( or delete ) Sensor.begin(port, baudrate);
b) after can setup add/ modify
Can0.watchFor(); // This allows all ids to pass
c) In void CheckCAN() add affter the closing bracket of the motor temperature if statement
if(inFrame.id >= 0x521 && inFrame.id <= 0x528 && inFrame.length == 8){
Sensor.ISA::gotFrame(&inFrame, 0);
}
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Awe mate thank you! I'll get this tested later this morning and let you know how it goes.
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
It didn't work.
I don't get anything from the ISA Shunt if I comment out the sensor.begin line and if I put it back in I get nothing on the display from the inverter.
The thing that confuses me is that with the sensor.begin line on place the VCU is still controlling the inverter and allowing me to drive the car, so it is only the display and the serial monitor that is not working. Does than mean the ISA shunt is effecting CAN1 and not CAN0? If so does that change anything?
I think I did everything as you suggested.
This is the ISA.h after I changed it
And this is the main sketch
I don't get anything from the ISA Shunt if I comment out the sensor.begin line and if I put it back in I get nothing on the display from the inverter.
The thing that confuses me is that with the sensor.begin line on place the VCU is still controlling the inverter and allowing me to drive the car, so it is only the display and the serial monitor that is not working. Does than mean the ISA shunt is effecting CAN1 and not CAN0? If so does that change anything?
I think I did everything as you suggested.
This is the ISA.h after I changed it
Code: Select all
#ifndef ISA2_h
#define ISA2_h
/* This library supports the ISA Scale IVT Modular current/voltage sensor device. These devices measure current, up to three voltages, and provide temperature compensation.
This library was written by Jack Rickard of EVtv - http://www.evtv.me copyright 2016
You are licensed to use this library for any purpose, commercial or private,
without restriction.
*/
#include <SPI.h>
#include <Arduino.h>
#include <DueTimer.h>
#include <due_wire.h>
#include <Wire_EEPROM.h>
#include "variant.h"
#include <due_can.h>
#define Serial SerialUSB
class EPROM {
public:
double AH;
double KWH;
double KWHtrip;
double KW;
double Odometer;
double Trip;
uint8_t capacity;
uint8_t SOC;
double jackoff;
uint16_t averageWHM;
uint8_t varsGood;
};
class ISA : public CANListener
{
public:
ISA();
~ISA();
void initialize();
void begin(int Port, int speed);
void initCurrent();
void sendSTORE();
void STOP();
void START();
void RESTART();
void deFAULT();
void initializeEPROM();
void gotFrame(CAN_FRAME *frame, int mailbox); // CAN interrupt service routine // Moved here by royhen99 from private
EPROM EVars;
float Amperes; // Floating point with current in Amperes
double ah; //Floating point with accumulated ampere-hours
double kwh;
double kwhtrip;
double Voltage;
double Voltage1;
double Voltage2;
double Voltage3;
double VoltageHI;
double Voltage1HI;
double Voltage2HI;
double Voltage3HI;
double VoltageLO;
double Voltage1LO;
double Voltage2LO;
double Voltage3LO;
double Temperature;
double KW;
double KWH;
float oldKWH;
float KWHtrip;
float SOC;
bool debug;
bool debug2;
bool firstframe;
int framecount;
unsigned long timestamp;
double milliamps;
long watt;
long As;
long wh;
CANRaw *canPort;
uint8_t canEnPin;
int canSpeed;
uint8_t page;
int READED;
int WROTED;
int ISGOOD;
private:
CAN_FRAME frame;
unsigned long elapsedtime;
double ampseconds;
int milliseconds ;
int seconds;
int minutes;
int hours;
char buffer[9];
char bigbuffer[90];
uint32_t inbox;
CAN_FRAME outframe;
void printCAN(CAN_FRAME *frame);
void handle521(CAN_FRAME *frame);
void handle522(CAN_FRAME *frame);
void handle523(CAN_FRAME *frame);
void handle524(CAN_FRAME *frame);
void handle525(CAN_FRAME *frame);
void handle526(CAN_FRAME *frame);
void handle527(CAN_FRAME *frame);
void handle528(CAN_FRAME *frame);
};
#endif /* ISA2_h */
Code: Select all
/*
Leaf Gen1 Inverter driver. Alpha software for testing.
Runs on the Arduino Due SAM3X8E MCU. V1 Leaf open source vcu.
Enter torque request on serial window.
As of now only responds to negative torque requests. e.g. -10
Positive torque requests trigger the inverter pwm but do not rotate the motor.
V5 incorporates ISA can shunt on CAN0. Let's hope the leaf inverter doesnt mind the isa messages and vice versa:)
WiFi on Serial2.
Precharge control : out1 = precharge , out2= main contactor
Copyright 2019
Perttu Ahola (all the hard work!)
http://productions.8dromeda.net/c55-leaf-inverter-protocol.html
Damien Maguire (copy and paste).
OpenSource VCU hardware design available on Github :
https://github.com/damienmaguire/Nissan-Leaf-Inverter-Controller
2011 Nisan Leaf Gen 1 EV CAN logs on Github:
https://github.com/damienmaguire/LeafLogs
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <Metro.h>
#include <due_can.h>
#include <due_wire.h>
#include <DueTimer.h>
#include <Wire_EEPROM.h>
#include <ISA.h> //isa can shunt library
#define Serial SerialUSB
template<class T> inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; }
CAN_FRAME outFrame; //A structured variable according to due_can library for transmitting CAN data.
CAN_FRAME inFrame; //structure to keep inbound inFrames
//////timers//////////////////////////////
Metro timer_Frames10 = Metro(10);
Metro timer_Frames100 = Metro(100);
Metro timer_wifi = Metro(1100);
Metro timer_hv = Metro(1000);
int inv_volts_local;
int16_t final_torque_request = 0;
#define INVERTER_BITS_PER_VOLT 2
#define INVERTER_BITS_PER_RPM 2
////////////////////Pin Map////////////////////////////////////////////////////
int led = 13; //onboard led for diagnosis
#define Throttle1 A0 //Analog throttle channel 1
#define Throttle2 A1 //Analog throttle channel 2
#define Brake 61 //Brake pedal switch. High = brake on
#define IN1 6 //General purpose digital input 1. High =12v
#define IN2 7 //General purpose digital input 2. High =12v
#define OUT1 48 //Low side switched general purpose digital output 1. high = on.
#define OUT2 49 //Low side switched general purpose digital output 2. high = on.
#define OUT3 50 //Low side switched general purpose digital output 3. high = on.
/////////////////////////////////////////////////////////////////////////////////
#define HVPreset 340 //voltage at which to enable main contactor
uint16_t ThrotVal =0; //analog value of throttle position.
uint16_t outRPM; //calculated value of rpm for e46 ike
static int16_t MaxTrq=2000; //max torque request
struct InverterStatus {
uint16_t voltage = 0;
int16_t speed = 0;
int8_t inverter_temperature = 0;
int8_t motor_temperature = 0;
bool error_state = false;
} inverter_status;
String readString;
byte ABSMsg=0x11;
uint8_t tempValue; //value to send to e46 temp gauge.
bool T15Status; //flag to keep status of Terminal 15 from In1
bool can_status; //flag for turning off and on can sending.
bool Pch_Flag; //precharge status flag
bool HV_Flag; //hv on flag
uint port=0; //Alibro Added
uint16_t datarate=500; //Alibro Added
ISA Sensor; //Instantiate ISA Module Sensor object to measure current and voltage
void setup()
{
//Sensor.begin(port,datarate); //Start ISA object on CAN 0 at 500 kbps //Alibro Added //royhen99 commmented out
Can0.begin(CAN_BPS_500K); // Inverter CAN
Can1.begin(CAN_BPS_500K); // Vehicle CAN
//Can0.watchFor(0x1da); //set message filter for inverter can. Note sure if I can use two seperate values here. it might just pick 1!
Can1.watchFor(0x1ff); //just a blank message to block receive from e46 messages.
Can0.watchFor(); // This allows all ids to pass //added by royhen99
Serial.begin(115200); //Initialize our USB port which will always be redefined as SerialUSB to use the Native USB port tied directly to the SAM3X processor.
Serial2.begin(19200); //Serial comms with ESP32 WiFi module on serial 2
// Timer3.attachInterrupt(Msgs10ms).start(10000); // 10ms CAN Message Timer
// Timer4.attachInterrupt(Msgs100ms).start(100000); //100ms CAN Message Timer
pinMode(led, OUTPUT);
pinMode(Brake, INPUT);
pinMode(IN1, INPUT); //T15 input from ign on switch
pinMode(IN2, INPUT);
pinMode(OUT1, OUTPUT);
pinMode(OUT2, OUTPUT);
pinMode(OUT3, OUTPUT);
//digitalWrite(led, HIGH);
digitalWrite(OUT1, LOW); //precharge
digitalWrite(OUT2, LOW); //main contactor
digitalWrite(OUT3, LOW); //inverter power
}
void loop()
{
Check_T15(); //is the ignition on?
if (timer_hv.check()) HV_Con(); //control hv system
Msgs100ms(); //fire the 100ms can messages
Msgs10ms(); //fire the 10ms can messages
readPedals(); //read throttle and brake pedal status.
SendTorqueRequest(); //send torque request to inverter.
ProcessRPM(); //send rpm and temp to e46 instrument cluster
CheckCAN(); //check for incoming can
handle_wifi(); //send wifi data
}
void Check_T15()
{
if (digitalRead(IN1))
{
T15Status=true;
can_status=true;
}
else
{
T15Status=false;
can_status=false;
Pch_Flag=false;
HV_Flag=false;
inv_volts_local==0;
}
}
void HV_Con()
{
inv_volts_local=(inverter_status.voltage / INVERTER_BITS_PER_VOLT);
if (T15Status && !Pch_Flag) //if terminal 15 is on and precharge not enabled
{
digitalWrite(OUT3, HIGH); //inverter power on
if(Sensor.Voltage<200) //Alibro changed inv_volts_local to Sensor.Voltage
{
digitalWrite(OUT1, HIGH); //precharge on
Pch_Flag=true;
}
}
if (T15Status && !HV_Flag && Pch_Flag) //using inverter measured hv for initial tests. Will use ISA derived voltage in final version.
{
if (Sensor.Voltage>340) //Alibro changed inv_volts_local to Sensor.Voltage
{
digitalWrite(OUT2, HIGH); //main contactor on
HV_Flag=true; //hv on flag
}
}
if (!T15Status)
{
digitalWrite(OUT1, LOW); //precharge off
digitalWrite(OUT2, LOW); //main contactor off
digitalWrite(OUT3, LOW); //inverter power off
}
}
void handle_wifi(){
if (timer_wifi.check())
{
/*
*
* Routine to send data to wifi on serial 2
The information will be provided over serial to the esp8266 at 19200 baud 8n1 in the form :
vxxx,ixxx,pxxx,mxxxx,oxxx,rxxx* where :
v=pack voltage (0-700Volts)
i=current (0-1000Amps)
p=power (0-300kw)
m=motor rpm (0-10000rpm)
o=motor temp (-20 to 120C)
r=inverter temp (-20 to 120C)
*=end of string
xxx=three digit integer for each parameter eg p100 = 100kw.
updates will be every 1100ms approx.
v100,i200,p35,m3000,o20,r100*
*/
//Serial2.print("v100,i200,p35,m3000,o20,r100*"); //test string
digitalWrite(13,!digitalRead(13));//blink led every time we fire this interrrupt.
Serial.print(inv_volts_local);
Serial.print(F(" Volts"));
Serial.println();
Serial.println(HV_Flag);
Serial2.print("v");//dc bus voltage
Serial2.print(Sensor.Voltage);//voltage derived from ISA shunt //To show voltage from inverter change Sensor.Voltage to inv_volts_local
Serial2.print(",i");//dc current
Serial2.print(Sensor.Amperes);//current derived from ISA shunt
Serial2.print(",p");//total motor power
Serial2.print(Sensor.KW);//Power value derived from ISA Shunt
Serial2.print(",m");//motor rpm
Serial2.print(inverter_status.speed);
Serial2.print(",o");//motor temp
Serial2.print(inverter_status.motor_temperature);
Serial2.print(",r");//inverter temp
Serial2.print(inverter_status.inverter_temperature);
Serial2.print("*");// end of data indicator
}
}
void Msgs10ms() //10ms messages here
{
if(timer_Frames10.check())
{
if(can_status)
{
static uint8_t counter_11a_d6 = 0;
static uint8_t counter_1d4 = 0;
static uint8_t counter_1db = 0;
static uint8_t counter_329 = 0;
static uint8_t counter_100ms = 0;
// Send VCM gear selection signal (gets rid of P3197)
outFrame.id = 0x11a; // Set our transmission address ID
outFrame.length = 8; // Data payload 3 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
// CAN_FRAME inFrame;
// inFrame.id = 0x11a;
// inFrame.length = 8;
// Data taken from a gen1 inFrame where the car is starting to
// move at about 10% throttle: 4E400055 0000017D
// All possible gen1 values: 00 01 0D 11 1D 2D 2E 3D 3E 4D 4E
// MSB nibble: Selected gear (gen1/LeafLogs)
// 0: some kind of non-gear before driving
// 1: some kind of non-gear after driving
// 2: R
// 3: N
// 4: D
// LSB nibble: ? (LeafLogs)
// 0: sometimes at startup, not always; never when the
// inverted is powered on (0.06%)
// 1: this is the usual value (55% of the time in LeafLogs)
// D: seems to occur for ~90ms when changing gears (0.2%)
// E: this also is a usual value, but never occurs with the
// non-gears 0 and 1 (44% of the time in LeafLogs)
outFrame.data.bytes[0] = 0x4E;
//outFrame.data.bytes[0] = 0x01;
// 0x40 when car is ON, 0x80 when OFF, 0x50 when ECO
outFrame.data.bytes[1] = 0x40;
// Usually 0x00, sometimes 0x80 (LeafLogs), 0x04 seen by canmsgs
outFrame.data.bytes[2] = 0x00;
// Weird value at D3:4 that goes along with the counter
// NOTE: Not actually needed, you can just send constant AA C0
const static uint8_t weird_d34_values[4][2] = {
{0xaa, 0xc0},
{0x55, 0x00},
{0x55, 0x40},
{0xaa, 0x80},
};
outFrame.data.bytes[3] = weird_d34_values[counter_11a_d6][0];
outFrame.data.bytes[4] = weird_d34_values[counter_11a_d6][1];
// Always 0x00 (LeafLogs, canmsgs)
outFrame.data.bytes[5] = 0x00;
// A 2-bit counter
outFrame.data.bytes[6] = counter_11a_d6;
counter_11a_d6++;
if(counter_11a_d6 >= 4)
counter_11a_d6 = 0;
// Extra CRC
nissan_crc(outFrame.data.bytes, 0x85);
/*Serial.print(F("Sending "));
print_fancy_inFrame(inFrame);
Serial.println();*/
Can0.sendFrame(outFrame);
// Send target motor torque signal
outFrame.id = 0x1d4; // Set our transmission address ID
outFrame.length = 8; // Data payload 3 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
// Data taken from a gen1 inFrame where the car is starting to
// move at about 10% throttle: F70700E0C74430D4
// Usually F7, but can have values between 9A...F7 (gen1)
outFrame.data.bytes[0] = 0xF7;
// 2016: 6E
// outFrame.data.bytes[0] = 0x6E;
// Usually 07, but can have values between 07...70 (gen1)
outFrame.data.bytes[1] = 0x07;
// 2016: 6E
//outFrame.data.bytes[1] = 0x6E;
// Requested torque (signed 12-bit value + always 0x0 in low nibble)
static int16_t last_logged_final_torque_request = 0;
if(final_torque_request != last_logged_final_torque_request){
last_logged_final_torque_request = final_torque_request;
//log_print_timestamp();
//Serial.print(F("Sending torque request "));
//Serial.print(final_torque_request);
//Serial.print(F(" (speed: "));
//Serial.print(inverter_status.speed / INVERTER_BITS_PER_RPM);
//Serial.print(F(" rpm)"));
//Serial.print(inverter_status.voltage / INVERTER_BITS_PER_VOLT);
//Serial.print(F(" Volts)"));
//Serial.println();
}
if(final_torque_request >= -2048 && final_torque_request <= 2047){
outFrame.data.bytes[2] = ((final_torque_request < 0) ? 0x80 : 0) |
((final_torque_request >> 4) & 0x7f);
outFrame.data.bytes[3] = (final_torque_request << 4) & 0xf0;
} else {
outFrame.data.bytes[2] = 0x00;
outFrame.data.bytes[3] = 0x00;
}
// MSB nibble: Runs through the sequence 0, 4, 8, C
// LSB nibble: Precharge report (precedes actual precharge
// control)
// 0: Discharging (5%)
// 2: Precharge not started (1.4%)
// 3: Precharging (0.4%)
// 5: Starting discharge (3x10ms) (2.0%)
// 7: Precharged (93%)
outFrame.data.bytes[4] = 0x07 | (counter_1d4 << 6);
//outFrame.data.bytes[4] = 0x02 | (counter_1d4 << 6);
counter_1d4++;
if(counter_1d4 >= 4)
counter_1d4 = 0;
// MSB nibble:
// 0: 35-40ms at startup when gear is 0, then at shutdown 40ms
// after the car has been shut off (6% total)
// 4: Otherwise (94%)
// LSB nibble:
// 0: ~100ms when changing gear, along with 11A D0 b3:0 value
// D (0.3%)
// 2: Reverse gear related (13%)
// 4: Forward gear related (21%)
// 6: Occurs always when gear 11A D0 is 01 or 11 (66%)
//outFrame.data.bytes[5] = 0x44;
//outFrame.data.bytes[5] = 0x46;
// 2016 drive cycle: 06, 46, precharge, 44, drive, 46, discharge, 06
// 0x46 requires ~25 torque to start
//outFrame.data.bytes[5] = 0x46;
// 0x44 requires ~8 torque to start
outFrame.data.bytes[5] = 0x44;
// MSB nibble:
// In a drive cycle, this slowly changes between values (gen1):
// leaf_on_off.txt:
// 5 7 3 2 0 1 3 7
// leaf_on_rev_off.txt:
// 5 7 3 2 0 6
// leaf_on_Dx3.txt:
// 5 7 3 2 0 2 3 2 0 2 3 2 0 2 3 7
// leaf_on_stat_DRDRDR.txt:
// 0 1 3 7
// leaf_on_Driveincircle_off.txt:
// 5 3 2 0 8 B 3 2 0 8 A B 3 2 0 8 A B A 8 0 2 3 7
// leaf_on_wotind_off.txt:
// 3 2 0 8 A B 3 7
// leaf_on_wotinr_off.txt:
// 5 7 3 2 0 8 A B 3 7
// leaf_ac_charge.txt:
// 4 6 E 6
// Possibly some kind of control flags, try to figure out
// using:
// grep 000001D4 leaf_on_wotind_off.txt | cut -d' ' -f10 | uniq | ~/projects/leaf_tools/util/hex_to_ascii_binary.py
// 2016:
// Has different values!
// LSB nibble:
// 0: Always (gen1)
// 1: (2016)
// 2016 drive cycle:
// E0: to 0.15s
// E1: 2 messages
// 61: to 2.06s (inverter is powered up and precharge
// starts and completes during this)
// 21: to 13.9s
// 01: to 17.9s
// 81: to 19.5s
// A1: to 26.8s
// 21: to 31.0s
// 01: to 33.9s
// 81: to 48.8s
// A1: to 53.0s
// 21: to 55.5s
// 61: 2 messages
// 60: to 55.9s
// E0: to end of capture (discharge starts during this)
// This value has been chosen at the end of the hardest
// acceleration in the wide-open-throttle pull, with full-ish
// torque still being requested, in
// LeafLogs/leaf_on_wotind_off.txt
//outFrame.data.bytes[6] = 0x00;
// This value has been chosen for being seen most of the time
// when, and before, applying throttle in the wide-open-throttle
// pull, in
// LeafLogs/leaf_on_wotind_off.txt
outFrame.data.bytes[6] = 0x30; //brake applied heavilly.
// Value chosen from a 2016 log
//outFrame.data.bytes[6] = 0x61;
// Value chosen from a 2016 log
// 2016-24kWh-ev-on-drive-park-off.pcap #12101 / 15.63s
// outFrame.data.bytes[6] = 0x01;
//byte 6 brake signal
// Extra CRC
nissan_crc(outFrame.data.bytes, 0x85);
/*Serial.print(F("Sending "));
print_fancy_inFrame(inFrame);
Serial.println();*/
Can0.sendFrame(outFrame);
//We need to send 0x1db here with voltage measured by inverter
outFrame.id = 0x1db; // Set our transmission address ID
outFrame.length = 8; // Data payload 3 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
outFrame.data.bytes[0]=0x00;
outFrame.data.bytes[1]=0x00;
outFrame.data.bytes[2]=0x00;
outFrame.data.bytes[3]=0x00;
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00;
outFrame.data.bytes[6]=counter_1db;
outFrame.data.bytes[7]=0x00;
counter_1db++;
if(counter_1db >= 4)
counter_1db = 0;
Can0.sendFrame(outFrame);
///////////////Originally sent as 100ms messages.///////////////////////////////////////////
outFrame.id = 0x50b; // Set our transmission address ID
outFrame.length = 7; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
// Statistics from 2016 capture:
// 10 00000000000000
// 21 000002c0000000
// 122 000000c0000000
// 513 000006c0000000
// Let's just send the most common one all the time
// FIXME: This is a very sloppy implementation
// hex_to_data(outFrame.data.bytes, "00,00,06,c0,00,00,00");
outFrame.data.bytes[0]=0x00;
outFrame.data.bytes[1]=0x00;
outFrame.data.bytes[2]=0x06;
outFrame.data.bytes[3]=0xc0;
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00;
outFrame.data.bytes[6]=0x00;
/*CONSOLE.print(F("Sending "));
print_fancy_inFrame(inFrame);
CONSOLE.println();*/
Can0.sendFrame(outFrame);
/////////////////////////////////////////////////////////////////////////////////////////////////////
//these messages go out on vehicle can and are specific to driving the E46 instrument cluster etc.
//////////////////////DME Messages //////////////////////////////////////////////////////////
outFrame.id = 0x316; // Set our transmission address ID
outFrame.length = 8; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
outFrame.data.bytes[0]=0x05;
outFrame.data.bytes[1]=0x00;
outFrame.data.bytes[2]=lowByte(outRPM); //RPM LSB
outFrame.data.bytes[3]=highByte(outRPM); //RPM MSB [RPM=(hex2dec("byte3"&"byte2"))/6.4] 0x12c0 should be 750rpm on tach
// outFrame.data.bytes[2]=0xc0; //RPM LSB
// outFrame.data.bytes[3]=0x12; //RPM MSB [RPM=(hex2dec("byte3"&"byte2"))/6.4] 0x12c0 should be 750rpm on tach
// outFrame.data.bytes[2]=0xff; //RPM LSB
// outFrame.data.bytes[3]=0x4f; //RPM MSB [RPM=(hex2dec("byte3"&"byte2"))/6.4] 0x4fff gives 3200rpm on tach
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00;
outFrame.data.bytes[6]=0x00;
outFrame.data.bytes[7]=0x00;
Can1.sendFrame(outFrame);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//********************temp sense *******************************
// tempValue=analogRead(tempIN); //read Analog pin voltage
// The sensor and gauge are not linear. So if the sensed
// Voltage is less than the mid point the Map function
// is used to map the input values to output values For the gauge
// output values (in decimal):
// 86 = First visible movment of needle starts
// 93 = Begining of Blue section
// 128 = End of Blue Section
// 169 = Begin Straight up
// 193 = Midpoint of needle straight up values
// 219 = Needle begins to move from center
// 230 = Beginning of Red section
// 254 = needle pegged to the right
// MAP program statement: map(value, fromLow, fromHigh, toLow, toHigh)
// if(tempValue < 964){ //if pin voltage < mid point value
tempValue= inverter_status.inverter_temperature; //read temp from leaf inverter can.
tempValue= map(tempValue,15,80,88,254); //Map to e46 temp gauge
// }
// else {
// tempValue= map(tempValue,964,1014,219,254); //Map upper half of range
// }
//Can bus data packet values to be sent
outFrame.id = 0x329; // Set our transmission address ID
outFrame.length = 8; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
outFrame.data.bytes[0]=ABSMsg; //needs to cycle 11,86,d9
outFrame.data.bytes[1]=tempValue; //temp bit tdata
outFrame.data.bytes[2]=0xc5;
outFrame.data.bytes[3]=0x00;
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00; //Throttle position currently just fixed value
outFrame.data.bytes[6]=0x00;
outFrame.data.bytes[7]=0x00;
Can1.sendFrame(outFrame);
counter_329++;
if(counter_329 >= 22) counter_329 = 0;
if(counter_329==0) ABSMsg=0x11;
if(counter_329==8) ABSMsg=0x86;
if(counter_329==15) ABSMsg=0xd9;
//From ECU, MPG, MIL, overheat light, Cruise
// ErrorState variable controls:
//Check engine(binary 10), Cruise (1000), EML (10000)
//Temp light Variable controls temp light. Hex 08 is on zero off
//**************** set Error Lights & cruise light ******
// hex 08 = Overheat light on
// // hex 08 = Overheat light on
//Set check engine. Binary 0010 (hex 02)
// No error light (zero)
// int z = 0x60; // + y; higher value lower MPG
//Can bus data packet values to be sent
outFrame.id = 0x545; // Set our transmission address ID
outFrame.length = 8; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
outFrame.data.bytes[0]=0x00; //2=check ewwngine on , 0=check engine off
outFrame.data.bytes[1]=0x00; //LSB fuel comp
outFrame.data.bytes[2]=0x00; //MSB fuel comp
outFrame.data.bytes[3]=0x00; // hex 08 = Overheat light on
outFrame.data.bytes[4]=0x7E;
outFrame.data.bytes[5]=0x10;
outFrame.data.bytes[6]=0x00;
outFrame.data.bytes[7]=0x18;
Can1.sendFrame(outFrame);
}
}
}
void Msgs100ms() ////100ms messages here
{
if(timer_Frames100.check())
{
if(can_status)
{
// digitalWrite(led, !digitalRead(led)); //toggle led everytime we fire the 100ms messages.
outFrame.id = 0x50b; // Set our transmission address ID
outFrame.length = 7; // Data payload 8 bytes
outFrame.extended = 0; // Extended addresses - 0=11-bit 1=29bit
outFrame.rtr=1; //No request
// Statistics from 2016 capture:
// 10 00000000000000
// 21 000002c0000000
// 122 000000c0000000
// 513 000006c0000000
// Let's just send the most common one all the time
// FIXME: This is a very sloppy implementation
// hex_to_data(outFrame.data.bytes, "00,00,06,c0,00,00,00");
outFrame.data.bytes[0]=0x00;
outFrame.data.bytes[1]=0x00;
outFrame.data.bytes[2]=0x06;
outFrame.data.bytes[3]=0xc0;
outFrame.data.bytes[4]=0x00;
outFrame.data.bytes[5]=0x00;
outFrame.data.bytes[6]=0x00;
/*CONSOLE.print(F("Sending "));
print_fancy_inFrame(inFrame);
CONSOLE.println();*/
Can0.sendFrame(outFrame);
}
}
}
void readPedals()
{
ThrotVal = analogRead(Throttle1); //read throttle channel 1 directly
ThrotVal = constrain(ThrotVal, 145, 620);
ThrotVal = map(ThrotVal, 145, 620, 0, MaxTrq); //will need to work here for cal.
if(ThrotVal<0) ThrotVal=0; //no negative numbers for now.
if(digitalRead(Brake)) ThrotVal=0; //if brake is pressed we zero the throttle value.
//Serial.println(ThrotVal); //print for calibration.
}
void SendTorqueRequest()
{
final_torque_request = ThrotVal; //send mapped throttle signal to inverter.
}
void ProcessRPM() //here we convert motor rpm values received from the leaf inverter into BMW E46 RPM can message.
{
outRPM = (inverter_status.speed)*6.4;
if(outRPM<4800) outRPM=4800; //set lowest rpm to 750 displayed on tach to keep car alive thinking engine is running.
if(outRPM>44800) outRPM=44800; //DONT READ MORE THAN 7000RPM!
//Serial.println(outRPM);
}
static int8_t fahrenheit_to_celsius(uint16_t fahrenheit)
{
int16_t result = ((int16_t)fahrenheit - 32) * 5 / 9;
if(result < -128)
return -128;
if(result > 127)
return 127;
return result;
}
void CheckCAN()
{
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//read incomming data from inverter//////////////////
//////////////////////////////////////////////////////
if(Can0.available())
{
Can0.read(inFrame);
//Serial.println(inFrame.id, HEX);
if(inFrame.id == 0x1da && inFrame.length == 8){
// last_received_from_inverter_timestamp = millis();
inverter_status.voltage = ((uint16_t)inFrame.data.bytes[0] << 2) |
(inFrame.data.bytes[1] >> 6);
int16_t parsed_speed = ((uint16_t)inFrame.data.bytes[4] << 8) |
(uint16_t)inFrame.data.bytes[5];
inverter_status.speed = (parsed_speed == 0x7fff ? 0 : parsed_speed);
inverter_status.error_state = (inFrame.data.bytes[6] & 0xb0) != 0x00;
}
if(inFrame.id == 0x55a && inFrame.length == 8){
// last_received_from_inverter_timestamp = millis();
inverter_status.inverter_temperature = fahrenheit_to_celsius(inFrame.data.bytes[2]);
inverter_status.motor_temperature = fahrenheit_to_celsius(inFrame.data.bytes[1]);
//Serial.println(inverter_status.inverter_temperature);
}
}
if(inFrame.id >= 0x521 && inFrame.id <= 0x528 && inFrame.length == 8){
Sensor.ISA::gotFrame(&inFrame, 0); //Added by royhen99
}
////////////////////////////////////////////////////////////////////////////////////////////////
}
static void nissan_crc(uint8_t *data, uint8_t polynomial)
{
// We want to process 8 bytes with the 8th byte being zero
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) ^ polynomial);
else
crc = (byte)((crc << 1) + bit);
}
}
data[7] = crc;
}
-
- Posts: 250
- Joined: Sun Feb 20, 2022 4:23 am
- Location: N. Wiltshire. UK
- Has thanked: 21 times
- Been thanked: 121 times
Re: Leaf Gen 1 Inverter Board
There's a small error in CheckCan
should be
Code: Select all
}
if(inFrame.id >= 0x521 && inFrame.id <= 0x528 && inFrame.length == 8){
Sensor.ISA::gotFrame(&inFrame, 0); //Added by royhen99
}
////////////////////////////////////////////////////////////////////////////////////////////////
}
Code: Select all
if(inFrame.id >= 0x521 && inFrame.id <= 0x528 && inFrame.length == 8){
Sensor.ISA::gotFrame(&inFrame, 0); //Added by royhen99
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
}
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Sorry mate, I've been messing about trying different things but I still only get Inverter CAN with this code. The ISA Shunt is not giving any output unless I put the sensor.begin line back in.
Would it help If I got some CAN logs? If so is there a sequence you would suggest? CAN0 and CAN1?
Sorry to be a bother.
Would it help If I got some CAN logs? If so is there a sequence you would suggest? CAN0 and CAN1?
Sorry to be a bother.
-
- Posts: 250
- Joined: Sun Feb 20, 2022 4:23 am
- Location: N. Wiltshire. UK
- Has thanked: 21 times
- Been thanked: 121 times
Re: Leaf Gen 1 Inverter Board
I have tested with your exact code with the } fixed and it all works. I am only sending messages to Can0 and don't have wifi so all readings are going to Serial ( I am using Arduino Due so just looking with serial monitor in IDE ). I thought it might be a timing problem as CheckCAN is polled in loop() so I have sent a version via DM that uses interrupts for the can0 messages.
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Thank you, I'll take a look and try it again. Maybe it's something stupid I'm doing.royhen99 wrote: ↑Tue Apr 16, 2024 4:40 pm I have tested with your exact code with the } fixed and it all works. I am only sending messages to Can0 and don't have wifi so all readings are going to Serial ( I am using Arduino Due so just looking with serial monitor in IDE ). I thought it might be a timing problem as CheckCAN is polled in loop() so I have sent a version via DM that uses interrupts for the can0 messages.
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
So I tried this code exactly as you sent it and although it enables the serial outputs from the Inverter and lets me drive I don't see anything from the shunt.
I only get Motor temp, Inverter temp and rpm on the display.
This is what I see in the serial monitor Could it be a library version issue?
I only get Motor temp, Inverter temp and rpm on the display.
This is what I see in the serial monitor Could it be a library version issue?
-
- Posts: 250
- Joined: Sun Feb 20, 2022 4:23 am
- Location: N. Wiltshire. UK
- Has thanked: 21 times
- Been thanked: 121 times
Re: Leaf Gen 1 Inverter Board
That's what I would expect. The ISA shunt data is sent to Serial2 which is the wifi interface. To view it on the terminal you to need to change Serial2 to Serial, or you could copy the code so it sends to both.
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Thanks mate, I'll give that a try tomorrow and report back.
I was hoping there would be a way to see Serial2 on the serial monitor but I guess not.
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
So for some reason with this new version the startup sequence of the car has become unreliable. Maybe 30% or 40% when I turn on the ignition I get positive contactor enabled but the throttle does not work. It may be something in my startup sequence but as it took me weeks to figure out a reliable startup I don't want to go through that again, at least not until I get everything else working properly.
For now the display is still showing either temps and rpm or Volts and Amps but not all at the same time.
Unfortunately I don't have time to work at it any more today so for now I am reverting back to the code that includes sensor.begin as it gives me voltage current and power which are great for seeing charging etc. I can live without rpm and temps for a day or two until I get something sorted.
I just want to say a huge thanks to Royhen99 for your time and effort with this. I suspect there are very few others in my situation so it was really only for me so thank you. I have learned a huge amount through the process so maybe I have enough now to resolve the issue myself and if I do I'll report back.
For now the display is still showing either temps and rpm or Volts and Amps but not all at the same time.
Unfortunately I don't have time to work at it any more today so for now I am reverting back to the code that includes sensor.begin as it gives me voltage current and power which are great for seeing charging etc. I can live without rpm and temps for a day or two until I get something sorted.
I just want to say a huge thanks to Royhen99 for your time and effort with this. I suspect there are very few others in my situation so it was really only for me so thank you. I have learned a huge amount through the process so maybe I have enough now to resolve the issue myself and if I do I'll report back.
- arturk
- Posts: 148
- Joined: Wed Oct 02, 2019 3:58 am
- Location: United States, MD
- Has thanked: 5 times
- Been thanked: 3 times
Re: Leaf Gen 1 Inverter Board
Hi Ali,
I did not have a chance to study entire thread related to your issue but it looks to me you may be having issue with different code functions competing over the same output pin.
This is the issue/feature everyone using ISA library should be aware of.
If you look in ISA.cpp file you will find following function:
Note values of "canEnPin" variables for corresponding CAN ports.
When we look in the code you posted earlier, we can see that the same ports are already being used for some of your outputs controlling relays.
If you try to use ISA library initializing it by calling Sensor.begin(port,datarate) on port:
I am assuming your VCU board has CAN transceivers permanently enabled therefore you don't need to control them.
In order to solve this issue I made small modification to ISA library.
Here is what my "begin"function looks like:
You can see I have added another parameter to specify port controlling CAN transceiver if your board requires it.
If not, you can simply pass value of 255 (which points to non-existing port on Due).
I see you have tried few things to get this working but perhaps you can revert to your original code and address issue with those pins and see if it works better.
Good luck!
I did not have a chance to study entire thread related to your issue but it looks to me you may be having issue with different code functions competing over the same output pin.
This is the issue/feature everyone using ISA library should be aware of.
If you look in ISA.cpp file you will find following function:
Code: Select all
void ISA::begin(int Port, int speed)
{
if (Port == 0)
{
canPort = &Can0;
canEnPin = 50;
canSpeed = speed * 1000;
}
else
{
canPort = &Can1;
canEnPin = 48;
canSpeed = speed * 1000;
}
...
}
When we look in the code you posted earlier, we can see that the same ports are already being used for some of your outputs controlling relays.
Code: Select all
#define OUT1 48 //Low side switched general purpose digital output 1. high = on.
#define OUT2 49 //Low side switched general purpose digital output 2. high = on.
#define OUT3 50 //Low side switched general purpose digital output 3. high = on.
- CAN0, it will want to control port 50 which is mapped to your "OUT3"
- CAN1, it will want to control port 48 which is mapped to your "OUT1"
I am assuming your VCU board has CAN transceivers permanently enabled therefore you don't need to control them.
In order to solve this issue I made small modification to ISA library.
Here is what my "begin"function looks like:
Code: Select all
void ISA::begin(int Port, int speed, int enPin)
{
if (Port == 0)
{
canPort = &Can0;
}
else
{
canPort = &Can1;
}
canEnPin = enPin;
canSpeed = speed;
canPort->begin(canSpeed, canEnPin);
canPort->setRXFilter(0, 0x520, 0x7F0, false);
canPort->attachObj(this);
attachMBHandler(0);
Wire.begin();
initializeEPROM();
initCurrent();
}
If not, you can simply pass value of 255 (which points to non-existing port on Due).
Code: Select all
Sensor.begin(0, CAN_BPS_500K, 255);
Good luck!
1998 Jaguar XJR, GS450h drivetrain, 48kWh/96s BMW battery
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Thanks Artur, I'll have another look at it later and report back.arturk wrote: ↑Fri May 31, 2024 4:45 am Hi Ali,
I did not have a chance to study entire thread related to your issue but it looks to me you may be having issue with different code functions competing over the same output pin.
This is the issue/feature everyone using ISA library should be aware of.
If you look in ISA.cpp file you will find following function:Note values of "canEnPin" variables for corresponding CAN ports.Code: Select all
void ISA::begin(int Port, int speed) { if (Port == 0) { canPort = &Can0; canEnPin = 50; canSpeed = speed * 1000; } else { canPort = &Can1; canEnPin = 48; canSpeed = speed * 1000; } ... }
When we look in the code you posted earlier, we can see that the same ports are already being used for some of your outputs controlling relays.If you try to use ISA library initializing it by calling Sensor.begin(port,datarate) on port:Code: Select all
#define OUT1 48 //Low side switched general purpose digital output 1. high = on. #define OUT2 49 //Low side switched general purpose digital output 2. high = on. #define OUT3 50 //Low side switched general purpose digital output 3. high = on.
- CAN0, it will want to control port 50 which is mapped to your "OUT3"
Apparently Jack originally developed this library for EVTV CAN adapter which required CAN transceivers to be enabled before communication could be established. It just so happened that the same ports are used on multiple Damien's "Due" based boards and perhaps other.
- CAN1, it will want to control port 48 which is mapped to your "OUT1"
I am assuming your VCU board has CAN transceivers permanently enabled therefore you don't need to control them.
In order to solve this issue I made small modification to ISA library.
Here is what my "begin"function looks like:You can see I have added another parameter to specify port controlling CAN transceiver if your board requires it.Code: Select all
void ISA::begin(int Port, int speed, int enPin) { if (Port == 0) { canPort = &Can0; } else { canPort = &Can1; } canEnPin = enPin; canSpeed = speed; canPort->begin(canSpeed, canEnPin); canPort->setRXFilter(0, 0x520, 0x7F0, false); canPort->attachObj(this); attachMBHandler(0); Wire.begin(); initializeEPROM(); initCurrent(); }
If not, you can simply pass value of 255 (which points to non-existing port on Due).I see you have tried few things to get this working but perhaps you can revert to your original code and address issue with those pins and see if it works better.Code: Select all
Sensor.begin(0, CAN_BPS_500K, 255);
Good luck!
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
So I've been playing with this over the last few days and still having the same issue. Initially I couldn't get it to compile then figured out I needed to adjust the ISA.h to reflect the change to ISA.CPP. Once that was done and the extra 255 added (without this I got an error) to the Sensor.begin line it compiled but didn't improve the problem.arturk wrote: ↑Fri May 31, 2024 4:45 am Hi Ali,
I did not have a chance to study entire thread related to your issue but it looks to me you may be having issue with different code functions competing over the same output pin.
This is the issue/feature everyone using ISA library should be aware of.
If you look in ISA.cpp file you will find following function:Note values of "canEnPin" variables for corresponding CAN ports.Code: Select all
void ISA::begin(int Port, int speed) { if (Port == 0) { canPort = &Can0; canEnPin = 50; canSpeed = speed * 1000; } else { canPort = &Can1; canEnPin = 48; canSpeed = speed * 1000; } ... }
When we look in the code you posted earlier, we can see that the same ports are already being used for some of your outputs controlling relays.If you try to use ISA library initializing it by calling Sensor.begin(port,datarate) on port:Code: Select all
#define OUT1 48 //Low side switched general purpose digital output 1. high = on. #define OUT2 49 //Low side switched general purpose digital output 2. high = on. #define OUT3 50 //Low side switched general purpose digital output 3. high = on.
- CAN0, it will want to control port 50 which is mapped to your "OUT3"
Apparently Jack originally developed this library for EVTV CAN adapter which required CAN transceivers to be enabled before communication could be established. It just so happened that the same ports are used on multiple Damien's "Due" based boards and perhaps other.
- CAN1, it will want to control port 48 which is mapped to your "OUT1"
I am assuming your VCU board has CAN transceivers permanently enabled therefore you don't need to control them.
In order to solve this issue I made small modification to ISA library.
Here is what my "begin"function looks like:You can see I have added another parameter to specify port controlling CAN transceiver if your board requires it.Code: Select all
void ISA::begin(int Port, int speed, int enPin) { if (Port == 0) { canPort = &Can0; } else { canPort = &Can1; } canEnPin = enPin; canSpeed = speed; canPort->begin(canSpeed, canEnPin); canPort->setRXFilter(0, 0x520, 0x7F0, false); canPort->attachObj(this); attachMBHandler(0); Wire.begin(); initializeEPROM(); initCurrent(); }
If not, you can simply pass value of 255 (which points to non-existing port on Due).I see you have tried few things to get this working but perhaps you can revert to your original code and address issue with those pins and see if it works better.Code: Select all
Sensor.begin(0, CAN_BPS_500K, 255);
Good luck!
With sensor.begin I get Voltage, Current and Power from the ISA shunt but no temps or rpm.
I tried enabling a the other changes suggested but still nothing.
I'm still working on it but ran into an issue when the laptop with all the code on it fell off the bench and damaged the power socket.
Now I need to get another laptop up and running or repair this one to test further.
- arturk
- Posts: 148
- Joined: Wed Oct 02, 2019 3:58 am
- Location: United States, MD
- Has thanked: 5 times
- Been thanked: 3 times
Re: Leaf Gen 1 Inverter Board
Hi Ali, sorry to hear you are still having the problem.
My suggestions solve the problem of interference with controlling of your relays. I thought you experienced this at some point, this is why I brought this up.
It is it possible that one may get lucky with the original library code under certain circumstances but with this fix we at least taking care of this potential issue once for all.
Sorry I forgot to mention about function declaration in ISA.h.
My suggestions solve the problem of interference with controlling of your relays. I thought you experienced this at some point, this is why I brought this up.
It is it possible that one may get lucky with the original library code under certain circumstances but with this fix we at least taking care of this potential issue once for all.
Sorry I forgot to mention about function declaration in ISA.h.
1998 Jaguar XJR, GS450h drivetrain, 48kWh/96s BMW battery
-
- Posts: 927
- Joined: Sun Feb 23, 2020 9:24 am
- Location: Northern Ireland
- Has thanked: 329 times
- Been thanked: 197 times
- Contact:
Re: Leaf Gen 1 Inverter Board
Hi Arturarturk wrote: ↑Mon Jun 03, 2024 2:11 am Hi Ali, sorry to hear you are still having the problem.
My suggestions solve the problem of interference with controlling of your relays. I thought you experienced this at some point, this is why I brought this up.
It is it possible that one may get lucky with the original library code under certain circumstances but with this fix we at least taking care of this potential issue once for all.
Sorry I forgot to mention about function declaration in ISA.h.
I think the issue I had with relay control is related to whether I forget to turn the car off before updating the VCU. It happened again over the weekend and I'm pretty certain this is what caused it. Basically if the car is turned on while I update the VCU firmware it will not operate correctly until I power cycle the VCU.
Having said that before I implemented your code I was not certain the VCU was behaving correctly with the ISA shunt enabled. I couldn't put a finger on it but it seemed different somehow. Now it seems to be better so thank you for that, I drove 60 miles on Sunday and turned it off and on multiple times with no issues.