ぼくのかんがえたさいきょうのレーザーポインター

#というわけで
私の考えたさいきょうのレーザーポインターの作り方をできる限り丁寧に説明します。
※さいきょうの定義は各個人によりますので予めご了承ください。
![laser.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145838/85395fb6-1a36-b8a3-4ff8-a9e126e94a3b.jpeg)

今回、さいきょうの定義は以下の三要素とします。

__その1. 高機能__
欲しい機能はこんな具合。

- スライドを送る／戻す
- スライドショー発表中に黒背景にする
- プレゼンタイマー

__その２. 拡張性がある__
機能を後から追加、変更できる。

__その３. 安い__
上の機能が入ったレーザーポインタを市販品より安く作れる。
今回比較対象としたのは、[ロジクール R800](https://www.amazon.co.jp/dp/B07RMQ2B69/) (2020/9/1時点で8,500円) です。

##対象読者

* Arduinoでの初歩的なスケッチの文法が分かる。例）`setup()`と`loop()`の使い分けができる。
* Arduino IDEを用いてマイコンに書き込みができる。
* ビルドでエラーが起きても諦めない心を持っている。


#用意する部品
![parts.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145838/2b0fdefa-954d-84f7-0e80-a89a7ab5b0db.jpeg)
M5StickC以外は秋月電子通商でほぼ揃います。M5StickCはマルツで購入できます。
どちらの店舗もオンライン通販に対応しているので、コロナ禍でも大丈夫です。

試作用の部品を除くと、__総額2,718円__で調達できます。

| 品名 | 価格 | 備考 |
|:-:|:-:|:-:|
| [40ピン基板用ピンヘッダー 40ピン×1列](https://www.marutsu.co.jp/pc/i/10041/)  | 88円 | 8ピン以上だったら大丈夫です。 |
| [タクトスイッチ](https://akizukidenshi.com/catalog/g/gP-03646/) | 10円 |   |
| [レーザーモジュール](https://akizukidenshi.com/catalog/g/gM-00765/) | 500円 |  |
| [単4形アルカリ乾電池](https://akizukidenshi.com/catalog/g/gB-03807/) | 80円 |  |
| [電池ボックス 単4×3本](https://akizukidenshi.com/catalog/g/gP-03195/) | 60円 |  |
| [M5StickC](https://www.marutsu.co.jp/pc/i/1526331/) | 1,980円 |  |

以下は試作用の部品なので、必須ではありません。

| 品名 | 価格 | 備考 |
|:-:|:-:|:-:|
| [ジャンパワイヤー(オス-オス)](https://akizukidenshi.com/catalog/g/gC-05371/) | 180円 | 抵抗の足で代用可能。 |
| [ブレッドボード](https://akizukidenshi.com/catalog/g/gP-05294/) | 200円 |   |

#電子回路
レーザーモジュールとM5StickCに乾電池を繋ぎます。
![step1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145838/e2b4333c-12f6-06d9-cd8b-75f755986b18.jpeg)


##注意事項
秋月電子通商のよくある質問を見ると、`レーザーモジュールは3Ｖ以上の電圧や電源逆接続すると壊れます。`と書いてありますが、[^1]
上の回路では乾電池3本の直列電圧(1.5V×3本=4.5V) を印加しています。
私の環境ではレーザーモジュールは壊れませんでしたが、気になる方は分圧なりしてください。

M5StickCの3.3Vから出すのもありですが、仕様上、電源OFF状態では3.3Vは出力されないようです。[^2]


#M5StickCのプログラム
Githubはこちら→[only-my-laser](https://github.com/jp7eph/only-my-laser)
とりあえず使ってみたい方はGithubから落として、Arduino IDE等で焼いてみて下さい。
何も変更しなくてもそのまま使えます。（多分）

完成形はこちら
![m5stickc_display.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145838/eb817203-048a-b312-5238-3822d66ff30b.jpeg)

では、__要点のみ__解説していきます。

##ざっくりした考え方
__スライド操作__
M5StickCはBLE(Bluetooth Low Energy)が喋れるので、キーボードとして認識させて、PowerPointのショートカットキーに対応させます。

__プレゼンタイマー__
M5StickCに内蔵されているタイマーで時間を計測をします。

##スライド操作
BLEキーボードとして利用するために、`BleKeyboard.h`を利用します。
有り難いことにライブラリを作ってくださっている方がいるので、活用しましょう。ライブラリへの追加方法やSampleは下のGithubに書かれています。
BLEキーボードのライブラリはこちら→[T-vK/ESP32-BLE-Keyboard](https://github.com/T-vK/ESP32-BLE-Keyboard)

__ライブラリのダウンロード/インクルード__
1. GithubからライブラリのZIPをダウンロードします。
2. Arduino IDEのメニューから`スケッチ > ライブラリをインクルード`を選択し、先程落としたZIPファイルを指定します。

__ライブラリの宣言__
スケッチ先頭の宣言部に書いてください。

```
#include <BleKeyboard.h>
BleKeyboard bleKeyboard("only-my-laser");
```

2行目でBLEキーボードの名前を指定しています。PC等からペアリングするときはこの名前で探します。

__PowerPointのショートカット送信__
`loop()`に書いてください。

```
    /* -- スライド操作関連 -- */
    // Bluetooth接続後の動作
    if( bleKeyboard.isConnected() ){
        FlagBleConnected = true;
        // ボタンAが押されたとき、[n]を送信
        if( M5.BtnA.wasReleased() ){
            Serial.println("Send n key");
            bleKeyboard.print("n");
        }
        // ボタンBが押されたとき、[p]を送信
        if( M5.BtnB.wasReleased() ){
            Serial.println("Send p key");
            bleKeyboard.print("p");
        }
        // ボタンBが指定秒数以上押されたとき、[b]を送信
        if( M5.BtnB.wasReleasefor(TIME_BTNB_LONG_PRESS) ){
            Serial.println("Send b key");
            bleKeyboard.print("b");
        }
        bleKeyboard.releaseAll();
    } else {
        FlagBleConnected = false;
    }
```

`M5.BtnA`は正面の「M5」と書かれたボタン、`M5.BtnB`は右側のボタンです。

`wasReleased()`はボタンが押されて、__離されたとき__に`True`が返ってきます。
`wasReleasefor()`は__何ms以上押して離したか__で`True`が返ってきます。

##プレゼンタイマー
__タイマーの宣言__
スケッチ先頭の宣言部に書いてください。

```
hw_timer_t * presenTimer = NULL;
```

__タイマーの作成と初期化__
`setup()`に書いてください。

```
presenTimer = timerBegin(0, 80, true);
// presenTimer 初期停止と初期化
timerStop(presenTimer);
timerWrite(presenTimer, 0);
```
`timerBegin(0, 80, true)`は
第1引数でタイマーのIDを指定し (`0〜3`の4つのタイマーが内蔵されています)、
第2引数で何クロックでカウントアップするのか指定し(通常は`80`MHz)、
第3引数でカウントアップを意味しています (`false`だとカウントダウンします)。
第2引数に関する解説はこちらに詳しく載っています。
[ESP32の高精度タイマー割り込みを調べる](https://lang-ship.com/blog/work/esp32-timer/)

__タイマー操作__
`loop()`に書いてください。

```
    /* -- タイマー関連 -- */
    // 電源ボタンの押下状態を取得する。
    // HACK: GetBtnPress()は0以外の値は一度しか値を取得できないから、ここで取得する。
    //       格納しないで下のif文で呼ぶと、ボタンを長押ししても[1]ではなく、先に短い場合の[2]を返してしまうため、長押しの分岐に入らない。
    int axpBtn = M5.Axp.GetBtnPress();
    // 電源ボタンが短時間押されたときは、タイマーのStart/Stop
    if( axpBtn == 2 ){
        if ( timerStarted(presenTimer) ){
            timerStop(presenTimer);
        } else {
            timerStart(presenTimer);
        }
    }
    // 電源ボタンが長時間(1.5s)押されたら、タイマーリセット
    if ( axpBtn == 1 ){
        timerStop(presenTimer);
        timerWrite(presenTimer, 0);
    }
```

今回、本体の電源ボタンをタイマー操作ボタンと兼用しています。
`BtnA`などの汎用ボタンと違い、押された秒数によって`M5.Axp.GetBtnPress()`の返り値が変化します。

#完成！
__というわけで完成！！:clap:__
![laser2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145838/e6ecb987-1a38-0385-6b0a-2390a28902f0.jpeg)

電源電圧や接続状態のモニターをしていますが、そちらはGithubのソースコードを読んでみてください。

##それで、結局「さいきょう」になったの？

__高機能__ :o:
スライドを送る/戻す、黒背景にする、プレゼンタイマーは実装できました。

__拡張性__ :o:
M5StickCは後でいくらでも書き換えできるので、機能変更にも対応できます。
今回はBluetoothぐらいしか使っていませんが、他にもいろんなセンサやモジュールが乗っているので夢は無限大ですね！

__低価格__ :o:
今回、外装は3Dプリンターで製作しました。
プリンターのフィラメント代を含めても安く作れたと思います。

強いて弱点を言うなら、「でかい」ぐらいでしょうか。
そこは外装設計を見直してバージョンアップしていきたいと思います。

__「さいきょう」のレーザーポインターを作ることが出来ました。めでたしめでたし。:clap:__

#(余談)本当に格安で作るなら
今回はプレゼン操作やタイマーのためにM5StickCを導入しましたが、
「レーザーポインターさえあればいい！余計な機能は不要だ！！」という方は、とっても低価格で作ることが出来ます。

作り方は簡単。
__レーザーモジュールと乾電池×2本を繋ぐ。__
以上。

外装は[蓋付きの単4形乾電池ケース](https://akizukidenshi.com/catalog/g/gP-03196/)(120円)にレーザ光の穴を開けるだけ。
単4形乾電池の直径(10.5mm)とレーザーモジュールの直径(最大で10mm程度)がほぼ同じなので、すっぽり収まるはずです。
そうすればなんと、__外装も含めて700円で作ることが出来ます__。

[^1]: [赤色レーザー発光モジュール（円筒型） LM-101-A2の質問と回答, 秋月電子通商](https://akizukidenshi.com/catalog/faq/goodsfaq.aspx?goods=M-00765)
[^2]: [AXP192の調査, M5StickC非公式日本語リファレンス](https://lang-ship.com/reference/unofficial/M5StickC/Tips/AXP192/#dcdc1esp32-3v3)