micro:bitでサーモグラフィーを作ってみた(さらに更新)

見栄えのする表示デモは未だ思いつかないが、電源投入時にぼんやり明るくなるようにしてみた。

makecode.microbit.org

micro:bitでサーモグラフィーを作ってみた(更新)

温度の表現方法を思いつき、プログラムを更新した。
ohguma.hatenablog.com

これまでは、

  • 20~30度を青~赤で(色相の変化 240~0、彩度99、輝度10)

表現していたが、以下を追加し、表現の幅が広がった。

  • 30度超~30度を赤~白で(彩度の変化 99~0、輝度10)
  • 20度未満~10度を青~黒で(輝度の変化 10~0、彩度99)

makecode.microbit.org



あとは、電源投入時に見栄えのする表示デモを時間ができたら作る予定にしておく。

TJ3Bのシリアルを無線化(再)

本ブログの記事には嘘がないよう注意していますが、勘違い等で誤ったことを記載する可能性があります。
本ブログの記事の内容を元に発生した損害については一切の保証は出来ません。
ご利用の際はご自身で十分に検証の上、自己責任で対応してください。


せとうちオープン2019」でシリアルの無線化について質問いただいたので、改めて無線化に挑戦してみる。
ohguma.hatenablog.com


以前のブログ記事で使ったモジュールは発見できたが、ICソケットはなくなっていた。
f:id:ohguma:20190612001316j:plain
同じモジュールはまだ秋月で扱いがある。

他に必要なものは、28ピンのICソケット、分圧用の1KΩ・2.2KΩの抵抗各2個、ユニバーサル基板等。


まずは、秋月のモジュール取扱説明書と「ダウンロード | 株式会社ダイセン電子工業」からTJ3B組み立て説明書を用意し、回路を確認する。

秋月のモジュールで使われているRN-42は電源等3.3Vで扱うが、TJ3Bは5V。
モジュール取扱説明書によると配線用のJ1,J2,J3に5Vの電源用の端子がないが、USB接続時にUSBの5V(VBUS)から3.3Vを供給するレギュレータが載っていることが分かるので、そのVINにTJ3Bの5Vをつなぎ、3.3Vを得る。


また、TJ3B側が出力、モジュール側が入力となる箇所は抵抗で分圧して、5Vの信号が概ね3.3Vになるよう調整する。


手元にあるモジュールでの接続は次のようになった。
f:id:ohguma:20190615081818j:plain
f:id:ohguma:20190615080728j:plain


ICソケットの金具とモジュールをはんだ付けする。このタイプのICソケットならば、足から押し込むと金具を外せる。ソケットの外側が溶けないよう、金具を浮かせて作業した。CPUの足が入るように、なるべく薄くはんだ付けした。。
f:id:ohguma:20190615065258j:plain

ユニバーサル基板上にモジュールと分圧用抵抗を配線して、ICソケットとも接続する。
f:id:ohguma:20190615065759j:plain

TJ3B内蔵のシリアルポートを無効化すべく、ICソケットの17,18,28ピンは足を外に曲げておく。
f:id:ohguma:20190615070248j:plain

ICソケットとモジュールがつながったらTJ3BのCPUを戻す。
f:id:ohguma:20190615071350j:plain
f:id:ohguma:20190615071501j:plain


TJ3Bの電源をONにすると、異常なければモジュール上の緑LEDが点滅する。
PCでBluetoothを有効にして、接続し、COMポート番号を確認する。
C-Styleの通信設定でCOMポート番号を選択、もしくは手打ちし、センサモニタを開いて、有線ケーブルと同様に各センサの動きが確認できれば、作業終了。
f:id:ohguma:20190615120357p:plain

せとうちオープン2019

「せとうちオープン2019」

参加した選手、スタッフ、メンター、保護者の皆様、お疲れ様でした。
いろいろな人と交流もでき、大変楽しかったです。

6月8・9日に広島市で開催された「せとうちオープン2019」の様子など紹介。
公式戦では参加資格のないOBや大人も参加できるオープン大会で、私も「ワールドリーグサッカー ライトウェイト」にチーム「何時方向」で参加させていただいた。
rcjj-hiroshima.com

3Dプリンタ製のオムニホイールはいくつか質問も頂いた。
f:id:ohguma:20190609103124j:plain


