[Library] CAN통신 모니터링 라이브러리

2025. 8. 29. 16:52·프로젝트 작업기
반응형

 

예전에 차량의 CAN, KLINE, LIN 통신 값을 읽어들이기 위해 구성해봤던 프로토타입이다.

 

 

그 프로토타입을 이렇게 샘플로 만들었었다.

 

그런데 최근에 ESP32를 더 많이 활용하고 있고 기존에 만들었던 샘플의 K-LINE과 LIN통신도 활용할 곳이 점점 사라져서 ESP32와 CAN통신 조합으로 프로토타입을 만들어 보려고 한다.

 

 

정말 깔끔해졌다.

 

ESP32는 CAN컨트롤러가 내장되어 있어 기존에 있던 MCP2515 모듈을 없애고 CAN트랜시버는 로직레벨이 다르기 때문에 기존의 TJA1050에서 SN65HVD230로 바꿔서 사용하기로 했다.

 

이외 K-LINE 트랜시버 MCZ33290 모듈과 LIN 트랜시버 TJA1020 모듈도 없애 더 간결해졌고 전원입력부는 원래 사용했던 7505 레귤레이터를 사용하여 ESP32 5V에 인가하기로 한다.

 

 

라이브러리 소스코드는 아래에 공유하겠다.

 

커맨드 혹은 파워쉘로 모듈의 시리얼 통신을 수신한다면 어느 플랫폼, 어느 프레임워크로 개발을 하던 CAN 값들을 모니터링 할 수 있으며 현재는 일반 CAN 값들을 모니터링 하는 정도이니 참고 바란다. (추후에 CANFD 혹은 여러 종류의 CAN을 자주 만지게 된다면 업데이트 하도록 하겠다.)

 

* 가동시간을 얻어오는 함수(get_time)은 프레임워크에 따라 수정해야한다.

 


can_monitor.h

#pragma once

#include <stdio.h>

typedef struct {
    uint8_t data;
    uint32_t time;
} can_data_t;

typedef struct {
    uint32_t id;
    uint8_t len;
    can_data_t data[8];
} can_msg_t;

typedef enum {
    CAN_NUM_10 = 10,
    CAN_NUM_20 = 20,
    CAN_NUM_30 = 30,
    CAN_NUM_50 = 50,
    CAN_NUM_100 = 100,
    CAN_NUM_MAX,
} can_num_t;

typedef enum {
    CAN_DISPLAY_TIME_1SEC = 1,
    CAN_DISPLAY_TIME_3SEC = 3,
    CAN_DISPLAY_TIME_5SEC = 5,
    CAN_DISPLAY_TIME_10SEC = 10,
    CAN_DISPLAY_TIME_30SEC = 30,
    CAN_DISPLAY_TIME_50SEC = 50,
    CAN_DISPLAY_TIME_100SEC = 100,
    CAN_DISPLAY_TIME_MAX,
} can_disp_time_t;

void can_monitor_begin(can_num_t _can_num, can_disp_time_t _can_disp_time);
void can_monitor_end();

void can_monitor_input(uint32_t* _can_id, uint8_t* _can_len, uint8_t* _can_data, int* _data_idx);
void can_monitor_output();

can_monitor.c

#include <string.h>
#include "esp_timer.h"
#include "can_monitor.h"

can_num_t can_num = 0;
can_disp_time_t can_disp_time = 0;
can_msg_t* can_msg = NULL;

void can_monitor_begin(can_num_t _can_num, can_disp_time_t _can_disp_time) {
    can_num = _can_num;
    can_disp_time = _can_disp_time;
    can_msg = malloc(sizeof(can_msg_t) * can_num);
    memset(can_msg, 0, sizeof(can_msg_t) * can_num);
}

void can_monitor_end() {
    free(can_msg);
}

// 1sec = 1
static uint32_t get_time() {
    /**
     * 개발 프레임워크에 따라 수정 해야하는 부분
     */
    return esp_timer_get_time() / 1000000;
}

// 삽입 정렬 알고리즘 적용
static void can_msg_sort(can_msg_t* _can_msg) {
    for (int i = 1; i < can_num; i++) {
        if (_can_msg[i].id == 0) continue;

        can_msg_t key = _can_msg[i];
        int j = i - 1;

        while (j >= 0 && _can_msg[j].id > key.id) {
            memcpy(&_can_msg[j + 1], &_can_msg[j], sizeof(can_msg_t));
            j--;
        }
        memcpy(&_can_msg[j + 1], &key, sizeof(can_msg_t));
    }
}

