PythonでTENORI-ONを動かすblog(仮)

PythonでMIDIを作ってYAMAHAのTENORI-ONをごにょごにょするよ。

(追記)M5Stackでタミヤ ダブルギヤボックスを動かす

ピンアサインで微妙にハマったので備忘録。

左右のクローラーの動作確認するところまでですが、BLEや障害物検知と組み合わせると楽しそう。

 

f:id:bastardeyes:20200802140855j:plain



用意したもの

  • M5Stack Gray(そこにあったので)
  • モータードライバ  TB6612FNG
  • タミヤ ダブルギヤボックス
  • タミヤ 楽しい工作シリーズ タンク工作基本キット(クローラーのみバラして使った)
  • コンデンサー 100nF 2つ(モーターにつける)
  • コンデンサー 1μF 1つ(外部電源側にパスコンとして入れる)
  • ジャンパーピン

モータードライバ ーはDRV8835を購入予定でしたが、秋月がコロナで日祝休業のため、千石で調達できるものを選びました。

www.sengoku.co.jp

 ピンアサイ

M5Stack TB6612FNG
3 PWM_A
1 AIN1
16 AIN2
17 PWM_B
18 BIN1
19 BIN2
26 STBY
3V3 VCC
GND GND
- (外部電源で5Vor3.3V)

 

Arduinoコード

↓こちらのコードをM5Stackに置き換えただけです、はい。。

回路図もこちらを参考に。。

www.moonmile.net

#include <M5Stack.h>

#define AIN1 1
#define AIN2 16
#define PWMA 3
#define BIN1 25
#define BIN2 26
#define PWMB 17
#define STBY 26
 
void setup() {
  Serial.begin(9600);
  Serial.println( "Motor Standby" );
  delay( 1000 );
  
  M5.begin();
  pinMode(AIN1,OUTPUT);
  pinMode(AIN2,OUTPUT);
  pinMode(PWMA,OUTPUT);
  pinMode(BIN1,OUTPUT);
  pinMode(BIN2,OUTPUT);
  pinMode(PWMB,OUTPUT);
  pinMode(STBY,OUTPUT);

  digitalWrite(AIN1,LOW);
  digitalWrite(AIN2,LOW);
  digitalWrite(PWMA,LOW);
  digitalWrite(BIN1,LOW);
  digitalWrite(BIN2,LOW);
  digitalWrite(PWMB,LOW);
  digitalWrite(STBY,HIGH);
  
}
 
void loop() {

  Serial.println("motorA forward");
  digitalWrite( PWMA, HIGH );
  digitalWrite( AIN1, HIGH );
  digitalWrite( AIN2, LOW );
  delay( 3000 );
  Serial.println("motorA stop");
  digitalWrite( PWMA, LOW );
  delay( 1000 );
  Serial.println("motorA back");
  digitalWrite( PWMA, HIGH );
  digitalWrite( AIN1, LOW );
  digitalWrite( AIN2, HIGH );
  delay( 3000 );
  Serial.println("motorA stop");
  digitalWrite( PWMA, LOW );
  delay( 1000 );
 
  Serial.println("motorB forward");
  digitalWrite( PWMB, HIGH );
  digitalWrite( BIN1, HIGH );
  digitalWrite( BIN2, LOW );
  delay( 3000 );
  Serial.println("motorB stop");
  digitalWrite( PWMB, LOW );
  delay( 1000 );
  Serial.println("motorB back");
  digitalWrite( PWMB, HIGH );
  digitalWrite( BIN1, LOW );
  digitalWrite( BIN2, HIGH );
  delay( 3000 );
  Serial.println("motorB stop");
  digitalWrite( PWMB, LOW );
  delay( 1000 );
}
〜

 

追記

今回はモータードライバ ーにTB6612FNGを使用したのですが、Twitterで別の製品のおすすめをいただきました。

 

Maker DriveからM5 Atom Liteに電力供給できるのは配線もシンプルで良さそう。

 

「キャタピラっぽいもので旋回するものを作るにはどうすれば良いか」教えてもらった話。

↑についてTwitterで「わかりません、助けてください(要約)」と助けを求めたところ、予想外にたくさんの方から多くのアドバイスをいただきました。

皆様ありがとうございます。

 

私と同じように途方に暮れる人もいるだろうと思い、いただいた内容をブログにまとめることにしました。

 