1日目は通常のロボカップジュニアのルールのリーグ戦。
プログラムの理屈は分かっているつもりだが、サッカーの戦術や、試合の準備がまだまだ未熟であることを痛感した。
順位はこんな感じで現役選手に敵わず。
f:id:ohguma:20190608173828j:plain

一人チームの運営で写真はほぼ取る間がなかったので、様子は他の参加者のブログ等に期待し、1日目の集合写真。
f:id:ohguma:20190608171143j:plain



2日目は「せとうちオープン」恒例のサッカーBigField。
今回はA・Bの2チームに別れ、

  • 1チーム 5台。Aチームは白テープ、Bチームは緑テープで色分け。
  • 前半10分、ハーフタイム5分、後半10分。ハーフタイムでの交代はOK。
  • オープン、ビギーナークラスは1台以上参加させる。
  • 故障、アウトバウンズは30秒の退場。カウントは各自で行う。(ビギナーはアウトバウンズなし)

という条件で3試合が行われた。私はBチームに参加し、各試合の前半で出場した。

以下、各試合の様子をスマホ動画で。
なお、試合参加中の前半は車載、後半は手持ちで撮影。

全体を通してボールにはまま絡むことができ、むちゃくちゃ面白かったです。

第1試合

▼第1試合前半

せとうちオープン2019 2日目 第1試合前半

第1試合前半は、開始から1分経つ前に相手ゴール前から真っ直ぐバックしてオウンゴールしてしまう大失態(回り込みをキャンセルしたプログラムを書き込んでいた)

▼第1試合後半

せとうちオープン2019 2日目 第1試合後半

ファイナル

また、予定の3試合が終了した時点で、選手から全台参加で試合をしたいとの提案があった。選手から審判の募集も行われ、それに応える審判も多数いたため、夢の11対11サッカーが実現した。

せとうちオープン2019 2日目 ファイナルその1


せとうちオープン2019 2日目 ファイナルその2


せとうちオープン2019 2日目 ファイナルその3

Wordpress Yahoo!地図対応ショートコード

Google Map回避のため。

/**
 * Yahoo!地図対応ショートコード
 *
 * [map id="map1" w="600" h="300" z="16" address="住所" marker="yes" infowindow="吹き出し"]
 * [map id="map1" w="600" h="300" z="16" lat="北緯" lon="東経"]
 *
 * Yahoo! JavaScriptマップAPI
 * https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/js/
 * Yahoo! JavaScriptマップAPIサンプル
 * https://developer.yahoo.co.jp/sample/map/sample5.html
 */

define("YAHOO_APPID", "XXXXXXXXXX");

function ymap_header() {
    echo '<script src="https://map.yahooapis.jp/js/V1/jsapi?appid=' . YAHOO_APPID . '" type="text/javascript" charset="UTF-8"></script>';
    echo '<script type="text/javascript" charset="UTF-8">';
    echo 'var ymap = [];';
    echo 'var ycenter;';
    echo '</script>';
}
add_action('wp_head', 'ymap_header');

