7 분 소요

M8에서 OCPP 1.6J 충전 거래를 끝까지 확인했다. 그런데 거래 기록을 보면 늘 이랬다.

StartTransaction  "meterStart": 0
StopTransaction   "meterStop":  0

충전기가 전력량계 값을 모르니 과금할 전력량도 0이다. 오늘 오후(M9-1)에는 보드에 달린 전력량계를 EVerest에 연결했다.

단계 결과
데이터시트 해석 ✅ 레지스터 표 확보, 11/02 시험 값과 일치
EVerest 기본 모듈 ❌ 정수 계량기를 읽을 수 없음
대안 외부 MQTT 프로그램 sc6-meter 작성
보드 단독 시험 ✅ 12/12 성공
OCPP 거래 ✅ meterStart, MeterValues, meterStop 에 계량기 값 전달
값 확인 (AC 부하) ⏳ 나중에

1. 전력량계와 레지스터 표

보드의 전력량계(PM1)는 JSY-MK-163T다. 단상 계량 모듈로, CT로 전류를 재고 TTL UART로 Modbus-RTU 통신을 한다. 보드의 UART5(/dev/ttySTM3)에 바로 붙어 있다. 11/02 하드웨어 시험 때 통신만 확인해 두었다.

요청  01 03 00 00 00 01 84 0A     ← 주소 1, 레지스터 0x0000 1개
응답  01 03 02 01 63 F9 FD        ← 값 0x0163

그때는 0x0163이 무엇인지 몰랐다. 데이터시트(제조사 매뉴얼 V1.2, 단방향판)를 받아 보니 0x0000은 “모델 1, 값 0x0163 고정” 이었다. 이 계량기가 맞다는 확인부터 됐다.

필요한 레지스터를 정리하면 이렇다. 통신 기본값은 4800bps 8N1, 주소 1이다. 값은 1초마다 갱신되고, 모두 부호 없는 정수다.

주소 항목 크기 환산 최대
0x0048 전압 16bit ÷100 → V 655.35 V
0x0049 전류 16bit ÷1000 → A 65.535 A
0x004A 유효전력 16bit ÷10 → W 6,553.5 W
0x004B–4C 누적 전력량 32bit ÷10000 → kWh 429,496 kWh
0x004D 역률 16bit ÷1000 —
0x0051 주파수 16bit ÷100 → Hz —
0x000C–0D 누적 전력량 (쓰기) 32bit 0을 쓰면 초기화 —

표를 보다가 걸리는 것이 하나 있었다. 유효전력이 16bit에 0.1W 단위라 6,553.5W까지만 표현된다. 32A × 230V는 약 7,360W다. 최대 전류로 충전하면 넘친다. 다행히 과금에 쓰는 누적 전력량은 32bit라 문제가 없다. 대책은 3장에 적었다.


2. EVerest 범용 모듈로는 안 된다

EVerest에는 GenericPowermeter라는 범용 전력량계 모듈이 있다. 레지스터 주소와 배율을 YAML 파일에 적으면 어떤 Modbus 계량기든 읽는다는 모듈이다. 원래 계획은 JSY용 YAML 하나를 쓰는 것이었다.

# 모델 파일 형식 (Eastron_SDM230.yaml 일부)
voltage_V:
  start_register: 30001
  function_code_start_reg: 4
  num_registers: 2
  multiplier: 1

그런데 값을 변환하는 함수를 열어 보니 이런 흐름이었다(powermeterImpl.cpp의 merge_register_values_into_element).

레지스터 1개면 그 16bit, 2개면 상위·하위를 합쳐 32bit 정수를 만든다
→ 그 32bit를 그대로 IEEE 754 float 로 재해석한다 (reinterpret_cast)
→ multiplier, 10^exponent 를 곱한다

레지스터 수와 상관없이 무조건 실수로 읽는다. 예제 모델이 모두 Eastron, Klefr 계량기인데, 이 계량기들은 값을 float로 준다. JSY는 정수로 준다. 전압 220.00V는 JSY에서 정수 22000(0x55F0)인데, 이것을 float로 읽으면 약 3×10⁻⁴¹이라는 의미 없는 수가 된다. multiplier로 고칠 수 있는 문제가 아니다.

