2021年12月31日金曜日

NetBSDとRaspberry Pi OS でOpenVPNを使った拠点間L2VPNの構築

自宅と実家をL2VPNで結びたくなったのでOpenVPNで繋ぐことにした。
自宅には前からあるNetBSD/amd64 9.2をOpenVPNのサーバとして使い、実家側にはラズパイを買って設置することにした。値段とか気にせずクリックしたらRaspberry Pi4 ModelB 4GBに必要な機材一式のセットを選んでいた。
試行錯誤して、やっとわかってきた。

・OpenVPNの設定以外にOS側のインターフェイスの設定が必要である。
・NetBSDとRaspberry Pi OSではbridgeインターフェイスの仕様が違う(NetBSDはbridgeインターフェイスにIPアドレスが付与できず、Raspberry Pi OSでは付与できる)
・L2VPNなので同一サブネットを共有するので拠点ごとにIPアドレスがぶつからない様に設計しておく。

いったん実家にもっていく前に自宅で試験環境を作成してやってみた。
構成はこんな感じ。




〇全般の設計
・共有するサブネットは192.168.0.0/24とする。
・サーバ側拠点は192.168.0.1~99、クライアント側拠点は192.168.0.100~254を利用する。
・デフォルトゲートウェイはサーバ側拠点は192.168.0.1、クライアント側拠点は192.168.0.100とする。
・OpenVPNはUDPでVPNを構築する。ポート番号をデフォルトの1194/UDPから(このblogでは)31194/UDPに変える。
・IPv4の事しか考えない。
・サーバ側拠点のグローバルIPアドレスは常時接続の為、滅多に変わらない事から自分で保有しているドメイン空間から切り出してAレコードで登録する。登録するDNSは個人契約のO365のサービスの中にあったので登録する。このblogでは「vpn.example.com」とする。
・今のところサーバ側、クライアント側でProxyArpの設定やFirewallの設定は無くても動いている。

〇NetBSD側(サーバ側)の対応
・クライアントから接続を受けるのでブロードバンドルータの設定を変更し外から31194/UDPがNetBSD側(サーバ側)に転送されるように設定(ポートフォワーディング)する。
・pkginを使い「openvpn-2.5.3」と「easy-rsa-3.0.8」を入れる。
・(メモ)netbsdのifconfigで一時的にIPアドレスを消すのは「ifconfig wm0 inet 192.168.0.3 delete
」という文法

1. インターフェイス側の設定を行う
・物理IF名はwm0のみである。
・wm0はIPアドレスを与えないため、「/etc/ifconfig.wm0」を作成し中身は「up」のみ記載。
・tap0インターフェイスが必要で、IPアドレスも付与するので「/etc/ifconfig.tap0」に下記の内容を記述する(「The NetBSD Guide」の「24.6. Setting up a network bridge device」にあるExample 24.10にドンピシャの設定が書いてあった。このtap0の設定の中でbridgeも設定してしまえば確かにブリッジの設定前にtap-IFが出来ていない依存関係が解決されるや
)。
---
create
inet 192.168.0.3 netmask 255.255.255.0
up
!ifconfig bridge0 create
!brconfig bridge0 add $int add wm0 up
---

・bridgeインターフェイスも必要だがtapの設定中で作成しているので不要
・「/etc/rc.conf」にはインターフェイスの設定は特に記載しない。
・ここで試しに再起動してみて、外部からsshなど出来るか、ネットワークインターフェイスやブリッジインターフェイスの状態、ルーティングテーブルの状態を確認すると区切りは良いでしょう。

tcpdumpでインターフェイスを指定してキャプチャして動作をみるとかも切り分けに良いかと。

ifconfig:
・wm0,bridge0,tap0はUPになっているか
・wm0,tap0は「PROMISC」が入っているか(netbsdのNW-IFはbridge-IFに参加すると自動でプロミスキャスモードになる)
・IPv4アドレスは、wm0のみに設定されているか
netstat -rn:
・defaultを含め、出力先IFがtap0になっているか(普通は物理IF(今回だとwm0)だが、今回はVPNの関係でtap0が出力先IFになる)。
brconfig bridge0:
・Interfacesにtap0とwm0が存在するか
・Address cacheに色々なMACアドレスが乗ってきているか。


2. OpenVPNのTLSの設定
証明書のくだりは全て下記のサイトに書いている通りに実施
VPNサーバー構築(OpenVPN) - CentOSで自宅サーバー構築 (centossrv.com)

3. OpenVPNの設定ファイル
・設定ファイル「server.conf」を「/usr/pkg/etc/openvpn」に設置。
・設定ファイルの中身は下記。動作のログはもともとこのサーバがsyslog.conf側で「*.info」を特定ファイルに書き出しているので、そっちに吐かれている
---
port 31194
proto udp
dev tap0
ca pki/ca.crt
cert pki/issued/server.crt
dh pki/dh.pem
server-bridge
keepalive 10 120
cipher AES-256-CBC
user nobody
group nobody
persist-key
persist-tun
status openvpn-status.log
verb 3
explicit-exit-notify 1
---

4. openvpnの起動設定
・起動ファイルのコピー「cp -p /usr/pkg/share/examples/rc.d/openvpn /etc/rc.d/」
・自動起動の設定「/etc/rc.conf」に「openvpn=YES」を追記する。
・この状態でrootにて「/etc/rc.d/openvpn start」としてログに「openvpn[2711]: Initialization Sequence Completed」が出ていれば待ち受け出来ている。
・プロセスなどをpsコマンドで見ておくと「/usr/pkg/sbin/openvpn --cd /usr/pkg/etc/openvpn --daemon --config server.conf」というプロセスが見える。
・sockstatでプロセス名でgrepをかければ「nobody   openvpn    2711   7 udp6   *.31194               *.*」が見える
・最後に再起動してみて、プロセスの状態やログが目的の通りであれば設定完了。

Raspberry Pi OS側(クライアント側)の対応
・必要なソフトのインストール
apt-get install openvpn
apt-get install tcpdump
apt-get install bridge-utils
・あると便利なツール
apt-get install locate
apt-get install emacs
・クライアントから最初のUDPを送るのでブロードバンドルータの設定は不要。
・(メモ)Raspberry Pi OSのifconfigで一時的にIPアドレスを消すのは「ifconfig eth0 0.0.0.0
」(この文法、気持ち悪い...)という文法
・なお私は
Raspberry Pi OSを含めLinux系のシステム管理は良く分からないのでググって適当にやっています。


1.
インターフェイス側の設定を行う
・eth0は、IPアドレスをつけずにUPだけさせる。
・「/etc/dhcpcd.conf」に下記を記述
---
interface eth0
static ip_address=0.0.0.0/0
---
・bridgeインターフェイスをbr0という名前で作成し、tapインターフェイスはtap0という名前で作成し、br0のブリッジにeth0とtap0を参加させる。
・「
/etc/network/interfaces」に下記を記述する。
---
auto br0
iface br0 inet static
    address 192.168.0.103
    netmask 255.255.255.0
    network 192.168.0.0
    broadcast 192.168.0.255
    gateway 192.168.0.101
    dns-nameserver 192.168.0.101
    bridge-ports eth0 tap0
    bridge_stp off
    bridge_maxwait 0
    bridge_fd      0
    pre-up ip tuntap add dev tap0 mode tap user root
    pre-up ip link set tap0 up
    post-down ip link set tap0 down
    post-down ip tuntap del dev tap0 mode tap