function ymap_shortcode($attr = array())
{
    $attr = shortcode_atts(array(
        'id'  => 'map',
        'w'   => '400',
        'h'   => '300',
        'lat' => 35.6578759,  //日本経緯度原点付近
        'lon' => 139.7414501, //日本経緯度原点付近
        'z'   => '15',  //1~20
        'address' => '',
        'marker'  => '',
        'infowindow' => '',
    ), $attr);
    $html = '
    <div id="' .$attr['id'] . '" style="width:' . $attr['w'] . 'px;height:' . $attr['h'] . 'px;"></div>
    ';
    $html .= '
    <script type="text/javascript">
        ymap["' . $attr['id'] . '"] = new Y.Map("' . $attr['id'] . '");
        ycenter = new Y.LatLng(' . $attr['lat'] . ', ' . $attr['lon'] . ');
        ymap["' . $attr['id'] . '"].drawMap(ycenter, ' . $attr['z'] . ');
        ymap["' . $attr['id'] . '"].addControl(new Y.LayerSetControl());
        ymap["' . $attr['id'] . '"].addControl(new Y.SliderZoomControlVertical());
        ';

    if($attr['address'] != '') {
        //住所検索あり
        $html .= '
        var address = \'' . $attr['address'] . '\';
        var geocoder = new Y.GeoCoder();
        geocoder.execute( { query : address } , function( result ) {
            if ( result.features.length > 0 ) {
                ymap["' . $attr['id'] . '"].drawMap(result.features[0].latlng );
                ';
        if ($attr['marker'] !='') {
            $html .= '
                ymap["' . $attr['id'] . '"].addFeature(new Y.Marker(result.features[0].latlng));
                ';
        }
        if($attr['infowindow'] != '') {
            $html .= '
                ymap["' . $attr['id'] . '"].openInfoWindow(result.features[0].latlng, "' . $attr['infowindow'] . '");
                ';
        }
        $html .= '
            } else {
                $("#' . $attr['id'] . '").after("<p style=\"color:red;font-weight:bold\">住所「' . $attr['address'] . '」が見つかりません。<a href=\"https://map.yahoo.co.jp/\" target=\"_blank\">Yahoo!地図</a>で検索可能な住所を設定してください。住所が見つからない場合は、経緯度で指定します(lat:緯度 北緯○度、lon:経度 東経○度)。</p>");
            }
        });
        ';
    } else {
        //住所検索なし
        if ($attr['marker'] !='') {
            $html .= '
        ymap["' . $attr['id'] . '"].addFeature(new Y.Marker(ycenter));
        ';
        }
        if ($attr['infowindow'] != '') {
            $html .= '
        ymap["' . $attr['id'] . '"].openInfoWindow(ycenter, "' . $attr['infowindow'] . '");
        ';
        }
    }
     $html .= '</script>';
     return $html;
}
add_shortcode('map', 'ymap_shortcode');

micro:bitでサーモグラフィーを作ってみた。

久しぶりにニコニコ技術部として動画をアップロードしてみた。

覚えているうちに工作の内容をまとめておく。

makecode.microbit.org

ハードウェア

www.switch-science.com

www.switch-science.com

www.switch-science.com

  • 動画で使ったものは数年前に購入したNeoPixel互換の別物

また、たまたま手元にあったGROVE関連のパーツで配線を省略化した。
www.switch-science.com
www.switch-science.com
www.switch-science.com

AMG8833はGROVEのプロとシールドにハンダ付けして利用したが、これならGROVEシールドと直結できる。
www.switch-science.com

ソフトウェア

「最初だけ」

スイッチサイエンスのArduino用ライブラリに習い、AMG8833の「移動平均出力モード」を有効にしたが、LED表示にはさほど影響がないと感じた。

他は、NeoPixel・変数の初期化など。

「ずっと」

I2Cで数値128(0x80)を書き出し、ループで連続してI2Cの数値読み取りを行う。
各画素からは12bitの2バイトデータ(先頭1bitが符号の2の補数形式)を読む取る必要がある。
1バイトずつ読む場合は、下位8bit、上位4bitのリトルエンディアンになるので、とりあえず符号なしの2バイト整数としてUInt16LEで読み取り、剰余計算で12bitを越える部分を無視する。
読み取った値は、「0~4095」の値になるが、先頭1bitは符号なので、「2048」以上の値はマイナスの値として処理し、0.25倍した数値が温度となる。
micro:bit本体のLEDで表示するための温度の最大・最小値も都度チェックする。

センサー計測値 :
2進数     : 符号なし10進数 : 符号あり10進数 : 温度
0111 1111 1111 : 2047 : 2047 : 511.75
0011 1111 1111 : 1023 : 1023 : 255.75
0001 1111 1111 : 511 : 511 : 127.75
0000 1111 1111 : 255 : 255 : 63.75
0000 0111 1111 : 127 : 127 : 31.75
0000 0011 1111 : 63 : 63 : 15.75
0000 0001 1111 : 31 : 31 : 7.75
0000 0000 1111 : 15 : 15 : 3.75
0000 0000 0111 : 7 : 7 : 1.75
0000 0000 0011 : 3 : 3 : 0.75
0000 0000 0001 : 1 : 1 : 0.25
0000 0000 0000 : 0 : 0 : 0
1111 1111 1111 : 4095 : -1 : -0.25
1111 1111 1110 : 4094 : -2 : -0.5
1111 1111 1100 : 4092 : -4 : -1
1111 1111 1000 : 4088 : -8 : -2
1111 1111 0000 : 4080 : -16 : -4
1111 1110 0000 : 4064 : -32 : -8
1111 1100 0000 : 4032 : -64 : -16
1111 1000 0000 : 3968 : -128 : -32
1111 0000 0000 : 3840 : -256 : -64
1110 0000 0000 : 3584 : -512 : -128
1100 0000 0000 : 3072 : -1024 : -256
1000 0000 0000 : 2048 : -2048 : -512


