チラシのうら

レゴとか、工作とか。

8pinoでAdafruit Neopixel Ringを光らせる その1

放置していた

8pinoハックですが、このままでは、やらない恐れがあると考え、後戻り出来ないように、モノを買ってみました。

Adafruit Neopixel Ring 16連

何か、かっこ良さそうというのと、ググるarduinoで動かしている人が沢山いらっしゃったので。

switch science様によると、

フルカラーシリアルLED NeoPixelが16個載ったリング状の基板です。信号線1本でそれぞれのLEDを別々の色に光らすことができます。

とのこと。スゲエ技術だ。

まずは、ハンダ付け

GWの時間を使って、早速ハック。

8pinoも、Neopixel Ringも汎用的に使い回したいので、ピンヘッダつけて、ブレットボード上で扱えるようにします。 まずは、島忠に行ってハンダセットを購入。コテ、台、ハンダで、しめて3000円。

  • Amazonだともっと安くて一度シュンとなっています。

プライベートでは、中学校以来のハンダ付け。ピンク色の鉛用石鹸が懐かしいです。 f:id:tetunori_lego:20150519075854j:plain

Neopixel Ringは

動作電圧:5V(最大7V)

とあるので、USBのVBusを直接入れてあげなければいけません。 * 8pinoのVCCは3.3V。

悩みながら、よいしょと、巨人の肩の上に乗ってみると(ググると)ソリューションが。

8pinoの裏にVBusが出ているので、これを空いている基板に出します。参照先では、8pino+と呼んでいます。良いですね。

早速、こうして、 f:id:tetunori_lego:20150519075417j:plain こう! f:id:tetunori_lego:20150519075715j:plain

あと、Neopixel Ringも、こう! f:id:tetunori_lego:20150519075505j:plain 今回は、OUT pinは使わないので、V、 Gnd、IN pinの3つだけピンヘッダつけます。

サンプルコードを動かす

githubにNeopixel用のサンプルコードが公開されています。

LEDの数と、ピン番号だけ変えて、焼いてみます。

おぉ!ちゃんと動作しました。 電子工作初心者としては、大きな進歩です。

ただ、良くYouTubeとかで見る動画では、LEDがレインボーカラーに輝いていたりするので、次回は、それにチャレンジしてみます!

IRKitってすばらしい! その3

注意

過去の記憶を遡って記載しています。20150625記

スマホアプリ

Android版、作りました。
f:id:tetunori_lego:20150625064936j:plain

仕様

  • BD Player、電気、テレビ、オーディオエアコンの全コマンド実行可能。
  • タブビュー&縦スクロールで快適な操作性を実現。
  • 各種マクロ対応
    • おやすみスイッチ
    • おでかけスイッチ
    • オーディオ向け、簡単Bluetoothペアリング
  • (バグ有り)Widget対応
  • (未実装)NFCシールにスマホかざしたら任意のコマンド発行。

画面仕様

BD Player

f:id:tetunori_lego:20150625065705j:plain
早送りとかは、タップ操作だとキツイです(笑)

電気

f:id:tetunori_lego:20150625065752j:plain
楽しくて、わざわざ寝室の布団の中から消す(そして、リビングを覗いて、電気が消えたか確認する)ブームが家族内で発生。

テレビ

f:id:tetunori_lego:20150625065908j:plain
f:id:tetunori_lego:20150625070147j:plain
家のテレビはネットワーク越しのリモコンに対応していますが、スマホ上での認識に、数秒かかります。IRKitは一瞬でコマンド発行できるので便利。

オーディオ

f:id:tetunori_lego:20150625070204j:plain
家のオーディオは、NFC搭載しておらず、スマホタブレットの音を出したいときは、少し面倒。リモコン経由でBluetoothのペアリングを開始できるだけですごい便利。

エアコン

f:id:tetunori_lego:20150625070223j:plain
これだけはクセモノでした。
毎回のコマンド発行時に、温度・湿度・時間設定などをのせているようで、ほんとんど解析出来ず。幾つかの決めうち設定しか出来ませんでした。

マクロ

