子どもでも使えるプログラミング言語ScratchとPythonをつなげてScratchの値を送受信。ScratchではできないメールやSlackの送信、画像認識やWeb連携、データをテキスト出力、OSコマンドの実行などをする話。
何ができるの?
- Pythonで動く何か(結果)をScratchで使いたい
- Scratchから任意のOSコマンドを実行したい(メールやSlackへ送信、Webからファイルを取得、テキストで出力など)
- USBカメラによる顔認識や色認識結果をScratchで使いたい
- 遠隔/ローカルのScratchへPythonの値を送りたい
- 遠隔/ローカルのScratchからPythonで値を受け取りたい
- 遠隔/ローカルのPython/ScratchへPython/Scratch/Pyonkeeの値を中継したい
などなど
工夫次第でいろんな事ができるようになるのでは無いかと思います。ちなみにScratchはver 1.4のみ対応しています。
対象の方
- 親子でプログラミングを楽しみたい(お父ちゃんが凄いのをPythonで作って、Scratchでつながせ凄さを見せつける)
- Scratchはだいたい分かる。Pythonに挑戦しようと思うが、作りたいものが思いつかない
- 黒い画面はさほど怖くない
- google先生に聞いて何となく進められる
対象じゃない方
- Pythonで何でもできる
環境の準備
Pythonが動く環境が必要です(Scratchと同一PCで無くても良いです)。
Pythonのインストール
今回はRaspberryPiのPython2.7を使いました。
RaspberryPiやMacの場合はPythonが初めから使えるのでそのままで。Windows10ならBash on windowsを有効にしてみると良いのではないかと思います。それ以外の方は… google先生に聞きながらPythonのインストールを頑張ってみて下さい。
- Windows10をお使いの方(Bash on Windowsの有効化)
Bash on Ubuntu on Windowsとは? そのインストールと使い方
http://www.buildinsider.net/enterprise/bashonwindows/01#bowinstall - Python
https://www.python.org/downloads/
python --version Python 2.7.3
pipのインストール
Pythonのパッケージを良い感じに管理(インストール)してくれるpipを入れます。
raspberry Piやbash on windowsなら、
sudo apt-update sudo apt-get install python-pip
Macなら、
easy_install pip
pip –versionと打ってエラーが出なければOKです。
$ pip --version pip 1.1 from /usr/lib/python2.7/dist-packages (python 2.7)
scratchpyのインストール
PythonでScratchの遠隔センサープロトコルを簡単に使うためのライブラリscratchpyをインストールします。
sudo pip install scratchpy
- scratchpy公式ページ
https://github.com/pilliq/scratchpy
動作テスト
pythonを実行して、import scratch と打ってエラーが出なければOK(Ctrl+Dで終了)。
$ python Python 2.7.6 (default, Oct 26 2016, 20:30:19) [GCC 4.8.4] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import scratch >>> Ctrl+D
エラーが出るようであれば、
https://raw.githubusercontent.com/pilliq/scratchpy/master/scratch/scratch.py
をダウンロードして、実行したい場所/ファイルと同じディレクトリに置いて試してみて下さい。
Scratchのインストール
Scratchは1.4のみ上記のプロトコルに対応しています。Windows/Mac/Linux(RaspeberryPiなど)/iPad(Pyonkee)で使えます。
- Scratch 1.4
https://scratch.mit.edu/scratch_1.4
Scratchの遠隔センサーとファイアーウォールの設定
Scratch1.4の遠隔センサー接続を有効にします(1.4以外では使えません)。「調べる→スライダーセンサーの値→右クリック→遠隔センサー接続を有効にする」を押します。
他のPCからScratchが実行されているPCへ値を送りたい場合は、Scratchが入っているPCのファイアーウォールの設定を変更する必要があります。TCPポート42001のみを開ける、Scratchの通信は全て許可すれば良いのですが、一時的に体験/テストするだけであればファイアーウォールを一時的に無効にしても良いかと思います。
- Windowsファイアウォールを無効にする方法(Windows 10)
http://faq.buffalo.jp/app/answers/detail/a_id/15776
scratchpyの使い方(基本)
詳しくは上記の公式ページに書いてありますが、これを使うとPythonからScratchへ「センサーの値」と「ブロードキャスト(○○を送る)」を送受信できます。
Python→Scratchへ値を送る
同一若しくは別PCのScratchへPythonの値を送ることができます。下記にセンサーと書いてありますが単に値を送ると思って下さい。数字だけでなく、文字列を送ることも可能です。
下記のコードを編集するには RaspberryPiとMacであればGUIエディタでも大丈夫ですが、Bash on windowsの場合はnanoとかを使ってみて下さい(2017/4現在Bash on Windowsでは日本語は怪しいので打たない感じで)。
- Raspberry Pi | viより直感的!標準エディタ”nano”の使い方ガイド@たぷん日記
http://www.tapun.net/raspi/raspberry-pi-nano-guide
# -*- coding: utf-8 -*- import scratch, time #192.168.11.20のScratchへ送る場合 s = scratch.Scratch(host='192.168.11.20') #同一PCのScratchへ送る場合 #s = scratch.Scratch(host='localhost') s.connect() a = 0 while True: try: a = a + 1 # センサーの値を送る s.sensorupdate({'a' : a}) time.sleep(0.5) except KeyboardInterrupt: print "Scratch: Disconnected from Scratch" break s.disconnect()
上記を下記のように実行します(終了はCtrl+C)。
python send_sensor.py
これでScrachのaセンサーの値が1づつ増えていきます。
Python→Scratchへブロードキャストを送る
※2017/4月現在、bash on windowsでは何故か動きませんでした。
# -*- coding: utf-8 -*- import scratch #192.168.11.20のScratchへ送る場合 s = scratch.Scratch(host='192.168.11.20') s.connect() # Hello, Scratch!を送る s.broadcast('Hello, Scratch!') # 切断 s.disconnect()
下記で実行。
python send_broadcast.py
Scratch→Pythonへ値を受け取る
Scratchのグローバル変数の値が変化したらPythonで値を受け取ることができます。
Scratch側は例えばこんなプログラム。
# -*- coding: utf-8 -*- import scratch #192.168.11.20のScratchから受け取る場合 s = scratch.Scratch(host='192.168.11.20') s.connect() while True: try: msg = s.receive() print "Scratch: receive:",msg # Scratchでの変数名send_msgの値があれば表示 if 'send_msg' in msg[1]: val = msg[1]['send_msg'] print val except KeyboardInterrupt: print "Scratch: Disconnected from Scratch" break s.disconnect()
Scratch→Pythonへブロードキャストを受け取る
# -*- coding: utf-8 -*- import scratch #192.168.11.20のScratchから受け取る場合 s = scratch.Scratch(host='192.168.11.20') s.connect() while True: try: msg = s.receive() print "Scratch: receive:",msg if msg[0] == 'broadcast': print msg[1] except KeyboardInterrupt: print "Scratch: Disconnected from Scratch" break s.disconnect()

