Can't Initialize Isabellenhütte ISA Sensor.  [SOLVED]

Introduction and miscellaneous that we haven't created categories for, yet
MoonUnit
Posts: 56
Joined: Fri Nov 05, 2021 9:08 am
Has thanked: 20 times
Been thanked: 10 times

Re: Can't Initialize Isabellenhütte ISA Sensor.

Post by MoonUnit »

All, I struggled to get a couple of ISA Isabellenhutte shunts initialised and working so am putting my solution below in case it helps anyone. I didn't have an Arduino to use the Jack Rickard-derived code linked in the wiki here https://openinverter.org/wiki/Isabellen ... te_Heusler but I did have a Teensy 4.1.

In addition, the linked code above contains libraries you don't need if all you need is to speak to the ISA shunt over CANbus (it includes duo_wire and wire_EEPROM which I can only assume Jack was using to do other stuff with).

The code below should allow you to communicate with the ISA shunt and change it from the factory defaults to whatever you need. In my case, I got two shunts from Ebay with unknown provenance. They turned out to be set to 500kbs baud rate (the ISA can be set to 250kbs, 500kbs and 1000kbs).

I strongly recommend you read the ISA data sheet, as the code below is the bare minimum you need.

Some gotchas:

The Jack-Rickard-derived code set the rtr flag on the CANmessages to 1, it should be set to 0.
By default, the ISA is big-endian so the shunt may be working fine, you just may be decoding it wrong.
That said, whilst the code below sets all the messages to come back little endian, the current persists in coming back big-endian and I haven't looked into why yet. Fixed, I wasn't sending the correct identifier.
Check your CANbus termination - some of the shunts have in-built termination and some don't.
Check your CANbus transceivers if you are using cheap rubbish like me. I have had two batches of SN65HVD230-based tranceivers from Amazon, some worked, and some didn't, and some turned out to be not terminated when others were, so don't assume the transceivers are good.

I used a Teensy 4.1, with code below, and you need the excellent flexcan_t4 library by Tonton81 from here https://github.com/tonton81/FlexCAN_T4.

Code: Select all

#include <FlexCAN_T4.h>

/*
 THIS CODE WAS ORIGINATED BY JACK RICKARD AND CONTAINED THE FOLLOWING MESSAGE:
 
 "This library supports 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 2014
    You are licensed to use this library for any purpose, commercial or private, 
    without restriction."
 
  I HAVE REMOVED A LOT OF STUFF AND RE-WITTEN FOR TEENSY 4.1 USING FLEXCAN_T4. ADDITIONALLY I HAVE CHANGED THE RSR BIT FROM 1 IN JACK'S CODE TO 0 IN THE COMMAND MESSAGES THAT GO TO THE SHUNT.

  READ THE ISA DATA SHEET!
 
 */

template<class T> inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; } //Allow streaming

float Version=1.00;
uint32_t datarate=500000;

class ISA
{
  
  public:
    ISA();
    ~ISA();
    float Amperes;   // Floating point with current in Amperes
    double Voltage;
    double Voltage1;
    double Voltage2;
    double Voltage3;
    double Temperature;
    double KW;
    double KWH;
    bool firstframe;
    int framecount;
    //unsigned long timestamp;
    double milliamps;
    long watt;
    //long As;
    //long wh;    
};

ISA::ISA()  // Define the constructor.
{
   //timestamp = millis(); 
  framecount=0;
  firstframe=true;
}

ISA::~ISA() //Define destructor
{
}

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1; //
ISA Sensor;  //Instantiate ISA Module Sensor object to measure current and voltage 




void setup() 
{

  Can1.begin();
  Can1.setClock(CLK_60MHz);
  delay(500);
  Can1.setBaudRate(datarate);
  Can1.setMaxMB(2);//NUM_TX_MAILBOXES + NUM_RX_MAILBOXES);
  Can1.setMB((FLEXCAN_MAILBOX)0,RX,STD); //receive on MB0
  Can1.setMB(MB1,TX); //transmit from MB1
  Can1.setMBFilter(REJECT_ALL); //block all data before setting filters
  //Can1.setMBFilter(MB0,0x521);
  Can1.setMBFilterRange(MB0, 0x521, 0x528);
  Can1.enableMBInterrupts();
 
  Can1.onReceive(canSniff);  //canSniff called when message received
  
  Serial.begin(115200);
  Serial.setTimeout(3000);
  
  Can1.mailboxStatus(); //diagnostic only.
  Serial.print("rate: "); Serial.println(Can1.getBaudRate());
  
  Serial<<"\nISA Scale Startup Successful \n";
 
  printMenu();
}