---
・ここで試しに再起動してみて、外部からsshなど出来るか、ネットワークインターフェイスやブリッジインターフェイスの状態、ルーティングテーブルの状態を確認すると区切りは良いでしょう。
tcpdumpでインターフェイスを指定してキャプチャして動作をみるとかも切り分けに良いかと。

ifconfig:
・eth0,br0,tap0はUPになっているか
・IPv4アドレスは、br0のみに設定されているか
netstat -rn:
・出力先IFがbr0になっているか
brctl show br0:
・Interfacesにtap0にeth0が存在するか

2. OpenVPNのTLSの設定
証明書のくだりは全て下記のサイトに書いている通りに実施
VPNサーバー構築(OpenVPN) - CentOSで自宅サーバー構築 (centossrv.com)

3. OpenVPNの設定ファイル
・設定ファイル「client.conf」を「/etc/openvpn/client」に設置。
・設定ファイルのシンボリックリンク「cd /etc/openvpn/」、「ln -s /etc/openvpn/client/client.conf ./」(自動起動時に何も考えないと/etc/openvpn/client.confを見に行くため)
・設定ファイルの中身は下記。
---
client
dev tap0
proto udp
remote vpn.example.com 31194
resolv-retry infinite
nobind
user nobody
group nogroup
ca client/ca.crt
cert client/client-A.crt
key client/client-A.key
remote-cert-tls server
tls-auth client/ta.key 1
cipher AES-256-CBC
verb 3
---

4. openvpnの起動設定
・お試しの起動を行うにはcd /etc/openvpn/」で降りて、「sudo openvpn --config client.conf」を実行。標準出力(画面)に「Initialization Sequence Completed」」が出ていれば接続できるはずなので、pingなどで疎通を確認。起動してから確立まで20秒ぐらいかかることもある。
・うまく行かない場合はtcpdumpでIFごとにパケットの状態を確認する。
・自動起動の設定「systemctl enable openvpn@client」を入力し、再起動をして動作確認
・クライアント側の再起動時、トンネル確立後、クライアント側のなんかのパケットを契機に(arp?)サーバ側からもパケットが送れるようになるので、アドホックにclontabに「@reboot sleep 10; ping -c 192.168.0.1 > /dev/null」を記述

〇状況
OpenVPNを使った拠点間L2VPNの構築が出来たが肝心のDLNAがだいたいうまく出来るがまれにできない時がある。
拠点間のRTT(作業用PCとNetBSD間のpingで計測)で50ms~70msでCATV会社提供のUSB-HDDに入れている地デジ番組やCATV番組をDLNAで視聴することや、CATV会社のDLNAをIOデータのNASにムーブしているものを視聴することができた。
オミクロン株が落ち着いたら実家にもっていって確認しよう。


〇参考サイト(感謝)
・OpenVPN本家:Ethernet Bridging
 ここにインターフェイスの設定はOpenVPNの設定の外で必要よと書いてあり気が付く。
・NetBSD本家: The NetBSD Guideの24.6. Setting up a network bridge device
   bridgeの設定方法などを確認 
・QiitaのKohei MATSUSHITA さんの記事: OpenVPNで拠点間L2接続
  構成図があったので、これをみてIFとIPアドレスの付与について気づきになった。
・CentOSで自宅サーバー構築: VPNサーバー構築(OpenVPN)
  TLSの設定は、内容を理解せずにこの記事を見よう見まねで設定。
・ある異邦人の技術メモ:自宅と実家をOpenVPNでつなぐ!

2021年11月7日日曜日

一般向けのISP契約でトラフィックを計測できる構成に変えてみた

 自宅からトラフィックがどれぐらい出ているのか、簡単にわかると良いなと常々思っており、ISPが配るホームルータには画面で分かるとかSNMPの対応をしてほしいと思っている。

そんな機能がついているのはであったことがなく、一般向けのルータにもついてない。

しょうがないので前のJCOM 320Mbps契約の際は、そもそもグローバルIPアドレスが1個だけ降ってきてホームルータも無いのでLinuxでNAT箱を作成し、そのNICのトラフィックを取り出して仕様帯域を確認していた。

普通に使う分にダウンロードとか遅いとかは無かった(*1)が、上りが10Mbpsしかない仕様なのでクラウドにデータバックアップをがっつりしていると帯域が飽和し、その状態でWeb会議を行うと仕事にならないので、JCOMのひかり1ギガに切り替えている。
(*1: Webを見ていると同軸の320Mbpsで全然速度も出ない人もいるようで運が必要なのかも)

JCOMの1ギガは約款を見ると、同軸1ギガ(1Gコース)、JCOM回線のひかりの1ギガ(ひかり1Gコース)、KDDIの1ギガの再販(光 1G コース on au ひかり)があるっぽい。契約したのはKDDIの再販のひかり1Gで契約するとONUとホームゲートウェイ(HGW)としてAterm BL1001HWが提供される。このBL1001HWが結構高性能で、自宅のWIFI6(802.11ax)はこのHGWのみが対応という状態。その為、WIFI6を使うとトラフィックが取れないという事を我慢していた。

先日、ふと気が付いて、ONUとHGW間のL2SWをかまして、そのL2SWからSNMPでデータを取り出せば良いじゃないと。

やってみたところ、うまく取り出せている模様。L2SWもポートVLAN機能が役に立ってうれしいだろう。ただISP契約を2.5Gbps以上にすると、この構成だと困るので、その際はMS510TXとか買い直さないといけないかも。




2021年11月6日土曜日

NASのHDDを交換

先日壊れたNASのHDDを交換した。
10月31日昼にノジマオンラインで注文したところ11月4日の昼に届いた。

稼働したままNASのslot1を引っこ抜いて、HDDを入れ替えたら勝手に認識してリビルドがスタート。5時間かけて再構成が完了

HDDを交換する直前の各種画面は下記の感じ。











2021年10月31日日曜日

NAS(QNAP TS-431P/RAID6/4本構成)で突然HDDの認識がなくなった。

 久しぶりにNASにログインしたら

・DISK4が認識していません。
・DISK1が壊れそうです。

とのエラー表示。

びっくり。RAID6で良かったと思いつつNASの前へ。ステータスランプはSTATUSもHDD1~HDD4もすべて緑ランプ。なんじゃいと思いながら確認して、前回のHDD交換(2021年12月7日)の作業記録を確認する。前回は壊れたHDDのランプは赤(橙)色になっているので、本当に急に認識が死んだのかもしれない。DISK1も死んでいるわけではないので、まずは再認識をさせてみるためにDISK4を抜く。
抜くと本体のランプが消え、再度差し込んでみると無事に認識してリビルドがスタート。
リビルドは5時間ぐらいの模様。
DISK1の処理をどうしようかと、思案する。このHDDは購入して5年近いので無料交換保証対象ではないので新規に注文することにした。
今使っている東芝の2.5インチ1TBのHDD,MQ01ABD100は4,800円ぐらいで東芝2.5インチ2TBのHDD,MQ04ABD200が6,000円ぐらいだったので,いつの日か容量を増やすことを考えて今回の購入は大きなサイズにしておくことにした。
メーカー型番内容購入先個数値段
東芝MQ04ABD200 2.5インチHDD 2TBノジマオンライン1個6,045円(税込)
10/31の昼に注文で、翌日に来る模様。
DISK1が不良の扱いなのはSMARTのID:197,198を見て決めている模様。