元ツイート

やりたいこと

キャタピラっぽいもので、前後移動・旋回できるものを作りたい

 

質問した背景

  • Twitterで見かけたM5Atom+タミヤ連結式クローラー&スプロケットセットを使った作品が素敵だったので、新作*1
  • しかしミニ四駆のようなモーターを使った工作を子どもの頃に経験していないので、正しい情報を得られる検索ワードがわからない*2
  • リアル戦車などの機構について情報を得られたものの、選ぶべきパーツがわからない。*3
  • 参考にタンク工作基本キットというものを買ったけれど、曲がるための部品は何を買えばいいかわからない

TwitterのTLの方々ならばすでに色々作ってるのでは?と思って質問することにしました。

教えていただいた検索キーワード

 →自分の引き出しにはキャタピラという言葉以外なかった。。

 

おすすめされて図書館に予約入れた本

 

おすすめされて買ったもの

  • タミヤ ダブルギヤボックス
  • タミヤ リモコンロボット製作セット(クローラータイプ)

  • モータードライバ ※秋月は祝日で閉まっていたので千石で買えるものにした。。

その他オススメいただいたパーツ、キット類

 

www.tamiya.com

おすすめいただいたブログ記事

 

実装に関するアドバイス

 

現状

追加購入したリモコンロボット製作セット(クローラータイプ)を組み立て、動作確認まで完了。

とはいえ機構の理解をがっつりやるよりも、イベント出展向けの作品を作るのがゴールなので先は長そう。

 

ゆるいロボ作りたくて、装備の面からMaixduinoでぼちぼちやっています。

f:id:bastardeyes:20200725154425j:plain

f:id:bastardeyes:20200725154511j:plain

 

追記

キャタピラというのはキャタピラ社の商標で、今はクローラーと呼ぶのが一般的という指摘をいただきました。

皆どこで知るんだろうと思ったのですが、よくよく思い返すと乗り物図鑑を読んだことがなかったのも一因と思われる。。

 

*1:2月のMini Maker Faire Ogakiへの出展が目標。募集が8月下旬と知って焦る。に使ってみたい

*2:

男子なら小学校低学年のうちに、親や友人の影響でミニ四駆ぐらいは触ると思うのですが、女子は上にお兄さんがいないときっかけを得ることができない。

興味関心を持つ以前に、オモチャ屋の売り場に行く機会もないので、知らないということさえ知らないものが多い気がします。。

*3:信地旋回で調べたらガチ戦車ばかり出てきた

GASで作る貧乏IoT(1) ESP32からPOSTして、レスポンスを取得する。

2021.05.30追記

どうもGCP(Google Cloud Platform)のアカウントを持っていると、GCP側で別の設定をしないと動かないようです。

`GCPの画面からGASのプロジェクト作る -> OAuth設定 -> GASファイル作成` という手順が必要っぽい。

Google Apps ScriptのログをGoogle Cloud Platformで確認する方法 – hidetoshl.com

-----

例によって久々の更新です。

近況

  • AWS色々覚えたものの、諸事情により、課金ダメージを受けた。(※1)
  • なので個人でIoT周りやるなら、請求が発生し得るものはしばらく触りたくないというお気持ちに。

で、あんまり好きじゃないけど、一時期仕事で触っていたGoogle Apps Script(以下GAS)やるか、、となったものの、相変わらずトラップが多いので、備忘録的にまとめようと思いました。

 

というわけで「GASで作る貧乏IoT」です

内容は主に電子工作趣味のハードウェア、組み込みの人向けに必要そうなことだけ。

気が向いたら何回か書くと思いますが、GASの文法とかWebのお作法などは特に触れません。


GAS(Google Apps Script)について雑に書く

  • Googleが提供するJavaScript互換のスクリプト環境。
  • 2020年2月からはEcmaScript対応になり、モダンな書き方ができるようになったらしい。(※2)
  • Googleフォーム、スプレッド、ドキュメント、メール、カレンダー連携には強い
  • 正確な時間ではないが、一応、定期実行もできる。
  • 6分を超える処理はできない。(タイムアウト扱いになって色々トラブルが起きる)
  • IDE上でデバッグやステップ実行できる
  • 無料で使える。(色々不満はあるけど、これ超重要)

 

