보드에 올리기 전에 PC에서 — EVerest 시뮬레이션과 ISO 15118 메시지 캡처
어제 충전기 소프트웨어로 EVerest를 고르면서 숙제가 생겼다. 우리 BSP(Dunfell)로는 EVerest를 올릴 수 없어서 OS부터 새로 만들어야 하는데, 몇 주짜리 작업이다. 그런데 이 칩(Cortex-A7 650MHz, RAM 512MB)으로 EVerest가 돌아가기는 할까? 그걸 모른 채 이식부터 하기는 아까웠다.
그래서 순서를 바꿨다. OS는 뒤로 미루고, 먼저 PC(WSL)에서 EVerest를 돌려 보기로 했다.
1. “RAM 최소 4GB”의 정체
시작 전에 한 번 겁을 먹었다. 설치 요구사항에 RAM 최소 4GB, CPU는 x86_64 또는 ARM64라고 적혀 있었다. 보드는 512MB에 32비트 ARMv7이다.
다시 읽어 보니 두 가지가 문제였다.
- 출처가 공식 문서가 아니었다. EVerest 저장소를 바탕으로 제3자가 자동 생성한 문서 사이트였다.
- 그 사양은 빌드하는 PC 기준이었다. 같은 항목의 “디스크 10GB, Ubuntu 패키지 설치”가 모두 개발 PC 이야기였다. C++ 대형 코드를 컴파일할 때 메모리를 많이 쓰는 것이지, 보드에서 실행할 때 4GB가 필요하다는 뜻이 아니다.
OS 조건(Yocto Kirkstone 이상)도 하드웨어 때문이 아니라 소프트웨어 버전 때문이다. EVerest가 기대는 OpenSSL 3, 새 컴파일러, Kirkstone부터 바뀐 레시피 문법(_append → :append)을 Dunfell이 갖고 있지 않다.
정리하면 OS 문제는 “업그레이드하면 풀리는 문제”이고, 성능은 “재 봐야 아는 문제”였다. 재 보기로 했다.
2. 설치 — 공식 문서와 비공식 자료가 달랐다
처음에는 비공식 자료대로 EVerest 의존성 관리자(EDM)를 pip로 설치하려 했다.
ERROR: No matching distribution found for everest-dev-environment
PyPI에 없었다. 소스를 받아 저장소 안의 공식 문서(docs/source/how-to-guides/getting-started/get-started-sw.rst)를 읽었더니 이렇게 적혀 있었다.
Since EVerest has been restructered into a (quasi)-mono-repository, manual installation of
edmis no longer recommended for building recent releases of EVerest (2026 and later).
2026년부터 저장소가 하나로 합쳐져서, 외부 의존성은 CMake의 CPM이 자동으로 받는다. 비공식 자료는 그 이전 기준이었다.
| 항목 | 비공식 자료 | 공식 문서 |
|---|---|---|
| EDM | 설치 필요 | 불필요 |
| MQTT 브로커 | 내장 | 필요 (Docker로 mosquitto) |
| 시뮬레이션 실행 | manager --config ... |
run-scripts/run-sil.sh |
교훈: 저장소 안의 docs/가 가장 정확하다.
빠진 패키지 두 가지
빌드 설정은 한 번 멈췄다.
None of systemd;elogind;basu libraries implementing sd-bus was found
공식 목록에 있던 libsystemd-dev가 빠져 있었다. 그리고 MQTT 브로커와 시뮬레이션 화면(Node-RED)이 Docker 컨테이너로 돌기 때문에 Docker도 필요했다. WSL은 systemd가 켜져 있어서 Docker를 바로 쓸 수 있었다.
sudo apt-get install -y libsystemd-dev docker.io docker-compose-v2 rfkill
sudo usermod -aG docker $USER # 이후 wsl --shutdown
빌드는 16코어 PC에서 동시 작업을 10개로 제한했다. C++ 컴파일 하나가 1GB 가까이 쓰기도 해서, RAM 15GB를 넘기지 않으려는 것이다. 빌드 설정 단계에서 보인 모듈 목록에는 우리에게 쓸모 있는 것들이 있었다.
| 모듈 | 우리에게 |
|---|---|
GenericPowermeter, SerialCommHub |
Modbus 시리얼 전력량계. ttySTM3의 JSY-MK-163T |
EvseSlac |
QCA7000 계열 PLC로 하는 SLAC |
TIDA010939, AdAcEvse22KwzKitBSP |
실제 AC 충전 보드의 BSP 모듈. 우리 보드 모듈의 참고 |
YetiDriver |
안전 기능을 MCU에 두는 구조. M4 구성의 참고 |
3. 시뮬레이션 구조
[Node-RED 화면] ─┐ http://localhost:1880/ui (Docker)
▼
[MQTT 브로커] localhost:1883 (Docker mosquitto)
▲
[EVerest manager] ─ 모듈 19개 (충전기 + 가상 차량)
모든 모듈이 별도 프로세스로 뜨고, MQTT 브로커를 거쳐 대화한다. 실행하니 2.6초 만에 준비가 끝났다.
🚙🚙🚙 All modules are initialized. EVerest up and running [2595ms] 🚙🚙🚙
🌀🌀🌀 Ready to start charging 🌀🌀🌀
Node-RED 실행 스크립트는 docker run -it라 터미널 없이는 실패해서, -d로 바꾼 래퍼를 만들어 썼다.
4. 첫 충전
화면에서 Car Plugin을 눌렀다.
EVSE IEC Session Started: EVConnected 차량 연결 (CP 12V → 9V)
Providing authorization 인증 (가상 RFID)
Transaction Started (0 kWh)
Charger state: PrepareCharging->Charging 충전 (CP 6V)
IEC allow_power_on requested: true 릴레이를 붙여도 된다
마지막 줄이 실제 보드에서는 K1 주 릴레이(pwmchip4/pwm2)를 켜는 순간이다. 어제 딸깍 소리로 확인한 그 릴레이다.
“IEC”라는 단어는 기본 충전(Basic Charging) 경로라는 뜻이다. ISO 15118 같은 디지털 통신 없이 CP 선의 전압(12/9/6V)과 PWM 듀티만으로 하는 충전이다. 충전기는 듀티로 허용 전류를 알리고(듀티% × 0.6 = A), 차는 저항으로 상태를 알린다. 지금 길거리 완속 충전기 대부분이 이 방식이다.
5. 얼마나 먹나
충전 중에 manager와 모든 모듈 프로세스를 측정했다. 메모리는 공유 라이브러리를 나눠 세는 PSS로 봤고(RSS는 공유분을 중복으로 세서 크게 나온다), CPU는 ps의 누적 평균 대신 10초 구간의 실제 사용량을 쟀다.
| 구분 | PSS |
|---|---|
| 충전기 쪽 (manager, EvseManager, 인증, 보안, ISO 15118 충전기 등 15개) | 약 120MB |
| 가상 차량: java (차량 쪽 ISO 15118) | 130MB |
| 가상 차량: python3 | 52MB |
| 전체 19개 | 303MB |
| CPU (10초 구간, 코어 1개 = 100%) | |
|---|---|
| 전체 | 8.6% |
| 가상 차량을 뺀 충전기 쪽 | 약 6.5% |
측정한 PC는 i7-13620H다. 그리고 보드에서 남은 메모리를 확인했다.
root@myir:~# free -m
total used free shared buff/cache available
Mem: 425 77 209 9 138 330
512MB 중 약 87MB는 GPU, M4, 보안 영역이 예약하고, 리눅스가 쓰는 425MB 중 330MB가 새 프로그램에 줄 수 있는 양이다.
| 질문 | 답 |
|---|---|
| 메모리 | 들어갈 것으로 본다. 330MB 여유에 약 120MB. 32비트 ARM은 보통 더 적게 쓴다. OCPP와 보드 모듈이 더해져도 여유가 있다 |
| CPU | 아직 모른다. 최신 x86 코어와 650MHz Cortex-A7은 성능 차이가 매우 커서, PC의 6.5%를 환산하는 건 오차가 너무 크다 |
남은 불확실성은 CPU 하나로 좁혀졌다. GPU를 쓰지 않으니 GPU 예약 영역을 풀면 메모리는 더 늘릴 여지도 있다.
6. 구성 파일에서 찾은 “가상 보드”
config/config-sil.yaml에서 가장 중요한 부분은 이것이다.
connector_1:
module: EvseManager # 충전 흐름의 중심
connections:
bsp: - module_id: connector_1_powerpath # 보드 지원 (CP, 릴레이)
ac_rcd: - module_id: connector_1_powerpath # 누전
powermeter_grid_side: - module_id: connector_1_powerpath
slac: - module_id: slac # SlacSimulator
connector_1_powerpath:
module: YetiSimulator # ← 가상 하드웨어
YetiSimulator가 가상 하드웨어 자리다. 실제 SC6_0을 붙일 때는 구성 파일에서 연결 대상만 바꾸면 된다.
| 지금 (가상) | 실제 보드 |
|---|---|
YetiSimulator |
우리 보드 모듈 (CP PWM, ADC, K1, RCD/EM_INT) |
SlacSimulator |
EvseSlac (QCA7005, eth1) |
| 시뮬레이터의 전력량계 | GenericPowermeter (Modbus ttySTM3) |
7. 충전 세션 기록
충전할 때마다 /tmp/everest-logs/ 아래에 세션 기록(eventlog.csv, .html)이 자동으로 남는다. 시험 삼아 32A 시나리오로 충전하다 고장 스위치를 켰다 끈 세션은 이렇게 기록됐다.
05:58:54.507 Set PWM On (53.3%) 32A 허용
05:58:56.346 Event PowerOn 릴레이 ON
05:59:40.769 Stop in Charging: [fatal error] 고장 스위치
05:59:40.824 Event PowerOff 릴레이 OFF (0.06초 뒤)
05:59:46.985 Set PWM On (53.3%) 고장 해제 후 재개
06:00:09.338 Transaction Finished (0.192 kWh)
PWM 듀티, 릴레이, 고장 시 차단까지 걸린 시간처럼 보드에서 일어날 일이 그대로 남는다.
8. ISO 15118 메시지를 패킷으로 잡기
시뮬레이션에서도 가상 차량과 충전기는 실제 IPv6 네트워크로 진짜 ISO 15118 메시지를 주고받는다. 그렇다면 실제 차량 시험과 같은 방법으로 볼 수 있다.
로그를 보니 충전기는 eth0을 쓰고 있었다.
iso15118_charge :: Using ethernet interface: eth0
그래서 eth0에서 tcpdump로 캡처했는데, 메시지는 분명히 오가는데 파일은 112바이트였다. 차량과 충전기가 같은 컴퓨터 안에 있어서, 리눅스가 자기 eth0 주소로 가는 통신도 내부적으로 루프백(lo)으로 처리했기 때문이다. lo로 바꾸자 잡혔다.
tcpdump -i lo -U -w v2g.pcap 'ip6 and (udp port 15118 or tcp)'
320개 패킷, 그중 V2G 메시지가 실린 패킷 156개. 인증서를 넣지 않아서 암호화 없이 평문이다. 같은 세션의 흐름은 이렇다.
Set PWM On (53.3%) 기본 신호 (HLC 가능 표시)
SLAC MATCHED PLC 매칭 (실물에서는 QCA7005)
SupportedAppProtocolReq / Res "어떤 규격으로 말할까" → ISO 15118-2
SessionSetupReq / Res
ServiceDiscoveryReq / Res
PaymentServiceSelectionReq / Res 결제 방식 (카드)
AuthorizationReq / Res
ChargeParameterDiscoveryReq / Res 차의 요구와 충전기 한계 교환
PowerDeliveryReq (Start) / Res → PowerOn
ChargingStatusReq / Res × 68 충전 중 약 0.6초마다
PowerDeliveryReq (Stop) / Res → PowerOff
SessionStopReq / Res
SLAC UNMATCHED
차가 충전기를 찾는 첫 SDP 요청은 멀티캐스트라 eth0으로 나가서 이 캡처에는 빠졌다. 다음엔 모든 인터페이스(-i any)로 잡으면 된다.
dsV2Gshark
ISO 15118 메시지는 EXI(XML을 바이너리로 압축한 형식)로 오가서, Wireshark만으로는 숫자 덩어리다. dsV2Gshark는 자동차 시험 장비 회사 dSPACE가 공개한 무료 Wireshark 플러그인으로, 이걸 사람이 읽을 수 있게 풀어 준다. V2GTP, SDP, SAP, DIN 70121, ISO 15118-2, ISO 15118-20(부분)을 해석하고, 한국의 Battery Data Exchange(VAS Korea)도 지원한다. 메시지를 규격 스키마에 대조하는 검증 기능과, 충전 중 전류·전압을 보는 I/O 그래프도 있다.
실제 보드에서는 더 쉽다. 설명서는 차와 충전기 사이를 엿보려면 PLC 스니퍼 장비가 필요하다고 하지만, 한쪽에 직접 이더넷으로 접근할 수 있으면 필요 없다고 한다. 우리는 충전기 쪽이니 보드의 eth1에서 바로 캡처하면 된다.
9. 보드 시험은 언제 가능한가
| 방식 | 지금 | 이유 |
|---|---|---|
| EVerest를 보드에서 실행 | ✘ | Dunfell BSP, CPU 미확인 |
| PC의 EVerest가 보드 하드웨어를 움직이기 | ✔ 가능성 높음 | EVerest의 공개 MQTT API |
| SLAC / ISO 15118 실물 | ✘ | SLAC은 보드 eth1에서 직접 돌아야 함 |
두 번째 방식의 근거는 EVerest가 보드 지원 기능을 MQTT로 공개된 API(evse_board_support_API)로도 제공한다는 점이다.
| 방향 | 메시지 | 보드에서 할 일 |
|---|---|---|
| EVerest → 보드 | pwm_on |
CP 듀티 설정 (pwmchip0) |
allow_power_on |
K1 릴레이 | |
| 보드 → EVerest | event |
ADC로 판정한 CP 상태 |
raise_error / clear_error |
누전, 비상정지 |
보드에 작은 에이전트 프로그램을 두면 지금 BSP 그대로 실제 CP와 릴레이를 EVerest의 충전 흐름으로 움직여 볼 수 있다. 여기에 PC 쪽에서 OCPP 서버(SteVe)를 붙이면 “실제 하드웨어 + OCPP 전문”까지 한 번에 된다. OCPP 1.6 시험 구성은 127.0.0.1:8180의 SteVe에 충전기 ID cp001로 붙고, 전문을 /tmp/everest_ocpp_logs에 HTML로 남기도록 돼 있다.
CPU 문제에는 다른 방법을 생각해 뒀다. SD 카드는 14.9GB 중 1.5GB만 쓰고 있다. 남는 공간에 32비트 ARM용 Debian과 EVerest를 넣고, 지금 커널 그대로 chroot로 실행하면 OS를 바꾸지 않고도 진짜 Cortex-A7에서 잴 수 있다.
정리
"RAM 4GB 필요" → 빌드 PC 사양, 비공식 문서
EDM 설치 실패 → 2026년부터 불필요 (공식 docs/ 확인)
AC SIL 충전 → 연결 → 인증 → 충전 → allow_power_on (= K1)
메모리 → 충전기 쪽 약 120MB / 보드 여유 330MB ✔
CPU → 아직 모름 (650MHz A7 실측 필요)
가상 보드 → config의 YetiSimulator 자리에 우리 모듈
ISO 15118 캡처 → eth0이 아니라 lo, 메시지 156개, dsV2Gshark로 해석
다음 → chroot로 보드 실측, 또는 MQTT API로 보드 연동
몇 주짜리 OS 이식 앞에서 하루를 PC에 쓴 셈인데, 덕분에 질문이 “EVerest가 될까?”에서 “650MHz에서 충분히 빠를까?” 하나로 줄었다. 그리고 차와 충전기가 실제로 무슨 말을 주고받는지 처음으로 눈으로 봤다. 보드에 올렸을 때 무엇이 오가야 정상인지, 이제 기준이 생겼다.
참고
- EVerest everest-core (저장소
docs/source/— 공식 문서) - dSPACE-group/dsV2Gshark
- SteVe (OCPP 관제 서버)
댓글남기기