リビルドから20分ぐらいしたところで、完全にDISK1が排除されたようで、大きな警告音が鳴った後、本体のSTUTASランプが赤と緑の点滅、HDD1が赤ランプに変わった。


とりあえず冗長を確保するために早く同期が終わってほしい....
一応、契約しているOneDriveへのバックアップ同期もしているのでロスト対策はしているが。

10/31 夜追記
・RAIDの再構成は4時間ほどで完了
・「10/31の昼に注文で、翌日に来る模様。」と書いたがそれは1TBのHDDで注文した2TBは11/9の配送だった。

2021年8月15日日曜日

WxBeacon2とSwitchBotの温湿度計の取得python3スクリプトを改修

自宅にはWxBeacon2とSwitchBotの温湿度計が各々1台ずつあるのだが、 先日のスクリプトだと1台のセンサから情報取得するのに毎回5秒かかるので1分間には最大12個しか取れない。クラス定義のscanを読むとタイムアウトまでの時間で見つかったデバイス全てに処理をしているようだったので、チェックしたいデバイスのMacアドレスをリストで渡すと、その中で見つかったセンサのデータを返すようにしてみた。

センサからのデータを成形したりbluepyを使う部分は前回同様に @c60evaporatorさんの記事のコードそのまま。

  1. Omron環境センサの値をRaspberryPiで定期ロギングする
  2. SwitchBot温湿度計の値をRaspberryPiでロギング

WxBeacon2のブロードキャストモードへの変更やSwitchBotのMACアドレスの取得、周辺モジュールのインストールや設定は上記の記事を参照の事。

「env_broadcast.py」、「bluetoothsensor.py」を同一ディレクトリに書くのすれば動くはず。

「env_broadcast.py」は、リスト型で調査対象のMACアドレスを一覧で受け取る。スキャン時に一致するMACアドレスが見つかるとWxBeacon2かSwitchBotの温湿度計かを自動判別してセンサ情報を取得する。取得したデータはMACアドレスをキーにしたdict型(バリューも更にdict型)に格納する。同じMACアドレスで2度取れてしまった際は上書きしている。

env_broadcast.py

from bluepy import btle
import struct

class ScanDelegate(btle.DefaultDelegate):
    #コンストラクタ
    def __init__(self, macaddrs):
        btle.DefaultDelegate.__init__(self)
        #センサデータ保持用変数
        self.macaddr = macaddrs
        self.sensorValue = dict()
        for mac in macaddrs:
            self.sensorValue[mac] = {'SensorType':'None'}

    # スキャンハンドラー
    def handleDiscovery(self, dev, isNewDev, isNewData):  
        # 新しいデバイスが見つかったら
        if isNewDev or isNewData:  
            # ターゲットのMACアドレス(環境センサ)であるか
            # print('dev.addr in self.macaddr ->', dev.addr, self.macaddr, dev.addr in self.macaddr)
            if dev.addr in self.macaddr:
                # アドバタイズデータを取り出し
                for (adtype, desc, value) in dev.getScanData():  
                    # 環境センサのとき、データ取り出しを実行
                    # まずWxBeacon2/Omronの場合
                    if desc == 'Manufacturer' and value[0:4] == 'd502':
                        #センサの種類(EP or IM)を取り出し
                        sensorType = dev.scanData[dev.SHORT_LOCAL_NAME].decode(encoding='utf-8')
                        #EPのときのセンサデータ取り出し
                        if sensorType == 'EP':
                            self.decodeSensorData_EP(value, dev.addr)
                        #IMのときのセンサデータ取り出し
                        if sensorType == 'IM':
                            self.decodeSensorData_IM(value, dev.addr)
                    # SwitchBotの温湿度計の場合
                    else:
                        for (adtype, desc, value) in dev.getScanData():  
                            #環境センサのとき、データ取り出しを実行
                            if desc == '16b Service Data':
                                #センサデータ取り出し
                                self._decodeSensorData(value, dev.addr)

    # センサデータを取り出してdict形式に変換(EPモード時)
    def decodeSensorData_EP(self, valueStr, macaddr):
        #文字列からセンサデータ(6文字目以降)のみ取り出し、バイナリに変換
        valueBinary = bytes.fromhex(valueStr[6:])
        #バイナリ形式のセンサデータを整数型Tapleに変換
        (temp, humid, light, uv, press, noise, discomf, wbgt, rfu, batt) = struct.unpack('<hhhhhhhhhB', valueBinary)
        #単位変換した上でdict型に格納
        self.sensorValue[macaddr] = {
            'SensorType': 'EP',
            'Temperature': temp / 100,
            'Humidity': humid / 100,
            'Light': light,
            'UV': uv / 100,
            'Pressure': press / 10,
            'Noise': noise / 100,
            'Discomfort': discomf / 100,
            'WBGT': wbgt / 100,
            'BatteryVoltage': (batt + 100) / 100
        }

    # センサデータを取り出してdict形式に変換(IMモード時)
    def decodeSensorData_IM(self, valueStr, macaddr):
        #文字列からセンサデータ(6文字目以降)のみ取り出し、バイナリに変換
        valueBinary = bytes.fromhex(valueStr[6:])
        #バイナリ形式のセンサデータを整数型Tapleに変換
        (temp, humid, light, uv, press, noise, accelX, accelY, accelZ, batt) = struct.unpack('<hhhhhhhhhB', valueBinary)
        #単位変換した上でdict型に格納
        self.sensorValue[macaddr] = {
            'SensorType': 'IM',
            'Temperature': temp / 100,
            'Humidity': humid / 100,
            'Light': light,
            'UV': uv / 100,
            'Pressure': press / 10,
            'Noise': noise / 100,
            'AccelerationX': accelX / 10,
            'AccelerationY': accelY / 10,
            'AccelerationZ': accelZ / 10,
            'BatteryVoltage': (batt + 100) / 100
        }
    
    # センサデータを取り出してdict形式に変換
    def _decodeSensorData(self, valueStr, macaddr):
        #文字列からセンサデータ(4文字目以降)のみ取り出し、バイナリに変換
        valueBinary = bytes.fromhex(valueStr[4:])
        #バイナリ形式のセンサデータを数値に変換
        batt = valueBinary[2] & 0b01111111
        isTemperatureAboveFreezing = valueBinary[4] & 0b10000000
        temp = ( valueBinary[3] & 0b00001111 ) / 10 + ( valueBinary[4] & 0b01111111 )
        if not isTemperatureAboveFreezing:
            temp = -temp
        humid = valueBinary[5] & 0b01111111
        #dict型に格納
        self.sensorValue[macaddr] = {
            'SensorType': 'SwitchBot',
            'Temperature': temp,
            'Humidity': humid,
            'BatteryVoltage': batt
        }

「bluetoothsensor.py」は、メインルーチンで取得したいデバイスのMACアドレスをリスト型で列挙しておく。取得データの処理の際はMACアドレスをキーに、まずはSensorTypeの格納している値を確認しNoneだと取れていない、ほかの値だとセンサーごとの処理に分離させる。今はrrdtoolに突っ込むのに便利な表示にしている。
(よく考えるとScanDelegateの方で取得時間も返すのに含めておかないタイムアウトの秒数の間のどこで取得したか分からないな)

