☀️

WiSUNモジュールでスマートメーターBルートと接続するときの情報とか

に公開

ROHM社BP35A1/BP35C0-J11-T01でスマートメーターBルートに接続する情報を書いてみます。

通信プロトコルスタック

スマートメーターBルートの通信プロトコルスタックは物理層にSub-Ghz(920MHz)にECHONET Lite が載ったプロトコル。

都合上画像引用を避けるので、このページの「通信プロトコルスタック」の図にある Wi-SUNの部分を参照してください。

SKSTACK-IPプロトコル

最終販売になっているBP35A1に組み込まれているSKSTACK-IPの情報はこのページの通りにしておけばいいです。
https://qiita.com/rukihena/items/82266ed3a43e4b652adb

・・・丸投げするのもどうかと思うので、Go言語で書いてみました。
Go言語は好きです。なぜかというと、ビルドが早いし同じプログラムがラズパイでも動くから。

スマートメーターBルート通信はBP35A1コマンドリファレンスマニュアルにある「コマンドチュートリル」の「10.3.2 Wi-SUN デバイスの起動」という章どおりに書けばいいです。

このプログラムはUSBドングル RL7023 Stick-D/IPSをUSBに接続した、Windows11上のWSL2で確認しています。

$ go version
go version go1.24.1 linux/amd64

WSL2上のubuntuにUSBを認識させるためにリンク先の方法を使ってます。

> usbipd list
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
1-7    1a81:2066  USB 入力デバイス                                              Not shared
1-8    0403:6015  USB Serial Converter                                          Attached
1-11   103c:84fd  USB 入力デバイス                                              Not shared
1-14   0bda:b00b  Realtek Bluetooth 4.2 Adapter                                 Not shared
3-1    046d:c52b  Logitech USB Input Device, USB 入力デバイス                   Not shared

VID:PID=0403:6015といえばFTDI社のUSB-UARTブリッジ、これがRL7023/IPSだから。

> usbipd attach --wsl --busid 1-8

この手順を行うと、プログラムから/dev/ttyUSB0を指定することでWSLからアクセスできるようになる。

事前にBルートIDとパスワードを入力しておいてください。
アクティブスキャンでスマートメーターを検出できれば、main関数のrunPairingSequenceフラグをfalseにしておくとアクティブスキャンを省略します。
BルートIDとパスワードは設定ファイルに書かれている方を使います。

main.go
// main.go BP35A1などでスマートメーターから消費電力他を得る
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2025 Akihiro Yamamoto <github.com/ak1211>
package main

import (
	"bufio"
	"bytes"
	"context"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"math"
	"net/netip"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/tarm/serial"
)

// 設定
type Settings struct {
	RouteBId       string `json:"RouteBId"`
	RouteBPassword string `json:"RouteBPassword"`
	Channel        int    `json:"Channel"`
	MacAddress     string `json:"MacAddress"`
	PanId          int    `json:"PanId"`
}

var (
	settingsFileName   = "settings.json"
	serialDeviceName   = "/dev/ttyUSB0"
	routeBId           = "input ID here"       // ルートB ID(32バイト)
	routeBPassword     = "input PASSWORD here" // ルートB パスワード(12バイト)
	activescanDuration = 5
	eSmartmeterProps   = EchonetliteFrame{
		ehd:  0x1081,
		tid:  0x0001,
		seoj: [3]byte{0x05, 0xff, 0x01}, // home controller
		deoj: [3]byte{0x02, 0x88, 0x01}, // smartmeter
		esv:  0x62,                      // get要求
		opc:  0x03,                      // 3つ
		edata: []EchonetliteEdata{
			// go言語では初期値0なのでpdc,edtは省略する
			{epc: 0x8a}, // メーカーコード
			{epc: 0xd3}, // 係数(存在しない場合は×1倍)
			{epc: 0xe1}, // 積算電力量単位(正方向、逆方向計測値)
		}}
	eLatestCWh = EchonetliteFrame{ // 定時積算電力量計測値を取得するechonet lite電文
		ehd:   0x1081,                          // 0x1081 = echonet lite
		tid:   1,                               // tid
		seoj:  [3]byte{0x05, 0xff, 0x01},       // home controller
		deoj:  [3]byte{0x02, 0x88, 0x01},       // smartmeter
		esv:   0x62,                            // get要求
		opc:   1,                               // 1つ
		edata: []EchonetliteEdata{{epc: 0xea}}, // 定時積算電力量計測値(正方向計測値)
	}
	eCWhHistories = EchonetliteFrame{ // 今日の積算電力量履歴を取得するechonet lite電文
		ehd:   0x1081,                          // 0x1081 = echonet lite
		tid:   1,                               // tid
		seoj:  [3]byte{0x05, 0xff, 0x01},       // home controller
		deoj:  [3]byte{0x02, 0x88, 0x01},       // smartmeter
		esv:   0x62,                            // get要求
		opc:   1,                               // 1つ
		edata: []EchonetliteEdata{{epc: 0xe2}}, // 積算電力量計測値履歴1
	}
	eCumlativeWattHour = EchonetliteFrame{ // 積算電力量計測値を取得するechonet lite電文
		ehd:   0x1081,                          // 0x1081 = echonet lite
		tid:   1,                               // tid
		seoj:  [3]byte{0x05, 0xff, 0x01},       // home controller
		deoj:  [3]byte{0x02, 0x88, 0x01},       // smartmeter
		esv:   0x62,                            // get要求
		opc:   1,                               // 1つ
		edata: []EchonetliteEdata{{epc: 0xe0}}, // 積算電力量計測値(正方向計測値)
	}
	eInstantWattAmpere = EchonetliteFrame{ // 瞬時電力と瞬時電流計測値を取得するechonet lite電文
		ehd:  0x1081,                    // 0x1081 = echonet lite
		tid:  1,                         // tid
		seoj: [3]byte{0x05, 0xff, 0x01}, // home controller
		deoj: [3]byte{0x02, 0x88, 0x01}, // smartmeter
		esv:  0x62,                      // get要求
		opc:  2,                         // 2つ
		edata: []EchonetliteEdata{
			{epc: 0xe7}, // 瞬時電力計測値
			{epc: 0xe8}, // 瞬時電流計測値
		},
	}
)

