M5Stackでスマホから操作できるラジコンを作ってみた

# 概要
マイコンを使ってなにか作りたいな〜と思い、ラジコンを作ってみます。
電子工作の入門の１つとしていい題材になるのではないかと思います。

![IMB_bXGkmj.GIF](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84072/b2faf5b2-4350-32c8-38c1-1ff4ab159c75.gif)


# 目標
スマホから操作できるラジコンを作ること。

# 開発環境
* OS: macOS Mojave
* IDE: Visual Studio Code + PlatformIO(拡張機能)
* その他: スマホからの操作にはBlynkを使用

# 部品の購入
### 今回使用したもの
* [モバイルバッテリー](https://www.amazon.co.jp/cheero-3200mAh-IoT%E6%A9%9F%E5%99%A8%E5%AF%BE%E5%BF%9C-%E3%83%A2%E3%83%90%E3%82%A4%E3%83%AB%E3%83%90%E3%83%83%E3%83%86%E3%83%AA%E3%83%BC-CHE-061/dp/B018KD0D82)
* ロボットカーキット　[2WD Mini Robot Mobile Platform Kit](https://akizukidenshi.com/catalog/g/gK-13650/)
* [M5Stack GRAY](https://www.amazon.co.jp/M5Stack-ESP32-Mpu9250-9Axies%E3%83%A2%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%BB%E3%83%B3%E3%82%B5%E3%83%BC%E3%82%B3%E3%82%A2%E9%96%8B%E7%99%BA%E3%82%AD%E3%83%83%E3%83%88Arduino%E7%94%A8%E3%81%AE%E6%8B%A1%E5%BC%B5%E5%8F%AF%E8%83%BD%E3%81%AAIoT%E9%96%8B%E7%99%BA%E3%83%9C%E3%83%BC%E3%83%89/dp/B0821YZV59/ref=sr_1_2?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=m5stack+gray&qid=1607145448&s=electronics&sr=1-2)

合計で１万円くらいになりますね...安くはないけど高くもないです。。
まぁM5Stackは色んな所に使えるので実質０円ですね！

### モバイルバッテリー
5Vが出力されればなんでもいいと思いますが、今回使用したのは自動パワーOFFモードがないもの。
最近のモバイルバッテリーは電流を一定以上引いていないときは自動的にパワーOFFしてくれる機能がついているものが多いですが、マイコン系にいたっては消費電流が少なく意図せずOFFしてしまう事があるため注意です。

### ロボットカーキット
ハードを作るのは色々と大変なので、キットを選択しました。モーター、タイヤ、シャシー、コントローラがついており、モーターは連続回転サーボなので制御も簡単なのではないかと思います。
自作する場合はそれぞれの部品を個別に購入する必要があり、シャシーに至っては3Dプリンタで出力するなりホームセンターで部材を買って加工する必要があるので最初はキットを選んでおくと楽です。

### M5Stack GRAY
マイコンは今流行のM5Stackを選択。少し値はしますがIMUやLCD等が搭載されているため別途購入が不要ですぐ使えるのでいろんなところに転用できます。決して無駄な出費ではない！はず

# 組み立て
説明書を見ながらキットを組み立て、モバイルバッテリーとM5Stackをいい感じに固定します。
いいものが周りになかったのでジャンパーワイヤーで固定しています(笑)

信号線は**右側を２ピン、左側を5ピン**に接続しました。**電源線は5VとGND**に接続しています。

![IMG_7227.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84072/904096fa-fc98-492f-ca34-f4de18a9b98e.jpeg)


# 動かしてみる

## パラメータ計算
ロボットカーキットのデータシートに２つの大事なことが書かれていました。
1. Highレベルのパルス幅の時間が`1000~1480μsならCCW回転`、`1480~1520μsならCW回転`、`1520~2000μsならCW回転`
2. パルス周期は20msは確保すること

これにより、パルス周期が20msとなるためにPWM周波数を`50Hz`に設定。それぞれの回転方向のデューティー比を算出し、コードを作成しました。

![IMG_3731.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84072/c1e366a2-eebb-69ae-4d05-6d25ed6f6550.jpeg)


## プログラミング
**モーターの回転方向が違う場合は配線を反転させるかソースコードのcw, ccwを逆にしてあげてください**
M5Stackのボタンを押すことでサーボが回転すれば成功です！

```cpp:main.cpp
#include <Arduino.h>
#include <M5Stack.h>

//PIN設定
const int PIN_MOTOR_RH = 2;
const int PIN_MOTOR_LH = 5;

const int PIN_BUTTON_A = 39;
const int PIN_BUTTON_B = 38;
const int PIN_BUTTON_C = 37;

//PWMの設定
const double PWM_FREQ = 50;  // PWMの周波数
const uint8_t PWM_BITS = 16; // PWMの分解能

// プロトタイプ宣言
void goStraight();
void turnRight();
void turnLeft();
void stop();

// パラメータ
int param_cw = 5243;   // 正回転 (パルス幅1600usecねらい)
int param_ccw = 4587;  // 逆回転 (パルス幅1400usecねらい)
int param_stop = 4915; // 停止 (パルス幅1500usecねらい)

// variables
String myStatus = "";

void setup()
{

  // LCD設定
  M5.begin();
  M5.Lcd.setBrightness(100);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setTextColor(WHITE, BLACK);

  // ピン設定
  pinMode(PIN_MOTOR_RH, OUTPUT);
  pinMode(PIN_MOTOR_LH, OUTPUT);

  pinMode(PIN_BUTTON_A, INPUT);
  pinMode(PIN_BUTTON_B, INPUT);
  pinMode(PIN_BUTTON_C, INPUT);

  //PWMチャンネル設定
  ledcSetup((uint8_t)1, PWM_FREQ, PWM_BITS);
  ledcSetup((uint8_t)2, PWM_FREQ, PWM_BITS);

  //モータのピンとチャンネルの設定
  ledcAttachPin(PIN_MOTOR_RH, 1);
  ledcAttachPin(PIN_MOTOR_LH, 2);
}

void loop()
{
  // ボタンの入力を取得
  bool buttonA = !digitalRead(PIN_BUTTON_A);
  bool buttonB = !digitalRead(PIN_BUTTON_B);
  bool buttonC = !digitalRead(PIN_BUTTON_C);

  if (buttonA)
  {
    if (myStatus != "right")
    {
      turnRight();
      myStatus = "right";
    }
  }
  else if (buttonB)
  {
    if (myStatus != "straight")
    {
      goStraight();
      myStatus = "straight";
    }
  }
  else if (buttonC)
  {
    if (myStatus != "left")
    {
      turnLeft();
      myStatus = "left";
    }
  }
  else
  {
    if (myStatus != "stop")
    {
      stop();
      myStatus = "stop";
    }
  }
}

void goStraight()
{
  M5.Lcd.clear();
  M5.Lcd.setCursor(0, 20);
  M5.Lcd.printf("Go straight\n");
  ledcWrite(1, param_cw);
  ledcWrite(2, param_ccw);
}

void turnRight()
{
  M5.Lcd.clear();
  M5.Lcd.setCursor(0, 20);
  M5.Lcd.printf("Turn right\n");
  ledcWrite(1, param_stop);
  ledcWrite(2, param_ccw);
}

void turnLeft()
{
  M5.Lcd.clear();
  M5.Lcd.setCursor(0, 20);
  M5.Lcd.printf("Turn left\n");
  ledcWrite(1, param_cw);
  ledcWrite(2, param_stop);
}

void stop()
{
  M5.Lcd.clear();
  M5.Lcd.setCursor(0, 20);
  M5.Lcd.printf("Stop\n");
  ledcWrite(1, param_stop);
  ledcWrite(2, param_stop);
}

```

# スマホから操作できるようにする

## どうやってスマホから操作するか
スマホとM5StackをBLE接続してデータを送信し、操作できるようにしたいですが、UIやBLE通信を実装するのは知識がないと難しそうなので簡単にできる方法の１つである**Blynk**を使おうと思います。


## UIの作成
BlynkでUIを作成していきます、こちらの手順は[「BlynkとM5Stackをつなぐ」](https://qiita.com/Teppei_s/items/726b4ddd919ad5d6d74c)を参考にします。

![IMG_7231.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84072/a005ee01-2b0b-2628-3f47-493c800cb803.png)


## マイコン側のプログラミング
Blynkのライブラリを導入し、ボタンの情報を受取って動作するようなプログラムに変更していきます


```cpp:main.cpp
#include <Arduino.h>
#include <M5Stack.h>
#include <BlynkSimpleEsp32_BLE.h>

char blynkAuth[] = "auth keyを入力";

//PIN設定
const int PIN_MOTOR_RH = 2;
const int PIN_MOTOR_LH = 5;

const int PIN_BUTTON_A = 39;
const int PIN_BUTTON_B = 38;
const int PIN_BUTTON_C = 37;

//PWMの設定
const double PWM_FREQ = 50;  // PWMの周波数
const uint8_t PWM_BITS = 16; // PWMの分解能

// プロトタイプ宣言
void goStraight();
void turnRight();
void turnLeft();
void stop();

// パラメータ
int param_cw = 5250;   // 正回転 (パルス幅1600usecねらい)
int param_ccw = 4580;  // 逆回転 (パルス幅1400usecねらい)
int param_stop = 4915; // 停止 (パルス幅1500usecねらい)

// variables
String myStatus = "";

// Blynkから受信するデータの変数
int v1 = 0;
int v2 = 0;
int v3 = 0;

// blynk functions
BLYNK_WRITE(V1)
{
  v1 = param.asInt();
}

BLYNK_WRITE(V2)
{
  v2 = param.asInt();
}

BLYNK_WRITE(V3)
{
  v3 = param.asInt();
}

void setup()
{

  // LCD設定
  M5.begin();
  M5.Lcd.setBrightness(100);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setTextColor(WHITE, BLACK);

  // ピン設定
  pinMode(PIN_MOTOR_RH, OUTPUT);
  pinMode(PIN_MOTOR_LH, OUTPUT);

  pinMode(PIN_BUTTON_A, INPUT);
  pinMode(PIN_BUTTON_B, INPUT);
  pinMode(PIN_BUTTON_C, INPUT);

  //PWMチャンネル設定
  ledcSetup((uint8_t)1, PWM_FREQ, PWM_BITS);
  ledcSetup((uint8_t)2, PWM_FREQ, PWM_BITS);

  //モータのピンとチャンネルの設定
  ledcAttachPin(PIN_MOTOR_RH, 1);
  ledcAttachPin(PIN_MOTOR_LH, 2);

  // Blynk設定
  Blynk.setDeviceName("M5Stack_radicon");
  Blynk.begin(blynkAuth);
}

void loop()
{
  // run Blynk proccess
  Blynk.run();

  if (v2)
  {
    if (myStatus != "right")
    {
      turnRight();
      myStatus = "right";
    }
  }
  else if (v1)
  {
    if (myStatus != "straight")
    {
      goStraight();
      myStatus = "straight";
    }
  }
  else if (v3)
  {
    if (myStatus != "left")
    {
      turnLeft();
      myStatus = "left";
    }
  }
  else
  {
    if (myStatus != "stop")
    {
      stop();
      myStatus = "stop";
    }
  }
}

void goStraight()
{
  M5.Lcd.clear();
  M5.Lcd.setCursor(0, 20);
  M5.Lcd.printf("Go straight\n");
  ledcWrite(1, param_cw);
  ledcWrite(2, param_ccw);
}

void turnRight()
{
  M5.Lcd.clear();
  M5.Lcd.setCursor(0, 20);
  M5.Lcd.printf("Turn right\n");
  ledcWrite(1, param_stop);
  ledcWrite(2, param_ccw);
}

void turnLeft()
{
  M5.Lcd.clear();
  M5.Lcd.setCursor(0, 20);
  M5.Lcd.printf("Turn left\n");
  ledcWrite(1, param_cw);
  ledcWrite(2, param_stop);
}

void stop()
{
  M5.Lcd.clear();
  M5.Lcd.setCursor(0, 20);
  M5.Lcd.printf("Stop\n");
  ledcWrite(1, param_stop);
  ledcWrite(2, param_stop);
}

```


## 動作確認

M5Stackへソースコードを書き込みます。
Blynkアプリから作成したUIを開き、BLEウィジェットをタップしてM5Stakとコネクトします。
RUNボタンでBlynkを動かし、ボタンを押したりすることでラジコンが動けば成功です！
遠隔コントロール部分がこんなに簡単に実現できるのは本当にありがたいですね！

![IMB_bXGkmj.GIF](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84072/b2faf5b2-4350-32c8-38c1-1ff4ab159c75.gif)