bluetoothsensor.py
#!/usr/bin/python3
from bluepy import btle
from env_broadcast import ScanDelegate
import time

while True:
    # 収集したいセンサのMACアドレスをリスト型で列挙(MACアドレスは小文字で記載)
    sensor_mac_list = ["c9:43:c3:XX:XX:XX","c5:d7:b1:XX:XX:XX"]

    # センサ値取得デリゲートを、スキャン時実行に設定
    scanner = btle.Scanner().withDelegate(ScanDelegate(sensor_mac_list))
    #スキャンしてセンサ値取得(タイムアウト10秒。10秒間で見つかったbluetoothデバイス全てにチェックを実施)
    scanner.scan(10.0)
    
    # 取得した値一覧
    # print(scanner.delegate.sensorValue)

    if scanner.delegate.sensorValue is not None:
        for macaddr in sensor_mac_list:
            if scanner.delegate.sensorValue[macaddr]['SensorType'] == 'EP':
                print("update /XXXX/RRD/wxbeacon2.rrd "
                    + str(int(time.time())) + ':'
                    + str(scanner.delegate.sensorValue[macaddr]['BatteryVoltage']) + ':'
                    + str(scanner.delegate.sensorValue[macaddr]['Temperature']) + ':'
                    + str(scanner.delegate.sensorValue[macaddr]['Humidity']) + ':'
                    + str(scanner.delegate.sensorValue[macaddr]['Light']) + ':'
                    + str(scanner.delegate.sensorValue[macaddr]['UV']) + ':'
                    + str(scanner.delegate.sensorValue[macaddr]['Pressure']) + ':'
                    + str(scanner.delegate.sensorValue[macaddr]['Noise']) + ':'
                    + str(scanner.delegate.sensorValue[macaddr]['Discomfort']) + ':'
                    + str(scanner.delegate.sensorValue[macaddr]['WBGT']), flush=True)
            elif scanner.delegate.sensorValue[macaddr]['SensorType'] == 'IM':
print("update /XXXX/NoName.rrd " + str(int(time.time())) + ':' + str(scanner.delegate.sensorValue[macaddr]['BatteryVoltage']) + ':' + str(scanner.delegate.sensorValue[macaddr]['Temperature']) + ':' + str(scanner.delegate.sensorValue[macaddr]['Humidity']) + ':' + str(scanner.delegate.sensorValue[macaddr]['Light']) + ':' + str(scanner.delegate.sensorValue[macaddr]['UV']) + ':' + str(scanner.delegate.sensorValue[macaddr]['Pressure']) + ':' + str(scanner.delegate.sensorValue[macaddr]['Noise']) + ':' + str(scanner.delegate.sensorValue[macaddr]['AccelerationX']) + ':' + str(scanner.delegate.sensorValue[macaddr]['AccelerationY']) + ':' + str(scanner.delegate.sensorValue[macaddr]['AccelerationZ']) + ':' + str(scanner.delegate.sensorValue[macaddr]['AccelerationX']), flush=True) elif scanner.delegate.sensorValue[macaddr]['SensorType'] == 'SwitchBot':
print('update /XXXX/RRD/swbot.rrd ' + str(int(time.time())) + ':' + str(scanner.delegate.sensorValue[macaddr]['Temperature']) + ':' + str(scanner.delegate.sensorValue[macaddr]['Humidity']) + ':' + str(scanner.delegate.sensorValue[macaddr]['BatteryVoltage']), flush=True) elif scanner.delegate.sensorValue.get[macaddr]['SensorType'] == 'None':
# 時間内にデバイスが見つからなかった pass else: # 想定外 print(scanner.delegate.sensorValue) time.sleep(50)

実行結果は下記の感じなので、rrdtoolが動いているサーバには「./bluetoothsensor.py |nc XXX.XXX.XXX.XXX 42217 >/dev/null &」な感じで飛ばしている。

$ ./bluetoothsensor.py
update /XXXX/RRD/swbot.rrd 1629028238:26.4:66:100
update /XXXX/RRD/wxbeacon2.rrd 1629028238:2.91:19.63:100.0:0:0.01:1010.1:31.9:67.33:23.03

2021年8月14日土曜日

WxBeacon2とSwitchBotの温湿度計を1台のラズパイで取得できるようにした。

(8/15)追記
この記事では1回のSCANで1デバイスから取得しているが1回のSCANで複数のデバイスからデータ取得できるのを記事にした(URL)。

--------------

Bluetooth気温計のデータをラズパイで取得しrrdtoolで描画できるようにした。」のセンサ接続とrrdtoolに繋げた部分について

 @c60evaporatorさんの記事

  1. Omron環境センサの値をRaspberryPiで定期ロギングする
  2. SwitchBot温湿度計の値をRaspberryPiでロギング

を参考にして作成。

まずはラズパイ3の1台に各記事ごとに設定して動作の確認。

「2」の方の記事ではまったのは、

sudo install libglib2.0-dev

との記載は「sudo apt-get install  libglib2.0-dev」 かと。てっきりripが抜けているのかとか悩んだ(linux,Python初心者なので勘がきかない)。
あとメインスクリプトの部分でMacアドレスを渡さないといけないのと、Macアドレスを小文字で書かないと駄目なのにはまる。

同一ディレクトリに、「1」の記事の「omron_env_broadcast.py」と「2」の記事の「switchbot.py」を放り込んで、両記事のメインスクリプトを合体させ、scanして見つからなかった際の処理を追加して出力をrrdtoolに都合の良いものに書き換えたbluetoothsensor.pyを放り込む。
(scanを1回で両方のデータを取れるようにできそうだけど、後日頑張ろう)

bluetoothsensor.py

#!/usr/bin/python3
from bluepy import btle
from omron_env_broadcast import ScanDelegate
from switchbot import SwitchbotScanDelegate
import datetime
import time

while True:
    dt_now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    sb_meter = { "kitchen":"c9:43:c3:xx:xx:xx"}

    #omron_env_broadcast.pyのセンサ値取得デリゲートを、スキャン時実行に設定
    scanner = btle.Scanner().withDelegate(ScanDelegate())
    #スキャンしてセンサ値取得(タイムアウト5秒)
    scanner.scan(5.0)

    if scanner.delegate.sensorValue is not None:
        #print(dt_now + "," + "OM, " + "update /home/XXXX/RRD/wxbeacon2.rrd "
        print("update /home/XXXX/RRD/wxbeacon2.rrd "
            + str(int(time.time())) + ":"
            + str(scanner.delegate.sensorValue['BatteryVoltage']) + ":"
            + str(scanner.delegate.sensorValue['Temperature']) + ":"
            + str(scanner.delegate.sensorValue['Humidity']) + ":"
            + str(scanner.delegate.sensorValue['Light']) + ":"
            + str(scanner.delegate.sensorValue['UV']) + ":"
            + str(scanner.delegate.sensorValue['Pressure']) + ":"
            + str(scanner.delegate.sensorValue['Noise']) + ":"
            + str(scanner.delegate.sensorValue['Discomfort']) + ":"
            + str(scanner.delegate.sensorValue['WBGT']), flush=True)
  ######SwitchBotの値取得######
    for key in sb_meter:
        #switchbot.pyのセンサ値取得デリゲートを、スキャン時実行に設定
        scanner = btle.Scanner().withDelegate(SwitchbotScanDelegate(sb_meter[key]))
        #スキャンしてセンサ値取得(タイムアウト5秒)
        scanner.scan(5.0)

        if scanner.delegate.sensorValue is not None:
            #print(dt_now + "," + "SW, " + "update /home/XXXXX/RRD/swbot.rrd "
            print("update /home/XXXX/RRD/swbot.rrd "
                + str(int(time.time())) + ":"
                + str(scanner.delegate.sensorValue['Temperature']) + ":"
                + str(scanner.delegate.sensorValue['Humidity']) + ":"
                + str(scanner.delegate.sensorValue['BatteryVoltage']), flush=True)

    time.sleep(50)