두 가지 길

  A. 외부 프로그램 + MQTT API B. GenericPowermeter 패치
방식 우리 C 프로그램이 계량기를 읽고 EVerest powermeter_API에 MQTT로 보냄 모듈에 “정수형” 옵션을 추가
장점 sc6-bsp와 같은 구조(M7에서 검증됨). EVerest 수정 없음. 넘침 보정 같은 처리를 우리 코드에서 설정 파일만으로 끝
단점 프로그램 하나 더 오전의 libocpp에 이어 두 번째 EVerest 패치. 업그레이드마다 관리

EVerest 트리에 powermeter_API 모듈이 있었다. sc6-bsp가 쓰는 evse_board_support_API와 같은 계열의 외부 MQTT API다. A로 정했고, 프로그램은 sc6-bsp와 따로 두기로 했다. 계량기 통신이 멈추거나 오류가 나도 CP·릴레이 제어에 영향이 없게 하기 위해서다.


3. 보드에서 먼저 직접 읽어 보기

프로그램을 쓰기 전에 데이터시트 해석이 맞는지 셸에서 확인했다. 데이터시트 예제와 같은 요청(0x0048부터 10개)을 보냈다.

stty -F /dev/ttySTM3 4800 raw -echo cs8 -cstopb -parenb
(timeout 2 cat /dev/ttySTM3 | od -An -tx1) & sleep 0.3
printf '\001\003\000\110\000\012\105\333' > /dev/ttySTM3; wait
01 03 14 | 00 00 | 00 00 | 00 00 | 00 00 00 00 | 00 00 | 00 00 00 00 | 00 00 | 00 00 | a3 67
주소 기능 20바이트 전압   전류   전력   전력량        역률    CO2           예약    주파수  CRC

길이와 형식이 데이터시트대로였고, CRC a3 67도 직접 계산한 값과 맞았다. 값이 모두 0인 것은 계량기 L/N에 AC가 없어서다.

두 번째부터 셸이 멈췄다

같은 명령을 다시 치자 응답 없이 셸이 멈췄다. 5번 반복하는 루프도 첫 응답 뒤에 멈췄다. 계량기가 아니라 보드 쪽 문제로 보였다. 명령마다 포트를 열고 닫는 것이 의심스러워, 포트를 한 번 열어 둔 채로 시험했다.

exec 3<>/dev/ttySTM3
for i in 1 2 3 4 5; do printf '...' >&3; timeout 1 dd bs=25 count=1 <&3 | od -An -tx1 | head -1; done
exec 3<&-
01 03 14 00 00 ...   -- 1
01 03 14 00 00 ...   -- 2
...                  -- 5
15:10:44.764  →  15:10:44.772   ← 닫는 데 8 ms

5번 모두 응답했다. 정확히 어느 단계가 멈췄는지는 끝까지 확인하지 못했다. 다만 실제 프로그램은 포트를 한 번 열고 계속 쓰므로 상관이 없다. 그래도 모뎀 신호를 무시하는 설정(CLOCAL)과 응답 타임아웃은 넣기로 했다.


4. sc6-meter

powermeter_API가 주고받는 메시지는 이렇다(토픽 앞부분 everest_api/1/powermeter/<모듈 이름>/).

방향 토픽 내용
프로그램 → EVerest m2e/powermeter_values 측정값. timestamp와 누적 전력량(Wh)은 필수, 나머지는 선택
프로그램 → EVerest m2e/communication_check “통신 정상” 신호. 5초간 없으면 CommunicationFault
EVerest → 프로그램 e2m/start_transaction, stop_transaction 서명 계량(독일 계량법, OCMF) 거래 요청. 응답 필요
EVerest → 프로그램 e2m/heartbeat EVerest가 살아 있다는 신호

놓치면 안 되는 것: 거래 요청에 꼭 답하기

EvseManager는 충전 거래를 시작할 때 전력량계에 start_transaction을 보낸다. powermeter_API는 외부 프로그램의 답을 기본 550초까지 기다린다. 답하지 않으면 그동안 충전이 시작되지 않는다. EvseManager 소스를 보니 응답이 NOT_SUPPORTED면 그대로 진행하고, UNEXPECTED_ERROR(대기 시간이 지났을 때의 기본 응답이기도 하다)면 설정(fail_on_powermeter_errors)에 따라 충전을 막는다. 그래서 sc6-meter는 이 요청에 즉시 NOT_SUPPORTED 로 답한다. EVerest 설정의 대기 시간도 5초로 줄였다.

