📝CAN Setup
배경
CAN (Controller Area Network)은 차량 및 산업용 장비에서 마이크로컨트롤러와 장치들이 호스트 컴퓨터 없이 서로 통신할 수 있게 해주는 직렬 통신 프로토콜이다. Linux에서는 SocketCAN이라는 네트워크 계층 구현을 통해 CAN 장치를 일반 네트워크 인터페이스처럼 다룰 수 있다.
문제 상황
USB-CAN 어댑터 (예: PEAK CAN)를 여러 개 사용할 때, 장치가 연결될 때마다 can0, can1 등의 이름이 연결 순서에 따라 임의로 할당된다. 시리얼 넘버가 있는 장치라면 udev 규칙으로 고유 이름을 지정할 수 있지만, 시리얼 넘버가 없는 CAN 장치의 경우 물리적인 USB 포트 위치를 기반으로 식별해야 한다.
이 문서에서는 USB 포트의 물리적 경로(ID_PATH_TAG)를 이용해 특정 USB 포트에 연결된 CAN 장치에 고정된 이름을 부여하는 방법을 설명한다.
사전 준비
필요한 패키지들이 설치되어 있는지 확인한다:
1단계: 현재 연결된 CAN 장치 확인
먼저 CAN 장치가 시스템에 인식되었는지 확인한다.
2단계: USB 포트의 물리적 경로(ID_PATH_TAG) 찾기
udevadm 명령어를 사용해 CAN 장치가 연결된 USB 포트의 고유한 물리적 경로를 찾는다:
출력 결과에서 ID_PATH_TAG 값을 찾는다:
ID_PATH_TAG 해석:
pci-0000_00_14_0: PCI 버스상의 USB 호스트 컨트롤러 위치usb-0_9_1_0: USB 허브/포트 경로 (0번 버스, 9번 포트, 1번 서브포트…)
이 경로는 물리적인 USB 포트 위치를 나타내므로, 항상 같은 포트에 장치를 연결하면 동일한 ID_PATH_TAG 값을 얻게 된다.
여러 CAN 장치가 연결된 경우, 각 장치의 경로를 개별적으로 확인할 수 있다:
3단계: Python 스크립트로 CAN 인터페이스 설정
위에서 찾은 ID_PATH_TAG를 사용해 CAN 장치에 원하는 이름을 부여하고 활성화하는 스크립트다. 위에서 찾은 ID_PATH_TAG를 MY_TAG에 지정한다.
import subprocess
import os
import sys
def run_command(cmd):
"""
Executes a shell command and raises an exception if an error occurs.
"""
try:
# Print the command being executed for debugging
print(f"Executing: {cmd}")
subprocess.run(cmd, shell=True, check=True, text=True)
except subprocess.CalledProcessError as e:
print(f"Error executing command: {cmd}")
print(f"Return code: {e.returncode}")
raise e
def find_interface_by_path_tag(target_tag):
"""
Searches all interfaces in /sys/class/net/ to find the device matching the ID_PATH_TAG.
(It searches the entire directory to find devices that may have already been renamed)
"""
net_path = "/sys/class/net"
if not os.path.exists(net_path):
return None
# Iterate through all network interfaces (excluding loopback 'lo')
for iface in os.listdir(net_path):
if iface == "lo": continue
sys_path = os.path.join(net_path, iface)
try:
# Execute udevadm info (options removed for backward compatibility)
cmd = ["udevadm", "info", sys_path]
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
# Parse output line by line
for line in result.stdout.splitlines():
if "ID_PATH_TAG=" in line:
parts = line.split("=", 1)
if len(parts) == 2:
current_tag = parts[1].strip()
if current_tag == target_tag:
return iface
except:
continue
return None
def setup_can_interface(target_tag, new_name, bitrate=1000000):
"""
Finds the CAN device by tag, renames it, and brings it up.
"""
print(f"--- Starting CAN device setup (Target: {new_name}) ---")
# 1. Find current device name
current_name = find_interface_by_path_tag(target_tag)
if not current_name:
print(f"[Failed] Could not find device matching tag ({target_tag}).")
return False
print(f"Device found: Current name is '{current_name}'.")
try:
# 2. Bring interface down (Required before renaming or changing type)
run_command(f"sudo ip link set {current_name} down")
# 3. Rename interface
# Only rename if the current name differs from the target name
if current_name != new_name:
print(f"Attempting to rename: {current_name} -> {new_name}")
run_command(f"sudo ip link set {current_name} name {new_name}")
else:
print(f"Name is already {new_name}. Skipping rename.")
# 4. Set bitrate (Type CAN)
# Use new_name since the interface has been renamed
run_command(f"sudo ip link set {new_name} type can bitrate {bitrate}")
# 5. Bring interface up
run_command(f"sudo ip link set {new_name} up")
print(f"\n[Success] Device {new_name} is up with bitrate {bitrate}bps.")
return True
except Exception as e:
print(f"\n[Error] An issue occurred during setup: {e}")
return False
# --- Usage Example ---
if __name__ == "__main__":
# 1. Tag of the device to find (obtained from udevadm info)
# - $ udevadm info /sys/class/net/can*
# ID_PATH_TAG=pci-0000_00_14_0-usb-0_9_1_0
MY_TAG = "pci-0000_00_14_0-usb-0_9_1_0"
# 2. Target name and bitrate
TARGET_NAME = "can_sensor_x"
TARGET_BITRATE = 1000000
# Run function
setup_can_interface(MY_TAG, TARGET_NAME, TARGET_BITRATE)스크립트 주요 기능 설명
| 함수 | 설명 |
|---|---|
find_interface_by_path_tag() |
/sys/class/net/ 하위의 모든 네트워크 인터페이스를 순회하며 ID_PATH_TAG가 일치하는 장치를 찾는다 |
setup_can_interface() |
장치를 찾아 이름을 변경하고, bitrate를 설정한 후 활성화한다 |
스크립트 실행 흐름
1. ID_PATH_TAG로 현재 인터페이스 이름 찾기 (예: can0)
↓
2. 인터페이스 비활성화 (ip link set down)
↓
3. 인터페이스 이름 변경 (ip link set name)
↓
4. CAN bitrate 설정 (ip link set type can bitrate)
↓
5. 인터페이스 활성화 (ip link set up)
4단계: 설정 확인
스크립트 실행 후 설정이 제대로 되었는지 확인한다:
대안: udev 규칙 사용하기
매번 스크립트를 실행하는 대신, udev 규칙을 만들어 자동화할 수도 있다:
규칙 적용:
단, udev 규칙만으로는 bitrate 설정과 인터페이스 활성화가 되지 않으므로, systemd 서비스와 조합하거나 위 Python 스크립트를 시작 시 실행하는 방식을 사용해야 한다.
트러블슈팅
인터페이스가 보이지 않는 경우
권한 문제
스크립트는 sudo 권한이 필요하다. 일반 사용자로 실행하려면:
bitrate 설정 실패
장치가 지원하지 않는 bitrate를 설정하면 에러가 발생한다. 일반적인 CAN bitrate:
- 125000 (125 kbps)
- 250000 (250 kbps)
- 500000 (500 kbps)
- 1000000 (1 Mbps)