これを動かすと、rrdtoolに食わすことができる形式で標準出力に出てきます。
$ ./bluetoothsensor.py
update /home/xxxxx/RRD/wxbeacon2.rrd 1628919666:2.92:21.5:99.0:76:0.02:999.7:35.89:70.63:24.46
update /home/xxxxx/RRD/swbot.rrd 1628919671:27.0:59:100
update /home/xxxxx/RRD/wxbeacon2.rrd 1628919726:2.92:21.5:99.03:81:0.02:999.8:31.58:70.63:24.46
update /home/xxxxx/RRD/swbot.rrd 1628919731:27.0:58:100

 のようなものが1分ごとに出てきます。ラズパイ側にデータをためるつもりはないのでデータ保存兼画像作成のサーバ側でrrdcachedを立ち上げラズパイから渡します。

サーバはNetBSD 9.2なので、「pkgin install rrdtool 」して「usr/pkg/bin/rrdcached -p /var/run/rrdcached/rrdcached.pid -l xxx.xxx.xxx.xxx:42217 -U rrdcached -w 60」(-lの後のxxxの部分は自サーバのIFについているIPv4アドレス)で立ち上げる。

ラズパイ側から疎通確認のために「telnet xxx.xxx.xxx.xxx 42217」で繋いで、「HELP」とか打ち込んで、


$ telnet XXX.XXX.XXX.XXX 42217
Trying XXX.XXX.XXX.XXX...
Connected to XXX.XXX.XXX.XXXX.
Escape character is '^]'.
HELP
22 Command overview
UPDATE <filename> <values> [<values> ...]
FLUSH <filename>
FLUSHALL
PENDING <filename>
FORGET <filename>
....
と出れば接続OK.QUITで抜ける。あとはラズパイ側でNC(NetCat)を利用して、「./bluetoothsensor.py |nc xxx.xxx.xxx.xxx 42217 &」とかで直接投げ込めば更新完了。 うまく行かない際はサーバ側のRRDファイルのパーミッションがユーザ「rrdcached」での書き込み権限があるか確認。 サーバ側で更新されているか確認は「rrdtool last RRDファイル名」などで最終更新日を確認する。以下にrrdtoolでのRRDの作成と画像のパラーメータを載せておく。 細かいオプションは「RRDtool 1.2系を使う」を参照の事。
rrdtool create wxbeacon2.rrd --start "00:00 07/01/2021" \
        --step 60 \
        --no-overwrite \
        DS:Battery:GAUGE:600:0:U \
        DS:Temperature:GAUGE:600:U:U \
        DS:Humidity:GAUGE:600:0:100 \
        DS:Light:GAUGE:600:0:U \
        DS:UV:GAUGE:600:0:U \
        DS:Pressure:GAUGE:600:0:U \
        DS:Noise:GAUGE:600:0:U \
        DS:Discomfort:GAUGE:600:U:U \
        DS:HeatStrokeRisk:GAUGE:600:U:U \
        RRA:MAX:0.5:1:5256000

rrdtool create swbot.rrd --start "00:00 07/01/2021" \
        --step 60 \
        --no-overwrite \
        DS:Temperature:GAUGE:600:U:U \
        DS:Humidity:GAUGE:600:0:100 \
        DS:BatteryVoltage:GAUGE:600:0:U \
        RRA:MAX:0.5:1:5256000

/usr/pkg/bin/rrdtool graph temp-2week.png -h 300 -w 500 -s 'now - 2weeks' \
    DEF:a=/XXXX/RRD/usbrh.rrd:Temperature:MAX \
    DEF:b=/XXXX/RRD/wxbeacon2.rrd:Temperature:MAX \
    DEF:c=/XXXX/RRD/swbot.rrd:Temperature:MAX \
    LINE2:a#FF0000:"usbrh(室内)\n" \
    LINE2:b#00FF00:"wx2(庭)\n" \
    LINE2:c#00FFFF:"SWbot(台所)" -t "気温" -v "degC"

/usr/pkg/bin/rrdtool graph temp-2day.png  -h 300 -w 500 -s 'now - 172800' \
    DEF:a=/XXXX/RRD/usbrh.rrd:Temperature:MAX \
    DEF:b=/XXXX/RRD/wxbeacon2.rrd:Temperature:MAX \
    DEF:c=/XXXX/RRD/swbot.rrd:Temperature:MAX \
    LINE2:a#FF0000:"usbrh(室内)\n" \
    LINE2:b#00FF00:"wx2(庭)\n" \
    LINE2:c#00FFFF:"SWbot(台所)" -t "気温" -v "degC"

/usr/pkg/bin/rrdtool graph temp-60min.png -h 300 -w 500 -s 'now - 3600'   \
    DEF:a=/XXXX/RRD/usbrh.rrd:Temperature:MAX \
    DEF:b=/XXXX/RRD/wxbeacon2.rrd:Temperature:MAX \
    DEF:c=/XXXX/RRD/swbot.rrd:Temperature:MAX \
    LINE2:a#FF0000:"usbrh(室内)\n" \
    LINE2:b#00FF00:"wx2(庭)\n" \
    LINE2:c#00FFFF:"SWbot(台所)" -t "気温" -v "degC"




パゴダを作成し庭に設置した

 「Bluetooth気温計のデータをラズパイで取得しrrdtoolで描画できるようにした。」のパゴダの作成部分の記事。

パゴダは10年前に一度作成していて勝手は分かっている。前回は軒下につるしたところ、直射日光が壁に当たり、その温まった空気がパコダに入り込むという問題があった。USBでセンサを繋いでいたので遠くに置けないこともあり、あきらめていたがbluetooth接続のWxBeacon2が手に入ったので庭に設置の前提で再度作り直してみた。
以前参照していたページもナチュラル研究所と引っ越していました。

材料費

最初は5号皿4枚で作成した。




直射日光が当たると数字がおかしい感じがしたので、対策を実施。
  1. 大きな屋根に相当する部分を追加する
  2. 空気がたまらないようにパゴダの中の空間を広げる




この対策を行って、一番暑い日のデータがアメダスとほぼ同じなので大丈夫かと。






usbrhのコードを再度書き直した

Bluetooth気温計のデータをラズパイで取得しrrdtoolで描画できるようにした。」のusbrhのコードを再度書き直したについて

 10年前にUSB温度・湿度計モジュール(以下usbrh)を連続的にとれるコードを書いていたが、サーバのHDDが壊れるとともに消えてしまった模様。
しょうがいないのでNetBSDのvmstatのコードを参考にしつつ連続的に取得するコードを追加。後日、GitHubにでも置いてみるかなぁ。使ったことないからよくわからん。

コマンドの使い方はこんな感じ