동작

1초마다:
  0x0048부터 10개 읽기 (응답 500 ms 대기, 실패 시 2번 재시도)
  → 주소·기능 코드·길이·CRC 확인
  → 전압, 전류, 전력, 누적 전력량, 역률, 주파수로 변환
  → powermeter_values + communication_check 발행

3주기 연속 실패 → communication_check 중단
  → 5초 뒤 EVerest CommunicationFault → EvseManager 가 충전 중지 (과금 불가)

나머지 처리는 이렇게 했다.

  • 전력 넘침 대비: 전압 × 전류 × 역률이 6000W 이상이면 레지스터 값 대신 계산값을 쓴다. 넘칠 때 레지스터가 0으로 돌아가는지, 최댓값에 머무는지는 부하 시험으로 확인할 것이다.
  • 전력량 감소 경고: 누적 전력량이 이전보다 작아지면 로그에 남긴다. 누가 계량기를 초기화했다는 뜻이다.
  • 초기화 명령 없음: 0x000C 쓰기 코드는 아예 넣지 않았다. 이 프로그램은 읽기만 한다.
  • -1 옵션: MQTT 없이 한 번 읽어 출력하고 끝낸다. 현장에서 계량기 확인용이다.

설정 읽기(/etc/default/sc6-meter, KEY=VALUE)와 MQTT 연결 부분은 sc6-bsp 코드를 그대로 가져왔다. 약 500줄이고, meta-sc6에 recipes-evse/sc6-meter 레시피로 넣어 빌드 PC에서 빌드했다.

단독 시험

$ ./sc6-meter -1
모델     0x0163 (JSY-MK-163)
전압     0.00 V
전류     0.000 A
전력     0.0 W (레지스터 0.0, 계산 V*I*PF 0.0)
전력량   0.0000 kWh (0.0 Wh, 원시값 0)

$ ./sc6-meter -v
15:22:10.460 MQTT 연결 localhost:1883, 구독 everest_api/1/powermeter/powermeter_1/e2m/#
15:22:10.553 0.00 V, 0.000 A, 0.0 W, ... (성공 1, 실패 0)
...
15:22:21.708 종료 (성공 12, 실패 0)

요청부터 응답까지 약 90 ms. 12번 모두 성공했다.


5. EVerest에 연결

EVerest 구성 파일에 모듈 하나와 연결 하나를 더했다.

  powermeter_1:
    module: powermeter_API
    config_module:
      cfg_communication_check_to_s: 5
      cfg_heartbeat_interval_ms: 1000
      cfg_request_reply_to_s: 5        # 기본 550초

  connector_1:
    module: EvseManager
    connections:
      powermeter_grid_side:            # 새로 추가
        - module_id: powermeter_1
          implementation_id: main

실행 스크립트(run-m7-bsp.sh)는 구성 파일에 powermeter_API가 있으면 sc6-meter를 함께 띄우고, 끝날 때 같이 끄도록 고쳤다.

15:25:28.267 [INFO] powermeter_1 :: Module powermeter_1 initialized
15:25:28.504 sc6-meter: EVerest heartbeat 수신 (토픽 everest_api/1/powermeter/powermeter_1/e2m/heartbeat 확인)
15:25:29.137 sc6-meter: stop_transaction 요청 -> NOT_SUPPORTED

heartbeat가 들어온 것으로 토픽 이름이 맞다는 것이 확인됐다. 바로 뒤의 stop_transaction은 EvseManager가 시작할 때 “혹시 열려 있는 거래가 있으면 닫으라”며 빈 거래 번호로 보내는 요청이다. 응답 경로까지 시험된 셈이다.

OCPP 접속도 정상이었다. 이번에도 접속 2 ms 만에 서버의 GetConfiguration이 먼저 왔지만, 52 ms 뒤 BootNotification이 나갔다. 오전에 넣은 libocpp 패치({ post_url 2026-11-06-sc6-m8-5-network-loss-libocpp-patch ))가 계속 효과를 내고 있다. 보드 시각도 서버와 맞았다({ post_url 2026-11-06-sc6-ntp-networkd-online-state }).