f:id:tetunori_lego:20150625070234j:plain 自分で作っておいてなんですが、めっちゃ便利。
一見、使えそうなNFC機能は未実装です。

Widget

f:id:tetunori_lego:20150625073855j:plain
Widget設定すると、
f:id:tetunori_lego:20150625074254j:plain
のようにホーム画面から直接リモコンコマンドを発行出来ます。

詳細

アイコン

このイケているアイコンは全て さんからダウンロードしました!
iOS向け?のアイコンを多用しています。

つまずいた所

Android上でのBonjour

APIはあります。NsdManager
言われた通りにやってはいるのですが、OSのせいなのか端末のせいなのか、10回やったら1回は失敗しました。 これに失敗すると、IRKit側がハマる事が多く、電源抜き差しが必要となり、使い物になりません。

というわけで、毎回の機器検索はあきらめて、初回起動時に設定値として覚えることにしました。
凝りに凝った機器検索UIは、ほとんど使われませんでした…
f:id:tetunori_lego:20150625074715j:plain
数百msec位表示されますけど…

コードはこちら

さすがに量が多くて、全部はのせられないので、IRKit制御回りのコードを一部だけ。

IRKitManager.java

package IRKit;

import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;

import android.util.Log;

public class IRKitManager {
    // For log
    private final static String TAG = IRKitManager.class.getSimpleName();

    // For Bonjour communication
    private NsdManager.DiscoveryListener discoveryListener;
    private NsdManager.ResolveListener resolveListener;
    private NsdManager mNsdManager;
    private static final String DNS_TYPE = "_irkit._tcp";
    
    // For IRKit
    private IRKitDiscoveryListner mListner = null;
    private IRKitDevice mIRKitDevice;
    
    // Others
    private Context mContext;
    
    // Constructor
    public IRKitManager( Context context ){
        Log.d(TAG, "IRKitManager constructor");
        this.mContext = context;
        setUpIRKitDiscoveryInfo();      
    }

    // Start Discovery IRKit
    public void startDiscoverIRKit( IRKitDiscoveryListner listner ){
        mListner = listner;
        if(mNsdManager!=null){
            mNsdManager.discoverServices(DNS_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener);
        }
    }

    // Stop Discovery IRKit
    public void stopDiscoverIRKit(){
        mListner = null;
        if(mNsdManager != null){
            mNsdManager.stopServiceDiscovery(discoveryListener);
        }
    }

    // Setup IRKit Discovery Information
    private void setUpIRKitDiscoveryInfo() {
        Log.d(TAG, "Setting up Bonjour");
        discoveryListener = new NsdManager.DiscoveryListener() {
 
            @Override
            public void onStopDiscoveryFailed(String serviceType, int errorCode) {
                // TODO Auto-generated method stub
 
            }
 
            @Override
            public void onStartDiscoveryFailed(String serviceType, int errorCode) {
                // TODO Auto-generated method stub
 
            }
 
            @Override
            public void onServiceLost(NsdServiceInfo serviceInfo) {
                // TODO Auto-generated method stub
 
            }
 
            @Override
            public void onServiceFound(NsdServiceInfo serviceInfo) {
                Log.d(TAG, "Service resolved: " + serviceInfo.getServiceName() + " host:" + serviceInfo.getHost() + " port:"
                        + serviceInfo.getPort() + " type:" + serviceInfo.getServiceType());
                mNsdManager.resolveService(serviceInfo, resolveListener);
            }
 
            @Override
            public void onDiscoveryStopped(String serviceType) {
                // TODO Auto-generated method stub
 
            }
 
            @Override
            public void onDiscoveryStarted(String serviceType) {
                // TODO Auto-generated method stub
 
            }
        };
        
        mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
        
        resolveListener = new NsdManager.ResolveListener() {

            @Override
            public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
            }

            @Override
            public void onServiceResolved(NsdServiceInfo serviceInfo) {
                Log.d(TAG, "IRKit is detected! IP Address is [" + serviceInfo.getHost().getHostAddress() + "].");
                mIRKitDevice = new IRKitDevice( serviceInfo.getHost().getHostAddress(), serviceInfo.getServiceName() );
                if( mListner != null ){
                    mListner.onIRKitDiscovered( mIRKitDevice );
                }else{
                    Log.e(TAG, "mListner is null!");
                }
            }
        };
        
    }
    
    public interface IRKitDiscoveryListner{
        public void onIRKitDiscovered( IRKitDevice irkit_dev );
    }
    
}