./usbrh -h
usage: usbrh [-v] [-f device] [-c count] [-w wait] [-t timeformat]
timeformat: unix=unix time, at=at time, default:RFC3339

./usbrh -w 60 -t unix
1628922942 28.72 63.7

rrdtoolに入れる際はこんな感じ

./usbrh -w 60 -t unix | awk '{ print "update /XXXX/usbrh.rrd "  $1 ":" $2 ":" $3; fflush()}' | rrdtool - &

ちなみにRRDファイルの作成は以下のコマンドで実施

rrdtool create usbrh.rrd --start "00:00 07/01/2021" \
        --step 60 \
        --no-overwrite \
        DS:Temperature:GAUGE:600:U:U \
        DS:Humidity:GAUGE:600:0:100 \
        RRA:MAX:0.5:1:5256000
GitHubにおいてないので、いったんにここに書くが、まずは USBRH on *BSDからtar玉をダウンロードしてmain.cを下記と入れ替えてコンパイルする。もとのaoyamaさんのコードがBSDライセンスなので、自分で書いた部分のライセンスをBSDライセンスの文言でさらに追加すればいいのかしら。。。
(動作確認 NetBSD/amd64 9.2)

/*
 * $Id: main.c,v 1.4 2007/01/10 13:11:30 aoyama Exp $
 *
 * Copyright (c) 2006 Kenji AOYAMA <aoyama@nk-home.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/ioctl.h>
#include <sys/param.h>

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>


#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>

/* prototypes */
void usage(void);
int main(int, char *[]);
int check_device(char *);

#define USBRH_VENDOR  0x1774
#define USBRH_PRODUCT 0x1001
#define DEFAULT_DEVICE "/dev/uhid0"

extern char *__progname;
extern char *optarg;
extern int optind;
extern float calc_temp(unsigned int);
extern float calc_humid(unsigned int, float);

int vflag = 0;

int
main(int argc, char *argv[])
{
        struct usb_ctl_report ucr;
        struct timespec interval;
        int ch, fd, ret;
        int reps;
        float t, h;
        u_char buf[8];
        char date[64];
        char devname[MAXPATHLEN];
        time_t ti;
        int uflag, aflag, rflag;

        reps = 0;
        uflag=aflag=rflag=0;
        interval.tv_sec = 0;
        interval.tv_nsec = 0;
        strlcpy(devname, DEFAULT_DEVICE, sizeof(devname));

        while ((ch = getopt(argc, argv, "c:f:ht:vw:")) != -1) {
                switch(ch) {
                case 'c':
                        reps = atoi(optarg);
                        break;
                case 'f':
                        strlcpy(devname, optarg, sizeof(devname));
                        break;
                case 'h':
                        usage();
                        break;
                case 't':
                        if(strcmp(optarg,"unix")==0){
                                uflag = 1;
                        }else if(strcmp(optarg,"at")==0){
                                aflag = 1;
                        }else{
                                rflag = 1;
                        }
                        break;
                case 'v':
                        vflag = 1;
                        break;
                case 'w':
                        interval.tv_sec = atol(optarg);
                        break;
                default:
                        usage();
                        /* NOTREACHED */
                }
        }
        argc -= optind;
        argv += optind;

        if (check_device(devname) != 1) {
                fprintf(stderr, "%s: can not find USBRH device on %s\n",
                        __progname, devname);
                exit(1);
        }


        if ((fd = open(devname, O_RDWR)) < 0) {
                perror(devname);
                exit(1);
        }

        /*
         * XXX: If we issue SET_REPORT only once, read(2) sometimes
         * blocks.  So we issue SET_REPORT twice, with 1 second interval,
         * that seems OK.
         */


        ucr.ucr_report = UHID_OUTPUT_REPORT;
        if (ioctl(fd, USB_SET_REPORT, &ucr) < 0) {
                perror("USB_SET_REPORT");
                exit(1);
        }

        sleep(1);

        for(;;){
                if (interval.tv_sec) {
                        if (!reps)
                                reps = -1;
                } else if (reps){
                        interval.tv_sec = 1;
                }

                ucr.ucr_report = UHID_OUTPUT_REPORT;
                if (ioctl(fd, USB_SET_REPORT, &ucr) < 0) {
                        perror("USB_SET_REPORT");
                        exit(1);
                }

                if ((ret = read(fd, buf, 7)) == -1) {
                        fprintf(stderr, "read error\n");
                        exit(1);
                }

                //               close(fd);

                /*
                 * Now we got 7 bytes data from the device.
                 *
                 * buf[0] Humidity raw data (high byte)
                 * buf[1] Humidity raw data (low byte)
                 * buf[2] Temperature raw data (high byte)
                 * buf[3] Temperature raw data (low byte)
                 * buf[4] ???
                 * buf[5] ???
                 * buf[6] ???
                 */

                /* Calculate the real values */
                t = calc_temp(buf[2] * 256 + buf[3]);
                h = calc_humid(buf[0] * 256 + buf[1], t);

                ti = time(NULL);
                if(uflag){
                        sprintf(date,"%ld", ti);
                }else if(aflag){
                        strftime(date, sizeof(date), "%H:%M %m/%d/%Y", localtime(&ti));
                }else if(rflag){
                        strftime(date, sizeof(date), "%Y-%m-%dT%H:%M:%S%z", localtime(&ti));
                }

                if (vflag) {
                        printf("Date\tTemperature\tHumidity\n");
                }
                printf("%s %.2f %.1f\n", date, t, h);



                fflush(stdout);
                if (reps >= 0 && --reps <=0)
                        break;
                (void)nanosleep(&interval, NULL);
        }
        return 0;
}

void
usage(void)
{
        fprintf(stderr, "usage: %s [-v] [-f device] [-c count] [-w wait] [-t timeformat]\n", __progname);
        fprintf(stderr, "timeformat: unix=unix time, at=at time, default:RFC3339\n");
        exit(1);
}

/*
 * Check USBRH vendor ID and product ID on specified device.
 * Return 1 if matches.
 */
int
check_device(char *devname)
{
        int fd;
        struct usb_device_info udi;

#if defined(__NetBSD__) || defined(__OpenBSD__)

        /* On NetBSD/OpenBSD, /dev/uhid? can accept USB_GET_DEVICEINFO ioctl. */

        if ((fd = open(devname, O_RDWR)) < 0) {
                perror(devname);
                exit(1);
        }

        if (ioctl(fd, USB_GET_DEVICEINFO, &udi) < 0) {
                perror("USB_GET_DEVICEINFO");
                exit(1);
        }

        close(fd);

        /* vendor and product ID check */
        if ((udi.udi_vendorNo == USBRH_VENDOR) &&
                (udi.udi_productNo == USBRH_PRODUCT)) {
                return 1;
        }

        return 0;

#else

        /* Otherwise, walk through from /dev/usb? devices. */

        int cnt, addr;
        char buf1[MAXPATHLEN], buf2[MAXPATHLEN];

        for (cnt = 0; cnt < 10; cnt++) {        /* XXX: is this enough? */
                snprintf(buf1, sizeof(buf1), "/dev/usb%d", cnt);
                fd = open(buf1, O_RDONLY);
                if (fd < 0)
                        continue;
                for (addr = 1; addr < USB_MAX_DEVICES; addr++) {
                        udi.udi_addr = addr;
                        if (ioctl(fd, USB_DEVICEINFO, &udi) < 0)
                                continue;
                        strlcpy(buf2, "/dev/", sizeof(buf2));
                        strlcat(buf2, udi.udi_devnames[0], sizeof(buf2));
#ifdef DEBUG
                        printf("%s Addr %d, Vendor 0x%04x, Product 0x%04x, "
                                "%s, %s, %s, %s, %s\n",
                                buf1, addr, udi.udi_vendorNo, udi.udi_productNo,
                                udi.udi_devnames[0], udi.udi_devnames[1],
                                udi.udi_devnames[2], udi.udi_devnames[3],
                                buf2);
#endif
                        if ((strcmp(devname, buf2) == 0) &&
                                (udi.udi_vendorNo == USBRH_VENDOR) &&
                                (udi.udi_productNo == USBRH_PRODUCT)) {
                                return 1;
                        }
                }
                close(fd);
        }
        return 0;
#endif
}