func main() {
	runPairingSequence := false // 初回実行はtrue
	// アクティブスキャンで一度スマートメーターを検出すると設定ファイルが作られるので、
	// それ以降はfalseにしておくと動作が早い。
	if runPairingSequence {
		ok, err := pairing()
		if err != nil {
			panic(err)
		}
		if !ok {
			return
		}
	}
	err := run()
	if err != nil {
		panic(err)
	}
}

// 接続するスマートメーターをアクティブスキャンで探す
func pairing() (bool, error) {
	config := &serial.Config{
		Name: serialDeviceName,
		Baud: 115200,
		Size: 8,
	}
	ser, err := serial.OpenPort(config)
	if err != nil {
		return false, err
	}
	sc := bufio.NewScanner(ser)

	commands := []string{
		"SKRESET", // リセット
		fmt.Sprintf("SKSETPWD %X %s", len(routeBPassword), routeBPassword), // パスワードを登録する。
		fmt.Sprintf("SKSETRBID %s", routeBId),                              // IDを登録する。
		fmt.Sprintf("SKSCAN 2 FFFFFFFF %X", activescanDuration),            // アクティブスキャン
	}
	for _, cmd := range commands {
		if err := sendCommand(sc, ser, cmd); err != nil {
			return false, err
		}
	}
	var events [][]byte
	// アクティブスキャン後のイベント待ち
	for {
		sc.Scan()
		if err := sc.Err(); err != nil {
			return false, err
		}
		if bytes.HasPrefix(sc.Bytes(), []byte("EVENT 22")) { // EVENT 0x22 = アクティブスキャン完了
			break
		}
		events = append(events, sc.Bytes())
	}
	// アクティブスキャン結果
	beacon := make(map[string]string)
	for i, b := range events {
		if bytes.Equal(b, []byte("EPANDESC")) {
			// EPANDESC 行の次行から6行
			for _, v := range events[i+1 : i+7] {
				tokens := bytes.Split(v, []byte{':'})
				key := bytes.Trim(tokens[0], " ")
				beacon[string(key)] = string(tokens[1])
			}
		}
	}
	if len(beacon) == 0 {
		fmt.Println("スマートメーターが見つかりませんでした")
		return false, nil
	}
	for key, value := range beacon {
		log.Printf("%s -> %s", key, value)
	}

	channel, err := strconv.ParseUint(beacon["Channel"], 16, 8)
	if err != nil {
		return false, err
	}
	panId, err := strconv.ParseUint(beacon["Pan ID"], 16, 16)
	if err != nil {
		return false, err
	}

	// 設定ファイルに見つかったスマートメーターの情報を保存する
	settings := Settings{
		RouteBId:       string(routeBId[:]),
		RouteBPassword: string(routeBPassword[:]),
		Channel:        int(channel),
		MacAddress:     beacon["Addr"],
		PanId:          int(panId),
	}
	jsonbytes, err := json.MarshalIndent(settings, "", strings.Repeat(" ", 2))
	if err != nil {
		return false, err
	}

	err = os.WriteFile(settingsFileName, jsonbytes, 0644)
	if err != nil {
		return false, err
	}

	fmt.Println("Bye")
	return true, nil
}