今回のお題 「ESP32からデータをPOSTして、レスポンスを取得する。」

注意!!!) doPost関数は業務利用は、ほぼ無理と思います。

趣味の範疇でのみ利用可能と捉えてください。(※3)

●概要

  1. ESP32(Arduino)からGASにデータをPOSTする
  2. POSTした結果をスプレッドに記入。
  3. 処理終了後にレスポンスを返す。

重要)スプレッドに書くだけならさして苦労はないものの、レスポンスを返す場合はライブラリ「HttpRedirect」を使う必要があります。(※4)

動作確認はM5Stackを利用。たぶんESP8266でも動くはず。

●ざっくりした流れ

1)GASのプロジェクトを用意し、Webアプリケーションとして公開する
2)Arduinoに書き込みを行う。

 

GAS側やること


スクリプトエディタを立ち上げる


方法1) スプレッドシートも使いたい場合
・スプレッドを起動。メニューの<ツール> - <スクリプトエディタ>を開く

 

方法2) 独立したプログラムファイルを作りたい場合
Googleドライブの新規作成で、"Google Apps Script"を選択


●大まかな書き方

  • "function doPost(e) {}"の中に処理を書く
  • "e.JSON.parse(e.postData.contents);"で、ESP32から送ったJSON風のデータを受ける。
  • "return ContentService.createTextOutput( 文字列 )"; でESP32にレスポンスを返す。

●コード例

 ファイル"GAS_sample.gs"

var sheet_id="*****";//スプレッドのID。https://docs.google.com/spreadsheets/d/{この部分}/edit#gid=2037815417

//デバイスからのPOSTで実行される関数
function doPost(e) {
  
  //POSTのpayloadを格納
  var parsed_data;
  try { 
    parsed_data = JSON.parse(e.postData.contents); 
  } catch(_exception){ 
    // JSON以外がPOSTされたら例外を返して終了
    return ContentService.createTextOutput("JSONデータの解釈が出来ません。: " + _exception.message); 
  }
  
  //GASデバッグ用なら↑をコメントアウトしてこういう書き方でOK
  //var parsed_data=JSON.parse('{"values_01":"10.0", "values_02":"20.5"}');
  
  var spread_file_object = SpreadsheetApp.openById(sheet_id);
  var sheet_name = "log";   
  var sheet_object = spread_file_object.getSheetByName(sheet_name);
  
  // スプレッドに行を追記
  //sheet_object.appendRow([new Date(), parsed_data.values_01, parsed_data.values_02]);
  
  //sheet_object.getRange("A1").setValue(parsed_data.values_01);
  SpreadsheetApp.flush(); //描かなくても動く。一応、遅延対策。
  
  //ESP32にレスポンスを返す
  return ContentService.createTextOutput( "success");
  
}

特定のセル1つに値を入れる場合はこう書く。

sheet_object.getRange("A1").setValue(parsed_data.values_01);


複数セルの場合はsetValues()になるので若干処理が変わる。

 

●アクセスできるようにする設定


1)スクリプトエディタのメニュー"公開" -> "webアプリケーションして導入" を開く

f:id:bastardeyes:20200606171617p:plain



Current web app URL:
-> 変更する部分はなし。URLはArduinoのコードに記載するので、コードの上の方にでも書いておくのがベター。
/macros/s/●●/exec というルール。Arduinoのファイルに書くScriptIDはこれを使う。

Project version
-> Newを選択
※コードの変更を反映する度に都度更新が必要。GASを書き換えてctrl+Sだけだと、最新に反映されない。

Execute the app as:
-> 自分のGoogleアカウントを選択

WHo has access to the app
-> Anyone,even anymous を選択

 

色々許可を求められます。

f:id:bastardeyes:20200606171933p:plain

f:id:bastardeyes:20200606171833p:plain

 

2)アクセスの許可
IDE上の虫マーク横の関数「doPost」を選択して、虫マークをクリックして実行。
途中、実行の許可を求められるのでOKする。

注意!!!)一度実行の許可をして動かさないと利用できない。

 

f:id:bastardeyes:20200606172056p:plain

f:id:bastardeyes:20200606172119p:plain

ESP32(Arduino側)

●やること
1)下記のGitHubからHTTPSRedirect.cppとHTTPSRedirect.hとDebugMacros.hをコピーする