Bluetooth気温計のデータをラズパイで取得しrrdtoolで描画できるようにした。

Bluetooth経由で温度が取れるセンサーをRaspberry Pi 3 Model Bで取得してrrdtoolで描画するようにしてみました。

用意したもの

  • Bluetooth対応のセンサ
  • USB接続のセンサ
  • データ取得
    • Raspberry Pi 3 Model B(標準でBluetooth/WiFi4対応)
      Raspberry Pi OS 5.10
    • rrdtoolサーバ
      NetBSD 9.2(amd64)  rrdtool 1.7.2

構成は下記の感じ。

下記は別記事にした。


東京でも酷暑になった2021年8月10日(ニュース「東京五地点で酷暑ラッシュ 体温超えの危険な暑さに」)の自宅のデータ(緑)とアメダスのデータを合わせて描画してみると、うちは府中アメダス(水色)のデータと一番近いのかも。横浜のアメダス(黒)は海に近いから独特の動きなかんじがする。






2021年7月28日水曜日

1x6材(3フィート) 3枚で踏み台を作ってみた

 以前、作った滑り台の台を段ボールにしていたが、だんだんとボロボロになってきたのでちゃんとした台を作ることにした。

参考にしたのは「2×4材(6F)1本で出来る踏み台♪」とか「1×6材で子供用2段ステップを手作り 1」とか。


オリンピック4連休の3日目にホームセンターで木材を買っておいて、最終日に作成した。この4連休はめちゃくちゃ暑かった。

踏み台の要件は2段あるタイプで、底面のサイズが30 cm x 30 cmで収まることにした。

踏むところが1x6か2x6で悩んだ。1(=19mm)だけだとたわむだろうと思い倍の厚さにするか横を補強するように渡すか。結果2(=38mm)の厚さだと持ち運ぶ際に重いのとねじ止めが面倒で、ネジも長いのを追加購入する必要があると思い板は増えるが1x6 3フィートを3枚で作ることにする。

設計図



自分で鋸を使って精度よく切れないので、カットはホームセンターに依頼してしまった。

価格は下記。あら、3か月で1割以上の値上がりかな。


ネジなどは既にストックしているものを利用。カットを自分でやらないとかなり楽。暑いので玄関で扇風機を回しながらやって2,3時間で完成。







滑り台を載せても落ちないように彫刻刀で引っ掛けるところを掘って完成。
結構、次男が椅子にして遊んでいる。

2021年7月26日月曜日

2021年春期の情報処理技術者試験の結果

論文がうまく書けないと思ったらやっぱり評価が低かった。 

令和3年度 春期   システムアーキテクト試験  成績照会

受験番号  SA350-XXXX の方は,   不合格   です

午前Ⅰ得点
***.**点
午前Ⅱ得点
68.00点
午後Ⅰ得点
75点
午後Ⅱ評価ランク
C

2021年4月29日木曜日

室内用滑り台を作ってみた。

 下の子(4歳)が滑り台が好きで、作ってあげた。
GWには緊急事態宣言で店がどうなるか分からないのでGWの前にやってみた。

参考にしたのは「DIYですべり台!作りました。収納も場所を取らずスッキリ」。

滑るところは幅14cmを3枚並べることにした。

ダボ錐も持っていなかったので1,042円で購入。他には釘やら金具、紙やすりで2,500円ほど同時購入。

滑るところを作って
踏台部分を金具で繋げて
側板になるように角を落として
側板をつけて完成
滑っているところ
ただ、大人が乗ったら踏台部分は早々に割れてしまった....






2021年春季のシステムアーキテクト試験を受けてきた

コロナ禍が続く中での開催する情報処理試験。

今回は町田駅が最寄だけど神奈川県にある医療ビジネス観光福祉専門学校で受験。
駅から近いので昼食に困らないのがとても良い。

学校は普通にきれい。なぜかマイコンのMZ-1200が廊下にほこりをかぶって本棚の上に置かれていた。初めてMZシリーズを見たよ。

試験は午前IIは特に問題なく通過と思われる。午後Iの方の問2の物流問題。なんか問題の説明で改善要望を受けてシステム更改で直した扱いにしているんだけど、依頼主の名前を通知してほしいという要望について改善したとは明に書いてなくて、後ろの設問で更なる実現可能な要望は何かとあるんだけど、関係あるのかな。なんかもやっとする近台の作りなんだよな。論文は...いつも通り3問中1問が組み込みの話なので2問から選ぶ形。その中の1問がアジャイル開発の事で、全く経験ないなぁと。うーん真面目に情シスのシステム開発にかかわったのが結構前になってきたので論文の書くストックが古くなってきたのかも。困った。 

午前結果
2021年春 免除 18/25  システムアーキテクト試験   結果
2020年秋 免除 19/25 システム監査技術者試験  結果
2020年春 コロナの為、試験そのものが中止
2019年秋 免除 18/25  システムアーキテクト試験   結果
2019年春 20/30 16/25 システム監査技術者試験 結果
2018年秋 申し込み忘れ。
2018年春 26/30 17/25 システム監査技術者試験 結果
2017年秋 免除 20/25システムアーキテクト試験   結果
2017年春 免除 15/25 システム監査技術社試験 確か論文で落ちた
2016年秋 免除 17/25 システムアーキテクト試験   結果
2016年春 免除 不戦敗 システム監査技術試験   結果
2015年秋 24/30 18/25 システムアーキテクト試験  結果
2015年春 免除 不明 システム監査技術者試験、確か午前IIで落ちたorz
2014年秋 免除 不明 システムアーキテクト試験 確か論文で落ちた
2014年春 免除 不明 システム監査技術者試験 確か論文で落ちた
2013年秋 免除 21/25 システムアーキテクト 結果は×(論文がB評価)
2013年春 23/30 15/25 システム監査技術社試験 結果
2012年秋 免除 15/25 システムアーキテクト 結果
2012年春 免除 ?/25 システム監査技術者 結果
2011年秋 免除 20/25 システムアーキテクト 結果
2011年春 免除 19/25 システム監査技術者 結果
2010年秋 27/30 22/25 ITサービスマネージャ-結果
2010年春 免除 18/25 システム監査技術者-結果
2009年秋 試験受けず ITサービスマネージャ
2009年春 25/30 20/25 システム監査技術者-結果
2008年秋 43/55 上級シスアド-結果
2008年春 44/55 テクニカルエンジニア(セキュリティ)-結果
2007年秋 39/55 上級シスアド-結果
2007年春 40/55 テクニカルエンジニア(セキュリティ)-結果
2006年秋 43/55 上級シスアド-結果
2006年春 41/55 テクニカルエンジニア(セキュリティ)-結果
2005年秋 41/55 上級シスアド-結果
2005年春 40/55 システム監査-結果

