본문 바로가기
카테고리 없음

CAN(Controller Area Network) Bus

by SuldenLion 2026. 6. 17.
반응형
CAN Bus — 임베디드 & IoT | IoT 프로토콜 #08
임베디드 & IoT · IoT 프로토콜 #08 of 08

// IoT Protocol — Controller Area Network

CAN Bus

자동차와 산업 기계의 신경계 — 1986년 Bosch가 만들고 전 세계 자동차 ECU가 신뢰하는 멀티마스터 직렬 버스

IoT 프로토콜약 15분 중급~고급자동차 · 산업기계 · ECU · 실시간

CAN Bus란?

자동차를 바꾼 통신 기술의 탄생

CAN(Controller Area Network)은 1986년 독일 Bosch가 자동차 내 ECU(Electronic Control Unit) 간 통신을 위해 개발한 직렬 버스 프로토콜입니다. 1991년 Mercedes-Benz W140에 최초 양산 적용되었고, 1993년 ISO 11898로 국제 표준화되었습니다.

오늘날 자동차 1대에는 70개 이상의 ECU가 탑재되어 있으며 이들 모두 CAN으로 통신합니다. 자동차를 넘어 산업 기계, 의료 기기, 로봇, 항공 분야에서도 핵심 통신 버스로 자리잡았습니다.

🚗
CAN의 핵심 강점: 마스터 없는 멀티마스터 버스 / 강력한 5가지 오류 검출 및 복구 / 차동 신호로 EMI 내성 / 최대 1Mbps (CAN FD는 8Mbps) / 우선순위 기반 자동 중재
표준식별자 길이ID 범위특징
CAN 2.0A11-bit0 ~ 2,047초기 표준, 2047개 고유 메시지
CAN 2.0B 현재 표준29-bit0 ~ 536,870,911확장 프레임. SAE J1939 사용
CAN FD 고속11 또는 29-bit위와 동일최대 64 bytes 데이터, 최대 8Mbps

메시지 구조

CAN 프레임과 아비트레이션

SOF1 bit
Arbitration
(ID 11/29b)
12/32 bits
Control6 bits
Data0~64 bytes
CRC15 bits
ACK2 bits
EOF7 bits

아비트레이션(비파괴 비트 중재): 여러 노드가 동시에 전송을 시작하면, ID가 작은(우선순위가 높은) 메시지가 자동으로 버스를 점유합니다. 패킷 충돌로 데이터가 손상되는 이더넷 CSMA/CD 방식과 달리, 높은 우선순위 메시지는 손상 없이 전송됩니다.

💡
ID와 우선순위: ID 0이 가장 높은 우선순위입니다. 자동차에서 에어백(ID 0x001)은 창문 제어(ID 0x500)보다 훨씬 높은 우선순위를 가집니다. 중요한 안전 신호가 항상 먼저 전달됩니다.

오류 검출

5가지 오류 검출 메커니즘

오류 유형검출 방법원인
CRC 오류수신 CRC와 계산 CRC 비교전송 중 비트 반전
Bit 오류전송 비트 ≠ 버스 비트드라이버 오류, 짧은 회로
Stuff 오류6연속 동일 비트 감지비트 스터핑 규칙 위반
Form 오류고정 필드 값이 잘못됨EOF, ACK Delimiter 오류
ACK 오류ACK 슬롯에서 ACK 없음수신 노드 없음 또는 오류

오류 카운터와 노드 상태: 각 노드는 TEC(Transmit Error Counter)와 REC(Receive Error Counter)를 유지합니다. 카운터가 누적되면 노드 상태가 전환되어 결함 노드가 버스 전체를 오염시키지 않도록 격리됩니다.

Error Active
TEC/REC < 128
Error Passive
TEC/REC 128~255
Bus-Off
TEC ≥ 256
복구
128 × 11 recessive

물리 계층

차동 신호 · RS-485 배선 · 종단 저항

신호 방식

차동 신호

CAN-H · CAN-L 두 선

종단 저항

120Ω × 2개

버스 양 끝에 필수

최대 속도

1 Mbps (CAN)

CAN FD: 최대 8 Mbps

최대 거리

500m @ 125kbps

1Mbps에서 최대 40m

최대 노드

이론 2032개

실제 ~110개 (부하)

케이블

Twisted Pair

ISO 11898-2

⚠️
배선 필수 규칙: 반드시 버스 양 끝에 120Ω 종단 저항을 달아야 합니다. 없으면 신호 반사로 통신 오류가 발생합니다. 가지(Stub) 배선은 최소화하고, CAN-H/CAN-L은 반드시 꼬임 쌍선을 사용하세요.