// スマートメーターから電力消費量を得る
func run() error {
	// 設定ファイルからスマートメーターの情報を得る
	jsonbytes, err := os.ReadFile(settingsFileName)
	if err != nil {
		return err
	}
	settings := Settings{}
	err = json.Unmarshal(jsonbytes, &settings)
	if err != nil {
		return err
	}
	// 設定ファイルの情報をグローバル変数にセットする
	routeBId = settings.RouteBId
	routeBPassword = settings.RouteBPassword
	macAddress, err := strconv.ParseUint(settings.MacAddress, 16, 64)
	if err != nil {
		return err
	}
	// MACアドレスからIPv6リンクローカルアドレスへ変換する
	// MACアドレスの最初の1バイト下位2bit目を反転して
	// 0xFE80000000000000XXXXXXXXXXXXXXXXのXXをMACアドレスに置き換える
	address16 := [16]byte{}
	binary.BigEndian.PutUint64(address16[0:8], 0xFE80_0000_0000_0000)
	binary.BigEndian.PutUint64(address16[8:16], macAddress^0x0200_0000_0000_0000)
	ipv6address := netip.AddrFrom16(address16)
	//
	config := &serial.Config{
		Name:        serialDeviceName,
		Baud:        115200,
		Size:        8,
	}
	ser, err := serial.OpenPort(config)
	if err != nil {
		return err
	}

	sc := bufio.NewScanner(ser)

	commands := []string{
		"SKRESET", // リセット
		fmt.Sprintf("SKSETPWD %X %s", len(routeBPassword), routeBPassword), // パスワードを登録する。
		fmt.Sprintf("SKSETRBID %s", routeBId),                              // IDを登録する。
		fmt.Sprintf("SKSREG S2 %X", settings.Channel),                      // 自端末の論理チャンネル番号を設定する
		fmt.Sprintf("SKSREG S3 %X", settings.PanId),                        // 自端末のPAN IDを設定する
		fmt.Sprintf("SKJOIN %s", ipv6address.StringExpanded()),             // PANA認証開始
	}
	for _, cmd := range commands {
		if err := sendCommand(sc, ser, cmd); err != nil {
			return err
		}
	}
	// イベント受信用ゴルーチンを起動する
	eChan := make(chan []byte, 10)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go eventReceiver(ctx, sc, eChan)

	// PANA認証開始後のイベントを処理する
	for e := range eChan {
		if bytes.HasPrefix(e, []byte("EVENT 24")) { // EVENT 0x24 = PANA接続失敗
			return errors.New(sc.Text())
		} else if bytes.HasPrefix(e, []byte("EVENT 25")) { // EVENT 0x25 = PANA接続完了
			break
		} else {
			log.Print(sc.Text())
		}
	}

	// 受信したERXUDPイベント処理用ゴルーチン
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			case e := <-eChan:
				log.Print(string(e))
				if bytes.HasPrefix(e, []byte("ERXUDP")) { // UDP受信イベント
					tokens := bytes.Split(e, []byte{' '}) // スペースで分割
					// tokens[0]: ERXUDP
					// tokens[1]: 送信元IPv6アドレス
					// tokens[2]: 送信先IPv6アドレス
					// tokens[3]: 送信元ポート番号
					// tokens[4]: 送信先ポート番号
					// tokens[5]: 送信元のMACアドレス
					// tokens[6]: 0=暗号化なし, 1=暗号化あり
					// tokens[7]: 受信したデータの長さ(4文字の16進数)
					// tokens[8]: 受信したデータ
					dataLen, err := strconv.ParseUint(string(tokens[7]), 16, 16)
					if err != nil {
						panic(err)
					}
					data := make([]byte, dataLen)
					if _, err := hex.Decode(data, tokens[8]); err != nil {
						panic(err)
					}
					frame, err := ParseEchonetliteFrame(data)
					if err != nil {
						panic(err)
					}
					frame.Show()
				}
			}
		}
	}()

	// 送信するメッセージ
	messages := []EchonetliteFrame{eSmartmeterProps, eLatestCWh, eCWhHistories,
		eCumlativeWattHour, eInstantWattAmpere, eInstantWattAmpere, eInstantWattAmpere,
	}
	for _, msg := range messages {
		time.Sleep(60 * time.Second) // 時間待ち(1分)
		payload := msg.Encode()
		cmd := fmt.Sprintf("SKSENDTO 1 %s %04X 1 %04X ", ipv6address.StringExpanded(), 0x0E1A, len(payload))
		log.Print(cmd + hex.EncodeToString(payload))
		bytes := append([]byte(cmd), payload...)
		_, err := ser.Write(bytes)
		if err != nil {
			return err
		}
	}
	time.Sleep(3 * time.Second) // 最後に送ったSKSENDTOによるERXUDPイベントの受信を待つ
	fmt.Println("Bye")
	return nil
}

func sendCommand(sc *bufio.Scanner, w io.Writer, cmd string) error {
	log.Print(cmd)
	_, err := w.Write([]byte(cmd + "\r\n"))
	if err != nil {
		return err
	}
	for done := false; !done; {
		sc.Scan()
		if err := sc.Err(); err != nil {
			return err
		}
		b := sc.Bytes()
		switch {
		case bytes.Equal(b, []byte(cmd)):
			// エコーバックなのでこの行は無かったことにする
		case bytes.HasPrefix(b, []byte("OK")):
			done = true
		case bytes.HasPrefix(b, []byte("FAIL")):
			return errors.New(string(b))
		}
	}
	return nil
}

// 受信したイベントをGoチャネルに送る
func eventReceiver(ctx context.Context, sc *bufio.Scanner, eChan chan []byte) {
	for {
		sc.Scan()
		if err := sc.Err(); err != nil {
			panic(err)
		}
		b := sc.Bytes()
		select {
		case <-ctx.Done():
			return
		default:
			if bytes.HasPrefix(b, []byte("OK")) {
				// OKの返答は無視する
			} else if bytes.HasPrefix(b, []byte("FAIL")) {
				panic(errors.New(string(b)))
			} else if bytes.HasPrefix(b, []byte("E")) {
				eChan <- b // 'E'から始まるイベントをGoチャネルに送る
			}
		}
	}
}

type EchonetliteFrame struct {
	ehd   uint16
	tid   uint16
	seoj  [3]byte
	deoj  [3]byte
	esv   byte
	opc   byte
	edata []EchonetliteEdata
}

func (e *EchonetliteFrame) Encode() []byte {
	var b []byte
	b = binary.BigEndian.AppendUint16(b, e.ehd)
	b = binary.BigEndian.AppendUint16(b, e.tid)
	b = append(b, e.seoj[:]...)
	b = append(b, e.deoj[:]...)
	b = append(b, e.esv, e.opc)
	for _, v := range e.edata {
		b = append(b, v.Encode()...)
	}
	return b
}

type EchonetliteEdata struct {
	epc byte
	pdc byte
	edt []byte
}

func (e *EchonetliteEdata) Encode() []byte {
	var b []byte
	b = append(b, e.epc, e.pdc)
	b = append(b, e.edt...)
	return b
}

func ParseEchonetliteFrame(data []byte) (*EchonetliteFrame, error) {
	if len := len(data); len <= 12 {
		return nil, fmt.Errorf("bad length(%d)", len)
	}
	var frame EchonetliteFrame
	//
	frame.ehd = binary.BigEndian.Uint16(data[0:2])
	if frame.ehd != 0x1081 {
		return nil, fmt.Errorf("ehd:0x%x this is not an echonetlite frame", frame.ehd)
	}
	frame.tid = binary.BigEndian.Uint16(data[2:4])
	frame.seoj = [3]byte(data[4:7])
	frame.deoj = [3]byte(data[7:10])
	frame.esv = data[10]
	frame.opc = data[11]
	props := data[12:]
	for i := 0; i < int(frame.opc); i++ {
		frame.edata = append(frame.edata, EchonetliteEdata{
			epc: props[0],              // 要求
			pdc: props[1],              // データ数
			edt: props[2 : 2+props[1]], // データ
		})
		props = props[2+props[1]:]
	}
	return &frame, nil
}

