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 pin alternate functions, 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.
Enabling CAN while RS485_0 is enabled, or RS485_0 while CAN is enabled, fails with 409 CONFIGURATION_CONFLICT ("Cannot enable CAN_0 while RS485_0 is enabled"). Disable the other interface first.
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), configure() or enable() fails with 409 CONFIGURATION_CONFLICT, 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
- Rust
import { AT1000 } from '@ikalogic/at1000';
let devices = await AT1000.findDevices(); // Find devices (returns host strings)
let tester = await AT1000.open(devices[0]); // Open the first detected device
await tester.reset(); // Reset the device to a known state
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_filter:{
mask:0x79F,
id: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({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 time
devices = AT1000.find_devices() # Find devices (returns host strings)
tester = AT1000.open(devices[0]) # Open the first detected device
tester.reset() # Reset the device to a known state
can = tester.com.can(0) # Access CAN bus 0 on AT1000
# Enable CAN bus on AT1000
can_cfg_result = can.configure(
baud_rate=500000,
enabled=True,
termination_resistors=True,
rx_filter={
'mask':0x79F,
'id':0x25A,
'extended_id': False
}
)
# Start receiving CAN messages
can.start_rx() # Updated to use the 'can' variable
# Transmit a CAN message
can.tx({'id':0x123, 'data':[0x11, 0x22, 0x33, 0x44]})
# add delay of 100ms
time.sleep(0.1)
# Receive messages (returns an array of objects containing ID & data)
data_rx = can.rx()
# Print received messages
for msg in data_rx:
print(f"CAN Message ID: {msg.id}, Data: {msg.data}")
# Disable CAN bus receiving
can.stop_rx()
use ikalogic_at1000::{At1000, CanConfigPatch, CanFrame, CanId, CanRxFilter};
use std::time::Duration;
let devices = At1000::find_devices(Duration::from_millis(500))?; // Find devices
let tester = At1000::open(&devices[0], None)?; // Open the first detected device
tester.reset()?; // Reset the device to a known state
let can = tester.com.can(0)?; // Access CAN bus 0 on AT1000
// Enable CAN bus on AT1000
let can_cfg_result = can.configure(CanConfigPatch {
baud_rate: Some(500_000),
enabled: Some(true),
termination_resistors: Some(true),
rx_filter: Some(CanRxFilter::Standard { id: 0x25A, mask: 0x79F }),
})?;
// Start receiving CAN messages
can.start_rx()?;
// Transmit a CAN message
can.tx(CanFrame::data(CanId::Standard(0x123), [0x11, 0x22, 0x33, 0x44]), None)?;
// add delay of 100ms
std::thread::sleep(Duration::from_millis(100));
// Receive messages (returns a vector of data and remote frames)
let data_rx = can.rx()?;
// Print received messages
for frame in data_rx {
match frame {
CanFrame::Data { id, data } => println!("CAN Message ID: {:#x}, Data: {:?}", id.raw(), data),
CanFrame::Remote { id, dlc } => println!("CAN Message ID: {:#x}, remote, dlc {}", id.raw(), dlc),
}
}
// Disable CAN bus receiving
can.stop_rx()?;
API Reference
Accessing CAN
- NodeJS
- Python
- Rust
const can = tester.com.can(id);
can = tester.com.can(id)
let id = 0;
let can = tester.com.can(id)?;
Parameters:
id(number/int/usize): The CAN interface identifier (0-based index)
Returns: Can instance / Result<Can, At1000Error> in Rust
Methods
configure(config)
Configures the CAN interface parameters without changing the enabled state.
- NodeJS
- Python
- Rust
await can.configure({
baud_rate: 500000,
termination_resistors: true,
rx_filter: {
extended_id: false,
id: 0x100,
mask: 0x7FF
}
});
can.configure(
baud_rate=500000,
termination_resistors=True,
rx_filter={
'extended_id': False,
'id': 0x100,
'mask': 0x7FF
}
)
use ikalogic_at1000::{CanConfigPatch, CanRxFilter};
let can = tester.com.can(0)?;
can.configure(CanConfigPatch {
baud_rate: Some(500_000),
termination_resistors: Some(true),
rx_filter: Some(CanRxFilter::Standard { id: 0x100, mask: 0x7FF }),
..Default::default()
})?;
Parameters:
config(CanConfigPatch): Configuration object with optional properties:enabled(boolean/bool/Option<bool>, optional): Enable or disable the interfacebaud_rate(number/int/Option<u32>, optional): CAN baud rate (see valid values below)termination_resistors(boolean/bool/Option<bool>, optional): Enable/disable 120Ω termination resistorsrx_filter(object/dict/Option<CanRxFilter>, optional): Receive filter configuration. In Rust it isCanRxFilter::Standard { id, mask }orCanRxFilter::Extended { id, mask };configurereturnsInvalidInputbefore sending for anidormaskwider than the format:extended_id(boolean/bool/CanRxFilter::Extendedvariant): Use extended (29-bit) or standard (11-bit) IDsid(number/int/u16forStandard,u32forExtended): Filter ID valuemask(number/int/u16forStandard,u32forExtended): Filter mask. A bit set to 1 must matchid, a bit set to 0 is "don't care". A mask of0accepts every frame, standard and extended. Any other mask accepts only frames of theextended_idformat (only standard frames whenextended_idisfalse, only extended frames when it istrue). The filter applies to both receive buffers of the CAN controller.
Valid baud rates: 5000, 10000, 20000, 31250, 33000, 40000, 50000, 80000, 83333, 95000, 100000, 125000, 200000, 250000, 500000, 1000000
Default configuration (after tester.reset()): disabled, baud_rate 125000, termination resistors off, rx_filter { extended_id: false, id: 0, mask: 0 } (accepts every frame).
Returns: Promise<CanConfig> / CanConfig / Result<CanConfig, At1000Error> - 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
- Throws error
409 CONFIGURATION_CONFLICTwhenenabled: trueis set while RS485_0 is enabled
enable([config])
Enables the CAN interface and optionally applies configuration.
- NodeJS
- Python
- Rust
await can.enable({
baud_rate: 250000,
termination_resistors: true
});
can.enable(
baud_rate=250000,
termination_resistors=True
)
use ikalogic_at1000::CanConfigPatch;
let can = tester.com.can(0)?;
can.enable(CanConfigPatch {
baud_rate: Some(250_000),
termination_resistors: Some(true),
..Default::default()
})?;
Parameters:
config(CanEnableConfig/CanEnableConfig/CanConfigPatch, optional): Optional configuration to apply. In Rust the patch is required (passCanConfigPatch::default()to keep the current settings), andenablesetsenabled: trueitself:baud_rate(number/int/Option<u32>, optional): CAN baud rate (see valid values above)termination_resistors(boolean/bool/Option<bool>, optional): Enable/disable termination resistorsrx_filter(object/dict/Option<CanRxFilter>, optional): Receive filter configuration
Returns: Promise<CanConfig> / CanConfig / Result<CanConfig, At1000Error> - The updated configuration with enabled: true
Exceptions:
- Throws validation error if
baud_rateis not one of the valid values - Throws error
409 CONFIGURATION_CONFLICTwhile RS485_0 is enabled
disable()
Disables the CAN interface.
- NodeJS
- Python
- Rust
await can.disable();
can.disable()
let can = tester.com.can(0)?;
can.disable()?;
Returns: Promise<CanConfig> / CanConfig / Result<CanConfig, At1000Error> - The updated configuration with enabled: false
tx(data)
Transmits a CAN frame.
- NodeJS
- Python
- Rust
// Send standard CAN frame
await can.tx({
id: 0x123,
data: [0x01, 0x02, 0x03, 0x04]
});
// Send frame with 8 bytes (maximum)
await can.tx({
id: 0x456,
data: [0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80]
});
// Send a frame with an extended (29-bit) ID and a 100 ms transmit timeout
await can.tx({ id: 0x12345678, extended_id: true, data: [0x01], timeout: 100 });
// Send several frames in one request (at most 1024)
await can.tx([
{ id: 0x123, data: [0x01, 0x02] },
{ id: 0x456, data: [0x10, 0x20] },
]);
# Send standard CAN frame
can.tx({
'id': 0x123,
'data': [0x01, 0x02, 0x03, 0x04]
})
# Send frame with 8 bytes (maximum)
can.tx({
'id': 0x456,
'data': [0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80]
})
# Send a frame with an extended (29-bit) ID and a 100 ms transmit timeout
can.tx({'id': 0x12345678, 'extended_id': True, 'data': [0x01], 'timeout': 100})
# Send several frames in one request (at most 1024)
can.tx([
{'id': 0x123, 'data': [0x01, 0x02]},
{'id': 0x456, 'data': [0x10, 0x20]},
])
use ikalogic_at1000::{CanFrame, CanId};
use std::time::Duration;
let can = tester.com.can(0)?;
// Send standard CAN frame
can.tx(CanFrame::data(CanId::Standard(0x123), [0x01, 0x02, 0x03, 0x04]), None)?;
// Send frame with 8 bytes (maximum)
can.tx(
CanFrame::data(CanId::Standard(0x456), [0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80]),
None,
)?;
// Send a frame with an extended (29-bit) ID and a 100 ms transmit timeout
can.tx(
CanFrame::data(CanId::Extended(0x12345678), [0x01]),
Duration::from_millis(100),
)?;
// Send several frames in one request (at most 1024)
can.tx(
[
CanFrame::data(CanId::Standard(0x123), [0x01, 0x02]),
CanFrame::data(CanId::Standard(0x456), [0x10, 0x20]),
],
None,
)?;
Parameters:
data(CanFrame | CanFrame[]/CanFrame | list[CanFrame]/CanFrameor aVec, slice or array ofCanFrame): CAN frame (or array of frames) to transmit:id(number/int/CanId): CAN identifier (11-bit: 0-0x7FF, 29-bit: 0-0x1FFFFFFF)extended_id(boolean/bool/CanId::Extended, optional, default:false): Use extended (29-bit) IDrtr(boolean/bool/CanFrame::Remote, optional, default:false): Remote transmission request framedlc(number/int/u8inCanFrame::Remote, optional): RTR frames only - requested data length (0 to 8). The server rejectsdlcon data frames and non-emptydataon RTR framesdata(number[]/list[int]/Vec<u8>inCanFrame::Data, optional): Data bytes (0 to 8 bytes, each 0-255)timeout(number/int/DurationorNone, second argument oftx(), optional): Transmit-only - max ms (1 to 5000) to wait for a free TX buffer and for the frame's transmission attempt to get onto the bus (server default 1000 ms). In a batch, each frame has its own timeout
Each frame is sent in one-shot mode: there is a single transmission attempt, and tx() returns once it has succeeded, which means another node acknowledged the frame. If the attempt fails (no other node acknowledged it, a bus error, or lost arbitration), tx() fails at once with 500 INTERNAL_ERROR ("Failed to send CAN_0 message: error_failtx"). Typical causes are no other node connected, missing termination, a baud rate mismatch or a wiring fault. The timeout only bounds the wait for a TX buffer, or for the attempt to get onto the bus. When it expires, the request also fails with 500 INTERNAL_ERROR and the frame is withdrawn, so it cannot leave the bus later.
In Python, the SDK's HTTP client waits at most 1 s for the device's answer. Keep the frame timeout well under 1000 ms (and a batch short enough to finish within 1 s) so the device answers before the client gives up.
Batches: one request carries at most 1024 frames. Every frame of the batch is validated before any is sent, so an invalid frame rejects the whole batch with nothing sent. Frames are then sent in order and sending stops at the first frame that fails; the error message tells how many frames went out, e.g. "Failed to send CAN_0 message 3 of 10 (2 sent successfully): …".
In Rust, a frame is either CanFrame::Data { id, data } or CanFrame::Remote { id, dlc }, and its identifier is either CanId::Standard(u16) or CanId::Extended(u32). Build frames with CanFrame::data(id, data) and CanFrame::remote(id, dlc). tx(frames, timeout) takes one frame or a batch, and a Duration from 1 ms to 5 s or None for the server default.
Returns: Promise<CanFrame | CanFrame[]> / CanFrame | list[CanFrame] / Result<Vec<CanFrame>, At1000Error> - The transmitted frame(s). Rust always returns a vector, with one frame when one frame was sent
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]
- Throws error
400 INVALID_VALUEifdlcis set on a data frame,datais non-empty on an RTR frame, or a batch has more than 1024 frames - Throws error
400 INVALID_CONFIGURATIONif the interface is disabled - Throws error
500 INTERNAL_ERRORif a frame's transmission attempt fails (e.g. no node acknowledges it), or if no TX buffer or bus access is obtained within its timeout - Rust returns
InvalidInputbefore sending for an identifier wider than its format, a payload over 8 bytes, adlcover 8, more than 1024 frames, or a timeout outside 1 ms to 5 s
start_rx()
Starts buffering received CAN frames.
- NodeJS
- Python
- Rust
await can.start_rx();
can.start_rx()
let can = tester.com.can(0)?;
can.start_rx()?;
Returns: Promise<void> / None / Result<(), At1000Error>
Note: start_rx() discards any frames still in the receive buffer. From then on, frames matching the configured filter are buffered and can be retrieved using rx().
stop_rx()
Stops buffering received CAN frames.
- NodeJS
- Python
- Rust
await can.stop_rx();
can.stop_rx()
let can = tester.com.can(0)?;
can.stop_rx()?;
Returns: Promise<void> / None / Result<(), At1000Error>
rx()
Reads all buffered CAN frames received since the last start_rx() or rx() call.
- NodeJS
- Python
- Rust
const frames = await can.rx();
for (const frame of frames) {
console.log(`ID: 0x${frame.id.toString(16)}, Data:`, frame.data);
}
frames = can.rx()
for frame in frames:
print(f"ID: 0x{frame.id:x}, Data: {frame.data}")
use ikalogic_at1000::CanFrame;
let can = tester.com.can(0)?;
for frame in can.rx()? {
match frame {
CanFrame::Data { id, data } => println!("ID: {:#x}, Data: {:?}", id.raw(), data),
CanFrame::Remote { id, dlc } => println!("ID: {:#x}, remote, dlc {}", id.raw(), dlc),
}
}
Returns: Promise<CanFrame[]> / list[CanFrame] / Result<Vec<CanFrame>, At1000Error> - Array of received CAN frames. Received RTR frames have rtr: true, dlc set to the requested length and an empty data array. In Rust they are CanFrame::Remote { id, dlc }, and data frames are CanFrame::Data { id, data }.
Note: This method returns all frames accumulated since the last start_rx() or rx() call and clears the buffer. The buffer holds up to 1024 frames; frames received while it is full are dropped, so call rx() often enough on a busy bus.
Exceptions:
- Throws error
400 INVALID_CONFIGURATIONif the interface is disabled
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
}
}
Rust: ikalogic_at1000::CanConfig and ikalogic_at1000::CanRxFilter. Any filter value builds; configure() and enable() return InvalidInput before sending for an id or mask wider than the format.
pub struct CanConfig {
pub enabled: bool,
pub baud_rate: u32,
pub termination_resistors: bool,
pub rx_filter: CanRxFilter,
}
pub enum CanRxFilter {
Standard { id: u16, mask: u16 }, // both at most 0x7FF
Extended { id: u32, mask: u32 }, // both at most 0x1FFFFFFF
}
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
}
}
Rust: ikalogic_at1000::CanConfigPatch (derives Default, so unset fields can be left to ..Default::default())
pub struct CanConfigPatch {
pub enabled: Option<bool>,
pub baud_rate: Option<u32>,
pub termination_resistors: Option<bool>,
pub rx_filter: Option<CanRxFilter>,
}
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
}
}
Rust has no separate enable type: enable takes CanConfigPatch and sets enabled: true itself.
CanFrame
CAN message frame structure.
{
id: number, // CAN identifier
extended_id?: boolean, // Use extended (29-bit) ID (default: false)
rtr?: boolean, // Remote transmission request (default: false)
dlc?: number, // RTR only: requested data length 0-8; present on received RTR frames
data?: number[], // Data bytes (0-8 bytes)
timeout?: number // TX-only: ms to wait for a TX buffer and bus access (1-5000)
}
Rust: ikalogic_at1000::CanFrame and ikalogic_at1000::CanId. Any value builds; tx() returns InvalidInput before sending for an identifier wider than its format, a payload over 8 bytes or a dlc over 8. The transmit timeout is the second argument of tx().
pub enum CanFrame {
Data { id: CanId, data: Vec<u8> },
Remote { id: CanId, dlc: u8 },
}
impl CanFrame {
pub const MAX_DATA: usize = 8;
pub fn data(id: CanId, data: impl Into<Vec<u8>>) -> Self;
pub const fn remote(id: CanId, dlc: u8) -> Self;
pub const fn id(&self) -> CanId;
}
pub enum CanId {
Standard(u16), // at most 0x7FF
Extended(u32), // at most 0x1FFFFFFF
}
impl CanId {
pub const STANDARD_MAX: u16 = 0x7FF;
pub const EXTENDED_MAX: u32 = 0x1FFF_FFFF;
pub fn raw(self) -> u32;
pub const fn is_extended(self) -> bool;
}
Usage Example
- NodeJS
- Python
- Rust
import { AT1000 } from '@ikalogic/at1000';
// Find and connect to device
const devices = await AT1000.findDevices();
const tester = await AT1000.open(devices[0]);
await tester.reset(); // Reset the device to a known state
// 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({
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 time
# Find and connect to device
devices = AT1000.find_devices()
tester = AT1000.open(devices[0])
tester.reset() # Reset the device to a known state
# Access CAN interface 0
can = tester.com.can(0)
# Enable with 500k baud rate and receive filter
can.enable(
baud_rate=500000,
termination_resistors=True,
rx_filter={
'extended_id': False,
'id': 0x100,
'mask': 0x700 # Accept IDs 0x100-0x1FF
}
)
# Start receiving frames
can.start_rx()
# Send a frame
can.tx({
'id': 0x123,
'data': [0x01, 0x02, 0x03, 0x04]
})
# Wait for responses
time.sleep(0.1)
# Read received frames
frames = can.rx()
print(f"Received frames: {frames}")
# Stop receiving
can.stop_rx()
# Disable interface
can.disable()
use ikalogic_at1000::{At1000, CanConfigPatch, CanFrame, CanId, CanRxFilter};
use std::time::Duration;
// Find and connect to device
let devices = At1000::find_devices(Duration::from_millis(500))?;
let tester = At1000::open(&devices[0], None)?;
tester.reset()?; // Reset the device to a known state
// Access CAN interface 0
let can = tester.com.can(0)?;
// Enable with 500k baud rate and receive filter
can.enable(CanConfigPatch {
baud_rate: Some(500_000),
termination_resistors: Some(true),
rx_filter: Some(CanRxFilter::Standard { id: 0x100, mask: 0x700 }), // Accept IDs 0x100-0x1FF
..Default::default()
})?;
// Start receiving frames
can.start_rx()?;
// Send a frame
can.tx(CanFrame::data(CanId::Standard(0x123), [0x01, 0x02, 0x03, 0x04]), None)?;
// Wait for responses
std::thread::sleep(Duration::from_millis(100));
// Read received frames
let frames = can.rx()?;
println!("Received frames: {frames:?}");
// Stop receiving
can.stop_rx()?;
// Disable interface
can.disable()?;
Common Patterns
CAN Filter Configuration
- NodeJS
- Python
- Rust
// Accept all frames, standard and extended
await can.configure({
rx_filter: {
extended_id: false,
id: 0x000,
mask: 0x000 // A zero mask accepts every frame, whatever extended_id says
}
});
// 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 frames, standard and extended
can.configure(
rx_filter={
'extended_id': False,
'id': 0x000,
'mask': 0x000 # A zero mask accepts every frame, whatever extended_id says
}
)
# Accept only specific ID (0x123)
can.configure(
rx_filter={
'extended_id': False,
'id': 0x123,
'mask': 0x7FF # All bits must match
}
)
# Accept range of IDs (0x200-0x2FF)
can.configure(
rx_filter={
'extended_id': False,
'id': 0x200,
'mask': 0x700 # Match upper 3 bits
}
)
# Extended (29-bit) ID filtering
can.configure(
rx_filter={
'extended_id': True,
'id': 0x12345678,
'mask': 0x1FFFFFFF
}
)
use ikalogic_at1000::{CanConfigPatch, CanRxFilter};
let can = tester.com.can(0)?;
// Accept all frames, standard and extended
can.configure(CanConfigPatch {
rx_filter: Some(CanRxFilter::Standard { id: 0x000, mask: 0x000 }), // A zero mask accepts every frame
..Default::default()
})?;
// Accept only specific ID (0x123)
can.configure(CanConfigPatch {
rx_filter: Some(CanRxFilter::Standard { id: 0x123, mask: 0x7FF }), // All bits must match
..Default::default()
})?;
// Accept range of IDs (0x200-0x2FF)
can.configure(CanConfigPatch {
rx_filter: Some(CanRxFilter::Standard { id: 0x200, mask: 0x700 }), // Match upper 3 bits
..Default::default()
})?;
// Extended (29-bit) ID filtering
can.configure(CanConfigPatch {
rx_filter: Some(CanRxFilter::Extended { id: 0x12345678, mask: 0x1FFFFFFF }),
..Default::default()
})?;
Request-Response Pattern
- NodeJS
- Python
- Rust
await can.start_rx();
// Remote request
await can.tx({ id: 0x321, rtr: true, dlc: 8 }); // remote request for 8 bytes
// Send request
await can.tx({
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.id >= 0x7E8 && frame.id <= 0x7EF) {
console.log("ECU response:", frame.data);
}
}
await can.stop_rx();
import time
can.start_rx()
# Remote request
can.tx({'id': 0x321, 'rtr': True, 'dlc': 8}) # remote request for 8 bytes
# Send request
can.tx({
'id': 0x7DF, # OBD-II broadcast
'data': [0x02, 0x01, 0x0C] # Request engine RPM
})
# Wait for response
time.sleep(0.05)
frames = can.rx()
for frame in frames:
if 0x7E8 <= frame.id <= 0x7EF:
print(f"ECU response: {frame.data}")
can.stop_rx()
use ikalogic_at1000::{CanFrame, CanId};
use std::time::Duration;
let can = tester.com.can(0)?;
can.start_rx()?;
// Remote request
can.tx(CanFrame::remote(CanId::Standard(0x321), 8), None)?; // remote request for 8 bytes
// Send request
can.tx(
CanFrame::data(
CanId::Standard(0x7DF), // OBD-II broadcast
[0x02, 0x01, 0x0C], // Request engine RPM
),
None,
)?;
// Wait for response
std::thread::sleep(Duration::from_millis(50));
for frame in can.rx()? {
if let CanFrame::Data { id, data } = frame {
if (0x7E8..=0x7EF).contains(&id.raw()) {
println!("ECU response: {data:?}");
}
}
}
can.stop_rx()?;
Continuous Monitoring
- NodeJS
- Python
- Rust
// 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.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 time
# Start monitoring
can.start_rx()
# Poll periodically
def monitor():
start_time = time.time()
while time.time() - start_time < 10: # Run for 10 seconds
frames = 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.id:03x}, Data: {data_str}")
time.sleep(0.1)
can.stop_rx()
monitor()
use ikalogic_at1000::CanFrame;
use std::time::Duration;
let can = tester.com.can(0)?;
// Start monitoring
can.start_rx()?;
// Poll periodically for 10 seconds
for _ in 0..100 {
for frame in can.rx()? {
match frame {
CanFrame::Data { id, data } => {
let data: Vec<String> = data.iter().map(|byte| format!("{byte:02x}")).collect();
println!("ID: {:#05x}, Data: {}", id.raw(), data.join(" "));
}
CanFrame::Remote { id, dlc } => println!("ID: {:#05x}, remote, dlc {}", id.raw(), dlc),
}
}
std::thread::sleep(Duration::from_millis(100));
}
can.stop_rx()?;
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
- 83333 (83.3 kbps): CANopen default