2021年2月9日火曜日

使ってきたスマホ

 スマホが登場して10年以上(iPhone3Gが登場したのが2008年)たつので、忘れないように何を使ったのかまとめておく。

前編のガラケーの話の続き

最初のスマホ(?)は(たぶん)2010年にauのIS01を購入したのが最初かと思う。当時1円祭りというのがあり、それで購入した。キーボードがあるのでtelnet(ssh)端末としてもまぁまぁ使えたのと、画面は大きいのでワンセグとしても便利。

たぶん2012年と思うが2011年にauが初めて取り扱ったiPhoneシリーズである4sを試しに買ってみた。iTunesとの連携は面白かったが、強くいろいろなものをapple社が統制するという仕組みになれず、1年ぐらいで売り飛ばした。

2015年発売のauのURBANO V02が日常的に使っている電話番号を移した最初のスマホ。物理ボタンがついているのが特徴の端末で使いやすかった。

2016年2月にauからmineoにMNPした。2017年の春にURBANOがもっさりになり、ASUS ZenFone 3 Laserをmineoから購入。ちょっとサイズが大きいのと背面が金属でツルツルして落としそうで気になる。結構カメラがいまいちで写真が汚い。2018年5月に充電の不具合が生じて、当時ASUSショップが赤坂にあったので持ち込み修理する。7560円(税込み)。

時期をはっきり覚えていないがWindows10mobileがMSの製品展開として怪しくなってころに安くなったのでトリニティのNuAns NEOを購入。仕事で使うのはWindows10なので、会社支給のモバイルもWindows10mobileに統一出来たら、どんだけ仕事が楽だっただろうか。仕事ではよさげだが私物としては特段活躍することもなく、事実上の買っただけ。毎月パッチが降ってくるのはWindowsだなぁと思ったような。

2019年2月ごろZenFone3LaserがもっさりしてきたのでmineoからASUS Zenfone Max(M1)を購入。思ったより画面が小さいのと相変わらずカメラ性能がいまいち。

2019年4月末にmineoからIIJ mioというかbicSIMにMNPする。その際に同時に買うならHUAWEI Mate 20 liteが10円とか100円とかそんなレベルというので買ってみたら、なかなかいい感じ。パッチもマメにあるし、カメラも悪くない。しいて言うと米国政府のファーウェイ禁止が気になるところか。

使っていたガラケーの思い出

使っていたガラケーを書いておかないと忘れそうなので、書き留めておく。

初めて携帯電話を持ったのは2002年に新入社員として通信会社に入ったとき。たぶん新入社員170人ぐらいで携帯電話を持っていないのは自分ぐらい。ちょうど第3世代携帯電話の出始めで消費者向けには確か第三世代サービスという名称で訴求していたように思う。

社内で割安で提供するという事で購入したのがカシオ製で初めてカメラがついていたA3012CA の青。たぶん2002年の夏ごろに買ったような気がする。丸みがあってかわいい感じで好きだった。

次に買ったのは三洋製のA5503SA。FMラジオが聞けて薄型。折りたたんだまま簡単な操作もできる。当時、端末を18か月ぐらい使うと端末代が安くなるというので買い換えたような気がするので2003年の年末か2004年の春に変えたと思う。この機種は小さくて軽めで使いやすくて長い事使った気がする。

2011年2月に、モバイルwifi欲しさにソニー製のS006に機種変更してみた(機種変した時のblog)。カメラ性能が良いのかと思ったのだが、使い始めると、超もっさりでやってられない。カタログスペック以上にUI/UXが大事と身をもって知った。

S006の次はスマホに変えたので別の記事で。


2021年2月7日日曜日

岩波新書の「マーティン・ルーサー・キング」、「アメリカ黒人の歴史」を読んで

2020年5月から盛り上がっているBLM(Black Lives Matter)運動を受けて公民権運動などを改めて読むかと思い、岩波新書の

を読んでみた。
今までキング牧師の非暴力を知ってはいたが、漠然として非暴力かと思っていた。それが成功への戦略にもとづいて組織的にやっていたという事を初めて知った。曰く、非暴力行動であるから、女性も子供も参加ができる。非暴力デモに対して白人至上主義者たちが暴行を行う。そのような弱者を暴力で排除する事が報道されると白人至上主義の組織内部から良心に耐えかねて崩壊に進む。これを実現するために、罵声を浴びせられても暴力を振るわない訓練や、拘留された際の保釈金を用意する。そして暴力的な対応が想像される地域を狙って行進などを行う。暴力で痛めつけられること自体が目的というか表現としての考えであった事は知らなかったし、死ぬかもしれないという手段を選択するという状況に驚いた。

憲法は平等をうたっているはずが「分離すれども平等」とされ、黒人専用座席などと日常に差別される状況があり、投票するにも識字試験などで言いがかりで黒人の権利を奪いっている状況であると。

闘いの歴史に引き込まれて読んでいった。
バスの後部座席が黒人専用席で前方の白人専用席が混んでくると黒人は立たされる。これを拒否して逮捕された事をきっかけに黒人がバス利用を拒否したバスボイコット運動。学生がカウンターで座り込むシット・イン運動など。そして事あるごとにリンチであったりと黒人が殺されいく状況で体を張って訴えている事に息が詰まる。

アメリカ黒人の歴史の方は、黒人奴隷などの話からして、不条理極まりないもので、なんともやりきれない。19世紀の黒人奴隷逃亡支援の地下鉄道という組織の事も初めて知った。先日のニュースによるとこの地下鉄道の闘志が米国20ドル札の肖像に採用されると言っていた

このような活動が続いている中でBLMという運動が出てくるという事は未だ克服できていない身近な問題であることが改めて認識した。

2021年1月5日火曜日

ドラクエ11のSwitch版をやってみて

 2020年の夏休みにSwitch版のドラゴンクエストXI 過ぎ去りし時を求めて S」をやってみた。2か月ぐらいかけてクリアした。ちなみに3Dモード。

基本的な操作は伝統的なドラクエと同じなので中年にやさしい。
3Dモードだと、箪笥を開けたりツボを壊したりするのが無頼漢にしか思えず、気持ち悪い。うーんおおもとの発売は2017年で未だに、おねぇキャラが存在したり、マルティナ(女性キャラ)に服装を変えるとお色気になるとか、ヒップアタックをさせるとか3Dモードでされると、感覚が古すぎて気持ち悪い。この状態でゲーム販売を子供も対象にしているというのもヤダ。

ゲーム自体は、まぁ最後までやる気が起きる程度には面白かった。

今の時代、ダンジョン攻略とかがサイトに転がっているのでありがたい。

2020年秋期の情報処理技術者試験の結果

 10月に受けたシステム監査技術者試験の試験結果が出ていた。

午後Iが低すぎる。選択問題の丸付けとか忘れたのかなぁ。。。。

おかしいなぁ。

令和2年度 10月   システム監査技術者試験  成績照会

受験番号  AU345-XXXX の方は,   不合格   です

午前Ⅰ得点
***.**点
午前Ⅱ得点
76.00点
午後Ⅰ得点
32点
午後Ⅱ評価ランク
-