func (e *EchonetliteFrame) Show() {
	n := len(e.edata)
	switch e.esv {
	case 0x50: // SetI_SNA
		log.Printf("SetI_SNAプロパティ値書き込み要求不可応答 N=%d", n)
	case 0x51: // SetC_SNA
		log.Printf("SetC_SNAプロパティ値書き込み要求不可応答 N=%d", n)
	case 0x52: // Get_SNA
		log.Printf("Get_SNAプロパティ値読み出し不可応答 N=%d", n)
	case 0x53: // INF_SNA
		log.Printf("INF_SNAプロパティ値通知不可応答 N=%d", n)
	case 0x71: // Set_res
		log.Printf("Set_resプロパティ値書き込み応答 N=%d", n)
	case 0x72: // Get_res
		log.Printf("Get_resプロパティ値読み出し応答 N=%d", n)
	case 0x73: // INF
		log.Printf("INFプロパティ値通知 N=%d", n)
	case 0x74: // INFC
		log.Printf("INFCプロパティ値通知(応答要) N=%d", n)
	default:
		log.Printf("よくわからないESV値 N=%d frame=%s", n, hex.EncodeToString(e.Encode()))
	}
	for i := 0; i < n; i++ {
		e.edata[i].Show()
	}
}

// EDATA値を表示する
func (e *EchonetliteEdata) Show() {
	switch e.epc {
	case 0x80: // 動作状態
		s := fmt.Sprintf("N/A(epc:0x%02x)", e.epc)
		switch {
		case e.edt[0] == 0x30:
			s = "動作中"
		case e.edt[0] == 0x31:
			s = "未動作"
		}
		log.Printf("動作状態=%s", s)
	case 0x88: // 異常発生状態
		s := fmt.Sprintf("N/A(epc:0x%02x)", e.epc)
		switch {
		case e.edt[0] == 0x41:
			s = "異常発生あり"
		case e.edt[0] == 0x42:
			s = "異常発生なし"
		}
		log.Printf("異常発生状態=%s", s)
	case 0x8a: // メーカーコード
		s := fmt.Sprintf("N/A(epc:0x%02x)", e.epc)
		if len(e.edt) >= 3 {
			manufacturer := [3]byte{}
			copy(manufacturer[:], e.edt)
			s = hex.EncodeToString(manufacturer[:])
		}
		log.Printf("製造者コード(hex)=%s", s)
	case 0xd3: // 係数
		s := fmt.Sprintf("N/A(epc:0x%02x)", e.epc)
		if len(e.edt) >= 1 {
			s = strconv.FormatInt(int64(e.edt[0]), 10)
		}
		log.Printf("係数=%s", s)
	case 0xd5: // インスタンスリスト通知
		var ss []string
		for i := 1; i < len(e.edt); i += 3 {
			ss = append(ss, hex.EncodeToString(e.edt[i:i+3]))
		}
		log.Printf("インスタンスリスト=%d個 [%s]", e.edt[0], strings.Join(ss, ","))
	case 0xd7: // 積算電力量有効桁数
		s := fmt.Sprintf("N/A(epc:0x%02x)", e.epc)
		if len(e.edt) >= 1 {
			s = strconv.FormatInt(int64(e.edt[0]), 10)
		}
		log.Printf("積算電力量有効桁数 %s 桁", s)
	case 0xe0: // 積算電力量計測値(正方向計測値)
		s := fmt.Sprintf("N/A(epc:0x%02x)", e.epc)
		if len(e.edt) >= 4 {
			cwh := binary.BigEndian.Uint32(e.edt)
			s = strconv.FormatInt(int64(cwh), 10)
		}
		log.Printf("積算電力量=%s", s)
	case 0xe1: // 積算電力量単位(正方向、逆方向計測値)
		var powersOfTen int
		switch {
		case e.edt[0] == 0x00:
			powersOfTen = 0
		case e.edt[0] == 0x01:
			powersOfTen = -1
		case e.edt[0] == 0x02:
			powersOfTen = -2
		case e.edt[0] == 0x03:
			powersOfTen = -3
		case e.edt[0] == 0x04:
			powersOfTen = -4
		case e.edt[0] == 0x0a:
			powersOfTen = 1
		case e.edt[0] == 0x0b:
			powersOfTen = 2
		case e.edt[0] == 0x0c:
			powersOfTen = 3
		case e.edt[0] == 0x0d:
			powersOfTen = 4
		default:
			powersOfTen = 0xff
		}
		s := fmt.Sprintf("%f kWh", math.Pow10(powersOfTen))
		log.Printf("積算電力量単位=%s", s)
	case 0xe2: // 積算電力量計測値履歴1 (正方向計測値)
		s := fmt.Sprintf("N/A(epc:0x%02x)", e.epc)
		if len(e.edt) >= 194 {
			day := binary.BigEndian.Uint16(e.edt[0:2])
			var ss [48]string
			for i := 0; i < 48; i++ {
				v := binary.BigEndian.Uint32(e.edt[2+4*i:])
				if v == 0xfffffffe {
					ss[i] = fmt.Sprintf("%8s", "N/A")
				} else {
					ss[i] = fmt.Sprintf("%8d", v)
				}
			}
			s = fmt.Sprintf("%d日前[", day) + strings.Join(ss[:], ",") + "]"
		}
		log.Printf("積算電力量計測値履歴1 (正方向計測値)=%s", s)
	case 0xe7: // 瞬時電力計測値
		s := fmt.Sprintf("N/A(epc:0x%02x)", e.epc)
		if len(e.edt) >= 4 {
			iwatt := int32(binary.BigEndian.Uint32(e.edt)) // マイナスの値もある
			s = strconv.FormatInt(int64(iwatt), 10)
		}
		log.Printf("瞬時電力=%s W", s)
	case 0xe8: // 瞬時電流計測値
		s := fmt.Sprintf("N/A(epc:0x%02x)", e.epc)
		if len(e.edt) >= 4 {
			r := int16(binary.BigEndian.Uint16(e.edt[0:2])) // マイナスの値もある
			t := int16(binary.BigEndian.Uint16(e.edt[2:4])) // マイナスの値もある
			if t == 0x7ffe {                                // 単相2線式
				s = fmt.Sprintf("(1φ2W) %3d.%01d A", r/10, r%10)
			} else {
				s = fmt.Sprintf("(1φ3W) R=%3d.%01d A, T=%3d.%01d A", r/10, r%10, t/10, t%10)
			}
		}
		log.Printf("瞬時電流=%s", s)
	case 0xea: // 定時積算電力量計測値(正方向計測値)
		s := "N/A"
		if len(e.edt) >= 11 {
			year := binary.BigEndian.Uint16(e.edt[0:2])
			month := e.edt[2]
			day := e.edt[3]
			hour := e.edt[4]
			minute := e.edt[5]
			second := e.edt[6]
			cwh := binary.BigEndian.Uint32(e.edt[7:])
			s = fmt.Sprintf("%04d/%02d/%02d %02d:%02d:%02d (%8d)", year, month, day, hour, minute, second, cwh)
		}
		log.Printf("定時積算電力量計測値(正方向計測値)=%s", s)
	default:
		log.Printf("edata epc(hex)=%02x pdc(hex)=%02x edt(hex)=%02x", e.epc, e.pdc, e.edt)
	}
}
$ go run main.go
2025/03/30 16:07:55 SKRESET
2025/03/30 16:07:55 SKSETPWD C ************
2025/03/30 16:07:55 SKSETRBID ********************************
2025/03/30 16:07:55 SKSREG S2 **
2025/03/30 16:07:55 SKSREG S3 ****
2025/03/30 16:07:55 SKJOIN fe80:0000:0000:0000:****:****:****:****
2025/03/30 16:07:55 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 02
2025/03/30 16:07:55 EVENT 02 FE80:0000:0000:0000:****:****:****:****
2025/03/30 16:07:56 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 02CC 02CC **************** 0 0028 ********************************************************************************
2025/03/30 16:07:56 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:07:56 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 02CC 02CC **************** 0 0068 ****************************************************************************************************************************************************************************************************************
2025/03/30 16:07:56 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:07:57 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 02CC 02CC **************** 0 0054 ************************************************************************************************************************************************************************
2025/03/30 16:07:57 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:07:57 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 02CC 02CC **************** 0 0058 ********************************************************************************************************************************************************************************
2025/03/30 16:07:58 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:08:01 ERXUDP FE80:0000:0000:0000:****:****:****:**** FF02:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 0012 ************************************
2025/03/30 16:08:01 INFプロパティ値通知 N=1
2025/03/30 16:08:01 インスタンスリスト=1個 [028801]
2025/03/30 16:09:03 SKSENDTO 1 fe80:0000:0000:0000:****:****:****:**** 0E1A 1 0012 1081000105ff0102880162038a00d300e100
2025/03/30 16:09:04 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:09:04 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 0016 1081000102880105FF0152038A03000009D300E10102
2025/03/30 16:09:04 Get_SNAプロパティ値読み出し不可応答 N=3
2025/03/30 16:09:04 製造者コード(hex)=000009
2025/03/30 16:09:04 係数=N/A(epc:0xd3)
2025/03/30 16:09:04 積算電力量単位=0.010000 kWh
2025/03/30 16:10:09 SKSENDTO 1 fe80:0000:0000:0000:****:****:****:**** 0E1A 1 000E 1081000105ff010288016201ea00
2025/03/30 16:10:09 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:10:09 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 0019 1081000102880105FF017201EA0B07E9031E100000001A5EFA
2025/03/30 16:10:09 Get_resプロパティ値読み出し応答 N=1
2025/03/30 16:10:09 定時積算電力量計測値(正方向計測値)=2025/03/30 16:00:00 ( 1728250)
2025/03/30 16:11:15 SKSENDTO 1 fe80:0000:0000:0000:****:****:****:**** 0E1A 1 000E 1081000105ff010288016201e200
2025/03/30 16:11:15 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:11:16 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 00D0 1081000102880105FF017201E2C20000001A5B7B001A5B8B001A5B9B001A5BAD001A5BC8001A5BD8001A5BE8001A5BF7001A5C09001A5C19001A5C29001A5C3A001A5C71001A5C96001A5CB7001A5CD9001A5CF6001A5D0F001A5D2B001A5D4A001A5D6B001A5D93001A5DB4001A5DD3001A5DF2001A5E12001A5E32001A5E58001A5E77001A5E9C001A5EBC001A5EDA001A5EFAFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFEFFFFFFFE
2025/03/30 16:11:16 Get_resプロパティ値読み出し応答 N=1
2025/03/30 16:11:16 積算電力量計測値履歴1 (正方向計測値)=0日前[ 1727355, 1727371, 1727387, 1727405, 1727432, 1727448, 1727464, 1727479, 1727497, 1727513, 1727529, 1727546, 1727601, 1727638, 1727671, 1727705, 1727734, 1727759, 1727787, 1727818, 1727851, 1727891, 1727924, 1727955, 1727986, 1728018, 1728050, 1728088, 1728119, 1728156, 1728188, 1728218, 1728250,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A]
2025/03/30 16:12:21 SKSENDTO 1 fe80:0000:0000:0000:****:****:****:**** 0E1A 1 000E 1081000105ff010288016201e000
2025/03/30 16:12:21 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:12:21 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 0012 1081000102880105FF017201E004001A5F07
2025/03/30 16:12:21 Get_resプロパティ値読み出し応答 N=1
2025/03/30 16:12:21 積算電力量=1728263
2025/03/30 16:13:26 SKSENDTO 1 fe80:0000:0000:0000:****:****:****:**** 0E1A 1 0010 1081000105ff010288016202e700e800
2025/03/30 16:13:26 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:13:27 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 0018 1081000102880105FF017202E70400000208E804002A000D
2025/03/30 16:13:27 Get_resプロパティ値読み出し応答 N=2
2025/03/30 16:13:27 瞬時電力=520 W
2025/03/30 16:13:27 瞬時電流=(1φ3W) R=  4.2 A, T=  1.3 A
2025/03/30 16:14:29 SKSENDTO 1 fe80:0000:0000:0000:****:****:****:**** 0E1A 1 0010 1081000105ff010288016202e700e800
2025/03/30 16:14:29 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:14:30 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 0018 1081000102880105FF017202E70400000218E804002C000D
2025/03/30 16:14:30 Get_resプロパティ値読み出し応答 N=2
2025/03/30 16:14:30 瞬時電力=536 W
2025/03/30 16:14:30 瞬時電流=(1φ3W) R=  4.4 A, T=  1.3 A
2025/03/30 16:15:37 SKSENDTO 1 fe80:0000:0000:0000:****:****:****:**** 0E1A 1 0010 1081000105ff010288016202e700e800
2025/03/30 16:15:37 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:15:37 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 0018 1081000102880105FF017202E70400000208E80400280010
2025/03/30 16:15:37 Get_resプロパティ値読み出し応答 N=2
2025/03/30 16:15:37 瞬時電力=520 W
2025/03/30 16:15:37 瞬時電流=(1φ3W) R=  4.0 A, T=  1.6 A
Bye

