※ 以下で行っていることは、PLEN:bit の基本的な使い方とは外れていると思いますので、そのあたり理解した上でお読みください。
micro:bit と、PLEN:bitの通信方法が分かったので、同じ感じで外部から信号を入れれば、他の機器からでもコントロール可能なはず!M5Stackからとか!
でも、micro:bitの端子は、ソケットに全部隠れちゃうので、ここに直接ピンをつなぐのは難しそう。
PLEN:bit の前面にのSDA/SCL等のホールにソケット付ければ行けそうではありますが、買ったばっかりなので、本体を改造するのは気が引ける…。
何かいい手は無いかと、いろいろ探してみると、micro:bit 風のユニバーサル基板を発見しました!
Micro:bit風なユニバ基板 [UP52x42BIT]
http://www.aitendo.com/product/17284
これ使えば、PLEN:bit本体に手を入れずに、外部からの信号入力できそうです!
1個120円ですが、こちらのサイトの通販最低価格は1000円なのでお気を付けを。
大きさ同じ!(実際は、微妙に横幅が長い。ほんのちょっとだけ。)
ほんのわずかですが横サイズが長いみたいで、micro:bit よりもキツキツな感じでした。少々グイッと押し込む感じで差し込みました。問題なしです!
通信処理のソースによれば、目玉LEDはデジタル16番ピン、サーボはI2Cのアドレス0x6Aでした。
ということで、M5Stackと接続。M5Stack GPIO2番ピンと、micro:bit 16番ピン、SDA / SCL をそれぞれ、あとGNDの4本を接続。
まずはお試しな感じなので、テストワイヤで接続しました。
M5Stack側のプログラムを Ardino で作成します。
目玉LEDの制御はデジタルのON/OFF。
1 2 3 4 | const int LED_PIN = 2; pinMode(LED_PIN,OUTPUT); digitalWrite( LED_PIN, HIGH ); //消灯 digitalWrite( LED_PIN, LOW ); //点灯 |
これだけ。
後は、I2C経由のサーボコントロールです。サーボコントローラのアドレス0x6Aへの書き込み処理を作成。データは、アドレスと値を連続で書けばOK。
1 2 3 4 5 | void write8( int addr, int val){ Wire.beginTransmission(0x6A); Wire.write(addr); Wire.write(val); } |
元ソースと同じ内容で初期化処理を作成。
1 2 3 4 5 6 7 8 9 | void secretIncantation() { write8(0xFE, 0x85); //PRE_SCALE write8(0xFA, 0x00); //ALL_LED_ON_L write8(0xFB, 0x00); //ALL_LED_ON_H write8(0xFC, 0x66); //ALL_LED_OFF_L write8(0xFD, 0x00); //ALL_LED_OFF_H write8(0x00, 0x01); servoInitialSet(); } |
servoInitialSet は、8個のサーボの初期値を設定。初期値は元ソースを参考に。
1 2 3 4 5 6 | void servoInitialSet() { int servoSetInit[] = {1000, 630, 300, 600, 240, 600, 1000, 720}; for ( int i = 0;i < 8;i++){ servoWrite(i, servoSetInit[i] / 10); } } |
servoWriteは、サーボを指定角度に動かす関数です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void servoWrite( int num, int degrees) { int servoNum = 0x08; bool highByte = false ; int pwmVal = degrees * 100 * 226 / 10000; pwmVal = pwmVal + 0x66; if (pwmVal > 0xFF) { highByte = true ; } write8(servoNum + num * 4, pwmVal); if (highByte) { write8(servoNum + num * 4 + 1, 0x01); } else { write8(servoNum + num * 4 + 1, 0x00); } } |
サーボのアドレスは、0x08 + サーボ番号 * 4。
サーボに与える値は、角度 * 100 * 226 / 10000 で 計算します。0~180 → 0~406( 0x00~0x196) という感じです。
値が計算できたら、下2桁を サーボアドレスに書き込み、上1桁(今回の場合は0か1だけですね)を(サーボアドレス+1)のアドレスに書き込み。
これで、サーボ動かすのに必要な関数は揃いました。
左手の前後動作用のサーボ(サーボ番号0番)を動かしてみます。前方(0度)と元の位置(100度)に回転。
1 2 | servoWrite(0,0); servoWrite(0,100); |
これを、A/Bボタン押したタイミングで動作、ついでにLEDも光るようにしてみました。あと、M5StackのLCDにも状況を表示。
プログラム全体は以下の通り。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #include <M5Stack.h> const int LED_PIN = 2; void setup(){ M5.begin(); Wire.begin(); pinMode(LED_PIN,OUTPUT); digitalWrite( LED_PIN, HIGH ); M5.Lcd.fillScreen(TFT_BLACK); M5.Lcd.setTextColor(TFT_WHITE); M5.Lcd.setTextSize(4); M5.Lcd.setCursor(0, 0); M5.Lcd.println( "PLEN:M5Stack" ); delay( 1000 ); secretIncantation(); } void loop(){ M5.update(); if (M5.BtnA.wasReleased()){ M5.Lcd.fillScreen(TFT_BLACK); M5.Lcd.setTextColor(TFT_WHITE); M5.Lcd.setTextSize(4); M5.Lcd.setCursor(0, 0); M5.Lcd.println( "PLEN:M5Stack" ); M5.Lcd.setCursor(0, 100); M5.Lcd.println( "UP!" ); digitalWrite( LED_PIN, LOW ); servoWrite(0,0); delay(1000); digitalWrite( LED_PIN, HIGH ); } if (M5.BtnB.wasReleased()){ M5.Lcd.fillScreen(TFT_BLACK); M5.Lcd.setTextColor(TFT_WHITE); M5.Lcd.setTextSize(4); M5.Lcd.setCursor(0, 0); M5.Lcd.println( "PLEN:M5Stack" ); M5.Lcd.setCursor(0, 100); M5.Lcd.println( "DOWN!" ); digitalWrite( LED_PIN, LOW ); servoWrite(0,100); delay(1000); digitalWrite( LED_PIN, HIGH ); } if (M5.BtnC.wasReleased()){ servoInitialSet(); } delay(10); } void write8( int addr, int val){ Wire.beginTransmission(0x6A); Wire.write(addr); Wire.write(val); //0: 成功 //1: 送信バッファ溢れ //2: アドレス送信時にNACKを受信 //3: データ送信時にNACKを受信 //4: その他エラー Serial.println(Wire.endTransmission()); } void secretIncantation() { Serial.println( "secretIncantation" ); write8(0xFE, 0x85); //PRE_SCALE write8(0xFA, 0x00); //ALL_LED_ON_L write8(0xFB, 0x00); //ALL_LED_ON_H write8(0xFC, 0x66); //ALL_LED_OFF_L write8(0xFD, 0x00); //ALL_LED_OFF_H write8(0x00, 0x01); servoInitialSet(); } void servoWrite( int num, int degrees) { Serial. printf ( "ServoWrite:%d,%d\n" ,num,degrees); int servoNum = 0x08; bool highByte = false ; int pwmVal = degrees * 100 * 226 / 10000; pwmVal = pwmVal + 0x66; if (pwmVal > 0xFF) { highByte = true ; } write8(servoNum + num * 4, pwmVal); if (highByte) { write8(servoNum + num * 4 + 1, 0x01); } else { write8(servoNum + num * 4 + 1, 0x00); } } void servoInitialSet() { int servoSetInit[] = {1000, 630, 300, 600, 240, 600, 1000, 720}; for ( int i = 0;i < 8;i++){ servoWrite(i, servoSetInit[i] / 10); } } |
LCD表示あたりが冗長ですが、まあ、お試しなのでこれで…。
I2C接続してPLEN:bit本体のサーボコントローラを制御 & デジタル入力で目玉LEDを制御! pic.twitter.com/vqygE1m8la
— Nochi (@shikarunochi) 2019年5月28日
動いたー。