2020年7月31日金曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - ソフトウェア設定編



の続きです。

Raspberry pi 2にインストールした後の設定方法は、一般的なi386/AMD64のFreeBSDとほぼ同じです。

rootのパスワードの変更は以下のコマンドで行います。

passwd

Raspberry pi 2版のFreeBSDでは、予めfreebsdというユーザが作られているので、必要に応じてrmuserで削除します。

rmuser freebsd

私の環境では、/etc/rc.confに以下の記述を追記しています。

Raspberry piには標準でRTCが搭載されていない為、再起動するたびに時間がクリアされてしまうため、起動毎にntpdateで時計合わせをするようにしています。
また、標準のkeypmapはUS配列ですので、これを106キー日本語配列に設定しています。

keymap="jp.kbd"
ntpdate_enable="YES"
ntpdate_flags="-b NTPサーバのIPアドレス"

タイムゾーンをJSTに変更するには、以下のようにします。

cp -p /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

これぐらいしておけば、あとはほぼ一般的なi386/AMD64版のFreeBSDと同じように使うことができると思います。

2020年7月30日木曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - ハードウェア設定編


の続きです。

FreeBSDをインストールしただけでは、Raspberry pi 2が持っている性能や機能をすべて使うことはできません。
いくつかの設定/チューニングが必要です。

まず、RPI2版のFreeBSDでは、デフォルトのCPUクロックが600MHzと低く設定されている為、これを標準の900MHzに設定します。
「/boot/msdos/config.txt」に以下の記述を追加します。

force_turbo=1
arm_freq=900
arm_freq_min=600

また、RPI2版のFreeBSDは、標準の状態ではGPIOは有効化されていますが、I2CやSPIは無効化されている為、必要に応じてこれを有効化します。
I2Cを有効にするには、config.txtに以下の記述を追加します。

dtparam=i2c=on

SPIを有効にするには、/boot/loader.confに以下の記述を追加します。

fdt_overlays="spigen-rpi2.dtbo"

I2CおよびSPIが有効されているかは、再起動後、以下のようにして確認してください。

dmesg | grep iic
dmesg | grep spigen

何らかの表示がされればOKです。

2020年7月27日月曜日

Raspberry pi 2とFreeBSDで7セグLED時計を作る - OSインストール編

Raspberry pi 2とFreeBSDで7セグLED時計を作る - はじめに
Raspberry pi 2とFreeBSDで7セグLED時計を作る - ハードウェア制作

の続きです。

前回までで、ハードウェアの制作が終わりましたので、次にRaspberry pi 2にFreeBSDをインストールしたいと思います。

先に言っておきますが、Raspberry piで手っ取り早く色々やろうとするならば、公式のOSであるRaspbianを使うのが圧倒的に便利です。
インターネット上に情報も多いですし、書籍もたくさん出ています。
どうしてもFreeBSDじゃなきゃ嫌、という人でない限り、Raspbianか他のLinuxディストリビューションを使うことをおすすめします。

Raspberry pi向けのFreeBSDですが、私の知る限りここに情報が集まっています。

https://wiki.freebsd.org/arm/Raspberry%20Pi

2020/07/25現在、FreeBSDでまともに使える/使えそうなRaspberry piは、

Raspberry pi B/B+
Raspberry pi 2
Raspberry pi 3
Raspberry pi Zero(W含む)

です、またRaspberry pi 3およびZero WのWi-Fi機能はFreeBSDでは使えません。
(Wi-Fi機能を提供しているICはSDIO接続で、FreeBSDがSDIOをまともにサポートしていないのが原因です。)

このページでは、私の手元にあるRaspberry pi 2の情報を中心にまとめていきたいと思います。

Raspberry pi 2をFreeBSDで使うには、本体にあったOSイメージをダウンロードしてSDカードに焼く必要があります。

OSイメージですが、FreeBSD 11ではRaspberry pi B/B+および2向けが、FreeBSD 12以降ではB/B+、2、3向けが提供されています。
Raspberry pi 2はarmv7でビルドされていて32bitをターゲットとしています。

Raspberry piに対応したFreeBSDは、以下からダウンロードできます。

https://www.freebsd.org/ja/where.html

RPI-B、RPI2、RPI3がそれぞれRaspberry pi B/B+、2、3に対応したSDイメージになります。