void can_monitor_input(uint32_t* _can_id, uint8_t* _can_len, uint8_t* _can_data, int* _data_idx) {
    if (can_msg == NULL) {
        printf("can_monitor_begin Not Initialized");
        return;
    }

    for (int i = 0; i < can_num; i++) {
        if (can_msg[i].id == *_can_id || can_msg[i].id == 0) {
            can_msg[i].id = *_can_id;
            can_msg[i].len = *_can_len;
            if (can_msg[i].data[*_data_idx].data != *_can_data || can_msg[i].data[*_data_idx].time == 0) {
                can_msg[i].data[*_data_idx].time = get_time();
            }
            can_msg[i].data[*_data_idx].data = *_can_data;
            break;
        }
    }

    can_msg_sort(can_msg);
}

void can_monitor_output() {
    if (can_msg == NULL) {
        printf("can_monitor_begin Not Initialized");
        return;
    }

    printf("\033[2J\033[H");
    printf("\033[1;32m");
    printf("%4s %4s  %s\n", "ID", "LEN", "DATA");
    for (int i = 0; i < can_num; i++) {
        printf("%4ld %4d  ", can_msg[i].id, can_msg[i].len);
        for (int j = 0; j < 8; j++) {
            if (get_time() - can_msg[i].data[j].time < can_disp_time) {
                printf("\033[1;4;7;31m");
                printf("%02x ", can_msg[i].data[j].data);
                printf("\033[0m");
                printf("\033[1;32m");
            } else {
                printf("%02x ", can_msg[i].data[j].data);
            }
        }
        printf("\n");
    }
    printf("\033[0m");
}

 

반응형
저작자표시 비영리 변경금지 (새창열림)

'프로젝트 작업기' 카테고리의 다른 글

[ESP32] CAN 신호를 받아 해석하고 특정 아날로그 신호로 내보내기(CAN to Analog Converter - CAC)  (0) 2025.10.21
[ESP32] TA모닝 에어컨 자동 컨트롤러를 만들어 출력 및 연비 개선하기(일명 세상에서 가장 빠르게 에어컨을 켠 채 오르막길을 올라가는 순정 모닝 만들기) 下  (0) 2025.09.25
[ESP32] TA모닝 에어컨 자동 컨트롤러를 만들어 출력 및 연비 개선하기(일명 세상에서 가장 빠르게 에어컨을 켠 채 오르막길을 올라가는 순정 모닝 만들기) 上  (6) 2025.08.18
[ATmega] EasyEDA를 이용하여 PCB를 만들고 안드로이드 올인원을 제어하여 모하비 에어컨 공조기 상태 띄우기 上  (0) 2025.04.22
[STM32] 도어락 해킹? 리버싱? 구형 삼성 도어락(SHS-6601) 블루투스와 와이파이 원격으로 잠금 해제  (1) 2025.02.03
'프로젝트 작업기' 카테고리의 다른 글
  • [ESP32] CAN 신호를 받아 해석하고 특정 아날로그 신호로 내보내기(CAN to Analog Converter - CAC)
  • [ESP32] TA모닝 에어컨 자동 컨트롤러를 만들어 출력 및 연비 개선하기(일명 세상에서 가장 빠르게 에어컨을 켠 채 오르막길을 올라가는 순정 모닝 만들기) 下
  • [ESP32] TA모닝 에어컨 자동 컨트롤러를 만들어 출력 및 연비 개선하기(일명 세상에서 가장 빠르게 에어컨을 켠 채 오르막길을 올라가는 순정 모닝 만들기) 上
  • [ATmega] EasyEDA를 이용하여 PCB를 만들고 안드로이드 올인원을 제어하여 모하비 에어컨 공조기 상태 띄우기 上
이니셜P
이니셜P
카카오톡 문의 : initial_p 유튜브 : https://www.youtube.com/@gun-ny
    반응형
  • 이니셜P
    #include <이니셜.P>
    이니셜P
  • 전체
    오늘
    어제
    • 분류 전체보기 (93)
      • 협력점 안내 (1)
      • 프로젝트 작업기 (11)
      • 프로젝트 포트폴리오 (3)
      • 끄적끄적 (2)
      • Arduino (11)
      • STM32 (0)
      • ESP32 (8)
      • EasyEDA (0)
      • QT (5)
      • LVGL (0)
      • Buildroot (14)
      • Yocto (2)
      • Git (2)
      • C언어, C++ (18)
      • 프로그래머스 (16)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      esp32
      RaspberryPi
      SN65HVD230
      can
      Qt
      rootfs
      Buildroot
      리눅스
      모닝
      임베디드
      아두이노
      라즈베리파이
      빌드루트
      Embedded
      루트파일시스템
      SError
      계기판
      Overlay
      0xbe000011
      linux
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.5
    이니셜P
    [Library] CAN통신 모니터링 라이브러리
    상단으로

    티스토리툴바