Wi-SUN Enhanced HAN plus B-Route Dual stackプロトコル

BP35A1とかWSR35A1-00とかRL7023 Stick-D/IPSとかのSKSTACKの情報はインターネットに沢山あるから他のページを見てほしい。

前座はほどほどにして、ここからが真打。

BP35C0-J11を搭載したevaluation boardであるBP35C2-J11-T01を入手したので同じことをしてみる。

BP35C0-J11のページにある「BP35C0-J11 UART IF 仕様書」を参考にこれを書き換えて、コマンドと応答を把握してから、同じくBP35C0-J11のページにあるアプリケーションノート「Wi-SUNモジュールBルート,Enhanced HAN対応BP35C0-J11 Bルート通信について」資料を参考に書いてみた。

https://github.com/ak1211/BRouteJ11

※ペアリング後は、上位アプリにて電力スマートメーターのチャネルを保持・設定することで、次回接続時よりアクティブスキャンすることなく接続が可能です。
※モジュールの動作モードがDual である場合のみ、B ルートのエンドデバイスとして電力スマートメーターとの接続が可能です。

資料に毎回アクティブスキャンする必要は無いように書いてあるので、検出したスマートメーターを設定ファイルに保存してから実行するようにした。

$ go build
$ ./BRouteJ11 pairing  --id "ID" --password "パスワード" -T 5
$ ./BRouteJ11  run
time=2025-03-30T14:52:46.870+09:00 level=DEBUG msg=CommandInitialSetup result=ok
time=2025-03-30T14:52:46.885+09:00 level=DEBUG msg=CommandSetPanaAuthInfo result=ok
time=2025-03-30T14:52:47.105+09:00 level=DEBUG msg=CommandBRouteStart result=ok rssi=-74
time=2025-03-30T14:52:47.120+09:00 level=DEBUG msg=CommandUdpPortOpen result=ok
time=2025-03-30T14:52:47.149+09:00 level=DEBUG msg=CommandBRouteStartPana result=ok
time=2025-03-30T14:52:48.220+09:00 level=INFO msg="connection successful"
time=2025-03-30T14:52:51.332+09:00 level=DEBUG msg=Received senderAddressType=マルチキャスト secure=暗号化あり rssi=-75 dataBytes=18 data(hex)=108193530ef0010ef0017301d50401028801
time=2025-03-30T14:52:51.332+09:00 level=INFO msg=INFプロパティ値通知 N=1
time=2025-03-30T14:52:51.332+09:00 level=INFO msg=edata インスタンスリスト="1個 [028801]"
time=2025-03-30T14:52:51.360+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:52:51.623+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-75 dataBytes=15 data(hex)=1081000102880105ff017201800130
time=2025-03-30T14:52:51.624+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=1
time=2025-03-30T14:52:51.624+09:00 level=INFO msg=edata 動作状態=動作中
time=2025-03-30T14:52:52.387+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:52:52.548+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-75 dataBytes=15 data(hex)=1081000102880105ff017201880142
time=2025-03-30T14:52:52.548+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=1
time=2025-03-30T14:52:52.548+09:00 level=INFO msg=edata 異常発生状態=異常発生なし
time=2025-03-30T14:52:53.413+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:52:53.648+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-74 dataBytes=17 data(hex)=1081000102880105ff0172018a03000009
time=2025-03-30T14:52:53.648+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=1
time=2025-03-30T14:52:53.648+09:00 level=INFO msg=edata 製造者コード(hex)=000009
time=2025-03-30T14:52:54.441+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:52:57.615+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-74 dataBytes=14 data(hex)=1081000102880105ff015201d300
time=2025-03-30T14:52:57.615+09:00 level=INFO msg=Get_SNAプロパティ値読み出し不可応答 N=1
time=2025-03-30T14:52:57.616+09:00 level=INFO msg=edata 係数=N/A(epc:0xd3)
time=2025-03-30T14:52:58.290+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:52:58.628+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-75 dataBytes=15 data(hex)=1081000102880105ff017201d70107
time=2025-03-30T14:52:58.628+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=1
time=2025-03-30T14:52:58.628+09:00 level=INFO msg=edata 積算電力量有効桁数="7 桁"
time=2025-03-30T14:52:59.317+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:52:59.479+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-75 dataBytes=15 data(hex)=1081000102880105ff017201e10102
time=2025-03-30T14:52:59.479+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=1
time=2025-03-30T14:52:59.479+09:00 level=INFO msg=edata 積算電力量単位="0.010000 kWh"
time=2025-03-30T14:53:00.344+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:53:00.505+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-75 dataBytes=25 data(hex)=1081000102880105ff017201ea0b07e9031e0e1e00001a5e9c
time=2025-03-30T14:53:00.505+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=1
time=2025-03-30T14:53:00.505+09:00 level=INFO msg=edata 定時積算電力量計測値(正方向計測値)="2025/03/30 14:30:00 ( 1728156)"
time=2025-03-30T14:53:01.371+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:53:02.148+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-74 dataBytes=14 data(hex)=1081000102880105ff017101e500
time=2025-03-30T14:53:02.148+09:00 level=INFO msg=Set_resプロパティ値書き込み応答 N=1
time=2025-03-30T14:53:02.148+09:00 level=DEBUG msg=edata epc(hex)=e5 pdc(hex)=0 edt(hex)=""
time=2025-03-30T14:53:02.397+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:53:02.910+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-74 dataBytes=208 data(hex)=1081000102880105ff017201e2c20000001a5b7b001a5b8b001a5b9b001a5bad001a5bc8001a5bd8001a5be8001a5bf7001a5c09001a5c19001a5c29001a5c3a001a5c71001a5c96001a5cb7001a5cd9001a5cf6001a5d0f001a5d2b001a5d4a001a5d6b001a5d93001a5db4001a5dd3001a5df2001a5e12001a5e32001a5e58001a5e77001a5e9cfffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffefffffffe
time=2025-03-30T14:53:02.910+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=1
time=2025-03-30T14:53:02.910+09:00 level=INFO msg=edata "積算電力量計測値履歴1 (正方向計測値)"="0日前[ 1727355, 1727371, 1727387, 1727405, 1727432, 1727448, 1727464, 1727479, 1727497, 1727513, 1727529, 1727546, 1727601, 1727638, 1727671, 1727705, 1727734, 1727759, 1727787, 1727818, 1727851, 1727891, 1727924, 1727955, 1727986, 1728018, 1728050, 1728088, 1728119, 1728156,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A,     N/A]"
time=2025-03-30T14:53:03.423+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:53:03.820+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-74 dataBytes=18 data(hex)=1081000102880105ff017201e004001a5eb6
time=2025-03-30T14:53:03.820+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=1
time=2025-03-30T14:53:03.820+09:00 level=INFO msg=edata 積算電力量=1728182
time=2025-03-30T14:53:36.434+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:53:36.731+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-77 dataBytes=24 data(hex)=1081000102880105ff017202e7040000026ce8040031000f
time=2025-03-30T14:53:36.731+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=2
time=2025-03-30T14:53:36.731+09:00 level=INFO msg=edata 瞬時電力="620 W"
time=2025-03-30T14:53:36.731+09:00 level=INFO msg=edata 瞬時電流="(1φ3W) R:  4.9, T:  1.5"
time=2025-03-30T14:54:09.221+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:54:09.555+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-76 dataBytes=24 data(hex)=1081000102880105ff017202e7040000026ce8040032000f
time=2025-03-30T14:54:09.555+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=2
time=2025-03-30T14:54:09.555+09:00 level=INFO msg=edata 瞬時電力="620 W"
time=2025-03-30T14:54:09.555+09:00 level=INFO msg=edata 瞬時電流="(1φ3W) R:  5.0, T:  1.5"
time=2025-03-30T14:54:39.293+09:00 level=DEBUG msg=Write "transmit result"=0 "data digest"=1081000105
time=2025-03-30T14:54:39.491+09:00 level=DEBUG msg=Received senderAddressType=ユニキャスト secure=暗号化あり rssi=-76 dataBytes=24 data(hex)=1081000102880105ff017202e70400000298e80400350011
time=2025-03-30T14:54:39.491+09:00 level=INFO msg=Get_resプロパティ値読み出し応答 N=2
time=2025-03-30T14:54:39.491+09:00 level=INFO msg=edata 瞬時電力="664 W"
time=2025-03-30T14:54:39.491+09:00 level=INFO msg=edata 瞬時電流="(1φ3W) R:  5.3, T:  1.7"
waiting
time=2025-03-30T14:55:19.773+09:00 level=DEBUG msg=CommandBRouteTerminatePana result=ok
time=2025-03-30T14:55:19.774+09:00 level=INFO msg=Bye