FreeBSDにおいてarm版はTier-2ですので、freebsd-updateが利用できません。
RELEASE版よりも、最新のSTABLE版をダウンロードして利用することをオススメします。
(常に最新の機能を追いかけるのならば、CURRENT版を使って下さい。)

FreeBSD-12.1-STABLE-arm-armv7-RPI2-202XXXXX-rXXXXXX.img.xz

xzアーカイブを解凍してimgファイルにします。
xzコマンドで展開できます。

xz -dv FreeBSD-12.1-STABLE-arm-armv7-RPI2-202XXXXX-rXXXXXX.img.xz

以下のようにしてSD/microSDカードに書き込みます。
(「da0」はご利用の環境によって変わります、dmesg等でSD/microSDカードに割り当てられるデバイス名を確認してから指定してください。)

dd if='PATH TO IMG FILE` of=/dev/da0 bs=16M

作成したSD/microSDカードをRaspberry pi本体にセットして電源を入れれば、FreeBSDがブートするはずです。

尚、rootのデフォルトのパスワードはrootになっています。

2020年7月26日日曜日

ルーターとは別にDHCPサーバーとDNSサーバを立てると吉


家で8年ほど使っていたルータが故障しました。

BHR-4GRVという機種に、DD-WRTをインストールして使っていたのですが、ある日突然リンクしなくなり、HUBとしてすら使えなくなりました。
簡易DNSやヘアピンNATができて重宝していたのですが…でもまあ、長持ちした方かも、今までありがとう。

DD-WRTは高機能なのですが、IPv6パススルーやPPPoEマルチセッションができなかったりします。
正確にいえば、CUIで色々やればできるのですが、GUIで設定できて使えるほどには整理されていません。
この2つの機能のどちらかがないと、フレッツのサービス情報サイトにアクセスできず、回線認証をするフレッツメンバーズクラブのポイント交換ができません。

今後、IPv6化もしたいので、DD-WRTとはここでお別れし、通常の市販ルータに置き換えたいと思います。

それにしても、ルータが壊れるのは悲劇ですね。
インターネットにアクセスできなくなるだけではなく、DHCPでIPアドレスも振られなくなるし、内部の名前も引けなくなるので、一瞬パニックになりました。
今後、ルータが壊れた時に、内部で最低限のことができるように、DHCPと内向きのDNSは別の機器で分けるようにしておきたいと思います。

まず、最初にルータ本体の置き換えから。
最近の大抵の機種では、IPv6パススルーやPPPoEマルチセッションはできるようです。
一方、簡易DNSは高級な機種のみにしかなく、ヘアピンNATはその機能の有無がカタログにすら載っていないことが多いです。
このため、簡易DNSとヘアピンNATをルータ本体で賄わない、と割り切ることにしました。

首都圏の市街地では2.4GHz帯は非常に混雑しており、我が家の周辺でも同様のため、11aに対応したモデルを選び、普通に設定して使いはじめました。
これでとりあえずはインターネットへのアクセスは確保できました。

次に内向きのDNSについて。
イントラ内のクライアントが少ない内向けのDNSの構築には、dnsmasqが向いています。
また、dnsmasqにはDHCP機能もあるので、最終的にDHCPもdnsmasqで賄い、ルータのDHCPを無効にすると良いと思います。

幸いにして、我が家には7セグメントLED時計を表示させるために電源は入りっぱなしのRaspberry pi 2があるので、これを流用することにしました。
このFreeBSDがインストールされたRaspberry pi 2に、portsnapを利用してdnsmasqをインストールします。

portsnap fetch extract
cd /usr/ports/dns/dnsmasq
make install clean

4コアといえども非力なARMに加え、SDカードの読み書きが遅いため、かなり時間がかかるので注意してください。
また、途中でビルドのオプションを問うダイアログがいくつも表示されるので、いちいちOKをしていく必要があります。

インストールが完了したら、「/usr/local/etc/dnsmasq.conf」をこんな感じで編集します。

# global settings

domain-needed
bogus-priv
expand-hosts
domain=local.kishiro.com
listen-address=127.0.0.1,192.168.0.6

# DHCP settings

dhcp-range=192.168.0.128,192.168.0.233,720m
dhcp-leasefile=/tmp/dnsmasq.leases
dhcp-option=option:router,192.168.0.1
dhcp-option=option:netmask,255.255.255.0
dhcp-option=option:dns-server,192.168.0.6
dhcp-option=option:ntp-server,192.168.0.6