void loop()
{
 
  Can1.events(); //supposedly needed for the callback to work but I think it may work anyway with can.receive above

  checkforinput(); //Check keyboard for user input 
}
 
 
void printStatus()
{
  char buffer[40];
 
   sprintf(buffer,"%4.2f",Sensor.Voltage1); 
   Serial<<"V1:"<<buffer<<"v ";
   sprintf(buffer,"%4.2f",Sensor.Voltage2); 
   Serial<<"V2:"<<buffer<<"v ";
   sprintf(buffer,"%4.2f",Sensor.Voltage3); 
   Serial<<"V3:"<<buffer<<"v ";
  
   sprintf(buffer,"%4.3f",Sensor.Amperes); 
   Serial<<"Amps:"<<buffer<<"A ";

  sprintf(buffer,"%4.3f",Sensor.KW); 
   Serial<<buffer<<"kW ";
  
 //  sprintf(buffer,"%4.3f",Sensor.ah); 
 //  Serial<<buffer<<"Ah "; 

 //  sprintf(buffer,"%4.3f",Sensor.KWH); 
 //  Serial<<buffer<<"kWh"; 
   
   sprintf(buffer,"%4.0f",Sensor.Temperature); 
   Serial<<buffer<<"C "; 
   
   Serial<<"Frame:"<<Sensor.framecount<<" \n";
 }

 
void printMenu()
{
   Serial<<"\f\n=========== ISA Shunt Configurator "<<Version<<" ==============\n************ List of Available Commands ************\n\n";
   Serial<<"  ?  - Print this menu\n ";
   Serial<<"  i  - initialize new sensor\n ";
   Serial<<"  1  - STOP\n ";
   Serial<<"  2  - STORE\n ";  
   Serial<<"  3  - START\n ";
   Serial<<"  4  - START\n ";
   Serial<<"  r  - Set new datarate\n ";
   Serial<<"  d  - deFAULT\n ";
   
   
   Serial<<"**************************************************************\n==============================================================\n\n";
   
}

void checkforinput()
{ 
  //Checks for keyboard input from Native port 
   if (Serial.available()) 
     {
      int inByte = Serial.read();
      switch (inByte)
         {
          
          case 'r':    
             getRate();
            break;
                   
          case 'i':     
              initialize();
            break;
           
          case '?':     
              printMenu();
            break;
            
          case '1':     
              STOP();
            break;

         case '2':
            sendSTORE();
            break;
        
         case '3':     
             START();
            break;
          case '4':     
             RESTART();
            break;
            
         case 'a':
            deFAULT();
            break;
          }        
      } 
}


void getRate()
{
  Serial<<"\n Enter the Data Rate in kbps you want for CAN: ";
  if (Serial.available())
  {          
    uint32_t V = Serial.parseInt();  
    if(V>0)
    {
       
       Can1.setBaudRate(V);
    }
    Serial.print("new rate: "); Serial.println(Can1.getBaudRate()); //check to see if entered rate has been set
    Can1.mailboxStatus();
  }
}


void canSniff(const CAN_message_t &msg)
{
  Serial.print("MB "); Serial.print(msg.mb);
  Serial.print("  OVERRUN: "); Serial.print(msg.flags.overrun);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id, HEX);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) 
  {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } 
  Serial.println();
  
  
  
  //CAN interrupt
  switch (msg.id)
  {
    case 0x511: //this ID may be getting filtered, see setMBFilterRange above.
    break;
      
    case 0x521:    
      handle521(msg);
    break;  

    case 0x522:
      handle522(msg);
    break;
      
    case 0x523:    
      handle523(msg);
    break;
      
    case 0x524:    
      handle524(msg);
    break;
      
    case 0x525:    
      handle525(msg); 
    break;
      
    case 0x526:    
      handle526(msg); 
    break;
      
    case 0x527:    
      handle527(msg); 
    break;
      
    case 0x528:
      handle528(msg);   
    break;      
  }
  printStatus(); 
}
void handle521(const CAN_message_t &msg)  //AMperes
{  
  Serial.println("hande521");
  Sensor.framecount++;
  long current=0;

  //https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
  //https://www.geeksforgeeks.org/bitwise-operators-in-c-cpp/
  
  //  current = (long)((msg.buf[5] << 24) | (msg.buf[4] << 16) | (msg.buf[3] << 8) | (msg.buf[2])); //Little Endian ...
    current = (long)(msg.buf[5] + (msg.buf[4] << 8) + (msg.buf[3] << 16) + (msg.buf[2] << 24)); //TdB Big Endian

    
    Sensor.milliamps=current;
    Sensor.Amperes=current/1000.0f;

    
      
}
void handle522(const CAN_message_t &msg)  //Voltage .. note to me, what's all this framecount stuff?
{  
  Serial.println("hande522");
  Sensor.framecount++;
  long volt=0;
  
  volt = (long)((msg.buf[5] << 24) | (msg.buf[4] << 16) | (msg.buf[3] << 8) | (msg.buf[2]));
  //volt =   (long)(msg.buf[5] + (msg.buf[4] << 8) + (msg.buf[3] << 16) + (msg.buf[2] << 24)); //TdB
  Sensor.Voltage1=volt/1000.0f;

}
void handle523(const CAN_message_t &msg) //Voltage2
{  
  Serial.println("hande523");
  Sensor.framecount++;
  long volt=0;
  volt = (long)((msg.buf[5] << 24) | (msg.buf[4] << 16) | (msg.buf[3] << 8) | (msg.buf[2]));
  //volt =   (long)(msg.buf[5] + (msg.buf[4] << 8) + (msg.buf[3] << 16) + (msg.buf[2] << 24)); //TdB
  Sensor.Voltage2=volt/1000.0f;
  
}
void handle524(const CAN_message_t &msg)  //Voltage3
{  
  Serial.println("hande524");
  Sensor.framecount++;
  long volt=0;
  
  volt = (long)((msg.buf[5] << 24) | (msg.buf[4] << 16) | (msg.buf[3] << 8) | (msg.buf[2]));
  //volt =   (long)(msg.buf[5] + (msg.buf[4] << 8) + (msg.buf[3] << 16) + (msg.buf[2] << 24)); //TdB
  Sensor.Voltage3=volt/1000.0f;
  
}