6. 충전 1회 (거래 8)

지그로 차량 연결 → 충전 → 3분 유지 → 분리를 했다.

시각 메시지 내용
15:27:43.915 sc6-meter start_transaction → NOT_SUPPORTED
15:27:44.003 StartTransaction meterStart: 0. 대기 없이 바로
15:28:44 MeterValues 60초 주기 (Sample.Periodic)
15:29:44 MeterValues 〃
15:30:00 MeterValues 15분 정각 기준 (Sample.Clock)
15:30:44 MeterValues 60초 주기
15:30:53.315 sc6-meter stop_transaction → NOT_SUPPORTED
15:30:53.448 StopTransaction meterStop: 0, transactionData에 측정값 4개

SteVe에 들어간 MeterValues 하나를 보면 이렇다.

{"connectorId":1, "transactionId":8,
 "meterValue":[{"timestamp":"2026-11-06T06:28:43.081Z",
   "sampledValue":[
     {"measurand":"Energy.Active.Import.Register","unit":"Wh","value":"0.00","context":"Sample.Periodic"},
     {"measurand":"Energy.Active.Import.Register","phase":"L1","unit":"Wh","value":"0.00", ...}]}]}
  • timestamp 06:28:43.081Z는 sc6-meter가 계량기를 읽은 시각이다. OCPP 전송(15:28:44.008)보다 약 1초 앞선다. 측정 시각이 그대로 전달된다.
  • 합계와 L1 두 값이 오는 것은 sc6-meter가 둘 다 보내서다.
  • 오류는 없었다. sc6-meter 통신 실패 0번, EVerest의 CommunicationFault 0번.

값이 0인 것은 계량기에 AC가 없어서다. 이번 시험으로 계량기 값이 거래 시작부터 끝까지 OCPP로 가는 경로는 확인됐다. 그 값이 맞는지는 아직 확인하지 못했다.


7. 정리

커밋

meta-sc6  9b5c077 M9-1: 전력량계 JSY-MK-163T -> EVerest powermeter_API 외부 모듈 sc6-meter
sc6-work  630ccde M9-1: EVerest 구성에 전력량계(powermeter_API + sc6-meter) 연결, 시험·조사 스크립트

오늘 배운 것

  1. “범용”의 범위를 소스에서 확인하자. 설정 파일로 무엇이든 될 것 같은 모듈이 실제로는 float 계량기만 읽었다. 예제가 모두 같은 형식이라는 것이 단서였다.
  2. 확인되지 않던 숫자가 데이터시트에서 풀렸다. 11/02의 0x0163은 “모델 1”이었다. 하드웨어 시험 때 남긴 원시값이 오늘 계량기 확인에 쓰였다.
  3. 요청-응답 API는 “응답 안 함”의 대가를 보자. 서명 거래 요청을 무시했다면 충전이 9분 넘게 시작되지 않았을 것이다. 지원하지 않는 기능이라도 바로 “지원 안 함”이라고 답해야 한다.
  4. 레지스터 크기는 제품 정격과 맞춰 보자. 16bit × 0.1W는 7kW 충전기에 부족하다. 과금 값(32bit)과 표시 값(16bit)의 차이를 미리 알고 보정해 두었다.
  5. 셸 시험과 프로그램은 다르게 움직인다. 명령마다 포트를 열고 닫으면 멈췄지만, 열어 둔 상태에서는 문제가 없었다. 시험 방법도 실제 사용 방식에 맞춰야 한다.

남은 것

항목 내용
AC 부하 시험 220V와 부하로 전압, 전류, 전력량이 맞는지 확인. 6.5kW 이상에서 유효전력 레지스터가 어떻게 되는지 (안전 조치는 설계자와 협의)
계량기 실패 정책 지금은 계량기 통신이 끊기면 충전이 멈춘다 (EVerest 기본: 과금 불가). 유지할지 결정
MeterValues 항목 지금은 전력량만 보낸다. 전력, 전류, 전압 추가 여부 (MeterValuesSampledData)
이미지 반영 sc6-meter와 구성 파일을 M7-6 때 이미지에

댓글남기기