※ 以下で行っていることは、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。
const int LED_PIN = 2; pinMode(LED_PIN,OUTPUT); digitalWrite( LED_PIN, HIGH ); //消灯 digitalWrite( LED_PIN, LOW ); //点灯
これだけ。
後は、I2C経由のサーボコントロールです。サーボコントローラのアドレス0x6Aへの書き込み処理を作成。データは、アドレスと値を連続で書けばOK。
void write8(int addr, int val){ Wire.beginTransmission(0x6A); Wire.write(addr); Wire.write(val); }
元ソースと同じ内容で初期化処理を作成。
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個のサーボの初期値を設定。初期値は元ソースを参考に。
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は、サーボを指定角度に動かす関数です。
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度)に回転。
servoWrite(0,0); servoWrite(0,100);
これを、A/Bボタン押したタイミングで動作、ついでにLEDも光るようにしてみました。あと、M5StackのLCDにも状況を表示。
プログラム全体は以下の通り。
#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日
動いたー。