CAN FD

CAN FD — 더 빠르고 더 많은 데이터

CAN FD(Flexible Data-Rate)는 2012년 Bosch가 도입한 CAN 확장 규격입니다. 아비트레이션 단계는 기존 CAN과 동일한 속도로 진행하고, 데이터 전송 단계에서만 최대 8Mbps로 속도를 올립니다. 또한 데이터 크기가 기존 8바이트에서 최대 64바이트로 확장되었습니다.

항목CAN 2.0CAN FD
최대 데이터8 bytes64 bytes
아비트레이션 속도최대 1 Mbps최대 1 Mbps (동일)
데이터 속도최대 1 Mbps최대 8 Mbps
CRC15-bit17-bit 또는 21-bit (강화)
ISO 표준ISO 11898-1ISO 11898-1:2015
하위 호환성CAN FD 노드와 기존 CAN 노드 혼용 불가 (별도 네트워크 필요)

상위 프로토콜

CANopen · SAE J1939 · OBD-II

CAN은 데이터 링크 계층까지만 정의합니다. 실제 응용에서는 CAN 위에 상위 프로토콜을 사용해 기기 유형별 표준화된 데이터 형식을 정합니다.

상위 프로토콜기반용도특징
CANopenCAN 2.0산업 자동화, 기계, 로봇Object Dictionary로 기기 파라미터 표준화
SAE J1939CAN 2.0B트럭·버스 등 대형 차량PGN(Parameter Group Number)으로 데이터 정의
OBD-II / ISO-TPCAN 2.0승용차 진단OBD-II 포트로 차량 상태 읽기, 스캐너 연결
AUTOSARCAN / CAN FD자동차 SW 아키텍처신규 차량 전기 아키텍처 표준, CAN FD 기반
DeviceNetCAN 2.0공장 자동화Rockwell Automation 주도, 산업 기기 연결

실전 예제

Python python-can · Arduino ESP32 TWAI

Python — python-can으로 OBD-II 데이터 읽기

Python — python-can OBD-II RPM · 속도 읽기
import can, time

# USB-CAN 어댑터 (Vector, PEAK, Kvaser, SavvyCAN 등)
bus = can.interface.Bus(channel="can0", bustype="socketcan")
# Windows: bustype="vector" / "pcan" / "kvaser"

def obd_request(bus, pid):
    """OBD-II 요청 전송 — ISO 15765-2 단일 프레임"""
    msg = can.Message(
        arbitration_id=0x7DF,   # OBD-II 브로드캐스트 요청 ID
        data=[0x02, 0x01, pid, 0x00, 0x00, 0x00, 0x00, 0x00],
        is_extended_id=False
    )
    bus.send(msg)
    resp = bus.recv(timeout=0.5)
    return resp

# PID 0x0C: 엔진 RPM = ((A×256) + B) / 4
r = obd_request(bus, 0x0C)
if r:
    rpm = ((r.data[3] * 256) + r.data[4]) / 4
    print(f"엔진 RPM: {rpm:.0f}")

# PID 0x0D: 차속 (km/h) = A
r = obd_request(bus, 0x0D)
if r:
    speed = r.data[3]
    print(f"차속: {speed} km/h")

# PID 0x05: 냉각수 온도 (°C) = A - 40
r = obd_request(bus, 0x05)
if r:
    temp = r.data[3] - 40
    print(f"냉각수 온도: {temp}°C")

# ── CAN 버스 모든 메시지 스니핑 ──
print("\n=== 버스 스니핑 시작 ===")
for msg in bus:
    print(f"ID: 0x{msg.arbitration_id:03X} DLC:{msg.dlc} Data: {msg.data.hex(' ')}")

bus.shutdown()

Arduino (ESP32) — TWAI 컨트롤러 송수신

Arduino (ESP32) — TWAI (Two-Wire Automotive Interface) CAN
#include "driver/twai.h"

// TWAI: ESP32 내장 CAN 컨트롤러 (GPIO21=TX, GPIO22=RX)
// 별도 CAN 트랜시버 필요 — TJA1050, SN65HVD230 등

