NGC Canbus Communication Specification: Illustration 1: Rj-45 Pinout Diagram
NGC Canbus Communication Specification: Illustration 1: Rj-45 Pinout Diagram
The NetGain Controls CANbus operates at 250Kbps. Refer to CAN2.0B for the data link layer, as well
as SAE J1939. The complete NGC specification is described in this document.
Note: All numbers shown in this document are in decimal format unless otherwise specified with the
prefixes “0x” for hexadecimal or “0b” for binary.
Physical Layer
The recommended connector is RJ-45. Twisted pair cable should be used. The pinouts for the
connectors are shown in the following illustration and table.
The WD1 RJ-45 connectors have routed to their pin 8 the WD1 constant 12V supply. Both these power
pins are protected by a resettable fuse and can each provide up to 150mA.
CAN Identifier
CAN extended frame is used. The following table and descriptions detail the use of the extended
frame.
Identifier – 11 bits Identifier Extension – 18 bits
Priority R DP PDU Format (PF) PF PDU Specific (PS) Source Address (SA)
3 2 1 1 1 8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1
28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
The above table does not show the SRR and IDE bits that separate the lower two bits of PF from the
higher 6 bits. The SRR and IDE bits are defined by CAN2.0B, and are not affected by the J1939
specification. The major deviation in the NGC specification from J1939 is the lack of address
claiming. Addresses are hard-coded into the devices. It is possible that future revisions will include
provision for network start-up as per J1939.
Priority
• 3 = High
• 5 = Medium
• 8 = Low
R: Reserved
This parameter indicates whether the message is destination specific or broadcast. If the message is
destination specific (PDU1), then PDU Format is 0-239. Otherwise, it is broadcast (PDU2), and falls in
the ranges of 240-255. The majority of these values (both PDU1 and PDU2) are assigned under
J1939 by the SAE. For destination specific, only 239 can be used for manufacturer-specific
assignments. For broadcast, 255 is available for manufacturer-specific assignments. The bulk of the
messages sent by NGC devices are broadcast.
The source address is the address of the device placing the message on the bus.
Network Addresses
The following devices are currently supported by the NetGain Controls CANbus protocol:
Messages
The following table lists the possible messages currently used on the NGC CANbus network. Detailed
descriptions of each message are listed after the table.
Destination specific message 0: WarP-Drive Error Clear. PS is usually the WarP-Drive address (88), and the SA is usually the IM
address (1).
Broadcast message 3.2: Short Command 2. Used to set new motor current limit in controller. SA can be any device intended for
setting new motor current limits, e.g. BMS.
Broadcast message 3.3: Short Command 3. Used to set new battery current limit in controller. SA can be any device intended for
setting new battery current limit, e.g. BMS.
Broadcast message 3.4: Short Command 4. Used to set new motor voltage limit in controller. SA can be any device intended for
setting new motor voltage limit.
Broadcast message 3.5: Short Command 5. Used to set desired throttle unit in controller. SA can be any device intended for
setting new throttle unit. Consult factory before attempting to use throttle unit other than those listed in this section.
Broadcast message 3.11: Short Command 11. Used to set a the leakage options in the controller.
Byte 0 11
Byte 1 Leakage option for leakage between pack 0: Take no action with detected leakage
and chassis 1: Warn on detected leakage
2: Halt operation on detected leakage
Broadcast message 3.12: Short Command 12. Request Non-Interval WarP-Drive Data
Broadcast message 3.13: Short Command 13. Used to set a new motor current ramp value in the controller.
Byte 0 13
Byte 1 Motor current ramp, 1-250 Motor current ramp in amps/10msec.
255 4 88 5 0 0 100
Data Bytes: 8
Position Data Comments
255 5 88 5 0 0 100
Data Bytes: 8
Position Data Comments
Byte 0 Throttle channel 1, high byte Throttle channel value is out of 1023.
Voltage is (value / 1023) * 5.0.
Byte 1 Throttle channel 2, low byte
Byte 2 Throttle channel 2, high byte
Byte 3 Throttle channel 1, low byte
Byte 4 Chillplate temperature Sent in Celsius as signed 8-bit value.
Byte 5 Filmcap temperature Sent in Celsius as signed 8-bit value.
Byte 6 Brake input 0: brake off
1: brake on
Byte 7 Reverse input 0: forward direction
1: reverse direction
255 6 88 5 0 0 100
Data Bytes: 5
Position Data Comments
255 7 88 5 0 0 100
Data Bytes: 8
Position Data Comments
255 9 88 5 0 0 By Request
Data Bytes: 8
Position Data Comments
255 10 88 5 0 0 By Request
Data Bytes: 8
Position Data Comments
255 11 88 5 0 0 By Request
Data Bytes: 8
Position Data Comments
Broadcast message 12: WarP-Drive Reverse Configured Limits, Brake, Reverse, and Motor Current Ramp.
255 12 88 5 0 0 By Request
Data Bytes: 6
Position Data Comments
255 13 28 5 0 0 100
Data Bytes: 8
Position Data Comments
Example Code
The following shows some code snippets that might be useful in understanding how the protocol is
applied. Please note, the code is not intended to be complete, but only to provide some assistance in
understanding how to implement J1939. Requests for additional pieces of code related to the following
examples will not be fulfilled.
#define QUEUE_FULL 0
#define SUCCESS 1
#ifndef TX_BUFFER_SIZE
#define TX_BUFFER_SIZE 8 // adjust if the tx_buffer gets clogged - unlikely unless trying to run too
much data through the pipe
#endif
#ifndef RX_BUFFER_SIZE
#define RX_BUFFER_SIZE 8 // adjust if messages are lost - unlikely if polled frequently enough
#endif
#define LOW_PRIORITY 8
#define MED_PRIORITY 5
#define HIGH_PRIORITY 3
#define NUM_BUFFER_BYTES 15
#define NUM_OVERHEAD_BYTES 7 // all bytes in the message prior to the data
#define MESSAGE_PRIORITY 0
#define MESSAGE_RESERVED 1
#define DATA_PAGE 2
#define PROTOCOL_FORMAT 3
#define PROTOCOL_SPECIFIC 4
#define MESSAGE_SOURCE_ADDRESS 5
#define MESSAGE_NUM_DATA_BYTES 6
#define MESSAGE_DATA0 7
#define MESSAGE_DATA1 8
#define MESSAGE_DATA2 9
#define MESSAGE_DATA3 10
#define MESSAGE_DATA4 11
#define MESSAGE_DATA5 12
#define MESSAGE_DATA6 13
#define MESSAGE_DATA7 14
// PDU Format
#define PROPRIETARY_PDU1 239
// PDU Specific
// Destination Address
// Data byte 0
#define CLEAR_ERRORS 0
#define CLEAR_WARNINGS 1
// PDU Format
#define PROPRIETARY_PDU2 255
// PDU Specific
#define IM_TIME_DATE 0
#define IM_FIRMWARE_DATA_PACKET 1
#define WD_CAPABILITY_UPGRADE_BYTE 2
#define SHORT_COMMANDS 3 // the command is in byte 0, and data specific to the
command can be placed in the other bytes.
// Data byte 0
#define IM_NEW_MOTOR_CURRENT_LIMIT 2
#define IM_NEW_BATTERY_CURRENT_LIMIT 3
#define IM_NEW_MOTOR_VOLTAGE_LIMIT 4
#define IM_SET_INSTALLED_THROTTLE 5
#define WD_NO_WARNINGS 11
#define WD_NO_ERRORS 12
#define IM_NEW_MOTOR_CURRENT_RAMP 13
#define SPEED_SENSORS 14
#define WD_POWER_DATA 4
#define WD_SENSOR_RELATED 5
#define WD_SUPPLY_VOLTAGES 6
#define WD_DTC_WARNING 7
#define WD_DTC_ERROR 8
#define WD_LIMITS_VOLTAGE_CURRENT 9
#define WD_SYSTEM_INFO_A 10
#define WD_SYSTEM_INFO_B 11
#define WD_LIMITS_VOL_CUR_REV 12
int level_1_tx_buffer[NUM_BUFFER_BYTES];
int level_0_tx_buffer[TX_BUFFER_SIZE][NUM_BUFFER_BYTES];
int tx_buffer_next_in;
int tx_buffer_next_out;
int tx_queue_count;
int level_1_rx_buffer[NUM_BUFFER_BYTES];
int level_0_rx_buffer[RX_BUFFER_SIZE][NUM_BUFFER_BYTES];
int rx_buffer_next_in;
int rx_buffer_next_out;
int rx_queue_count;
int1 tx_buffer_full;
int1 rx_buffer_full;
received_messages_have_been_dropped = FALSE;
}
for(i=0;i<(NUM_OVERHEAD_BYTES +
level_1_tx_buffer[MESSAGE_NUM_DATA_BYTES]);i++) level_0_tx_buffer[tx_buffer_next_in][i] =
level_1_tx_buffer[i];
temp_index=tx_buffer_next_in;
tx_buffer_next_in=(tx_buffer_next_in+1) % TX_BUFFER_SIZE;
tx_queue_count++;
if(rx_queue_count > 0) { // this should be checked by the calling application, but verify here
for(i=0;i<level_0_rx_buffer[rx_buffer_next_out][MESSAGE_NUM_DATA_BYTES]
+NUM_OVERHEAD_BYTES;i++) level_1_rx_buffer[i] = level_0_rx_buffer[rx_buffer_next_out][i];
rx_buffer_next_out=(rx_buffer_next_out+1) % RX_BUFFER_SIZE;
rx_queue_count--;
// produce the 29-bit ID (package in a 32 bit number) from the given message info
int32 form_id() {
int8 high_byte;
return make32(high_byte,level_0_tx_buffer[tx_buffer_next_out][PROTOCOL_FORMAT],
level_0_tx_buffer[tx_buffer_next_out][PROTOCOL_SPECIFIC],
level_0_tx_buffer[tx_buffer_next_out][MESSAGE_SOURCE_ADDRESS]);
}
void parse_id(int32 the_id) {
level_0_rx_buffer[rx_buffer_next_out][MESSAGE_SOURCE_ADDRESS]=make8(the_id,0);
level_0_rx_buffer[rx_buffer_next_out][PROTOCOL_SPECIFIC]=make8(the_id,1);
level_0_rx_buffer[rx_buffer_next_out][PROTOCOL_FORMAT]=make8(the_id,2);
level_0_rx_buffer[rx_buffer_next_out][DATA_PAGE]=0x01 & make8(the_id,3);
}
// transmit
if(tx_queue_count > 0 && can_tbe() ) { // there's data to transmit and room to send it
if( can_putd(form_id(),
&level_0_tx_buffer[tx_buffer_next_out][MESSAGE_DATA0], // data bytes beginning
location
level_0_tx_buffer[tx_buffer_next_out][MESSAGE_NUM_DATA_BYTES], // number
of data bytes
level_0_tx_buffer[tx_buffer_next_out][MESSAGE_PRIORITY], // priority
1, // extended ID
0 // remote frame - not currently using
)) {
tx_queue_count--;
// receive
if( can_kbhit() ) {
if(rx_buffer_full) received_messages_have_been_dropped = TRUE;
else {
can_getd(rx_id, &level_0_rx_buffer[rx_buffer_next_in][MESSAGE_DATA0], rx_len, rxstat);
temp_index=rx_buffer_next_in;
rx_buffer_next_in=(rx_buffer_next_in+1) % RX_BUFFER_SIZE;
Sending of data:
level_1_tx_buffer[MESSAGE_PRIORITY] = MED_PRIORITY;
level_1_tx_buffer[MESSAGE_RESERVED] = 0;
level_1_tx_buffer[DATA_PAGE] = 0;
level_1_tx_buffer[PROTOCOL_FORMAT] = PROPRIETARY_PDU2;
level_1_tx_buffer[MESSAGE_SOURCE_ADDRESS] = 88;
level_1_tx_buffer[PROTOCOL_SPECIFIC] = WD_POWER_DATA;
level_1_tx_buffer[MESSAGE_NUM_DATA_BYTES] = 8;
level_1_tx_buffer[MESSAGE_DATA0] = make8(motor_amps_averaged,1);
level_1_tx_buffer[MESSAGE_DATA1] = make8(motor_amps_averaged,0);
level_1_tx_buffer[MESSAGE_DATA2] = make8(pack_voltage,1);
level_1_tx_buffer[MESSAGE_DATA3] = make8(pack_voltage,0);
level_1_tx_buffer[MESSAGE_DATA4] = make8(drive,1);
level_1_tx_buffer[MESSAGE_DATA5] = make8(drive,0);
level_1_tx_buffer[MESSAGE_DATA6] = controller_state;
level_1_tx_buffer[MESSAGE_DATA7] = last_halt_reason;
Receiving of data:
wdp_poll();
if(rx_queue_count > 0) {
wdp_dequeue();
if(level_1_rx_buffer[PROTOCOL_FORMAT] == PROPRIETARY_PDU2) {
if(level_1_rx_buffer[PROTOCOL_SPECIFIC] == WD_SUPPLY_VOLTAGES ) {
supply_dcin_voltage = level_1_rx_buffer[MESSAGE_DATA0];
supply_15V_a_voltage = level_1_rx_buffer[MESSAGE_DATA1];
supply_15V_b_voltage = level_1_rx_buffer[MESSAGE_DATA2];
supply_5V_a_voltage = level_1_rx_buffer[MESSAGE_DATA3];
supply_5V_b_voltage = level_1_rx_buffer[MESSAGE_DATA4];
}
}
}
Revision History:
2/27/12, RJB: Fixed a mistake in the identifier description where “bytes” was used where “bits” should
have been.
8/29/11, RJB
• Fixed error in listing of DTCs related to 15V and 5V power supplies. Added leakage error for
WD2.
• Added WD2 addresses.
8/24/11, RJB: Added broadcast message 3.11 for changing leakage options. Also added byte 6 to
broadcast message 12 for indicating what leakage option is set in the industrial controller.
12/13/10, RJB
11/29/10, RJB: Broadcast messages 7 and 8 changed to transmit 4 bytes each, containing the DTCs
which are bit representations of the error codes. Previously, each byte contained a single DTC.
Messages 3.11 and 3.12 are now available as the no error message is not required.
11/15/10, RJB: Corrected source address on broadcast messages 4-12 from “1, others” to “88”.
9/28/10, RJB: Prior to this date, all data transmitted from the controller with multiple bytes was sent
low byte first, then high byte. All received data was accepted in the opposite order, high byte first, then
low byte. Now, all data containing multiple bytes is transmitted high byte(s) first, low byte(s) last.