○○を送るをPythonで受け取れます
Scratch→Pythonへ値かブロードキャストを受け取る
上記のサンプルは一度に複数の値が変化した場合1つしか受け取れませんが、下記なら複数受け取れます。
# -*- coding: utf-8 -*- import scratch s = scratch.Scratch(host='192.168.11.20') s.connect() def listen(): while True: try: yield s.receive() except scratch.ScratchError: s.disconnect() raise StopIteration except KeyboardInterrupt: print "Scratch: Disconnected from Scratch" s.disconnect() break for msg in listen(): if msg[0] == 'broadcast': # ブロードキャストを受け取った時の処理をこの下に書く print msg elif msg[0] == 'sensor-update': # センサーを受け取った時の処理をこの下に書く if 'send_msg' in msg[1]: val = msg[1]['send_msg'] print 'send_msg:' + str(val)
値を中継する
Scratch1 – PC(Python) – Scratch2など、Scratchから他のScratchへ値を中継します。上記の例をひっつけただけです。ただ、これはScratch 1.4であれば標準でもMESHの仕組みを使えば可能で、MESHのほうが簡単です。
特定のセンサーやブロードキャストだけを転送したい。受け取った値をPythonで加工してから転送したい時などに使えるのでは無いかと思います。
- Scratchで相互に通信可能なMESHネットワークを作る方法(RaspeberryPiのScratchでも可能です)
簡単だけど奥深い!Scratchプログラミングの魅力 第3回 分散プログラミング
http://itpro.nikkeibp.co.jp/article/COLUMN/20111019/371083/
下記は、scratch1のsend_msgをscratch2のfrom_srcatch1へ転送するサンプル。
# -*- coding: utf-8 -*- import scratch # scratch1のIPアドレス s = scratch.Scratch(host='192.168.11.20') s.connect() # scratch2のIPアドレス s2 = scratch.Scratch(host='localhost') s2.connect() def listen(): while True: try: yield s.receive() except scratch.ScratchError: s.disconnect() s2.disconnect() raise StopIteration except KeyboardInterrupt: print "Scratch: Disconnected from Scratch" s.disconnect() s2.disconnect() break for msg in listen(): if msg[0] == 'broadcast': # デバッグ用 print msg elif msg[0] == 'sensor-update': if 'send_msg' in msg[1]: val = msg[1]['send_msg'] # デバッグ用 print 'send_msg:' + str(val) # Scratch1から受け取ったsend_msg値を、Scratch2へ送る s2.sensorupdate({'from_scratch1' : val})
scratchpyの使い方(応用)
楽しいのはココからです。上記の基本を踏まえ、色々作っていきます。
任意のOSコマンドを実行する
既存のプログラム(バイナリ、Python、他の言語)を使って色々遊びたい場合、OSコマンドを実行できると便利ですよね。実行したいコマンドを受け取って、Pythonのsubprocessで、shell実行をすればOSコマンドを実行できます。
※コマンド次第ではファイルの書き換えや削除など、何でもできてしまうので色々気をつけて下さい。
# -*- coding: utf-8 -*- import scratch import subprocess # ScratchのIPアドレス s = scratch.Scratch(host='192.168.11.20') s.connect() def listen(): while True: try: yield s.receive() except scratch.ScratchError: s.disconnect() raise StopIteration except KeyboardInterrupt: print "Scratch: Disconnected from Scratch" s.disconnect() break for msg in listen(): if msg[0] == 'broadcast': # デバッグ用 print msg elif msg[0] == 'sensor-update': if 'shell' in msg[1]: val = msg[1]['shell'] cmd = str(val) # デバッグ用 print 'shell:' + str(val) # shellコマンドの実行 p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout_data, stderr_data = p.communicate() # 標準、エラー出力をScratchに送り返す s.sensorupdate({'shell_stdout' : stdout_data.replace('\r\n',' ')}) s.sensorupdate({'shell_stderr' : stderr_data.replace('\r\n',' ')}) # デバッグ用 print 'stdout:' + str(stdout_data) + 'stderr:' + str(stderr_data)
上記を実行し、グローバル変数shellを変化させるとOSコマンドが実行されます。
現時刻を取得
気をつけなければならないのは、先にも書きましたが変数に変化が無いとsensor-updateを送らないこと。2回目を送る場合は少し何かを変えるなど工夫が必要です。
export LANG=C;time=`date +'%Y-%m-%d %H:%M:%S'`;echo $time
テキストログを保存
上を少しだけ応用して、現時刻の入ったログを保存。Pi_scratch等を使用してIoT的な何かを作ってログやCSVを残したいという時に使えるのではないかと。
export LANG=C;time=`date +'%Y-%m-%d %H:%M:%S'`;echo $time 1 >> test.log
root@raspberrypi:~/Python# cat test.log 2017-04-18 14:14:23 1 2017-04-18 14:14:23 2 2017-04-18 14:14:23 3 2017-04-18 14:14:23 4 2017-04-18 14:14:23 5 2017-04-18 14:14:23 6 2017-04-18 14:14:23 7 2017-04-18 14:14:23 8 2017-04-18 14:14:23 9 2017-04-18 14:14:23 10
Scratchからメールを送信
ワンライナーで書ければコマンドで動く物はだいたい動きます。改行したい場合はPythonスクリプトで\rとかを改行に置換すると良さそうですがやってません。Scratch→Pythonは日本語も大丈夫でした。
echo 'Scratchからのメール送信テストです。' | mail -s 'test' root
↓
ScratchからSlackへメッセージを送信
コマンドが打てると言うことは、アイデア次第でかなり色々な事ができます。curlの戻り値はstdoutに入るので、他のWebAPI(天気や本など)にもアクセス可能です。
- Slack APIを使用してメッセージを送信する
http://qiita.com/rubytomato@github/items/6558bfdb37d982891c09
curl -XPOST -d "token=トークンをココへ" -d "channel=#general" -d "text=test from scratch" -d "username=Scratch" "https://slack.com/api/chat.postMessage"
↓
他にも下記のように、OpenJTalkで喋らせたり、一眼レフカメラに写真を取らせたり、音声認識の結果を知らせたりもできるのでは無いかと思います(宣伝)。
- 子どもがいる家庭で使うRaspberryPi&Slack @もやし工房
子どもがいる家庭で使うRaspberryPi&Slack電子工作なし。幼稚園~小学生くらいのお子様がいるリビングで使うRaspberry Pi(+ちょっとだけSlack)。写真撮影、Slackからおしゃべり、音声認識で音楽再生など。
USBカメラで顔認識したらScratchに知らせる
Pythonで書かれたサンプルコードに先程のsensorupdateかbroadcastをはさんであげます。
今回の例だけでなく、Pythonで書かれた何かに下記の数行を入れてあげるだけでScratchから使えるかもしれません。Pythonだけで作られた何かの場合はコードが複雑で、挟む個所も分かりにくいですが、今回のようにメイン(OpenCV)があり、それを利用するようなプログラムの場合、コードも短く、見やすい場合が多い気がします。
顔認識にはopen cvを使います。RaspberryPi以外でのOpenCVの入れ方は… 調べてみて下さい。PythonからOpenCVを使えるようにセットアップする必要があります。
RaspberryPi(debian系)なら↓だけ。あら簡単。
apt-get update apt-get install libopencv-dev apt-get install python-opencv
このあたりを見てテストして、顔認識ができるところまでやります。
- Raspberry Pi で OpenCV(リベンジ)@ Qiita
http://qiita.com/suppy193/items/91609e75789e9f458c39
あとは、顔が見つかった部分で先程のsensorupdateを挟めば良いので↓の感じで。特定色の色認識などもOpenCVを使って同様にできると思います。
#!/usr/bin/python """ This program is demonstration for face and object detection using haar-like features. The program finds faces in a camera image or video stream and displays a red box around them. Original C implementation by: ? Python implementation by: Roman Stanchak, James Bowman """ import sys import cv2.cv as cv from optparse import OptionParser import scratch s = scratch.Scratch(host='localhost') s.connect() # Parameters for haar detection # From the API:
if faces: for ((x, y, w, h), n) in faces: # the input to cv.HaarDetectObjects was resized, so scale the # bounding box of each face and convert it to two CvPoints pt1 = (int(x * image_scale), int(y * image_scale)) pt2 = (int((x + w) * image_scale), int((y + h) * image_scale)) cv.Rectangle(img, pt1, pt2, cv.RGB(255, 0, 0), 3, 8, 0) print "face!!" s.sensorupdate({'face' : 1}) else: s.sensorupdate({'face' : 0}) cv.ShowImage("result", img)
ソースコード
ここに上げました。Python慣れていないので、この書き方はイマイチでは?などありましたら、pull requestいただけると幸いです。
git clone https://github.com/koike-moyashi/scratch_python_sample.git
とかすると良いのではなかろうかと。
最後に
これならPythonで全部やっちゃえば… と言う気持ちは分からないでも無いのですが、画面描画するゲームやインタラクティブ作品と組み合わせたり、Pythonやってみたけどhello world以上何を作れば良いのかなぁと思ってる方なんかは徐々にPython触れられる気がしました。
また、ScratchをGUIフロントとして使い、タッチパネルでスイッチ的な何かに使い、裏側ではPythonで動くみたいなのも作れそう。
既存のバイナリプログラムやpythonプログラム、RaspberryPi+Scratchで各種センサーやモータがあれこれできるpi_scratchなどと組み合わせると、楽しい作品を作ることができるのではないかなぁと思いました。
こんなの作ったよとかありましたら、是非コメント欄で教えてください。
コメント
もやし工房 様
突然の長文のメールにて申し訳ありません。
pythonとscratchの連携の記事を熟読して、インターネットで関係するサイトを参考にIOT機器からデータを取得して、scratch1.4でグラフを描画するのと同時に、text形式で時刻とともに温度、湿度、CO2濃度、水温のデータをパソコンに蓄積可能なシステムを作ったので報告します。
自分は仕事で農家の技術支援をしていますが、農業にもICT技術の導入が進み、複数の環境データを参考に、その後の環境制御を決める取り組みが始まりました。
しかし、農家の栽培ハウスには自宅近く以外はwifiを活用できる環境がほとんどないことから、まずはパソコンにデータを蓄積する方法を取ることにしました。また、scratchを活用すれば、学校と連携して、普段scratchに触れている学生さんに課外授業の形で、少しでも農業に興味を持ってもらえるのでは、と思い、作ってみました。
使用した機器は
OSをwindows7からLinuxにした中古パソコン
Esp8266またはEsp32
そのパーツとして、DHT11または22で温湿度測定
MZ19bでCO2濃度測定
DS18B20温度センサーで養液の水温測定
です。
記事に掲載されていたscratchプログラムを参考に作成した自作プログラムとpythonプログラム、はLinuxOSのパソコンで稼働し、Esp8266またはEsp32にはarduinoからプログラムをコンパイルして、scratchでのデータ取得を可能にしました。
3つほど作り、稼働状況を確認しましたが、1カ月以上連続稼働可能でした。
textデータはエクセルで加工すれば、scratchで常に表示していたグラフの再現も可能でした。
作成して3カ月経ちましたが、突然の入院から復帰して間もなくなので、まだ農家のハウス内データの取得はこれから取り組みます。
その前にお伝えしようと思いメールした次第です。
約30年ぶりに自分でプログラム作成しました。Esp8266またはEsp32からのデータ取得と、同時にグラフ表示、そしてtext形式でのデータ蓄積までを一体的に行うscratchプログラムが何とか完成した時は本当にうれしかったです。
貴方の記事のおかげです。
ありがとうございます。
作例紹介ありがとうございます。良いですね。
python2のサポートが終わっているので、近い将来使えなくなってしまうのでは無いかと言う心配はありますが、お役にたてて良かったです。