Wednesday, September 11, 2019

J1939 CAN bus and the Thomson Electak HD

For awhile I wanted to see if the standard CAN bus chip set and Arduino programs could interface with J1939 CAN bus.  I was betting it could but wanted to prove it.  I got the opportunity to borrow an industrial actuator that utilized J1939 protocol.  It is an Thomson Electrak HD actuator with the CAN bus option.  Here is a link to the product page of the one I used. (link)  It was part number HD24B045-0150CNO2MNS.


After a little research it appears J1939 uses the same physical layer (wiring, pulses etc.) as the standard automotive high speed CAN bus network.  Which meant the hardware should work.  But would the standard Arduino programs be able to decode the data stream?  Long story short.  Yes it can.

The hardware I used was an Arduino mega with a can bus shield based on the MCP2515 and MCP2551 chip set.  This is pretty typical for Arduino CAN bus shields.  Like the spark fun or seeed studio shields which I’ve used in the past.  Though the shield I used for this project was one I had previously put together from components.  And of course, I included two 120 ohm terminating resistors.

First thing I found trying to read the data stream was that the baud rate of the Thomson HD actuator was 250 kbps.  After some looking it appears that J1939 may support both 250 and 500 kbps.  But perhaps 250 kbps is more popular.  Either way once I set the baud rate to 250 kbps I could capture a CAN data stream for a few seconds every time the unit was powered up.

Decoding the Actuators feedback message

J1939 uses extended ID format.  Most of the CAN streams I’ve messed with were standard ID format.  But the extended format is not a problem and it just shows as a longer ID.  I had downloaded the instruction manual for the actuator and it specified what the data was in the message packets.  But it assumed one has in-depth knowledge of J1939.  Which I didn’t.  I wasn’t sure if the data was big endian or little endian or what.  And how that would work with the Arduino CAN shield software.  Best way to know is to look at sample the data.  Which I did.

Turns out the bytes of the message ID come out in the correct order.  But the data bytes need to be reversed.  Guess that would be the byte order is little endian.  Or in other words, the first byte received is the smallest value in the message.  If writing the bytes down as they come out we'd do it from right to left instead of left to right.  The order of the bits within each byte was standard order.    Probably easier to see with an example.


Say we get the CAN message from the actuator of:  ID: 19EFFF13,   Data: DD 01 80 01 C1 FF FF

Decoding the ID
  • The first 2 bytes: 0x19EF is a combination of a few things.  But in this case it tells us it's the feedback message from the actuator. (Priority 6 and PDU of 0x01EF00)
  • The third byte: 0xFF is the destination address.  FF is a broadcast message.
  • The last byte: 0x13 is the source ID.  In this case the address of the actuator.
Decoding the Data bytes

Once the bit and byte order were sorted out setting up a program to read and decode the messages was just a matter of extracting and combining the right bits and conversion factors as described in the actuator’s instruction manual.  Below is a visual showing how the bytes are decoded (click to see it bigger).

Yes the Arduino with a CAN shield can read the J1939 can bus data.  But can it talk to the actuator and make it move? 

Sending Commands to the actuator:

Between the actuators instruction manual and some J1939 info I found on the net, came to the conclusion that the ID needed to command the actuator would be 18EFxxyy.  The xx being the actuators address (default is 0x13) and yy being the address of my sending device.  The address of the sending device looked like it could be anything except the highest couple of values.  I chose 0xAA for my controller address.  The resulting ID for my command message was then 0x18EF13AA.  Now for data bytes.

Similar to decoding the feedback message.  Creating the command message was done in a simlar manner.  In the command message there are several attributes to be chosen.  I’ll give a brief description of each.
  • The position desired for the actuator to move to.  Any value could be chosen between zero and it’s maximum stroke.  But it turned out it needed to be a greater than about 2mm to 5mm from it’s current position for it to actually make a move. 
  • The maximum current the actuator is allowed to use.  This value had to be in the range 1 Amp to the maximum amp draw rating of the actuator.  If not in this range the actuator would respond with an error.  And if more current was used during a move then the value set here, the actuator would throw an error and stop.  Found it best to just set this value to the Max value for the unit. In my case 12.5 Amps and leave it be.  But this could be a trick feature if trying to do some load limiting logic.
  • Speed desired.  This value could be from 20% to 100%.  This was a fun one to play with.  Changing it could make the actuator move fast or slow.  I even tried changing the speed while it was already moving. And that worked too.
  • Motion enable.  This flag has to be 1 for the actuator to move.  Really the only reason to set it to zero is to clear errors or if just sending a message to keep the actuator awake and not wanting to risk moving it.
 The following image shows how the values are converted to the data bytes. (click to see it bigger)
 

To command the actuator with the above settings I’d send ID:
18EF13AA with Data A1 42 1F 15 00 00 00.

Attempting to send different values I found that I’d often get error flags in the actuator feedback message and at times the actuator wouldn’t move.  To clear the error messages I’d have to send a command message with valid parameters and the motion flag set to “0”.  Then I could go back to trying sending the message with the move flag set to “1” to move it.  After playing with it for a bit I found a setup that would work and keep everything happy. 

Here are the basics of what I found keeps everything happy

The parameters must be within the proper ranges in the command message.
  • Amps: between 1 and the max amps for the unit.  (Suggest setting the amps to the max of the unit.)
  • Position: From 0 mm to the max stroke of the unit (150 mm  in my case).
  • Speed: Between 20% and 100%
Don’t let the actuator fall asleep unintentionally
  • The actuator requires constant bus traffic to stay awake (destination FF or the actuator’s address)
  • If the actuator falls asleep and then receives a message it will go through Address claim procedure again.
  • Sending a command message every 2s or less will keep it awake.
Reply to the address claim messages (ID: 18EEFF13, Data: 01 00 60 44 00 FF 00 80)
  • When the actuator wakes or is powered up it sends and address claim message. If it is not followed by an command message within 400 ms an overload flag will automatically set.  Then the error will have to be cleared before the actuator can move.
  • Seems the best practice is to reply to all address claims from the actuator immediately with an command message.  Then keep sending a valid command message every 2s or less.  Probably a good idea to set motion flag zero on initial responses to ensure no movement is made unexpectedly.  And as a bonus the message will clear any errors if there were any.
  • The standard address claim message from the actuator is ID: 18EEFF13, Data: 01 00 60 44 00 FF 00 80.  The last byte of the ID could change if there are address conflicts or if the actuator is hardwired to a different address.  But the data bytes of the address claim are ALWAYS the same.  I set up my program to look for those data bytes and then take the last byte of the ID in that message as the address for the actuator.  Then used that address to form the ID of the commands sent to the actuator.  Oh and I have the Arduino reply to the address claim message with a command with motion set to zero right after getting the claim message. 
Using the above I could get the actuator to power up or wake up perfectly every time.  No errors or problems.

Conclusion

This was an interesting and fun little project.  With the CAN interface it was really easy to run the actuator with the Arduino.  And the variable speed feature was way cool.  Made me want to build something with one.  Maybe an Arduino controlled solar tracker?  I dunno.