M5stamp picoでプレゼンテーションリモコンを作ったら見事に敗北した件

この記事は「M5Stack Advent Calendar 2021」22日目の記事です。

# はじめに
今年の夏、M5stack社より新たなプロダクト「M5stamp pico」が発売されました。
![801fe712-c908-498c-bc79-08288806e2cc.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/13349509-0cc4-7afd-8b26-fc92fa0b70e2.jpeg)

その特徴はなんといっても**圧倒的な小ささ!!**
寸法がタテ24mm x ヨコ18mm と、名の通り切手サイズなのです。

発表当初から界隈で注目を集めており、省スペースに組み込むことが出来ると話題でした。

私も国内で販売されてすぐ、某電子工作パーツショップにて購入しました。

これを見た私は、

**「これでプレゼンのリモコンを作れないか…？」**

と、考えたわけです。

# プレゼンのリモコンを作ろうと思ったワケ

私は昨年よりM5stack社製品を使っており、主にM5stickCで遊んできました。

![f38b9598-6d09-4f5a-9427-5e16d9bba835.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/37963807-dc6c-f916-2678-2d07ad897add.jpeg)

このM5stickCというのは非常に優秀なデバイスで、ESP32搭載(Wi-FiとBTが使える)、ボタンあり、液晶あり、拡張性あり、と値段の割に素晴らしいスペックなのです。

以前、このデバイスを用いて「Bluetooth プレゼンテーション リモコン」を作製しました。

といっても、すでにM5stickCをBTキーボードとして認識させるためのライブラリが公開されており、それを用いてM5stickCのボタンにキーを割り振るという簡単なプログラムで実現できます。

しかし、M5stickCのボタンは、正面に大きいものが1つ、側面に小さいものが2つ(ただし片方は電源)、とあまり数は多くないのです。

高機能プレゼンリモコンを実現するためにはやはりもう少しボタンが必要であると感じました。

そこで出てきたのがM5stamp picoです!

これを使って、イチからプレゼンリモコンを作ってしまおうと考えたのです。

その時の理想形は以下のような感じです。

- Bluetoothで簡単接続!!
- **片手で軽く握れるコンパクトサイズ!!**
- 4ボタン搭載で簡単操作!!
- 外部電源必要なし! 電池駆動!!
- ディスプレイ搭載!!
- ページ送り・戻し以外にもストップウォッチ機能など多機能に!!

この高すぎる理想が、のちに私を苦しめることとなるのです…

# つくりかた

ひとまず、順を追って作製方法を記していきます。

## パーツ選定

まずは使うパーツを選びます。

### 電源周り

今回、メインとして用いるM5stamp pico、動作電圧は3.3Vもしくは5Vです。

ESP32は非常に有用なチップであるものの、起動時に少しばかり大きな電流が流れます。
すなわち、安定して電力を供給できる電源が必要です。

今回は安定的に電力を供給することを優先するため、昇圧は諦め、降圧して電力を供給することとします。
(私があまり昇圧に関する知識が無いのと、降圧の方が低コストで済むからという理由が大きいです。)

単四電池3本、約4.5Vを3.3Vへ降圧します。その降圧用に、三端子レギュレータ`BA033T`を用います。

![スクリーンショット 2021-12-22 185244.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/420372b3-d9d3-253f-0bc8-959710232ce0.png)


三端子レギュレータを使う際は出力電圧を安定させるためコンデンサを用いる必要があります。
そこで`10μFのセラミックコンデンサ`と`47μFの電解コンデンサ`を用います。

あとは単四電池が3本入る`電池ケース`と基盤接続用に`phコネクタ`、電源管理用`スライドスイッチ`も用います。

### 操作系

理想に従い、ボタンを4つ搭載します。
使うのは何の変哲もない`タクトスイッチ`4つです。

### ディスプレイ

arduinoを用いた電子工作ではもはやおなじみの`SSD1306`とうい制御チップのOLEDディスプレイを用います。
サイズは0.96インチ、128x64ドットのタイプです。
amazonで適当なものを購入しました。

![61iGyiodmDL._AC_SL1500_.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/b08b64b7-40a9-3a87-cbeb-e8f7398922f3.jpeg)

## 基板作成

続いて基板作成です。
今まで私はユニバーサル基板を用いて、いちいち配線していましたが、いい加減しんどくなってきました。
さらに、大学のラボ的なところで基板加工機(CNC)があり、自由に使えることを知った私。
使わない手はありません。

`EAGLE`という回路設計ソフトを用いて、回路を作製します。
出来たのが以下のような回路です。

![スクリーンショット 2021-12-22 190627.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/7217d066-621f-dc14-4ce6-78f8c805ac9e.png)