IRKitDevice.java

package IRKit;

import java.io.IOException;
import android.util.Log;

public class IRKitDevice {
    // For log
    private final static String TAG = IRKitManager.class.getSimpleName();

    private String m_ip_addr = "";
    private String m_dev_name = "";
    
    public IRKitDevice( String ip_addr, String dev_name ){
        m_ip_addr = ip_addr;
        m_dev_name = dev_name;
    }
    
    public String learn() {
        return "";
    };
    
    public int send( final String send_data ){
        Log.d(TAG, "send is called.");
        new Thread() {

            @Override
            public void run() {
                String responseJson = "NULL";
                
                try {
                    // Log.d(TAG, "send_data: " + send_data);
                    responseJson = IRKitHttpClient.httpPost("http://" + m_ip_addr + "/messages", send_data);  // 読み替えてね。
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                Log.d(TAG, "Response: " + responseJson);
            }
            
        }.start();
        
        return 0;
    };
    
    public String getIPAddress(){ return m_ip_addr; }
    public String getDevName(){ return m_dev_name; }
    
}

IRKitってすばらしい! その2

注意

過去の記憶を遡って記載しています。20150624記

何はともあれ、ドキュメント

を確認します。
といっても、全てがIRKitのサイトに書いてある(しかも日本語!)ので、大して時間はかかりません。
15分もあれば、一通り目を通すことができます。
具体的には、IRKit-Device-APIです。

ハックステップ概要

任意のリモコンコマンドを発行するまでのハックステップは、ざっくり下記の通り。

  1. 機器発見 : IRKitをネットワーク上で発見する。
  2. 学習 : IRKitにリモコンコマンドを学習させる。
  3. GET data : 学習した結果(コマンドデータ)をIRKitから取得する。
  4. POST data : 取得したコマンドデータをIRKitへ送る。
  5. 発行 : IRKitが2.の赤外線コマンドを発行する。

順を追って記録に残していきます。

ハックステップ詳細

1. 機器発見

IRKitをおうちのネットワークに繋いだ状態からスタートします。
IRKitのAPIIPアドレスがわかれば即座に叩けるので、まずは、IPアドレスを取得したいです。

この方法は世の中には幾つかありますが、IRKitでは、Appleさん謹製のBonjourという機器発見プロトコルを使って見つけられるそうです。

最近めちゃくちゃ動作の遅いMacMiniのターミナルから、Bonjour!と挨拶してみました。

手順書通りで確かに取得できました。

% dns-sd -B _irkit._tcp  
(中略)  
Instance Name 23:12:55.768 Add 2 4  
 local. _irkit._tcp. irkitd2a4  
^C  
  
% dns-sd -G v4 irkitd2a4.local    
(中略)  
Hostname         Address  
irkitd2a4.local. 10.0.1.2  

この例ですと、IPアドレスとして、 10.0.1.2がとれています。

2. 学習

IRKitは常に学習Readyとなっていますので、IRKitに向かってリモコンを向けて、リモコンのボタンを押すだけです。
簡単。

IRKitのLEDが点滅したら、(たぶん)学習出来ています。 f:id:tetunori_lego:20150625061718j:plain
おうちではカウンターの下に貼り付けてます。

3. 学習データのGET

それでは、2.で学習させたリモコンコードを読み出してみたいと思います。
やることは単純です。(手段はともかく)1.で取得したIPアドレス/messagesに対してGET発行するだけです。

GET from http://10.0.1.2/messages

すると、以下のような値(★1)が返ってきます。後でコピペするだけなので、データを理解する必要はありません。

{"format":"raw","freq":38,"data":[18031,(中略), 4400,1190]}

リモコン学習データがとれたので、いよいよ、発行してみます。

4. IRKitへデータを送る

これも簡単です。GETと同じ口に★1のデータをPOSTするだけです。

POST to http://10.0.1.2/messages ★1

5. リモコンコマンド発行(発光)

これで、めでたくIRKitからリモコンコマンドが発行され、おうちの中の何かが動いたと思います。

ツールを作る。

コマンドラインからばっかりだと、データをコピペするのも面倒になってくるので、ツールを作ります。
こだわりはないので、一番速く実現出来そうな、html/jsで書いてみます。 f:id:tetunori_lego:20150624075724j:plain
chromeブラウザでは動作確認済み。

単純なプログラムですが、家中の機器をブラウザから制御できるのは、新感覚。 実にスマートホームっぽいです。

これを使って、リモコンコードデータベースも出来上がったので、いよいよスマホアプリに実装してみようかと思います。

その他ノウハウ