ECHONET Lite

ここまでが第1層から第4層までのプロトコル。第5層から上は共通のECHONET Lite。

具体的に言うと'SKSENTO'だとか「データ送信コマンド」'0x0008'のペイロード部分に指定するメッセージがECHONET Lite。

ECHONET Liteで規定している範囲は、OSI参照モデルにおける第5層以上で、下位の通信仕様には依存しません。通信アドレスは、IPアドレスや伝送メディアのMACアドレスなどを利用して送信先を指定します。したがって、サービスやアプリケーションなどの様々な要件に基づいて、市場からリーズナブルな伝送メディアを選択することが可能です。
https://echonet.jp/about/features/

一度ここからダウンロードした低圧スマート電力量メータ・コントローラ間の規格書を見てください。

ECHONET Liteフレームフォーマット

https://qiita.com/miyazawa_shi/items/725bc5eb6590be72970d

送信するフレームフォーマットは ネットワークバイトオーダー=ビッグエンディアンで上のページ通りに作る。
具体的には上のプログラムを参照してください。(go言語であってもpythonみたいな感じで大体わかるでしょ)

スマートメーターから情報を得る

https://echonet.jp/spec_object_rh/
ここにある「APPENDIX ECHONET機器オブジェクト詳細規定 」をダウンロードしてください。