タブ追加でファイルを追加してコピペで良い。
 

2)HTTPSRedirect.cppの89行目。 //stop();の//を消して実行できるようにする。

case 301:
case 302:
{
Serial.print(""piyo"");
// Get re-direction URL from the 'Location' field in the header
if (getLocationURL()){
stop(); // may not be required

※コメントアウトしたままだと、スプレッドは更新されるがESP32側が停止する。
※ESP32での動作確認時に変更。もしかしたら8266は変更不要かもしれない。

参考)https://github.com/electronicsguy/ESP8266/issues/91

 

コード例

ファイル名「ESP32_TO_GAS.ino」

 

 

//#include <ESP8266WiFi.h> //ESP8266なら<WiFi.hの代わりにこちら>
#include <WiFi.h> // ESP32用
#include "HTTPSRedirect.h"

// Fill ssid and password with your network credentials
const char* ssid = "****"; // TODO)入力
const char* password = "****"; // TODO)入力

const char* host = "script.google.com"; 
const int httpsPort = 443;
// Replace with your own script id to make server side changes

//TODO) google scriptのscriptIDを入力
const char *GScriptId = "*****"; 

const String url = String("/macros/s/") + GScriptId+ "/exec";
// https://script.google.com/macros/s/{この部分}/exec


HTTPSRedirect* client = nullptr;

void setup() {
  Serial.begin(115200);
  Serial.print("Connecting to wifi: ");  Serial.println(ssid);
  Serial.flush();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("IP address: ");  Serial.println(WiFi.localIP());
}

int count;

void loop() {
  do_post();
  delay(60000);
}

void do_post(){
    
  bool flag;
  char stringCount[10];
    sprintf( stringCount, "%d", count);
    count++;
    // Use HTTPSRedirect class to create a new TLS connection
    client = new HTTPSRedirect(httpsPort);
    client->setPrintResponseBody(true);
    client->setContentTypeHeader("application/json");
    Serial.print("Connecting to ");  Serial.println(host);
    // Try to connect for a maximum of 5 times
    flag = false;
    for (int i=0; i<5; i++){
      int retval = client->connect(host, httpsPort);
      if (retval == 1) {
         flag = true;
         break;
      }
      else
        Serial.println("Connection failed. Retrying...");
    }
    if (!flag){
      Serial.print("Could not connect to server: ");
      Serial.println(host);
      Serial.println("Exiting...");
      return;
    }

    float sensor_value_01=10.0; //TODO)任意のセンサ値を設定
    float sensor_value_02=20.0; //TODO)任意のセンサ値を設定

    String payload = "{";
    payload += "\"values_01\": \"" + String(sensor_value_001) +"\"";
    payload += "\"values_02\": \"" + String(sensor_value_002) +"\",";
    payload += "}";

    
    Serial.println("==POSTCOMMAND==============================");
    Serial.println( payload );
    client->POST(url, host, payload, false);
    Serial.println("==POSTCOMMAND==============================");
    
    Serial.print( "Status      :");  Serial.println( client->getStatusCode() );
    Serial.print( "reasonPhrase:");  Serial.println( client->getReasonPhrase() );
    Serial.print( "body        :");  Serial.println( client->getResponseBody() ); //GASでreturnした値
    
    // delete HTTPSRedirect object
    delete client;
    client = nullptr;
  
  }

以上。

GAS側のレスポンスは適当な文字列にしていますが、レスポンスによってESP32で分岐を加えると色々遊べると思います。

 

ぶっちゃけAWS S3にデータ置く方がはるかに楽だし、マイコンよりもラズパイ使った方が情報も多くて簡単。
しかし、ハードを小さく安価に。ソフトもお金をかけたくない。Ambientのようなサービスを使わず自前で用意したいという場合は、もう一周回ってGASでいいやと思いました。

 

次回は時間があれば、M5CameraかMaixduinoの画像をGoogleドライブにアップロードなど。

 

ちなみにGET/POSTがどうとか、Webのお作法的なものを基礎から押さえたいという方は「Webを支える技術」あたり読むのが良いかもしれません。

 

 

※1) 仕事都合により個人で使ってたAWS RDSが、ほんのちょっとしか使ってないのに請求$400overした。
  カードの不正利用疑いで一瞬止められてたのもあり、深夜に4-50万と見間違えて死ぬかと思った。