  • 得られたリモコンコードは、毎回少しずつ違っている事がある。でも、どれも動く。
  • 長いデータだな、と思って良く見ると、同じコードの繰り返し(3回位)だったりする。繰り返し部は削除して、最初の部分だけにしても、動作した。
  • 窓の近く、お昼過ぎにコウコウと太陽の光が注ぐと、IRKitから発行しても届かない(?)場合がある。

コードはこちら

index.html

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>IRKit JavaScript Test</title>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js"></script>
</head>

<body>
<br>
<br>

<textarea name="log_textarea" cols="50" rows="5"></textarea>
<br>
<br>

<div id="Rows" ></div>

<!--div id="test" >
   <input type="text" readonly name="command_label_1">
   <input type="button" disabled name="btGet_1" value="GET" onclick="onClickedGet( 1 );">
   <input type="button" name="btPost_1" value="POST" onclick="onClickedPost( 1 );">
   <input type="text" name="gotten_data_1">
   <br>
   <br>

   <input type="text" readonly name="command_label_2">
   <input type="button" disabled name="btGet_2" value="GET" onclick="onClickedGet( 2 );">
   <input type="button" name="btPost_2" value="POST" onclick="onClickedPost( 2 );">
   <input type="text" name="gotten_data_2">
   <br>
   <br>

