機械設備や装置の稼働を計測する「AC電流ロガー」
しまねソフト研究開発センターでは、IoT分野における先端技術支援や研究活動の一環として、mruby/cを使ったIoTデバイスの開発・製作を行っております。
このページでは、機械設備や装置の稼働を計測するニーズに対して、簡易にデータを計測・収集するためのAC電流ロガーの開発を行いましたので、その取組事例をご紹介いたします。
目次
1.概要 |
2.内容 |
2-1.ドキュメント |
2-2.サンプルコード |
2-3.注意事項 |
2-4.執筆者紹介 |
3.お問い合わせ先 |
概要
目的
- 主に製造業の機械設備や装置において、稼働状況を「見える化」すれば作業を平準化するフローを導入する事で「生産性の向上」が図れます。
- しかしながら、機械設備や装置にインターネット通信機能が搭載していない、またはオプション機能で実現できるものの導入コストが高いといった課題があります。
- そこで、上記の課題を解決するため、より簡単かつ低コストで機械設備や装置の稼働を計測可能なAC電流ロガーを開発しました。
AC電流ロガーについて
- 今回、カレントトランスでセンシングしたAC電流を、mruby/cを標準搭載するマイコンボード「RBoard」のADコンバータを使って測定を行い、一定時間ごとにWi-Fi経由でサーバへ送信するものです。
- これにより、既に導入された機械設備や装置でも取り付けが容易となるクランプ型電流センサーを採用しており、測定値はサーバ上でリアルタイムに確認できるとともに、稼働状況を判定することができます。
内容
AC電流ロガーの開発にあたり、ハードウェアおよびソフトウェア、動作実績、基板回路図に関するドキュメントを公開いたします。
併せて、サンプルコードを公開しておりますので、予め注意事項をご確認の上、参照ください。
ドキュメント
このAC電流ロガー開発に関するドキュメントは、以下のPDFファイルでご覧いただけます。
併せて、AC電流ロガー開発で用いたパーツリストをご覧いただけます。
「ドキュメント(AC電流ロガー開発について)」をダウンロードする(PDF:496kB)
「パーツリスト(AC電流ロガーについて)」をダウンロードする(PDF:80kB)
サンプルコード
main.rb
# coding: utf-8
#
# カレントトランスを使った電流測定サンプル
#
# Copyright (C) 2022 Shimane IT Open-Innovation Center.
#
# This file is distributed under BSD 3-Clause License.
#
POST_URL = "http://example.jp/cgi-bin/datalogger.rb?ctrl=ct&action=post"
CT_RATIO = 3000 / 100 # カレントトランスの変流比 / 接続する抵抗値 (オーム)
SLEEP_TIME = 10 # 一回測定ごとにスリープする時間 (秒)
GPIO_WIFI_RESET = 2 # WiFi reset pin に繋いだ GPIO Pin 番号
##
# AC波形の測定
#
# (note)
# 100ms間のAC波形を取得する
# この回路ではマイナス電圧が測定できず全てゼロになるため、
# プラス側とマイナス側が同じ波形で電流が流れていると仮定して計算している。
#
def ct_meas( adc )
sum = 0.0
n = 0
t1 = VM.tick() + 100
while VM.tick() < t1
v = adc.read
sum += v*v
n += 1
end
return Math.sqrt( 2 * sum / n ) * CT_RATIO
end
#
# send data to server
#
def send_data( data )
if !$wifi.wait_for_connect()
wifi_hard_reset()
return nil
end
res = $wifi.http_post_json( POST_URL, data )
$wifi.reboot() if !res
return res
end
#
# WiFiモジュールのハードウェアリセット
#
def wifi_hard_reset()
$wifi_reset.write( 0 )
sleep 1
$wifi_reset.write( 1 )
end
#
# main
#
sleep 2
puts "AC current maasurement system"
$adc = ADC.new(20)
$wifi_reset = GPIO.new( GPIO_WIFI_RESET )
$wifi_reset.setmode( 0 )
$wifi_reset.write( 1 )
$wifi = AMW037.new( UART.new( 115200 ))
#
# WiFi module setup
#
#$wifi.setup_module("SSID", "PASSWORD"); $wifi.reboot; sleep 1000
while !(mac_address = $wifi.mac_address())
puts "WiFi mac address error."
wifi_hard_reset()
end
puts "WiFi mac address is #{mac_address}"
send_count = 1
while true
i_ac = ct_meas( $adc )
printf "I = %.2f A(rms)\n", i_ac
data = {:type=>"AC current", :mac_address=>mac_address,
:i_ac=>i_ac, :count=>send_count }
if send_data( data )
send_count += 1
puts "Data send OK."
else
puts "ERROR: Data send failed."
end
sleep SLEEP_TIME
end
amw037.rb
# coding: utf-8
#
# Silicon Labs WiFi module AMW037 control class.
#
# Copyright (C) 2015-2022 Shimane IT Open-Innovation Center.
#
# This file is distributed under BSD 3-Clause License.
#
##
# debug print
#
def dp( s )
puts s
end
class Hash
def to_json_tiny
ret = "{"
flag_comma = false
self.each {|k,v|
ret << ", " if flag_comma
ret << %!"#{k}":#{v.inspect}!
flag_comma = true
}
ret << "}"
end
end
##
# AMW037 Class
#
class AMW037
attr_reader :is_read_timeout
##
# constructor
#
def initialize( node = nil )
@rfm = node || UART.new( 1 )
clear_buffer()
end
##
# clear tx/rx buffer
#
def clear_buffer()
@rfm.clear_tx_buffer()
@rfm.clear_rx_buffer()
@is_read_timeout = false
end
##
# send any command
#
def command( cmd )
dp ">>>SEND \"#{cmd}\""
@rfm.write("#{cmd}\r\n")
end
##
# get string with timeout
#
def get_string( timeout = 1000 )
@is_read_timeout = false
cnt = 0
timeout /= 10
while cnt < timeout
txt = @rfm.gets()
return txt if txt
cnt += 1
sleep_ms 10
end
@is_read_timeout = true
return nil
end
##
# chat
#
#@param [String] send message.
#@param [Integer] timeout timeout (ms)
#@return [String] result message
#@return [nil] seaquence error.
#@return [false] status code error.
#
def chat( send, timeout = 1000 )
if send
dp ">>>C:SEND \"#{send}\""
@rfm.write("#{send}\r\n")
end
# (see)
# https://docs.zentri.com/zentrios/wl/latest/serial-interface#response-format
@res_code = get_string( timeout )
dp "<<<C:RES #{@res_code.inspect}"
return nil if !@res_code || @res_code[0] != "R"
@res_code.chomp!
len = @res_code[2,5].to_i
@res_text = nil
cnt = 0
if len > 0
while (cnt += 1) < 100
@res_text = @rfm.read( len )
break if @res_text
sleep_ms 10
end
end
@is_read_timeout = (cnt == 100)
if @res_text
@res_text.chomp!
dp "<<<C:TEXT #{@res_text.inspect}"
else
@res_text = ""
end
return false if !@res_code.start_with?("R0")
return @res_text
end
##
# reboot
#
def reboot()
command("\r\nreboot")
sleep 5
clear_buffer()
end
##
# get MAC address
#
#@return [String] mac address
#@return [nil] retry error.
#
def mac_address()
mac = nil
retry_count = 10
while !mac
clear_buffer()
command("get wlan.mac")
while mac = get_string()
dp "<<<RECV #{mac.inspect}"
break if mac.size == 19
end
return nil if (retry_count -= 1) == 0
sleep 1
end
return mac.chomp
end
##
# setup module
#
def setup_module( ssid = nil, passkey = nil )
# factory reset.
mac = mac_address()
@rfm.write("factory_reset #{mac}\r\n")
puts ">>> SEND: factory_reset #{mac}"
sleep_ms 5000
puts "<<< RECV: " + @rfm.read_nonblock(1000).to_s
# to machine friendly mode.
# and set wlan parameter
cmds = <<-EOL.split("\n")
set setup.gpio.control_gpio -1
set system.print_level 0
set system.cmd.header_enabled 1
set system.cmd.prompt_enabled 0
set system.cmd.echo off
set wlan.hide_passkey 1
set wlan.auto_join.enabled 1
EOL
cmds << "set wlan.ssid #{ssid}\n" if ssid
cmds << "set wlan.passkey #{passkey}\n" if passkey
cmds << "save\n"
cmds.each {|cmd|
@rfm.write( cmd + "\r\n" )
puts ">>> SEND: " + cmd
sleep_ms 500
puts "<<< RECV: " + @rfm.read_nonblock(1000).to_s
}
end
##
# web setup mode
#
def setup_web()
return chat("setup web") == "In progress"
end
##
# start WiFi connection
#
def start_connect()
return if chat("network_up") == "In progress"
sleep_ms 100
clear_buffer()
end
##
# connected now?
#
def is_connected()
return chat("get wlan.network.status") == "2"
end
##
# wait for connect
#
#@return [Boolean] connect / disconnect
#
def wait_for_connect()
return true if is_connected()
retry_cnt = 0
while retry_cnt < 3
start_connect()
cnt = 0
while (cnt += 1) < 60
return true if is_connected()
sleep 1
end
retry_cnt += 1
end
return false
end
##
# HTTPサーバーへ、JSONデータをPOSTする
#
#@param [String] url URL (e.g. http://example.com/cgi-bin/sample.cgi)
#@param [Hash] data data hash.
#@return [Array<Integer,String>] returned status and contents.
#@return [Nil] error.
#
def http_post_json( url, data )
data_json = data.to_json_tiny()
handle = chat("http_post -o #{url} -l #{data_json.size} application/json", 10000)
return nil if !handle
command("stream_write #{handle} #{data_json.size}")
@rfm.write(data_json)
ret = nil
while !ret
break if !chat(nil)
status_code = chat("http_read_status #{handle}", 30000)
break if !status_code
status_code = status_code.to_i
contents = read_stream( handle, 1000 )
break if !contents
ret = [status_code, contents]
end
chat("stream_close #{handle}")
return ret
end
##
# Read data from stream
#
#@param [String,Integer] handle handle
#@param [Integer] max_len maximum length of return size.
#
def read_stream( handle, max_len = nil )
ret = ""
cnt = 0
while true
dp ""
res = chat("stream_poll #{handle} -r")
break if !res
status,size = res.split(",") # get status and data size
size = size.to_i
# break if no data status continue 5 seconds.
# because ZentriOS cannot detect closed HTTP stream
if status == "0"
break if (cnt += 1) > 5
sleep 1
next
end
cnt = 0
# read chunk data. max 1000 bytes.
# https://docs.zentri.com/zentrios/wl/1.5/cmd/commands#stream-read
while size > 0
size2 = size
size2 = 1000 if size2 > 1000
size -= size2
command("stream_read #{handle} #{size2}")
res = get_string() # "Rxxxxxx"
return nil if !res || !res.start_with?("R0")
# read size2 bytes.
cnt2 = 0
while size2 > 0
if data = @rfm.read_nonblock(size2)
dp ">>> Readed size #{data.size}"
ret << data if !max_len || ret.size < max_len
size2 -= data.size
cnt2 = 0
else
return nil if (cnt2 += 1) > 100
sleep_ms 10
end
end
get_string() # skip CRLF
end
break if status == "2" # break if connection has closed.
end
if max_len && ret.size > max_len
return ret[0, max_len]
end
return ret
end
end
注意事項
- 本事例の掲載情報の閲覧及び利用により、利用者自身、もしくは第三者が被った損害に対して、直接的、間接的を問わず、しまねソフト研究開発センターは責任を負いかねます。
- 本事例の内容を実践する中で用意された機器やパーツについてのご質問は、それぞれの機器やパーツの提供元にお問い合わせをお願いします。なお、機器やパーツの仕様は、本事例の公開当時のものです。
執筆者紹介
しまねソフト研究開発センター
東 裕人(Higashi Hirohito)/ 専門研究員
しまねソフト研究開発センターの専門研究員として、IoT分野で活用が期待できる小型デバイス向け開発言語「mruby/c」の研究開発、企業や大学・高専などとの共同研究、県内ITエンジニアの技術相談対応などの活動を行っています。詳しいプロフィールはこちら。
*このページで公開されている情報は2022年3月31日時点のものです。
お問い合わせ先
しまねソフト研究開発センター(担当:渡部)
Phone:0852-61-2225
Email:itoc@s-itoc.jp