PLEN:bit:M5Stackと接続

※ 以下で行っていることは、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表示あたりが冗長ですが、まあ、お試しなのでこれで…。

動いたー。