# DNS

address=/www.kishiro.com/192.168.0.26

address=/router.local.kishiro.com/192.168.0.1
    :
    :
    :

「192.168.0.6」はRaspberry pi 2に割り当てられているIPアドレスで、「local.kishiro.com」はイントラ内のドメイン名です。

「www.kishiro.com」は外部に公開しているwwwサーバで、外部DNSではルーターのWAN側のIPアドレスが引かれるように設定しています。
また、ルーター側の設定で、WAN側の80/443番ポートへのアクセスをLAN側の「192.168.0.26」にポートフォワードするようにしています。

置き換えた新しいルータは、案の定ヘアピンNATが使えない(内部ネットワークからwww.kishiro.comに接続してもエラーになる)機種でしたので、dnsmasqの設定に以下の一行を加えています。

address=/www.kishiro.com/192.168.0.26

これにより、内部ネットワークから「www.kishiro.com」の名前解決を試みたときに、外のDNSに問い合わせに行く前にdnsmasqがローカルIPアドレスの方を回答します。
結果、パケットがWAN側に出ていかず、折り返しが発生しなくなるわけですね。

長くなりましたが、これでルータが壊れたとしても、Raspberry pi 2さえ動いていれば、dnsmasqによる内部のネットワークのIPアドレス払い出しと、名前解決が担保されるようになりました。
Raspberry pi 2のほうが壊れにくいかは謎ですが。

そして何より、今後、高級なルータを買わなくてもすむようになりました。

皆様もよろしければお試しください。

2020年7月12日日曜日

Linuxで使える軽量のイメージビューア「gPicView」

普段メインで使っているPCには、Xubuntuをインストールしています。
Xubuntuでは、xfceプロジェクトがメンテしているRistrettoって画像ビューアが標準で付属しています。
このRistrettoはよくできているんですが、イメージファイルを開くときに同じフォルダの中に入っている全てのイメージファイルをスキャンするようで、イメージファイルがたくさん入っているディレクトリ、特にネットワークドライブ上のイメージファイルを開くと、やたらと時間がかかります。
色々設定を吟味してみましたが、この動作を抑止するような方法が見つかりませんでした。

ということで、他のビュアーを色々と吟味してみましたが、GPicViewが一番いい感じです。

apt install gpicview

でインストールできますので、同じ現象でお困りの方はお試し下さい。

2020年7月11日土曜日

コマンドラインでmp3のv1、v2ヘッダを除去する

わが家では、FreeBSDをインストールした共有サーバにmp3ファイルを置いて、samba経由でPCやスマホなど色々な機器で再生していますが、mp3ファイルに勝手にヘッダをつける行儀の悪いアプリケーションが多く、難儀しています。
アルバム名や曲名は全てファイル名で管理しているので、v1ヘッダとかv2ヘッダは不要です。

意図せずヘッダがつけられてしまったファイルに対し、一括でヘッダを除去したいと思い、調べてみましたが、mp3のv1ヘッダとv2ヘッダの両方を、コマンドラインで除去できるid3v2というものが提供されているようです。

早速試してみたいと思います。

以下のようにしてインストールします。

pkg install id3v2

ヘッダを除去するには、以下のようにすればよいです。

id3v2 -D filename.mp3

id3v2単体でもワイルドカードが使えるようですが、特定のフォルダ下のmp3のヘッダを全て除去するには、findを組み合わせて以下のようにすればいいです。

find /path/to/mp3 -type f -iname "*.mp3" -exec id3v2 -D {} \;

スッキリしました。

2020年7月4日土曜日

marionetteを使ってfirefoxを操作する

phantomjsからchromiumのheadlessモードに引っ越してからしばらく経ちましたが、やはりスクリプト実行後のDOMを取得するだけでは、やりたいことに足らないケースが多いです。
より広範囲のスクレイピングを考えるならば、ログインなどの自動化は必須です。

とはいえ、chromiumとSeleniumの組み合わせは心理的なハードルが高いし、どうしようかなと悩んでいたところ、ふとしたきっかけでfirefoxにもmarionetteという自動操作のフレームワークがあることを知りました。