   <input type="text" name="command_label_3">
   <input type="button" name="btGet_3" value="GET" onclick="onClickedGet( 3 );">
   <br>
   <br>
</div-->

<script src="./js/irkit_test.js"></script>
</body>

</html>

irkit_test.js

var gCurrentRowNum = 0;
var gArrayRowCmdName = new Array(20);
var gArrayRowData = new Array(20);

initialize();

function initialize(){
  output_textarea("initialize is called!");
  var elm = document.getElementById( "Rows" );
  elm.innerHTML = '';
  addRow();
};

function output_textarea( text ){
  var objTextarea = document.getElementsByName('log_textarea')[0];
  objTextarea.value += text;
  objTextarea.value += "\n";
}


function onClickedGet( currentIndex ){
  output_textarea( "onClickedGet is called." );
  output_textarea( "currentIndex is " + currentIndex );
  
  if( document.getElementsByName('command_label_' + currentIndex)[0].value == "" ){
    alert("Please Enter command name.");
    return;
  }
  
  if( currentIndex > 20 ){
    output_textarea( "Fully registrated..." );
    alert("Fully registrated... Please Reflesh by F5!" );
    return;
  }else{
    addRow();
  }
  
  preparePost( currentIndex );
  
  getIRData( currentIndex );
  
}

function onClickedPost( currentIndex ){
  output_textarea( "onClickedPost is called." );
  output_textarea( "currentIndex is " + currentIndex );
  
  sendIRData( currentIndex );
  
}

function addRow(){
  
  var i = 0;
  var numNextIndex = gCurrentRowNum + 1;
  var elm = document.getElementById( "Rows" );
  var obj = '<div id="Row_' + numNextIndex + '"><input type="text" name="command_label_' + numNextIndex + '">' + 
         '<input type="button" name="btGet_' + numNextIndex + 
         '" value="GET" onclick="onClickedGet( ' + numNextIndex + ' );"></div>' + 
         '<br>';
  
  for( i = 1; i < gCurrentRowNum + 1; i++ ){
    gArrayRowCmdName[i-1] = document.getElementsByName('command_label_' + i)[0].value;
    if( gCurrentRowNum != i ){
      gArrayRowData[i-1] = document.getElementsByName('command_data_' + i)[0].value;
    }
  }
  
  elm.innerHTML += obj;
  
  for( i = 1; i < gCurrentRowNum + 1; i++ ){
    document.getElementsByName('command_label_' + i)[0].value = gArrayRowCmdName[i-1];
    if( gCurrentRowNum != i ){
      document.getElementsByName('command_data_' + i)[0].value = gArrayRowData[i-1];
    }
  }
  
  gCurrentRowNum++;
  
}

function preparePost( currentIndex ){
  document.getElementsByName('command_label_' + currentIndex)[0].readOnly = true;
  document.getElementsByName('btGet_' + currentIndex)[0].disabled = true;
  
  var elm = document.getElementById( "Row_" + currentIndex );
  var obj = '<input type="button" name="btPost_' + currentIndex + 
         '" value="POST" onclick="onClickedPost( ' + currentIndex + ' );">';
  
  obj += '<input type="text" name="command_data_' + currentIndex + '">'
  
  elm.innerHTML += obj;
  document.getElementsByName('command_label_' + currentIndex)[0].value = gArrayRowCmdName[currentIndex-1];
  
}

/* Constants */
var HTTP_SCHEME_STRING = "http://";
var IRKIT_IP_ADDR = "10.0.1.2";
var API_CONTROL_PORT = "/messages";

function getIRKitURL()
{
  
  return HTTP_SCHEME_STRING + IRKIT_IP_ADDR + API_CONTROL_PORT;
  
}

function postWebAPI( webAPI_command, callback_func )
{
  var url = getIRKitURL();
  
  jQuery.ajax( url, { crossDomain: true, type: "POST", dataType: 'text', 
               data: webAPI_command ,
    success: function(result){
      output_textarea( "Post Success" );
    }
  });
 
}

function sendIRData( currentIndex )
{
  
  output_textarea( "sendIRData is called." );
  
  var data = document.getElementsByName('command_data_' + currentIndex)[0].value;
  
  var webAPI_command = JSON.stringify( data );
  postWebAPI( webAPI_command, sendIRDataCallback );
  
}

// getIRData
function getIRData( currentIndex )
{
  
  output_textarea( "getIRData is called." );
  output_textarea( "currentIndex is " + currentIndex );
  
  var url = getIRKitURL();
  
  jQuery.ajax( url, { crossDomain: true, type: "GET", dataType: 'text', success: function(text){
    output_textarea( text );
    document.getElementsByName('command_data_' + currentIndex)[0].value = text;
    output_textarea( "GET method success!" );
  }});
  
}

// unused....
function sendIRDataCallback( responseText )
{
  
  output_textarea("sendIRDataCallback is called.");
    output_textarea("responseText is " + responseText);
  
}

IRKitってすばらしい! その1

注意

過去の記憶を遡って記載しています。20150623記

2014の夏

は個人的にIoTブーム、特にスマートホームブームがやって来ていました。
(非公開ではありますが、)私は個人的に、それにむけた指向性無しリモコンを作っていたのですが、困ったのは、そもそもWi-Fiで操作できる・API公開されている機器が圧倒的に少ないこと。
また、あっても価格が高かったり、すぐに買い換えないものであったりします。
※さすがに、これを機にエアコンを買い換えよう…とはなりません。

ただ、既存の機器にも、大抵は赤外線を使った(指向性の)リモコンがついています。 これを利用してスマートホーム化を実現してみようと思いたちました。

ソリューションの検討

世の中には、赤外線⇔Wi-Fiの変換をしてくれて、かつAPI公開してくれる、有難い機器が存在します。

財布と妻の顔色を伺いながら、比較しました。

機器名 価格†2 デザイン API公開
iRemocon 22,400円 好き
Pluto 13,800円 普通 非Open†1
eRemote 6,700円 普通 非Open†1
IRKit 7,700円 好き

†1. 非Openとは、問合せしないとAPI仕様がわからない事を意味します。
†2. 価格はAmazon価格。

以上の結果、(タイトルでネタバレしてますが)ギリギリ予算内で、かつ、要件を満たすIRKitを選択しました!

早速注文

したら、すぐに来ました。
f:id:tetunori_lego:20150623183536j:plain 思った以上に小さくてびっくり!
小さすぎて、軽すぎて、逆に本当に動くのかな?と心配になるくらいです。

IRKitの特徴