読み取った温度を色に変換するために、micro:bitのNeoPixelで利用可能なHSL色空間を利用する。
HSL色空間では、色味を0~360度の範囲の角度で表すことができ、0度は赤となる。
室内でデモする場合を想定し、30℃以上は赤、20℃以下は青となるよう、micro:bitの計算にある「数値のマップ」と「範囲の制限」を組み合わせて利用して温度を色相に変換する。

0 赤
30 橙
60 黄
90 黄緑
120 緑
150 水色
180 水色
210 水色
240 青
270 青紫
300 紫
330 赤紫


2重ループで各画素を順番に読み、温度を計算するが、画素の並び順とLEDの接続順が異なるので、そこは計算で工夫する。
センサを裏向きに付けた場合のミラー表示(左右反転)も同時に処理する。

(センサ) 行ごとに順番は同じ。センサに向かって見たときにこの順番
7 6 5 4 3 2 1 0
13 12 11 10 9 8

(LED) 配線を短くするよう接続したために、行ごとに順番は折り返す。
0 1 2 3 4 5 6 7
13 12 11 10 9 8

「ボタンAが押されたとき」

ミラー表示(左右反転)用の設定値を入れ替え。
また、micro:bit本体のLED表示を更新。

「ボタンB」

変数に保存した、計測温度の最大値、最小値をmicro:bit本体のLEDで表示。
本体LEDの表示は遅いので、押されたタイミングで表示するテキストを作成する。




(以上)

C-Style の実行時間について

次のようなプログラムで1秒間に各コマンドが何回実行できるか調べる。
6行目に調査したいコマンドを入れる。

f:id:ohguma:20190302095649p:plain


手元のTJ3Bでの結果は次の通り。INPUTの参照は変数の参照と変わらない回数が実行できた。
だが、PINGポートの参照は実行回数が大幅に少なく、ぶっちぎりに時間がかかっていることが確認できる。

  • 74,323回 X=X+10
  • 74,323回 X=CN1
  • 46,123回 MT(L:50 R:50)
  • 201回 MT(6ch 0,0,0,0,0,0)
  • 50回 X=CN10:PING


このことをロボットに活用することを考える。
CN10に超音波センサーを接続し、

  • 20cm 以上離れたら緑LEDをON
  • 10cm 以上離れたら赤1LEDをON、

というプログラムを上の6行に入れて、1秒間の処理回数を調べる。処理回数が多いほど細かい制御ができる。

2つのIF文でそれぞれ距離を判断する場合、1秒間に25回の処理ができた。
f:id:ohguma:20190302121733p:plain

IF文を入れ子にし、10cm以上離れている場合のみ20cmの判断をする場合は、距離に応じて1秒間の処理回数が変わった。
10cm未満ならば1秒間に50回、10cm以上なら25回の処理ができた。
距離が変動する場合は、25~50回の間になった。
f:id:ohguma:20190302121748p:plain


PINGポートの参照は、変数の参照比べて数桁レベルで遅いことが上でわかっているので、処理回数をかせぐには、PINGポートの参照を減らすことが大切になる。
PINGポートを距離を変数に代入しておけば、2つのIF文がある場合でも1秒間に50回の処理ができた。
最初の例と比べると処理結果は同じにもかかわらず、処理回数が2倍(1回あたりの処理時間が半分)になっている。
f:id:ohguma:20190302121801p:plain

当然、変数を使いかつIF文を入れ子にすれば、さらに高速化されるが、PINGポートの参照と比べれは他の処理は十分に速いので、入れ子にしてプログラムの可読性を落とすよりは、入れ子はなるべく使わず、プログラムの見やすさを保つことも大切だと思う。