Modbusの利用 (6) 電力計SDM120M
EASTRONの電力計SDM120Mは、Modbus/RTUのインターフェースをもっています。スリムな形状をしているので、DINレールに取り付けてもスペースを食いません。Windows10のPython3から利用します。実行環境などは、前回の記事を参照してください。
●SDM120Mのおもなスペック
- 単相230V(176~276V AC)※100Vでも動作した
- 電圧/電流確度 0.5%
- 測定電流範囲 0.25~45A
- 動作温度 -25~55℃
- インターフェース Modbus/RTU、デフォルト・アドレス=1、RS-485通信デフォルト値、2400bps、8ビット、パリティなし、ストップ・ビット1
●Modbus
Input Registerから下記のパラメータを読み取ります。
Address | Description | Units |
---|---|---|
30001 | Voltage | Volts |
30007 | Current | Amps |
30013 | Active Power | Watts |
30019 | Apparent Power | VoltAmps |
30025 | Reactive Power | VAr |
30031 | Power Factor | None |
30037 | Phase Angle | Degrees |
30071 | Frequency | Hz |
30073 | Import Active Energy | kWh |
30075 | Export Active Energy | kWh |
30077 | Import Reactive Energy | kVArh |
30079 | Export Reactive Energy | kVArh |
30343 | Total Active Energy | kWh |
30345 | Total Reactive Energy | kVArh |
●接続
終端抵抗は入れずに実験しています。
●プログラム
Input Registerから読み取ったデータは、16ビット・データが2ワードで、フォーマットはIEEE754(単精度浮動小数点数の形式: binary32)です。4バイトの16進データを読み取った後、10進数へ変換します。Pythonはいろいろなライブラリがあるのでと思い探しましたが、見つかりませんでした。
データシートにも、次に示すサンプル・データを10進数へ変換する手順が掲載されています。ウィキペディアの「単精度浮動小数点数」の「単精度バイナリ形式から十進への変換」の項目にも手順が書かれていますが、少し手法が異なります。ここでは、データシートの手法に従います。
上位レジスタの上位バイト | 上位レジスタの下位バイト | 下位レジスタの上位バイト | 下位レジスタの下位バイト |
0x43 | 0x70 | 0x80 | 0x00 |
0b0100011 | 0b01110000 | 0b10000000 | 0b00000000 |
A = master.execute(Address, cst.READ_INPUT_REGISTERS, 0, 2) |
この処理は、2バイトを読み出すと思っていたのですが、実際は、2バイトを二つ読み出しました。したがって、print(A[0],A[1])は、0x4370、0x8000になります。扱いやすいように、上位ワードを16ビット・シフトし、下位ワードと加算して一つにまとめます。
A0 = A[0]*65536+A[1]
|
ですが、最上位の0が消えているので実際は、0b01000011011100001000000000000000です。
IEEE754(単精度浮動小数点数の形式)のデータは下記の順序になっています。最初の1ビットは符号です。次の8ビットはexponent=指数です。
|
ここで、指数部の2進数10000110を10進に直すと134です。理由は置いておいて127を引くと7です。7を本来の指数と呼ぶようです。正の数だと次のプログラムで取り出せます。この測定器では負の数は出てこないので、符号が負のときの判別はしていません。
fp = int(str(bin(A0)[2:10] ),2) - 127
|
仮数部は23ビットあります。暗黙の了解で、先頭の1.0が省略されています。したがって、
仮数部 1.11100001000000000000000 |
と正規化されています。正規化されているというのは、指数が1になったときです。
仮数部 × 2^指数部 |
ここで指数部は7ですから、ゼロ(.)を右に7、ずらします。
11110000.1000000000000000 |
小数点の左が整数、右が小数の位になります。
newFr = '1'+str(bin(A0)[10:])
|
整数(integer)は、次のように10進に直せます。
(1 × 2^7) + (1 × 2^6) + (1 × 2^5) + (1 × 2^4) + (0 × 2^3)+ (0 × 2^2) + (0 × 2^1)+ (0 × 2^0) = 240 |
小数(decimal)は次のように10進に直せます。
(1 × 2^-1) + (0 × 2^-2)+ (0 × 2^-3) + … = 0.5 |
合成すると240.5になります。
FdecimalisとFintergerisは内包形式で1行で記述しています。Fdecimalisはコメントのところは、ループ形式で記述したプログラムです。
integeris = newFr[:fp+1] decimalis = newFr[fp+1:] Fdecimalis = sum([(float(Decimal(x)*Decimal(2**(-(i+1))))) for (i,x) in enumerate(decimalis)]) print(Fdecimalis) ''' data="11110000"#sample sum0 =Decimal(0) for (i,x) in enumerate(data) : sum0 += Decimal(x)*Decimal(2**((fp-i))) print(Decimal(x),i,sum0) ''' Fintergeris = sum([(float(Decimal(x)*Decimal(2**(fp-i)))) for (i,x) in enumerate(integeris)]) print(Fintergeris) print(Fintergeris+Fdecimalis) |
全体のプログラムです。上記のように、途中経過の計算例も入っているので長いです。
import sys sys.path.append('C:\\Users\\ユーザ名\\AppData\\Local\\Programs\\Python\\Python38\\Lib\\site-packages') import serial import modbus_tk.defines as cst from modbus_tk import modbus_rtu from decimal import * PORT = 'COM8' Address = 1 master = modbus_rtu.RtuMaster(serial.Serial(port=PORT, baudrate=2400, bytesize=8, parity='N', stopbits=1, xonxoff=0)) master.set_timeout(1.0) master.set_verbose(True) #print("connected") A = master.execute(Address, cst.READ_INPUT_REGISTERS, 70, 2) #A=(0x4370,0x8000) print(A[0],A[1]) print(bin(A[0]),bin(A[1])) print(bin(A[0]*65536+A[1])) print(hex(A[0]*65536+A[1])) A0 = A[0]*65536+A[1] print(bin(A0)) fp = int(str(bin(A0)[2:10] ),2) - 127 print('exponet is ', fp) newFr = '1'+str(bin(A0)[10:]) print('1fraction is ', str(newFr)) print('2fraction integer is ', newFr[:fp+1]) print('3fraction decimal is ', newFr[fp+1:]) integeris = newFr[:fp+1] decimalis = newFr[fp+1:] Fdecimalis = sum([(float(Decimal(x)*Decimal(2**(-(i+1))))) for (i,x) in enumerate(decimalis)]) print(Fdecimalis) ''' data="11110000"#sample sum0 =Decimal(0) for (i,x) in enumerate(data) : sum0 += Decimal(x)*Decimal(2**((fp-i))) print(Decimal(x),i,sum0) ''' Fintergeris = sum([(float(Decimal(x)*Decimal(2**(fp-i)))) for (i,x) in enumerate(integeris)]) print(Fintergeris) print(Fintergeris+Fdecimalis)
●実行例
電圧は、
A = master.execute(Address, cst.READ_INPUT_REGISTERS, 0, 2) |
周波数は、
A = master.execute(Address, cst.READ_INPUT_REGISTERS, 70, 2) |
(2020/04/11)Structを使うともっと簡単に記述できるかもしれない。