Leaf Gen 1 Inverter Board

Nissan Leaf/e-NV200 drive stack topics
Alibro
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

Post by Alibro »

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.
Alibro
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

Post by Alibro »

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
Alibro
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

Post by Alibro »

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. :lol:

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

Post by royhen99 »

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
Alibro
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

Post by Alibro »

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.
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
Would you mind having another look at this please? With this added I get the error "base operand of '->' is not a pointer"
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. :(
User avatar
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

Post by crasbe »

Alibro wrote: Sun Apr 14, 2024 9:09 pm Would you mind having another look at this please? With this added I get the error "base operand of '->' is not a pointer"
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. :(
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);
HOWEVER I don't think this is right either, because the center part is not correct C code. It needs another comma:

Code: Select all

Can0.setRXFilter(0, 0x100, 0x300, false);
You can check out the documentation to see what this does: https://github.com/collin80/due_can/blo ... xt#L75-L84


I can not assess if this code does what you want it to do, but it should compile :)
Alibro
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

Post by Alibro »

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.
Alibro
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

Post by Alibro »

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. :(
royhen99
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

Post by royhen99 »

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);
}
Alibro
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

Post by Alibro »

Awe mate thank you! I'll get this tested later this morning and let you know how it goes. :D
Alibro
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

Post by Alibro »

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

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 */
And this is the main sketch

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

Post by royhen99 »

There's a small error in CheckCan

Code: Select all

  }
  if(inFrame.id >= 0x521 && inFrame.id <= 0x528 && inFrame.length == 8){
  Sensor.ISA::gotFrame(&inFrame, 0);                                        //Added by royhen99
  }
////////////////////////////////////////////////////////////////////////////////////////////////

  
}
should be

Code: Select all

  
  if(inFrame.id >= 0x521 && inFrame.id <= 0x528 && inFrame.length == 8){
  Sensor.ISA::gotFrame(&inFrame, 0);                                        //Added by royhen99
  }
  
 }
////////////////////////////////////////////////////////////////////////////////////////////////
  
  
}
Alibro
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

Post by Alibro »

Thanks mate, I'll give it another go.
Alibro
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

Post by Alibro »

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.
royhen99
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

Post by royhen99 »

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.
Alibro
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

Post by Alibro »

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.
Thank you, I'll take a look and try it again. Maybe it's something stupid I'm doing.
Alibro
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

Post by Alibro »

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
Untitled.png
Could it be a library version issue?
royhen99
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

Post by royhen99 »

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.
Alibro
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

Post by Alibro »

royhen99 wrote: Tue Apr 16, 2024 8:45 pm 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.
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. :?
Alibro
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

Post by Alibro »

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.
User avatar
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

Post by arturk »

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:

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

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.
If you try to use ISA library initializing it by calling Sensor.begin(port,datarate) on port:
  • 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"
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.
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();
}
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).

Code: Select all

  Sensor.begin(0, CAN_BPS_500K, 255);
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!
1998 Jaguar XJR, GS450h drivetrain, 48kWh/96s BMW battery
Alibro
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

Post by Alibro »

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:

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

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.
If you try to use ISA library initializing it by calling Sensor.begin(port,datarate) on port:
  • 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"
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.
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();
}
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).

Code: Select all

  Sensor.begin(0, CAN_BPS_500K, 255);
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!
Thanks Artur, I'll have another look at it later and report back.
Alibro
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

Post by Alibro »

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:

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

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.
If you try to use ISA library initializing it by calling Sensor.begin(port,datarate) on port:
  • 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"
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.
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();
}
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).

Code: Select all

  Sensor.begin(0, CAN_BPS_500K, 255);
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!
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.
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. :cry:
User avatar
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

Post by arturk »

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.
1998 Jaguar XJR, GS450h drivetrain, 48kWh/96s BMW battery
Alibro
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

Post by Alibro »

arturk 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.
Hi Artur
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.
Post Reply