参照するのは「3.3.25 低圧 スマート電力量メータクラス規定」の章。

  • クラスグループコード 0x02
  • クラスコード 0x88
  • インスタンスコード 0x01

ホームコントローラ(このプログラム自身のこと)SEOJ:05ff01からスマートメーターに要求するときに設定しているDEOJ:028801のこと。
これに対する返答が反転した状態(SEOJ:028801/DEOJ:05ff01)となって返ってくる。

とりあえず0xEA:定時積算電力量計測値(正方向計測値)と0xE7:瞬時電力計測値と0xE8:瞬時電流計測値の読み方を書いておく。

0xEA:定時積算電力量計測値(正方向計測値)

プログラムでESV:0x62(=Get要求)で0xEA(定時積算電力量計測値)を送ると、このように返ってくる

2025/03/30 16:10:09 SKSENDTO 1 fe80:0000:0000:0000:****:****:****:**** 0E1A 1 000E 1081000105ff010288016201ea00
2025/03/30 16:10:09 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:10:09 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 0019 1081000102880105FF017201EA0B07E9031E100000001A5EFA
2025/03/30 16:10:09 Get_resプロパティ値読み出し応答 N=1
2025/03/30 16:10:09 定時積算電力量計測値(正方向計測値)=2025/03/30 16:00:00 ( 1728250)
EHD TID SEOJ DEOJ ESV OPC EPC PDC EDATA
1081 0001 028801 05FF01 72(Get要求の応答) 01 EA 0B 07E9031E100000001A5EFA