void handle525(const CAN_message_t &msg)  //Temperature
{ 
  Serial.println("hande525");
  Sensor.framecount++;
  long temp=0;
  temp = (long)((msg.buf[5] << 24) | (msg.buf[4] << 16) | (msg.buf[3] << 8) | (msg.buf[2]));
   
  Sensor.Temperature=temp/10;
    
     //if(debug2)Serial<<"Temperature: "<<Temperature<<" C  frames:"<<framecount<<"\n";
   
}

void handle526(const CAN_message_t &msg) //Kilowatts .... what voltage is this using? U1 I believe.
{ 
  Serial.println("hande526");
  Sensor.framecount++;
  
  long watt=0;
    watt = (long)((msg.buf[5] << 24) | (msg.buf[4] << 16) | (msg.buf[3] << 8) | (msg.buf[2]));
    
    Sensor.KW=watt/1000.0f;
    
    // if(debug2)Serial<<"Power: "<<watt<<" Watts  "<<KW<<" kW  frames:"<<framecount<<"\n";
    
}


void handle527(const CAN_message_t &msg) //Ampere-Hours
{ 
  Serial.println("hande527");
  Sensor.framecount++;
  /* 
  //ignored
  */
}

void handle528(const CAN_message_t &msg)  //kiloWatt-hours
{ 
  Serial.println("hande528");
  Sensor.framecount++;

  /* 
  //ignored
  */
}

void STOP()
{

  //SEND STOP///////
  Serial.println("Stop called, status is ");
  
  CAN_message_t outframe;

      outframe.id = 0x411;      // Set our transmission address ID
        outframe.len = 8;       // Data payload 8 bytes
        outframe.flags.extended = 0; // Extended addresses  0=11-bit1=29bit
        outframe.flags.remote   = 0; //<--- THIS MUST BE SET TO ZERO. IF YOUR SENSOR IS IGNORING YOU CHECK THIS BIT
        outframe.buf[0]=0x34;
        outframe.buf[1]=0x00;  
        outframe.buf[2]=0x01;
        outframe.buf[3]=0x00;
        outframe.buf[4]=0x00;
        outframe.buf[5]=0x00;
        outframe.buf[6]=0x00;
        outframe.buf[7]=0x00;
    Can1.write(MB1,outframe);

    Serial.println("Sent, status is ");
    Can1.mailboxStatus();
    
    
} 

