CAN Bus
The CAN (Controller Area Network) interface provides robust communication for automotive and industrial applications. Supports standard and extended CAN IDs with configurable filtering and optional 120Ω termination resistors.
As described in the introduction, industrial communication interfaces share GPIOs, meaning only one feature can be enabled at a given time: Either regular GPIOs, or one of the industrial communication interfaces.
The CAN bus and RS485_0 share the same GPIO pins for communication. Therefore, only one of these interfaces can be enabled at a time.
When enabling either the CAN bus or RS485_0, the shared GPIO pins will be automatically configured for the selected interface.
In order to avoid any confusion, we recommend following this standard procedure when using an industrial communication port:
- Start by enabling the port
- Disable the port when done using it.
If there are any conflicts (e.g. enabling an interface before disabling another one that uses the same resources), an error will be raised during the configure() phase, allowing you to easily detect and correct any conflicts.
Quick Start
The CAN bus supports enabling/disabling, termination resistance, setting message acceptance filters, transmitting, and receiving data.
- NodeJS
- Python
import AT1000 from '@ikalogic/at1000';
let testers = await AT1000.findDevices(500); // Find the device with serial number 12345
let tester = testers[0]; // Target the first detected device
let can = tester.com.can(0); // Access CAN bus 0 on AT1000
// Enable CAN bus on AT1000
let can_cfg_result = await can.configure(
{
baud_rate:500000,
enabled : true,
termination_resistors:true,
rx_filters:[{
mask:0xFF9F,
filter:0x25A,
extended_id: false
}]
});
// Start receiving CAN messages
await can.start_rx(); // Updated to use the 'can' variable
// Transmit a CAN message
await can.tx({can_id:0x123, data:[0x11, 0x22, 0x33, 0x44]});
//add delay of 100ms
await new Promise(resolve => setTimeout(resolve, 100));
// Receive messages (returns an array of objects containing ID & data)
let data_rx = await can.rx();
// Print received messages
data_rx.forEach(msg => {
console.log(`CAN Message ID: ${msg.id}, Data: ${msg.data}`);
});
// Disable CAN bus receiving
await can.stop_rx();
from ikalogic_at1000 import AT1000
import asyncio
testers = await AT1000.find_devices(500) # Find the device with serial number 12345
tester = testers[0] # Target the first detected device
can = tester.com.can(0) # Access CAN bus 0 on AT1000
# Enable CAN bus on AT1000
can_cfg_result = await can.configure(
{
'baud_rate':500000,
'enabled' : True,
'termination_resistors':True,
'rx_filters':[{
'mask':0xFF9F,
'filter':0x25A,
'extended_id': False
}]
})
# Start receiving CAN messages
await can.start_rx() # Updated to use the 'can' variable
# Transmit a CAN message
await can.tx({'can_id':0x123, 'data':[0x11, 0x22, 0x33, 0x44]})
# add delay of 100ms
await asyncio.sleep(0.1)
# Receive messages (returns an array of objects containing ID & data)
data_rx = await can.rx()
# Print received messages
for msg in data_rx:
print(f"CAN Message ID: {msg['id']}, Data: {msg['data']}")
# Disable CAN bus receiving
await can.stop_rx()
API Reference
Accessing CAN
- NodeJS
- Python
const can = tester.com.can(id);
can = tester.com.can(id)
Parameters:
id(number): The CAN interface identifier (0-based index)
Returns: Can instance
Methods
configure(config)
Configures the CAN interface parameters without changing the enabled state.
- NodeJS
- Python
await can.configure({
baud_rate: 500000,
termination_resistors: true,
rx_filter: {
extended_id: false,
id: 0x100,
mask: 0x7FF
}
});
await can.configure({
'baud_rate': 500000,
'termination_resistors': True,
'rx_filter': {
'extended_id': False,
'id': 0x100,
'mask': 0x7FF
}
})
Parameters:
config(CanConfigPatch): Configuration object with optional properties:enabled(boolean, optional): Enable or disable the interfacebaud_rate(number, optional): CAN baud rate (see valid values below)termination_resistors(boolean, optional): Enable/disable 120Ω termination resistorsrx_filter(object, optional): Receive filter configuration:extended_id(boolean): Use extended (29-bit) or standard (11-bit) IDsid(number): Filter ID valuemask(number): Filter mask (0 = don't care, 1 = must match)
Valid baud rates: 5000, 10000, 20000, 31250, 33000, 40000, 50000, 80000, 83300, 95000, 100000, 125000, 200000, 250000, 500000, 1000000
Returns: Promise<CanConfig> - The updated configuration
Exceptions:
- Throws validation error if
baud_rateis not one of the valid values - Throws validation error if parameters don't match expected types
enable([config])
Enables the CAN interface and optionally applies configuration.
- NodeJS
- Python
await can.enable({
baud_rate: 250000,
termination_resistors: true
});
await can.enable({
'baud_rate': 250000,
'termination_resistors': True
})
Parameters:
config(CanEnableConfig, optional): Optional configuration to apply:baud_rate(number, optional): CAN baud rate (see valid values above)termination_resistors(boolean, optional): Enable/disable termination resistorsrx_filter(object, optional): Receive filter configuration
Returns: Promise<CanConfig> - The updated configuration with enabled: true
Exceptions:
- Throws validation error if
baud_rateis not one of the valid values
disable()
Disables the CAN interface.
- NodeJS
- Python
await can.disable();
await can.disable()
Returns: Promise<CanConfig> - The updated configuration with enabled: false
tx(data)
Transmits a CAN frame.
- NodeJS
- Python
// Send standard CAN frame
await can.tx({
can_id: 0x123,
data: [0x01, 0x02, 0x03, 0x04]
});
// Send frame with 8 bytes (maximum)
await can.tx({
can_id: 0x456,
data: [0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80]
});
# Send standard CAN frame
await can.tx({
'can_id': 0x123,
'data': [0x01, 0x02, 0x03, 0x04]
})
# Send frame with 8 bytes (maximum)
await can.tx({
'can_id': 0x456,
'data': [0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80]
})
Parameters:
data(CanFrame): CAN frame to transmit:can_id(number): CAN identifier (11-bit: 0-0x7FF, 29-bit: 0-0x1FFFFFFF)data(number[]): Data bytes (0 to 8 bytes, each 0-255)
Returns: Promise<CanFrame> - The transmitted frame
Exceptions:
- Throws validation error if
dataarray length is not between 0 and 8 - Throws validation error if any byte value is outside the range [0, 255]
start_rx()
Starts buffering received CAN frames.
- NodeJS
- Python
await can.start_rx();
await can.start_rx()
Returns: Promise<void>
Note: Frames matching the configured filter will be buffered and can be retrieved using rx().
stop_rx()
Stops buffering received CAN frames.
- NodeJS
- Python
await can.stop_rx();
await can.stop_rx()
Returns: Promise<void>
rx()
Reads all buffered CAN frames received since start_rx() was called.
- NodeJS
- Python
const frames = await can.rx();
for (const frame of frames) {
console.log(`ID: 0x${frame.can_id.toString(16)}, Data:`, frame.data);
}
frames = await can.rx()
for frame in frames:
print(f"ID: 0x{frame['can_id']:x}, Data: {frame['data']}")
Returns: Promise<CanFrame[]> - Array of received CAN frames
Note: This method returns all frames accumulated since the last start_rx() call and clears the buffer.
Types
CanConfig
Complete configuration object returned by configuration methods.
{
enabled: boolean,
baud_rate: number, // Valid baud rate from list
termination_resistors: boolean,
rx_filter: {
extended_id: boolean,
id: number,
mask: number
}
}
CanConfigPatch
Partial configuration for the configure() method. All properties are optional.
{
enabled?: boolean,
baud_rate?: number, // Valid baud rate from list
termination_resistors?: boolean,
rx_filter?: {
extended_id: boolean,
id: number,
mask: number
}
}
CanEnableConfig
Optional configuration for the enable() method.
{
baud_rate?: number, // Valid baud rate from list
termination_resistors?: boolean,
rx_filter?: {
extended_id: boolean,
id: number,
mask: number
}
}
CanFrame
CAN message frame structure.
{
can_id: number, // CAN identifier
data: number[] // Data bytes (0-8 bytes)
}
Usage Example
- NodeJS
- Python
import AT1000 from '@ikalogic/at1000';
// Find and connect to device
const devices = await AT1000.findDevices(500);
const tester = devices[0];
// Access CAN interface 0
const can = tester.com.can(0);
// Enable with 500k baud rate and receive filter
await can.enable({
baud_rate: 500000,
termination_resistors: true,
rx_filter: {
extended_id: false,
id: 0x100,
mask: 0x700 // Accept IDs 0x100-0x1FF
}
});
// Start receiving frames
await can.start_rx();
// Send a frame
await can.tx({
can_id: 0x123,
data: [0x01, 0x02, 0x03, 0x04]
});
// Wait for responses
await new Promise(resolve => setTimeout(resolve, 100));
// Read received frames
const frames = await can.rx();
console.log("Received frames:", frames);
// Stop receiving
await can.stop_rx();
// Disable interface
await can.disable();
from ikalogic_at1000 import AT1000
import asyncio
# Find and connect to device
devices = await AT1000.find_devices(500)
tester = devices[0]
# Access CAN interface 0
can = tester.com.can(0)
# Enable with 500k baud rate and receive filter
await can.enable({
'baud_rate': 500000,
'termination_resistors': True,
'rx_filter': {
'extended_id': False,
'id': 0x100,
'mask': 0x700 # Accept IDs 0x100-0x1FF
}
})
# Start receiving frames
await can.start_rx()
# Send a frame
await can.tx({
'can_id': 0x123,
'data': [0x01, 0x02, 0x03, 0x04]
})
# Wait for responses
await asyncio.sleep(0.1)
# Read received frames
frames = await can.rx()
print(f"Received frames: {frames}")
# Stop receiving
await can.stop_rx()
# Disable interface
await can.disable()
Common Patterns
CAN Filter Configuration
- NodeJS
- Python
// Accept all standard IDs
await can.configure({
rx_filter: {
extended_id: false,
id: 0x000,
mask: 0x000 // All bits are "don't care"
}
});
// Accept only specific ID (0x123)
await can.configure({
rx_filter: {
extended_id: false,
id: 0x123,
mask: 0x7FF // All bits must match
}
});
// Accept range of IDs (0x200-0x2FF)
await can.configure({
rx_filter: {
extended_id: false,
id: 0x200,
mask: 0x700 // Match upper 3 bits
}
});
// Extended (29-bit) ID filtering
await can.configure({
rx_filter: {
extended_id: true,
id: 0x12345678,
mask: 0x1FFFFFFF
}
});
# Accept all standard IDs
await can.configure({
'rx_filter': {
'extended_id': False,
'id': 0x000,
'mask': 0x000 # All bits are "don't care"
}
})
# Accept only specific ID (0x123)
await can.configure({
'rx_filter': {
'extended_id': False,
'id': 0x123,
'mask': 0x7FF # All bits must match
}
})
# Accept range of IDs (0x200-0x2FF)
await can.configure({
'rx_filter': {
'extended_id': False,
'id': 0x200,
'mask': 0x700 # Match upper 3 bits
}
})
# Extended (29-bit) ID filtering
await can.configure({
'rx_filter': {
'extended_id': True,
'id': 0x12345678,
'mask': 0x1FFFFFFF
}
})
Request-Response Pattern
- NodeJS
- Python
await can.start_rx();
// Send request
await can.tx({
can_id: 0x7DF, // OBD-II broadcast
data: [0x02, 0x01, 0x0C] // Request engine RPM
});
// Wait for response
await new Promise(resolve => setTimeout(resolve, 50));
const frames = await can.rx();
for (const frame of frames) {
if (frame.can_id >= 0x7E8 && frame.can_id <= 0x7EF) {
console.log("ECU response:", frame.data);
}
}
await can.stop_rx();
import asyncio
await can.start_rx()
# Send request
await can.tx({
'can_id': 0x7DF, # OBD-II broadcast
'data': [0x02, 0x01, 0x0C] # Request engine RPM
})
# Wait for response
await asyncio.sleep(0.05)
frames = await can.rx()
for frame in frames:
if 0x7E8 <= frame['can_id'] <= 0x7EF:
print(f"ECU response: {frame['data']}")
await can.stop_rx()
Continuous Monitoring
- NodeJS
- Python
// Start monitoring
await can.start_rx();
// Poll periodically
const monitor = setInterval(async () => {
const frames = await can.rx();
for (const frame of frames) {
console.log(`[${Date.now()}] ID: 0x${frame.can_id.toString(16).padStart(3, '0')}, Data:`,
frame.data.map(b => b.toString(16).padStart(2, '0')).join(' '));
}
}, 100);
// Stop after some time
setTimeout(async () => {
clearInterval(monitor);
await can.stop_rx();
}, 10000);
import asyncio
import time
# Start monitoring
await can.start_rx()
# Poll periodically
async def monitor():
start_time = time.time()
while time.time() - start_time < 10: # Run for 10 seconds
frames = await can.rx()
for frame in frames:
data_str = ' '.join(f'{b:02x}' for b in frame['data'])
print(f"[{int(time.time()*1000)}] ID: 0x{frame['can_id']:03x}, Data: {data_str}")
await asyncio.sleep(0.1)
await can.stop_rx()
await monitor()
Common Baud Rates
- 1000000 (1 Mbps): High-speed CAN, short distance
- 500000 (500 kbps): Most common for automotive CAN
- 250000 (250 kbps): Industrial applications
- 125000 (125 kbps): Longer distance CAN networks
- 83300 (83.3 kbps): CANopen default