※2) JavaScriptに対する愛が薄いので、突っ込んだ話はしません。詳しく知りたい場合は検索「EcmaScript GAS」を推奨。

※3) 多くの企業は情シスが社内ドメイン以外からのGsuiteアカウントへのアクセスに制限をしているはず。

※4) GASの仕様でdoPost関数使うとリダイレクトが発生するが、ライブラリ「WiFiClientSecure」はリダイレクトに追従しない。
  そのため、レスポンスを取ろうとすると、レスポンスコード302で詰まる。

※4) 書籍もいくつか出ているけれど、特に買う必要はないと思う。仕事のために数日で覚えるために買ったけれど、あまり必要なかった。

 

趣味TECH祭2019出展しました

気づいたら8/4(日)の出展から早1か月強が経ってましたが改めて。

 

趣味TECH祭2019 2日目に出展しました。

f:id:bastardeyes:20190908162513j:plain

 

元々はMaker Faire Tokyo 2019に落選し、特に出展予定もなかったのですが、たまたま2日目にキャンセルがあったとのことで「せっかくだからMaker Faire に出せないものを出そう!!」と勢いで参加させていただきました。

 

出展直前・当日に壊れてしまったものもあり、世に出すつもりでなかった小ネタも総動員となりました。(出展中に壊れるのは、メイカーあるある)

 

 

パリピ般若V2

 NT金沢に遊びに行った時に「ウェアラブル出展するぜ!」と言って初号機が爆誕

 

続いてM5Stack User MeetingにJimmyさんが来日すると聞いて、「M5Stack使おう!!」という流れでV2"M5Hannya"ができました。

 

技術的に難しいことはしていないのですが、字面と絵面が強い。

制作記事も書かせていただきました。

 

●M5Stack + LIFEGAME +YAMAHA TENORI-ON

動的にMIDI信号生成して、既成の楽器"TENORION"をハックする、弊ブログでおなじみの作品。
これは出さない予定でしたが、アンメルツ工作が事故ったので数合わせに急遽出展。

 

●ワンボタンGetWild
ボタンを押すとGetWildが流れてシティーハンターのエンディング気分になれる。

ワイルドさが足りない時に補うのに便利。

 

 

●どこかで聞いた効果音Z
ドラゴンボールの効果音が出る。最初は超サイヤ人の音を延々流すだけだったのだけど、ノリで増やした。

 

 

 

で、こちらは壊れてしまって出展できなかったアンメルツ作品2点。

なぜウケてしまったのかいまだに謎。幻の代表作。

 

 

ブース数20ぐらい、日曜なのでそんなにお客さんもいないと思っていたのですが、蓋を開けてみたら結構な繁盛ぶりで、他の出展者のブース写真とかあんまり撮れてなかった。。

 

Maker Faireはなんとなく"Maker Faireらしい作品"という秩序ができている一方、趣味TECH祭は初期のMake Tokyo Meetingやニコニコ技術部イベントに似たニオイがするというか。

機械学習あり、ロボットあり、同人ハードあり、定礎あり、カレースパイス&エアギターありとジャンル盛りだくさんで、今振り返ってみても「あれは一体なんだったのか?」というカオスイベントで面白かったです。

 

来年10月はイタチグマ祭になるらしいです。謎が深まる。

 

 

まとめ)Tamagotchi Hacking

新作のために16*16pxの勝手に動くものを探していた矢先、トーキョーピクセルで「90年代後半の携帯ミニゲームの詰め放題イベント」があると知り。

偽たまごっち入手・解体をするうちに、本家たまごっちのハッキング情報に辿り着いた。

ハードウェアハッキングやリバースエンジニアリングに興味がある人にとって、プロセスを学ぶのはとても有益と感じたのでまとめておこうと思った。


ことの発端。

ロシアのメディアアーティストdmitry morozov(ディミトリ・モロゾフ) の作品「Umbilical Digital」
これがArduino I2C libraryを利用して動かしている、と知った。

 

検索「tamagotchi hacking」

@natashenkaさん(Natalie Silvanovich)のスライド資料を見つける。(リンク踏むとpptがダウンロードされるので注意)
これはとてもわかりやすいダイジェストだった。
出ている信号から出力文字を探るプロセス、特定したプロトコル、エポキシ樹脂の除去からSoCの特定、ダンプの取得まで一連の作業が掲載されている。

 