EDATA部分は

YYYY MM DD hh mm ss 積算電力量計測値
07E9 03 1E 10 00 00 001A5EFA


????あれっ???なんかおかしくないか?


おれのプログラム通り(1728250)やろがい。なにわろとんねんな、こいつはクビにする。
計算方法を知っているからわかるけど、知らなければだまされるじゃないか。

定時積算電力量計測値(正方向計測値)はこれに係数を掛け算して出す。
係数(0xd3)プロパティ値は(N/A)つまり存在しない。その場合は1となるのでそのまま読む。
積算電力量単位は0.01kWhなので定時積算電力量計測値(正方向計測値)は17282.50kWhとなる。(メーターを見に行って確認した)

0xE7:瞬時電力計測値,0xE8:瞬時電流計測値

この2つはまとめて要求した。(1つづつ送っても良いというかその方が良いかも)

2025/03/30 16:13:26 SKSENDTO 1 fe80:0000:0000:0000:****:****:****:**** 0E1A 1 0010 1081000105ff010288016202e700e800
2025/03/30 16:13:26 EVENT 21 FE80:0000:0000:0000:****:****:****:**** 00
2025/03/30 16:13:27 ERXUDP FE80:0000:0000:0000:****:****:****:**** FE80:0000:0000:0000:****:****:****:**** 0E1A 0E1A **************** 1 0018 1081000102880105FF017202E70400000208E804002A000D
2025/03/30 16:13:27 Get_resプロパティ値読み出し応答 N=2
2025/03/30 16:13:27 瞬時電力=520 W
2025/03/30 16:13:27 瞬時電流=(1φ3W) R=  4.2 A, T=  1.3 A
EHD TID SEOJ DEOJ ESV OPC EPC1 PDC1 EDT1 EPC2 PDC2 EDT2
1081 0001 028801 05FF01 72 02 E7 04 00000208 E8 04 002A000D

2つ要求したから当然返答は2つある

EPC1 PDC1 EDT1
E7 04 00000208

0xE7:瞬時電力計測値はhex(208)なので

W = 2*16^2+8*16^0 = 512+8 = 520 \mathrm{W}
EPC2 PDC2 EDT2
E8 04 002A000D

0xE8:瞬時電流計測値はR相電流がhex(002A),T相電流がhex(000D)なので

\begin{align} I_R &= 2*16^1+10*16^0 = 32+10 = 42\mathrm{dA} \\ I_T &= 13*16^0 = 13\mathrm{dA} \end{align}

単位はdA(デシアンペア1/10A)なので

\begin{align} I_R = 4.2\mathrm{A} \\ I_T = 1.3\mathrm{A} \end{align}

逆潮流が発生している場合(余剰買取の太陽光発電があって発電が優勢の場合など)に負数になることがある。

ちなみに単相3線式というのはこんなの
https://www2.panasonic.biz/jp/basics/electric/electricity/wiring-systems/

電力計に接続されている電線の一番左側から(1次側)R相,N相,T相そして(2次側)T相,N相,R相の順番。

普通は赤色の電線がR相,白色がN相,黒色がT相となる。これは単相だから三相のR,S,Tと混同しないように。
個人的にL1,N,L2表記のほうが単相なんだとわかりやすいような気がするが、規格書がそうなっているからそうなんだが。

Discussion