bottleでScratchXとDigisparkをつなげる

![bottle_scratchx_min.jpg](https://qiita-image-store.s3.amazonaws.com/0/110426/3b7591a1-f06d-6695-af7c-6230ffba8c04.jpeg)

### タイトルの簡単な説明
+ bottle → python で作られたシンプルな Webフレームワーク。
+ [ScratchX](http://scratchx.org/) → ウェブブラウザベースな [Scratch2.0](https://scratch.mit.edu/) にプラグイン拡張をできるようにしたもの?(曖昧) 
+ [Digispark](http://digistump.com/products/1) → 1000円ちょっとで買える、小さくて Arduino クローンなすごいやつ。

### やること
[DigisparkのRGB Shield](http://digistump.com/products/3)(LED Shieldではなく)を使ってプログラミング入門的なことをしたいので、プログラミング入門に最適であろう Scratch (ScratchX) からDigisparkを操作する。
**※実際にやる際は、ご自分の責任でお願いいたします。**

### 動作の流れ
+ bottleで http://localhost:9000 のローカルサーバーを立てる。
+ ScratchXから http://localhost:9000/blink にRGBデータを送信する。
+ bottleで http://localhost:9000/blink に送られてきたデータを解析する。
+ pyusbでDigisparkにLEDを点灯させる命令を送信する。

### 環境
#### Digispark
+ [DigisparkのWiki](https://digistump.com/wiki/digispark/tutorials/connecting)を参考にArduino IDEをインストールする。
+ 上記ページを参考にArduino IDEにDigisparkのボードマネージャーを入れる。
+ ArduinoIDEで、ファイル > スケッチの例 > DigisparkUSB > DigiBlink を開いて、Digisparkに書き込む。  
  **※"→"のアイコンのボタンが書き込むボタン。"Plug in device now..."の表示が出てきたら、Digisparkを抜き差しする。上手く書き込めなかった場合は、諦めず、また書き込む。2,3回の書き込み失敗はよくある。諦めない、大事。**

#### libusb
USBライブラリ
##### Windows(Win7)
+ Windowsではドライバのせいか、うまくいかなかったので、ドライバを書き換えた。
+ zadig(usbドライバ書き換えソフト)をダウンロードする。
+ zadigでDigiUSBのドライバを`libusb-win32`に変更する。 *※他のデバイスのドライバを書き変えるとマズいので、慎重に…*

##### OSX(El Capitan 10.11)
+ homebrewでlibusbをインストールする。
```
brew install libusb
```

##### Linux(Ubuntu14.04)
+ apt-getでlibusbをインストールする。(なんとなくdevバージョンを入れた)
```
sudo apt-get install libusb-dev
```

#### Python
**※いずれの環境下でも、バージョンは `3.5.0` だった**

+ pythonをインストール
+ pip(pythonパッケージマネージャー)をインストール
  ```
  easy_install pip
  ```
+ bottleとpyusbをインストール
  ```
  pip install bottle pyusb
  ```
+ LinuxでユーザーローカルのPythonだと、pyusbを使ってUSBにアクセスするとき、パーミッションエラーが出たので、[Stack Overflowのこの投稿](http://stackoverflow.com/questions/3738173/why-does-pyusb-libusb-require-root-sudo-permissions-on-linux) や [USB Devices - Google Chrome](https://developer.chrome.com/apps/app_usb#caveats) を参考に解決した。(Chromeのページを見ると、pyusbに限ったことではない感じ。)

 + 私の場合は、`/etc/udev/rules.d/`に`50ーdigispark.rules`というファイルを作り、
```
SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="05df", MODE="0664", GROUP="plugdev"
```
と書き込み、再起動した。

### ウェブブラウザ
 ScratchX が Flash Player で動作してるので、 Flash Player が動くブラウザなら何でも良いはず。

### プログラム
#### `app.py`
+ bottle のローカルサーバーを建てて、ScratchX で動いている javascript からRGBデータを受ける。
+ pyusbでDigisparkに命令を送る。

```python
#!python
#-*-coding:utf-8-*-
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import division


# for bottle
from bottle import route, run, template, request, response, static_file

# for scratchx
import usb
import sys
sys.path.append("..")
from arduino.usbdevice import ArduinoUsbDevice

theDevice = None

@route("/")
def top():
    return '''
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
  <a href="http://scratchx.org/?url=http://localhost:9000/main.js">ScratchX</a>
</body>
</html>
'''

@route("/<filepath:path>")
def server_static(filepath):
    return static_file(filepath, root="./")

@route("/blink")
def blink():
    theDevice = ArduinoUsbDevice(idVendor=0x16c0, idProduct=0x05df)

    red = request.query.get('red')
    green = request.query.get('green')
    blue = request.query.get('blue')

    print("red:{}, green:{}, blue:{}".format(red, green, blue))

    red = int(mapping(red))
    green = int(mapping(green))
    blue = int(mapping(blue))

    theDevice.write(ord("s"))
    theDevice.write(red)
    theDevice.write(green)
    theDevice.write(blue)

    print("mred:{}, mgreen:{}, mblue:{}".format(red, green, blue))

def mapping(arg):
    arg = float(arg)
    if arg < 0:
        return 0
    elif arg > 100:
        return 255
    else:
        return arg * 0.01 * 255

if __name__ == '__main__':
    run(host="localhost", port=9000, debug=True, reloader=True)
```

#### `main.js`
+ ローカルサーバーにRGBデータを送るためのブロックを ScratchX に作る。

```javascript
(function(ext){
  var device = null;

  ext._deviceConnected = function(dev){
    if(device) return;
    device = dev;
    console.log(device);
    device.open();
  };

  ext._deviceRemoved = function(dev){
    if(device != dev) return;
    device = null;
  };

  ext._shutdown = function(){
    if(device) device.close();
    device = null;
  };

  ext._getStatus = function(){
    if(!device) return {status: 1, msg: 'digiUSB disconnected'};
    return {status: 2, msg: 'digiUSB connectd'};
  };

  ext.blink = function(r, g, b){
    $.ajax({
      type: "GET",
      url: "http://localhost:9000/blink",
      dataType: "script",
      data: {
        red: r,
        green: g,
        blue: b
      }
    });
  };

  var descriptor = {
    blocks: [
      ["",  "red: %n, green: %n, blue: %n で光らせる", "blink",
       "100", "100", "100"]
    ],
    menus: {},
    url: 'http://localhost:9000'
  };

  var hid_info = {type: 'hid', vendor: 0x16c0, product: 0x05df};
  console.log(ScratchExtensions.register('DigiUSB', descriptor, ext, hid_info));
})({});
```

### 動作手順
+ `app.py`と`main.js`を同じディレクトリに入れる。
+ `app.py`から Digispark に命令を送るには、[Digisparkが作ったライブラリ](https://github.com/digistump/DigisparkExamplePrograms/tree/master/Python/DigiBlink/source) も必要なのでもらってくる。上記リンクの `arduino` ディレクトリを`app.py`と`main.js`があるディレクトリに入ればOK。
+ ここまでで、ディレクトリの中身は、

```
Directory
|- arduino/
|- app.py
|- main.js
```

+ bottleを起動する。
`python app.py`
+ ウェブブラウザで`http://localhost:9000`にアクセス。"ScratchX"というリンクがあるので、リンク先に飛ぶと同階層にある`main.js`が読み込まれたScratchXのページが開く。
+ スクリプトタブの"その他"にある、"~で光らせる"ブロックを使ってプログラムを作成すると、その通り動く。
  (RGBがそれぞれ2byteのデータ(数値では0~255)を受けるが、入門状態ではその説明からすることになり面倒なので、`app.py`の中で0~100を0~255にスケールしている。要は、ScratchX上では0~100の範囲で数値を吐き出そう、ということ)

### 最後に
+ [ScratchXのWiki](https://github.com/LLK/scratchx/wiki) を見ると、USB-HID に javascript からデータを直接送れるようになっているはずだが、上手くいかなかったので、今回のようなまわりくどい形になってしまった…
上記ページのコードを見ると、このあたりを使っている感じではある。  
 + [chrome.hid](https://developer.chrome.com/apps/hid)
 + [chrome.usb](https://developer.chrome.com/apps/app_usb)

+ `main.js`にはその名残りを残してて、[Scratch Extensions Browser Plugin](http://scratch.mit.edu/info/ext_download/)のプラグインを入れると、ScratchX上の "その他"のシグナルが未接続状態の赤ではなく、接続状態の緑色になる。(OSX, Chromeでのみ確認)

+ 今回はRGB Shieldを使ったが、今は在庫切れのようなので、たぶん同じことができる[LED Shiled](http://digistump.com/products/118)を取り寄せ中。Wikiを見る限り、AdafruitのNeoPixelクローンのような感じ。(曖昧)

+ 前述の通り、ScratchXは Flash Player 上で動作をする。しかし、[LLKのgithub](https://github.com/LLK)を見ると、Playerであるものの [ScratchのHTML5バージョンのリポジトリ](https://github.com/LLK/scratch-html5)もあって、今後の完全HTML5化に期待。できるのかは不明。

+ プログラミングの敷居は本当に低くなった(技術的にも、経済的にも)。フィジカルコンピューティングまがいなこともいろいろとできる。「プログラミングの更なる低年齢化」が話題になってほしい。

## 参考ページなど
+ [LLK/scratchx/wiki](https://github.com/LLK/scratchx/wiki) → ScratchXのWiki
+ [Digispark Wiki](https://digistump.com/wiki/digispark) → DigisparkのWiki
+ [ScratchXの開発を爆速で開始できるテンプレートを作りました](http://mactkg.hateblo.jp/entry/2015/10/26/125215) → ScratchXの拡張についての貴重な日本語記事、node.jsを使う方法など
+ [ScratchXのExtensionsをES2015で開発できるテンプレートを作りました](http://masawada.hatenablog.jp/entry/2015/12/21/165718) → この記事も貴重。これからはES2015(ES6)だよねということで…
+ [895円の超小型Ardunoクローン DigiSparkを買った/橋本商会](http://shokai.org/blog/archives/8665) → Digisparkの日本語記事。Rubyとの連携についても書かれている。
+ [bottle_scratchx.git](https://github.com/uitspitss/bottle_scratchx) → 今回の`app.py`と`main.js`はここに置いた。