  • Arduino inside. 未使用ピン引き出し済!お好きに改造可能。
  • Open Hardware(回路、外装データ)
  • Open Source(SDK).
  • Open API(JSON).
  • おうちの外からも、スマホからリモコン操作できる。
  • 個人で作られている。スゲェ。

あぁ、もうお腹いっぱいです。

まずは純正アプリを使ってみる

純正アプリはiOS向けにしかアプリが提供されておらず、私のスマホは、iphoneではないので、妻のiPod touchを使わせていただくことに。
f:id:tetunori_lego:20150624062804j:plain
Wi-Fi接続までの導入では、これ以上無いくらい丁寧な説明がありました。

f:id:tetunori_lego:20150624062806j:plain
とりあえず、おうちの中にあるものを配置してみました。すぐに画面いっぱいになります。

分かりやすいUIで、難なく使えました。 ただ、日常用途で激しく使うためには、もっとおうち向けにカスタマイズが必要です。

次回から、早速ハック開始。

8pinoがやって来た!

幸運にも

2014 Maker Faire Tokyoにて、運良く8pinoを購入することが出来ました! f:id:tetunori_lego:20150520182448j:plain

ホントに小さくて、これでプログラミングができるなんて、にわかには信じがたい。 パッケージデザインや、取り出しのUXが美しくて、それだけでも嬉しくなります

動かしてみる

当方、数学屋さんのため、電子工作セット等は持ち合わせておりません。 とりあえず、8pinoに実装されているLEDで、Lチカを試みます…

8pinoで、Lチカ 337拍子! レゴのミニフィグと比較して、この小ささ。

なんとも、ビックリしたのは、8pino袋から取り出して、20分足らずで実現できるこの簡単さ!arduino、adafruit 、8pinoの皆様、心から尊敬します。

コードはこちら。

int led = 1;

void setup() { 
  pinMode( led, OUTPUT ); 
}

void loop() {
  // 3
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(500);
  
  // 3
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(500);
  
  // 7
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(200);
  digitalWrite(led, HIGH);
  delay(200);
  digitalWrite(led, LOW);
  delay(500);
  
}

今冷静に思い返すと、関数化すべきだったが、興奮していて、そこまで気が回らず。

日本語の説明書もしっかり出来ていて、ほぼ戸惑う事なく、導入出来ました。

Lチカが出来たので、はしゃぐ

以下、8pinoの小ささを活かせないか、色々試した(はしゃいだ)記録。

トイ・ストーリーのグリーンアーミーメンに意味もなく持たせた図。  f:id:tetunori_lego:20150520182540j:plain

とにかく、レゴのミニフィグとコラボ出来ないか考える。  f:id:tetunori_lego:20150520182633j:plain

うん、ゴーストにはぎりぎり入るな。光るのがいい感じ。むしろUSBケーブルのコネクタの方が大きくてひっかかる。  f:id:tetunori_lego:20150520182651j:plain

暗いところに行ってみる。最近の妖怪ブームにのって、8pinoお化けの登場。 偶然だが、おまけでついているブレイカブル基板の穴が、目にfit。  f:id:tetunori_lego:20150520182714j:plain

Lチカで、火災現場を再現。  f:id:tetunori_lego:20150520182734j:plain

こうやってゴミ箱を…  f:id:tetunori_lego:20150520182811j:plain

さぁ

年末年始、何作ろう?