今回はこのfirefoxのmarionetteを使って、より高度なブラウザの操作を試みてみたいと思います。

まず、marionetteですが、前述の通りfirefoxを操作する為のフレームワークです。
marionetteを有効にすると、firefoxは2828番ポートで待ち受け、操作の為のコマンドを受け付けるようになります。
クライアントは、このポートにコマンドを送り、ブラウザを操作します。
firefoxの開発者がブラウザのテストに利用しているとのことですので、廃れることはなさそうな雰囲気です。

ちなみに、Seleniumからもfirefoxを操作できますが、これはfirefoxのWebドライバであるGeckodriverが内部でmarionetteのコマンドに置き換えてfirefoxに送っています。
つまり、firefoxを操作する為だけにSeleniumを導入するなら、直接marionetteを叩いたほうがリーズナブルです。

mozillaから、marionetteを操作するための謹製Webドライバ(marionette_driver)が提供されています。
これを使ってpythonでmarionetteを制御してみたいと思います。

まず、はじめにfirefoxのインストールから、
FreeBSDでは、pkgを使って以下のようにインストールします。

pkg install firefox

次に、pythonがインストールされていなければ、pythonをインストールします。
私の環境だけかもしれませんが、marionette_driverはpython 2.7でないとうまく動きませんでした。

pkg install python27

次に、marionette_driverをインストールするために、pythonのパッケージ管理システムであるpipをインストールします。

pkg install py27-pip

最後に、pipを利用してmarionette_driverをインストールします。

pip-2.7 install marionette_driver

これで環境の構築は完了です。

インストールが完了したら、早速marionetteを試してみたいと思います。
まずは、marionetteを有効にしてfirefoxを起動します。

firefox -private -marionette -p "default" &

Xがない環境では、ヘッドレスモードで起動します。

firefox -headless -private -marionette -p "default" &

これでmarionetteが有効になり、2828番ポートでコマンドを受け付けるようになります。

marionette_driverのAPIリファレンスは以下の場所にあります。

https://firefox-source-docs.mozilla.org/python/marionette_driver.html

それほど大きいものではないですし、直感的に理解できるようにAPIが切られているので、私のようなpython初心者でもなんとかなるのではないかと思います。

とはいいつつも、すぐに試したい人のために、自作の自動ログインとDOM表示のサンプルコードをつけておきます。
自動ログインの方は、ログインするページのHTMLソースを見て、ログインIDとパスワードに割り当てられたnameを確認した上で適宜修正してください。

自動ログイン用スクリプト

import sys

from marionette_driver.marionette import Marionette
from marionette_driver.legacy_actions import Actions
from marionette_driver.errors import NoSuchElementException

gURL="https://www.kishiro.com/login/"
gID="id"
gPassword="password"

def findHTMLElement(aHTMLElements,aAttribute):
    tResult=None
    for tHTMLElement in aHTMLElements:
        if tHTMLElement.get_attribute("name")==aAttribute:
            tResult=tHTMLElement
            break
    return tResult

tCurrentBrowser=Marionette('localhost',port=2828)
tCurrentBrowser.start_session()
 
tCurrentBrowser.navigate(gURL)

try:
    tElements=tCurrentBrowser.find_elements("tag name","input")
except NoSuchElementException:
    tElements=None

if tElements is not None:
    tElementID=findHTMLElement(tElements,"id")
    tElementPassword=findHTMLElement(tElements,"password")
    tElementSubmit=findHTMLElement(tElements,"submit")

    tElementID.send_keys(gID)
    tElementPassword.send_keys(gPassword)

    tAsction=Actions(tCurrentBrowser)
    tAsction.click(tElementSubmit).wait(2)
    tAsction.perform()

tCurrentBrowser.delete_session()

DOM取得用スクリプト

import sys

from marionette_driver.marionette import Marionette
from marionette_driver.legacy_actions import Actions

gURL="https://www.kishiro.com/content/"

tCurrentBrowser=Marionette('localhost',port=2828)
tCurrentBrowser.start_session()
 
tCurrentBrowser.navigate(gURL)

print(tCurrentBrowser.page_source.encode('utf_8'))

tCurrentBrowser.delete_session()

「python2.7 sample.py」みたいな感じで1つ目のスクリプトを実行した後、2つ目のスクリプトを実行すると、ログイン後のページを取得できると思います。