さらに@natashenkaさんも参加しているハックコミュニティにたどり着く。
初代たまごっちのデバッグモードのやり方、歴代のたまごっちシリーズの解体写真や特定したSoCのデータシートやメモリマップまで公開されている。

 

ハッキングコード

@natashenkaさんのGitHub。2010年モデル「TamaTown Tama-Go」に実際に自作のキャラを入れて動かせる模様。。

感想

アンドリュー“バニー"ファン著書「ハードウェアハッカー」においてもハードウェア・ハッキングの手法は掲載されている。
しかし、1つのプロダクトを掘り下げて、まとまった情報として公開されているものはそうそうないのでは?
彼ら、彼女らの情熱や探究心の凄さを垣間見て、改めてクレイジーと感じた。(無論、褒め言葉)


残念ながら16*16のちびたまごっちはデバッグできず、自分のスキルでは到底リバースエンジニアリングはできそうもないが、とても勉強になった。

 

尚、我が家の下記の偽たまごっち達は、初代たまごっち同様にデバッグ可能らしいので、欲しい方が入ればお声がけください。 

f:id:bastardeyes:20190430162847j:plain

偽たまごっち

 

M5Stack + MIDIモジュール + MycroPython + ライフゲーム + テノリオン

気がつけば3か月も放置していた。

 

ひとまず、M5Stack + MIDIモジュール + MycroPython + ライフゲーム +テノリオンの報告まとめです。
1月6日から着手して、ようやく完成したのが3月19日。長かった。

完成形。

 

 2-3月ダイジェスト

  • M5Stack MIDI モジュールが到着。
  • MicroPythonでMIDI信号がうまく出せない。

  =>MIDI system exclusiveのサンプルを色々探すが音が鳴らない。。

 

  • Twitterで話題になっていた1000円ロジアナ購入

       =>ドライバの問題なのか、家のMacで動かない。

 

  • 中古でちょっとお高いロジアナ購入。

  =>またエラーで進まず。Mojaveの既知バグらしく対応版のドライバを海外サポートからもらって解決。

 

  • ロジアナでUARTの信号を読む。一瞬データが取れて、途中から何も読めない状態が続く。

  => 8bit送信後にsleepを入れないと信号がひとかたまりになる、MIDI信号と認識されないのが原因。解決。

 

MicroPythonハマったところ

  • ライブラリの定義

     OK) from machine import UART

     NG) from pyb import UART

    => pybは古いバージョンなので、M5Stackでは使えなかった
  • UARTのボーレート

          =>MIDIは31250が正解

  • uart.writeで書き込むデータの形式

          =>リストをbytes()で変換することが必要

  • 8bit送信ごとにスリーブが必要

   =>time.sleep_ms(100)が適正値かは不明。もう少し短くても動くかも。

 

完成コード

 

MIDIモジュール取扱店

日本)necobit公式秋葉原ラジオデパート、スイッチサイエンス

海外)tindie

 

さて、そろそろMaker Faire Tokyo 2019の出展申し込みをしなければ。

 

M5STACKでライフゲームを動かす。

テノリオンを動かすべく、M5Stack用MIDI Moduleを発注したので、年明けに作っていたライフゲームを移植してみた。

 

Pyxelを置き換えるぐらいですぐに終わると思いきや、予想外の展開。

 

MicroPythonはnumpyが使えない。

 

つまりnumpyで書かれた配列は全て書き換えなければならないという。

 

・・・numpy、今まで甘えてばかりでごめんね。反省。

 

数時間後、動いた。

 

  

ボタンぽちぽちで、簡単にリセットできるのは強い。

 

ランチ占いと同じく、再起動後の乱数が決まっているのだけれど、逆に「起動から何回めは全滅パターン」というのが分かっているので、結果オーライということにしておこう。

 

しかしMycroPython触るようになって、ライブラリのありがたみを痛感する。

触らなければ、ライブラリがない場合の実装方法を考えることはずっとなかったと思うので、勉強になった。しみじみ。

 

4/30追記)書籍「作って動かすAlife」のサンプル(MITライセンス)をもとに、MicroPython用にnumpyを除外して作っています。

GitHubのリンクはこちら。