void START()
{
       Serial.println("Start called, status is "); 
       
       
 //SEND START///////
  CAN_message_t outframe;
    outframe.id = 0x411;      // Set our transmission address ID
        outframe.len = 8;       // Data payload 8 bytes
        outframe.flags.extended = 0; // Extended addresses  0=11-bit1=29bit
        outframe.flags.remote=0;   //<--- THIS MUST BE SET TO ZERO. IF YOUR SENSOR IS IGNORING YOU CHECK THIS BIT
        outframe.buf[0]=0x34;
        outframe.buf[1]=0x01;  
        outframe.buf[2]=0x01;
        outframe.buf[3]=0x00;
        outframe.buf[4]=0x00;
        outframe.buf[5]=0x00;
        outframe.buf[6]=0x00;
        outframe.buf[7]=0x00;
    Can1.write(MB1,outframe);
   
    Serial.println("Sent, status is ");
    Can1.mailboxStatus();  
}
void RESTART()
{
  //Has the effect of zeroing AH and KWH  
  Serial.println("Restart called, status is ");
         
  CAN_message_t outframe;
      outframe.id = 0x411;      // Set our transmission address ID
        outframe.len = 8;       // Data payload 8 bytes
        outframe.flags.extended = 0; // Extended addresses  0=11-bit1=29bit
        outframe.flags.remote=0;   //<--- THIS MUST BE SET TO ZERO. IF YOUR SENSOR IS IGNORING YOU CHECK THIS BIT
        outframe.buf[0]=0x3F;
        outframe.buf[1]=0x00;  
        outframe.buf[2]=0x00;
        outframe.buf[3]=0x00;
        outframe.buf[4]=0x00;
        outframe.buf[5]=0x00;
        outframe.buf[6]=0x00;
        outframe.buf[7]=0x00;
    Can1.write(MB1,outframe);

     Serial.println("Sent, status is ");
    Can1.mailboxStatus(); 
}
void deFAULT()
{
  //Returns module to original defaults  
  Serial.println("Default called, status is");
  Can1.mailboxStatus();

  STOP();
  delay(1000);
           
  CAN_message_t outframe;
    outframe.id =0x411;      // Set our transmission address ID
        outframe.len = 8;       // Data payload 8 bytes
        outframe.flags.extended = 0; // Extended addresses  0=11-bit1=29bit
        outframe.flags.remote=0;    //<--- THIS MUST BE SET TO ZERO. IF YOUR SENSOR IS IGNORING YOU CHECK THIS BIT
        outframe.buf[0]=0x3D;
        outframe.buf[1]=0x00;  
        outframe.buf[2]=0x00;
        outframe.buf[3]=0x00;
        outframe.buf[4]=0x00;
        outframe.buf[5]=0x00;
        outframe.buf[6]=0x00;
        outframe.buf[7]=0x00;
    Can1.write(MB1,outframe);

    delay(1000);
    sendSTORE();
    delay(1000);
    START();
    delay(1000);

    Serial.println("Done, status is ");
    Can1.mailboxStatus(); 
}
void sendSTORE()
{
  Serial.println("sendSTORE called, status is ");
  Can1.mailboxStatus();

  //SEND STORE///////
  CAN_message_t outframe;
  outframe.id = 0x411;      // Set our transmission address ID
        outframe.len = 8;       // Data payload 8 bytes
        outframe.flags.extended = 0; // Extended addresses  0=11-bit1=29bit
        outframe.flags.remote=0;    //<--- THIS MUST BE SET TO ZERO. IF YOUR SENSOR IS IGNORING YOU CHECK THIS BIT
        outframe.buf[0]=0x32;
        outframe.buf[1]=0x00;  
        outframe.buf[2]=0x00;
        outframe.buf[3]=0x00;
        outframe.buf[4]=0x00;
        outframe.buf[5]=0x00;
        outframe.buf[6]=0x00;
        outframe.buf[7]=0x00;
    Can1.write(MB1,outframe);

    Serial.println("Sent, status is ");
    Can1.mailboxStatus();
   
        
}

void initialize()
{
  

  Sensor.firstframe=false;
  STOP();
  delay(2000);
  for(int i=0;i<9;i++)
  {
  
      Serial.println("initialization \n");
      CAN_message_t outframe; 
      outframe.id =1041;// 0x411;      // Set our transmission address ID
        outframe.len = 8;       // Data payload 8 bytes
        outframe.flags.extended = 0; // Extended addresses  0=11-bit1=29bit
        outframe.flags.remote=0;     //<--- THIS MUST BE SET TO ZERO. IF YOUR SENSOR IS IGNORING YOU CHECK THIS BIT
        outframe.buf[0]=(0x20+i); //loop over each param to reset
        outframe.buf[1]=0x42; //I think this sets Little Endian and Cyclic Running  
        outframe.buf[2]=0x00;
        outframe.buf[3]=0xFA; //(0x60+(i*18)); //This sets varying transmission interevals. Not sure if we want this or just the same value for each message
        outframe.buf[4]=0x00;
        outframe.buf[5]=0x00;
        outframe.buf[6]=0x00;
        outframe.buf[7]=0x00;

     Can1.write(MB1,outframe);
    Can1.mailboxStatus();
     delay(2000);
     Can1.mailboxStatus();
     
     delay(2000);
      
       sendSTORE();
       delay(2000);
     }
    
     START();
                      
}
Post Reply