M5stampをEAGLEで表現するにあたり、@NaohiroIIDA 様の作製されたライブラリをお借りしました。
この場を借りて感謝申し上げます。
([@NaohiroIIDA 様 M5Stamp Picoを触ってみた治具作成＆リフローしてみた](https://qiita.com/NaohiroIIDA/items/4581d1332873160e4edc))

それでこのEAGLEですが、回路設計だけでなくPCB(プリント基板)のデザインもできます。
これで実際の基板もデザインします。

![スクリーンショット 2021-12-22 190229.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/44951df8-c1e2-5d8a-35f5-456c76ec9e3e.png)

あとは基板加工機に任せれば完成です！

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Qiitaの記事用<br>基板加工機の映像 <a href="https://t.co/6K9PQ0TjRh">pic.twitter.com/6K9PQ0TjRh</a></p>&mdash; Koushiro (@Koushiro_A) <a href="https://twitter.com/Koushiro_A/status/1473598007702867969?ref_src=twsrc%5Etfw">December 22, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

てなわけで完成した基板がこちら！

![20211111_130414414_iOS.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/c7bfacf8-15af-dd79-e3f9-f614d490a39e.jpeg)

あ、この二枚は失敗作でした。
左のは無駄なスペースが多すぎてボツ。
右のは間違って両面とも導体の板を選んでしまい、回路がショートしてしまったのでボツ。
まあ、いずれにしても完成形はこんな感じです。

さて、ちゃんと作成できた基板に、上で紹介したパーツをもろもろ載せたのが、下の画像です。

![20211113_014432090_iOS.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/34703ef5-8176-7f24-7e59-5721f4e70f9f.jpeg)

ここまででハード面は完成です！

## ソフト作成・動作確認

### ソースコード

プログラミングはVisual Studio Codeの拡張機能、`PlatformIO`を用いて、フレームワークはarduinoで行います。

ひとまず、ソースコードを載せます。

```c++:main.cpp
#include "M5Atom.h"
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <BleKeyboard.h>
#include "image.h"

BleKeyboard bleKeyboard("M5stamp BLE Key"); //Bluetooth Keyboardの定義

/* OLEDの設定 */

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define BMP_WIDTH    12
#define BMP_HEIGHT   17

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); //OLED displayの定義

/* OLEDの設定(終) */

void setup() {
  M5.begin(true, false, true);
  Serial.begin(115200);
  delay(50);
  M5.dis.drawpix(0, 0x707070);
  Serial.print("Reset:\n");

  pinMode(26,INPUT_PULLUP);
  pinMode(18,INPUT_PULLUP);
  pinMode(19,INPUT_PULLUP);
  pinMode(25,INPUT_PULLUP);

  bleKeyboard.begin();

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  // Clear the buffer
  display.clearDisplay();

  display.setTextSize(1);    //文字サイズを設定
  display.setTextColor(WHITE);    //文字色を設定
  display.setCursor(0, 0);

  display.println("M5stamp pico");
  display.println("Presen BT Con");
  display.display();
  delay(2000);

  display.clearDisplay();
  display.display();
  
}

void loop() {
  display.clearDisplay();

  if(bleKeyboard.isConnected()){
    display.drawBitmap(2, 0, BT_icon, BMP_WIDTH, BMP_HEIGHT, 1);
    display.setTextSize(1);
    display.setCursor(0, 24);
    display.print("<<<<");
    display.setCursor(0, 32);
    display.print("BACK");
    display.setCursor(100, 24);
    display.print(">>>>");
    display.setCursor(100, 32);
    display.print("NEXT");
    display.setTextSize(2);

    if(!digitalRead(26)){
      M5.dis.drawpix(0, 0x00f000);
      display.setCursor(55, 22);
      display.print("UP");
      display.display();
      while(!digitalRead(26));
    }
    else if(!digitalRead(18)){
      bleKeyboard.write(KEY_RIGHT_ARROW);
      M5.dis.drawpix(0, 0x0000f0);
      display.setCursor(37, 22);
      display.print("RIGHT");
      display.display();
      while(!digitalRead(18));
    }
    else if(!digitalRead(19)){
      M5.dis.drawpix(0, 0xf00000);
      display.setCursor(42, 22);
      display.print("DOWN");
      display.display();
      while(!digitalRead(19));
    }
    else if(!digitalRead(25)){
      bleKeyboard.write(KEY_LEFT_ARROW);
      M5.dis.drawpix(0, 0xf0f000);
      display.setCursor(42, 22);
      display.print("LEFT");
      display.display();
      while(!digitalRead(25));
    }
    else{
      M5.dis.drawpix(0, 0x707070);
      display.setCursor(42, 22);
      display.print("NONE");
    }
  }
  else{
    M5.dis.drawpix(0, 0x707070);
    display.clearDisplay();
    display.drawBitmap(57, 0, BT_icon2, BMP_WIDTH, BMP_HEIGHT, 1);
    display.setTextSize(2);
    display.setCursor(46, 16);
    display.print("NOT");
    display.setCursor(10, 32);
    display.print("CONNECTED");
  }

  display.display();
}
```

かいつまんで説明していきます。

### Bluetooth Keyboardの定義

```c++
#include <BleKeyboard.h>

BleKeyboard bleKeyboard("M5stamp BLE Key"); //Bluetooth Keyboardの定義

bleKeyboard.write(KEY_RIGHT_ARROW);
```

`ESP32 BLE Keyboard`というライブラリをインポートし、上記のように定義します。
キーを送る時には3行目のようにします。使い方はarduinoの`Keyboard.write()`と同じです。
([特殊キーはこちらから](https://www.arduino.cc/reference/en/language/functions/usb/keyboard/keyboardmodifiers/))

### OLEDの使い方

```c++
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

/* OLEDの設定 */

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define BMP_WIDTH    12
#define BMP_HEIGHT   17

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); //OLED displayの定義

/* OLEDの設定(終) */

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
}

display.clearDisplay(); //バッファのクリア
display.setCursor(0, 0); //描画位置の設定
display.print("HELLO"); //文字の出力
display.display(); //ディスプレイに表示
display.drawBitmap(2, 0, BT_icon, BMP_WIDTH, BMP_HEIGHT, 1); //画像表示

```

今回は`Adafruit_SSD1306`というライブラリと、それに必要な`Adafruit_GFX`、さらにI2C通信に必要な`Wire.h`をインポートしました。
大体の使い方は上記のコメントアウトに書かれた通りです。

画像表示に関しては、@unasenohkoku 様の記事、[遊舎工房のOLEDモジュール(A01OL)に任意の画像を描画する](https://qiita.com/unasenohkoku/items/e2ac2a20223d84c2b22a)を参考にさせて頂きました。
こちらも、この場を借りて感謝申し上げます。

SSD1306チップの解説はインターネット上に沢山あるので、それらも参考にしてください。

### M5stamp picoのLED点灯

```c++
M5.dis.drawpix(0, 0x707070);
```

これは`M5Atom.h`によって使えるようになる関数ですが、もともとはM5AtomのLED用のものです。
ですが、M5stamp picoはM5Atomと同じ構造をしているため、そのまま流用できます。
二つ目の引数で3原色を16進数2桁で表現しています。

### 動作動画

ソフトを書きこんでみた結果です。
しっかり動作しています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">M5stamp picoを使ったプレゼンリモコン、ソフトの初期バージョン完成<br><br>BTの接続状況とどのボタンが押されたかを画面に表示する<br>右が押されたら次のページ、左が押されたら前のページのキーを送信<br><br>あとは筐体を作らねばならん <a href="https://t.co/YsAUOVnTRz">pic.twitter.com/YsAUOVnTRz</a></p>&mdash; Koushiro (@Koushiro_A) <a href="https://twitter.com/Koushiro_A/status/1461357780149751811?ref_src=twsrc%5Etfw">November 18, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 筐体作成

筐体に関しても、今までは適当なケースを買ってきて加工していましたが、いかんせん私の加工技術が乏しく、上手く出来た試しがありません。
そこで上記でも出てきた大学のラボにある3Dプリンターを用いて、筐体を作製します。

3DCADソフトで有名な`Fusion360`を使います。
先ほど回路設計で用いた`EAGLE`と同じ会社(Autodesk)が作っているので、ファイルをソフト間で連携することが出来ます。
これら連携機能を用いてモデリングしていきます。

以下、モデリング画像です。

![スクリーンショット 2021-12-23 002458.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/0e3c5a10-de56-3faf-65d0-978d91ce00cd.png)

はい、そろそろ敗北原因が見えてくるかと思います…

それはともかく、3Dプリンターで上のモデルを印刷します。

## 完成!

さて、いよいよ完成画像です！
**こちら!!**

![IMG_4714.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/fa09eb46-ee19-0fba-a7c6-e1736b952bb8.jpeg)

はい…
**「クソでかい」**

![IMG_4718.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/a30f3944-84f4-bb58-55a3-ee3bea31a5b6.jpeg)

なんと、テレビのリモコンかってくらい大きくなってしまいました。

しかも蓋の接着方法が無く、養生テープで巻きつける始末…

ちなみに、上で試した通り、プレゼンリモコンとしての機能は果たしています。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">M5stamp picoを用いたプレゼンリモコン<br>完成映像<a href="https://twitter.com/hashtag/M5stack?src=hash&amp;ref_src=twsrc%5Etfw">#M5stack</a> <a href="https://t.co/FG118GSbJ9">pic.twitter.com/FG118GSbJ9</a></p>&mdash; Koushiro (@Koushiro_A) <a href="https://twitter.com/Koushiro_A/status/1473680457762226177?ref_src=twsrc%5Etfw">December 22, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

が、決して実用的ではありません…

中身はこんな感じです…

![IMG_4715.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183170/06951cd4-9008-64af-40cc-6cd57484225e.jpeg)

# 敗因

- 電源として単四電池3本を選んでしまった
- 電源用レギュレータに場所を取った
- 自作基板にまだまだ無駄なスペースがある

などなど、原因は多岐に渡ります…

# 最後に

敗北したとはいえ、いい経験になりました。
今回の目標がなければ、M5stamp picoの使い方、EAGLEと基板加工機の使い方、Fusion360と3Dプリンタの使い方、PlatformIOの導入などはやっていなかったでしょう。
今回は経験を積めただけで良しとしたいと思います。

長文に渡りお読みいただきありがとうございました。

# p.s.

アドベントカレンダー用に急いで書いたため、文章に乱れがあるかもしれません。
ご了承ください…