void setup() {
    Serial.begin(115200);

    twai_general_config_t g_cfg =
        TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, TWAI_MODE_NORMAL);
    twai_timing_config_t  t_cfg = TWAI_TIMING_CONFIG_500KBITS(); // 500kbps
    twai_filter_config_t  f_cfg = TWAI_FILTER_CONFIG_ACCEPT_ALL();

    if (twai_driver_install(&g_cfg, &t_cfg, &f_cfg) != ESP_OK) {
        Serial.println("TWAI 드라이버 설치 실패"); return;
    }
    twai_start();
    Serial.println("CAN 버스 준비");
}

void loop() {
    // ── 메시지 전송 ──
    twai_message_t tx;
    tx.identifier       = 0x100;        // 11-bit ID
    tx.extd             = 0;             // 0=표준, 1=확장 29-bit
    tx.data_length_code = 4;
    tx.data[0] = 0x01; tx.data[1] = 0x02;
    tx.data[2] = 0x03; tx.data[3] = 0x04;

    esp_err_t r = twai_transmit(&tx, pdMS_TO_TICKS(100));
    if (r == ESP_OK)
        Serial.printf("TX: ID=0x%03X Data=%02X %02X %02X %02X\n",
                      tx.identifier, tx.data[0], tx.data[1], tx.data[2], tx.data[3]);

    // ── 메시지 수신 ──
    twai_message_t rx;
    if (twai_receive(&rx, pdMS_TO_TICKS(100)) == ESP_OK) {
        Serial.printf("RX: ID=0x%03X DLC=%d Data=", rx.identifier, rx.data_length_code);
        for (int i = 0; i < rx.data_length_code; i++)
            Serial.printf("%02X ", rx.data[i]);
        Serial.println();
    }
    delay(500);
}

CANopen — Python canopen 라이브러리

Python — canopen 라이브러리 (Object Dictionary 접근)
import canopen

# CANopen 네트워크 초기화
network = canopen.Network()
network.connect(channel="can0", bustype="socketcan")

# 노드 추가 (Node ID=5, EDS 파일로 Object Dictionary 로드)
node = canopen.RemoteNode(5, "motor_drive.eds")
network.add_node(node)

# NMT — 노드 상태 제어
node.nmt.send_command(0x01)   # Operational 상태로 전환

# SDO — Object Dictionary 읽기/쓰기
device_name = node.sdo[0x1008].raw    # Manufacturer Device Name
print(f"기기명: {device_name}")

node.sdo[0x6040].raw = 0x0006          # Controlword: Shutdown
node.sdo[0x6040].raw = 0x000F          # Controlword: Enable Operation

# PDO — 주기적 실시간 데이터 수신
def on_rpdo1(msg):
    pos = int.from_bytes(msg.data[0:4], 'little')
    vel = int.from_bytes(msg.data[4:8], 'little')
    print(f"위치: {pos}, 속도: {vel}")

node.rpdo.read()   # PDO 설정 읽기
node.rpdo[1].add_callback(on_rpdo1)
node.rpdo[1].start(0.01)   # 10ms 주기 수신

network.disconnect()

장단점

CAN Bus 선택 가이드

강점: 마스터 없는 멀티마스터 버스 / 강력한 5가지 오류 검출 / 차동 신호 EMI 내성 / 결함 노드 격리(Bus-Off) / 우선순위 기반 실시간 보장 / 전 세계 자동차·산업 기기 지원 / 풍부한 상위 프로토콜 생태계
⚠️
한계: 기본 8바이트 페이로드 제한 (CAN FD: 64B) / 보안 기능 없음(암호화·인증 없음) / 최대 거리 500m / 종단 저항·비트 타이밍 설정 복잡 / 이더넷 대비 낮은 대역폭
상황권장
자동차 ECU 간 통신CAN / CAN FD
산업 기계·로봇 관절 제어CAN / CANopen
PLC·계측기·인버터 통신Modbus RTU 검토
차량 진단 (OBD-II)CAN 2.0 + ISO-TP
고속 대용량 영상·데이터 전송이더넷 기반 고려

// Quick Reference — CAN Bus

표준ISO 11898-1 (CAN 2.0A/B, CAN FD)
속도CAN 최대 1Mbps / CAN FD 최대 8Mbps
최대 데이터CAN: 8 bytes / CAN FD: 64 bytes
토폴로지버스형, 양끝 120Ω 종단 저항
오류 검출CRC · Bit · Stuff · Form · ACK (5가지)
상위 프로토콜CANopen · SAE J1939 · OBD-II
주요 라이브러리python-can · canopen (Python) · TWAI (ESP32)
추천 용도자동차 ECU · 산업 기계 · 로봇 · 의료 기기
반응형

댓글