電子回路の知識無しで電力計を作る方法のソフト編です.
ハード編に引き続き,電力計のソフトの準備について説明します.
流れとしては,次のようになります.
- SD カードに Ubuntu をインストールし,それを使って BeagleBone Black を起動.
- 電力測定スクリプトを実行.
では,早速内容に入ります.
SD カードへの Ubuntu のインストール
BeagleBoardUbuntu のページの「Method 1: Download a Complete Pre-Configured Image」に記載された手順に従い,ディスクイメージをダウンロードして, dd コマンドで SD カードに書き込みます.
具体的な手順は下記のようになります.(ファイル名や,デバイスファイルについては適宜読み替え要)
1 2 3 |
$ wget https://rcn-ee.net/deb/microsd/trusty/bone-ubuntu-14.04-console-armhf-2014-08-13-2gb.img.xz $ unxz bone-ubuntu-14.04-console-armhf-2014-08-13-2gb.img.xz $ sudo dd if=./bone-ubuntu-14.04-console-armhf-2014-08-13-2gb.img of=/dev/sdX |
SD カードからの Ubuntu のブート
- SD カードを挿入.
- LAN ケーブルを接続.
- ブートボタンを押しながら 5V 電源を接続
ブートボタンの位置については,左の写真を参照してください.
初期設定
ここまでうまく言っていれば SSH でログインできるようになっていますので,ログインしてみます.
1 2 3 4 |
$ ssh ubuntu@arm -o PreferredAuthentications=password # パスワードは「temppwd」です. # 以降に記載するコマンドは,全て BeagleBone Black 上で実行するものもなります. |
ログインできたら最初に,NTP の設定を行っておきます.
1 2 |
$ sudo emacs /etc/rc.local ntpdate ntp.nict.jp |
BeableBone Black は電池でバックアップされた時計がついていないため,電源を切るたびに時刻がリセットされてしまします.そこで,上記のようにすることで起動時に時刻合わせを行うようにします.
ちなみに,これを行っていないと,何度か電源の抜き差しをやっているうちに SSH でログインできなくなります.(多分,SSH が時刻を不正と判断してるんだと思いますが,詳細未確認)
I2C バスに接続されている部品の確認
i2cdetectコマンドを使うと,I2C バスに接続された機器のアドレス一覧を表示できます.
1 2 3 4 5 6 7 8 9 10 |
$ i2cdetect -y -r 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- UU UU UU UU -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- |
上のように, 3eと 40が表示されていれば OK です. 40は電力計モジュール, 3eは液晶モジュールになりますので,表示されていない場合は回路を再チェックします.
電力計測スクリプトの実行
以下の内容を「sense_power.rb」という名前で BeagleBone Black に保存し,実行します.液晶画面に電力が表示されれば完成です.起動時に自動的に実行したい場合,スクリプトのパスを /etc/rc.local に追記しておきましょう.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
#!/usr/bin/env ruby # -*- coding: utf-8 -*- IOCTL_I2C_SLAVE = 0x0703 # Script for power meter using the following parts # - Power Meter module (IC: INA226) # https://strawberry-linux.com/catalog/items?code=12031 # - I2C LCD (IC: ST7032i) # https://strawberry-linux.com/catalog/items?code=27001 class CharacterLCD def initialize(i2c_bus=1, dev_addr=0x3E) @i2c = File.open(sprintf('/dev/i2c-%d', i2c_bus), 'rb+') @i2c.ioctl(IOCTL_I2C_SLAVE, dev_addr) # initialize exec_cmd("i2cset -y 1 0x3e 0 0x39 0x14 0x78 0x5E 0x6c i") # display ON exec_cmd("i2cset -y 1 0x3e 0 0x0C 0x06 i") # contrast set exec_cmd("i2cset -y 1 0x3e 0 0x39 i") exec_cmd("i2cset -y 1 0x3e 0 0x72 i") # set instruction table 0 exec_cmd("i2cset -y 1 0x3e 0 0x38 i") clear end def clear # clear display exec_cmd("i2cset -y 1 0x3e 0 0x01 i") set_cursor(0) end def display(text) @i2c.write("\x40" + text) @i2c.flush end def set_cursor(pos) # set cursor position @i2c.write("\x00" + [(0x80 | pos)].pack('C')) @i2c.flush end def exec_cmd(cmd) val=`#{cmd} 2> /dev/null` raise StandardError, "FAIL: #{cmd}" unless $?.success? return val end end class PowerSenseor def initialize(i2c_bus=1, dev_addr=0x40) @i2c = File.open(sprintf('/dev/i2c-%d', i2c_bus), 'rb+') @i2c.ioctl(IOCTL_I2C_SLAVE, dev_addr) @dev_addr = dev_addr @v_val = 0 @c_val = 0 @p_val = 0 # shunt resistor = 0.002Ω exec_cmd("i2cset -y 1 0x#{dev_addr.to_s(16)} 0x05 0x0a 0x00 i") # conversion time = 332us, number of average = 16 exec_cmd("i2cset -y 1 0x#{dev_addr.to_s(16)} 0x00 0x04 0x97 i") end def sense # i2cset/i2cget は 10ms オーダーの時間を消費するのでここでは使用しない @i2c.write(0x02) @v_val = conver_signed(@i2c.read(2)) @i2c.write(0x04) @c_val = conver_signed(@i2c.read(2)) @i2c.write(0x03) @p_val = conver_signed(@i2c.read(2)) end def conver_signed(bytes) # convert endian return bytes.unpack('n').pack('S').unpack('s')[0].abs end def get_voltage return calc_voltage(@v_val) end def get_current return calc_current(@c_val) end def get_power return calc_power(@p_val) end def exec_cmd(cmd) val=`#{cmd} 2> /dev/null` raise StandardError, "FAIL: #{cmd}" unless $?.success? return val end def calc_voltage(v_val) return v_val * 1.25 / 1000.0 end def calc_current(c_val) return c_val / 1000.0 end def calc_power(p_val) return p_val * 0.025 end end def get_ip_addr iface_list = %w(wlan0 eth0) iface_list.each{|iface| ip_addr=`LC_ALL=C ifconfig #{iface} | grep 'inet addr:'`.chomp if ip_addr.match(%r|inet addr:(\d+\.\d+\.\d+\.\d+)|) return $1 end } return 'UNKNOWN' end require 'optparse' params = ARGV.getopts('lq') data_list = [] Signal.trap(:INT){ if params['l'] then printf("time,voltage,current,power\n") data_list.each{|data| printf("%10d,%.3f,%.3f,%.3f\n", data[0], data[1], data[2], data[3]) } end exit(0) } sensor = PowerSenseor.new lcd = CharacterLCD.new if !params['l'] && !params['q'] lcd.set_cursor(40) lcd.display(get_ip_addr) 5.times{|i| lcd.set_cursor(0) lcd.display(sprintf('IP: %-5s', '#' * (5-i))); sleep(1) } lcd.clear end start_time = Time.now i = 0 while true sensor.sense v = sensor.get_voltage c = sensor.get_current p = sensor.get_power if params['l'] then data_list.push([(Time.now - start_time) * 1000, v, c, p]) end if ((i & 0x7F) == 0) then lcd.set_cursor(0) lcd.display(sprintf("%6.3fV %6.3fA", v, c)) lcd.set_cursor(40) lcd.display(sprintf("%6.3fW", p)) lcd.display(((i & 0x80) == 0x80) ? [0x5F].pack('C') : ' ') lcd.set_cursor(0) else sleep 0.001 end i = (i & 0xff) + 1 end |
ロギングの仕方
「sense_power.rb」は -l オプションをつけることで RAM 上にロギングを行い, CTRL-C でコマンドを終了すると,内容を標準出力にはき出します.
従って,下記のようにすることで,電力の推移を記録したファイルを取得できます.
1 2 |
$ ./sense_power.rb -l > power.log # 測定を終了したいタイミングで「CTRL-C」 |
なお,スクリプトがこのような仕様になっているのは,ループ中に標準出力への出力を行うと,1回当たり 10ms 以上の処理時間が発生し,ロギングの間隔が空いてしまうためです.
自動 SD カードブートの設定
最後に,自動的に SD カードからブートさせる方法について説明しておきます.やり方は BeagleBone Black の Rev.B 以前と Rev.C 以降で異なります.ここでは Rev.C 以降でのやり方について記載します.
- SD カード外して起動.
- root でログイン.(ホスト名は beaglebone,パスワードは空)
- /boot/uboot/MLO を別の名前に変更.
コメント
[…] ソフト編に続く. […]
[…] Rabbit Note BeagleBone Black で作るロギング機能付き電力計 (ソフト編) […]