West Gate Laboratory

人生を少しでも,面白く便利にするモノづくり

ピアノ演奏可視化装置”Bright Note”のNeoPixel制御について

概要

無事Maker Faire Tokyo 2020に出展が決まったピアノ演奏可視化装置”Bright Note”。前回の記事ではそのMIDI信号入力について述べた。

westgate-lab.hatenablog.com

今回は、演奏の表示部分であるLEDディスプレイ部分におけるNeoPixelの使い方について解説する。
使っているマイコンはESP32、LEDはNeoPixelだ。

Youtubeチャンネル作りました

このBright Noteを使った演奏の様子を記録に残すためにYoutubeチャンネルを作った。
動画を投稿し始めたので、興味ある人は見てみてほしい。
初投稿はショパンエチュード、「別れの曲」だ。

www.youtube.com

今後、趣味のクラシックピアノの練習の記録も兼ねてちょこちょこアップロードしていこうと思う。

NeoPixel

NeoPixelはICが内蔵されたフルカラーLEDで、数珠つなぎにして所定の信号を流すことでひとつひとつ任意の色・明るさで光らせることができるLEDだ。しかも、信号線は1本。電源±と合わせてたったの3本で接続は完了する。

www.adafruit.com

NeoPixelはAmazonなどで購入可能だ。LED間隔や長さなど色々な種類がある。
Bright Noteで使ったのは以下の種類のもの。

ICが内蔵されているのが肝で、通常なら複数のLEDをマイコンから独立に制御しようと思ったら、普通IOをそれぞれにつないだりダイナミック制御をしなければいけなかったりするところを、ICがよしなにLEDを制御してくれる。
以下は、私が使っているWB2812Bを前提に述べる。が、NeoPixelならだいたい同じだろう。

NeoPixelに対し信号を送るときは、以下のように各LEDの色情報をシリアルで流す。信号を受け取ったNeoPixel内のICは信号の先頭の色情報だけを読み取り、自身のLEDを制御する。そして、後続のNeoPixelへは受け取った先頭の色情報を除いたデータ列を流す。これを順々に繰り返す。こうすることで全てのLEDを独立して制御できる。なお、WB2812Bでは色情報はGRBの順だ。

f:id:kaname_m:20200726224619p:plain
NeoPixel制御信号(LED0到達前)

f:id:kaname_m:20200726224659p:plain
NeoPixel制御信号(LED1到達前)

f:id:kaname_m:20200726224735p:plain
NeoPixel制御信号(LED2到達前)

なので、原理的には無数のNeoPixelをつないでも1マイコン・3線で制御できるということだ。(もちろん電源や処理時間の問題はあるが)

信号を更に詳しく見ていくと、NeoPixel上では0、1が以下のようにパルスのデューティで表され、GRBはそれぞれ8bit、全部で24bitで表現される。

f:id:kaname_m:20200726224827p:plain
WB2812Bのbit表現(データシートより)

1bitは1.25usのため、1LED=24bitで30usとなる。
上述のようにシリアルに信号を流すため、LEDの数が増えれば増えるほど制御周期は遅くなる。
30fpsで動かそうと思ったら、単純に計算すると(1/30[s])/30[us/LED] ≒ 1111[LED]となる。1信号線で1000個以上のLEDを30fpsで制御できるってすごいね。

Bright Noteでは、88鍵全てに対しそれぞれ15個のLEDを割り当てている。つまり合計1320個だ。上の計算をすると、約25fpsで制御できる。
テレビアニメが24fpsということなので、それに匹敵する周期で制御していることになる。

以下がBright Noteの内部の図だ。15×88個のLEDを、数珠つなぎで接続している。
信号線をできるだけ短くするため、信号方向は隣同士で上下入れ替えている。
また、NeoPixelのストリングの裏には両面テープが付いているため、ベースに使っているアクリル板に簡単に貼り付けることが可能だ。(鍵盤の寸法にピタリ合わせて貼り付けるのがめっちゃ大変だった)

f:id:kaname_m:20200726230041p:plain
内部のNeoPixelの接続。互い違いに信号線を接続していく

Adafruit NeoPixel vs FastLED

Bright NoteではESP32を使っているため、Arduino環境を使うことができる。
Arduino環境におけるNeoPixelのライブラリには主にAdafruit NeoPixelFastLEDの2種類がある。

少ないNeoPixelであればどちらでも構わないが、今回のように大量のNeoPixelをつないで使う場合は、FastLEDをオススメする。
Adafruit NeoPixelは、大量のLEDを使うとところどころ意図しない点灯をする。一方FastLEDは1320個をつないでも正しく点灯する。恐らくFastLEDは制御信号のタイミングをハードウェアを使って正しく生成しているのだろう。

FastLEDの使い方については、同GitHubのWikiが詳しい。

まずNeoPixelを光らせて見るなら、FastLEDをインストールしたら入ってくるExampleの”FirstLight”を試してみるといいだろう。それを見れば最低限の使い方がわかる。(LEDの数、データピンの指定、ICの指定をしたら色情報配列に任意の色情報を突っ込んでFastLED.show()するだけ。とっても簡単。)

簡易バスバーによる電圧降下対策

NeoPixelを大量に使う上で注意する必要があるのが、電源電圧降下だ。
1000個ものLEDを単純につないで点灯させると、電源から遠くなるほど電源電圧が落ち、意図した色が出ない。

NeoPixelのLEDの特性は以下の通りだが、緑・青LEDは赤LEDと比較して電圧降下が大きいため、電源電圧が落ちてくるとLEDが赤みがかってくる。

f:id:kaname_m:20200726221521p:plain
WB2812Bの特性(データシートより)

これを防ぐため、電源はストリングの途中途中から適切に供給してやる必要がある。感覚的にはざっくり100LEDごとに電源を供給すれば良さそう。

Bright Noteでは、上端と下端に銅テープでバスバーを作り、そこから各ストリングに電源を供給している。信号線を邪魔しないように、電源とGNDは互い違いにストリングに接続されている。

f:id:kaname_m:20200726225314j:plain

HSVによる色の割り当て

Bright Noteでは、動画映えするように、鍵盤の場所に応じて色相を割り当てている。
FastLEDではHSVによる色の指定が簡単にできるため、それを使っている。

具体的には、FastLEDではHSVを使う場合色相(Hue)を0-255で指定するため、鍵盤のMIDIノート番号(21-108)を0-255にマッピングして指定している。

各ストリングに色相を割り当てることで、こんなゲーミングピアノに仕立てることができる。

まとめ

長くなって息切れ間が否めないが、ピアノ演奏可視化装置”Bright Note”に使っているNeoPixelについて、使い方や注意点など述べた。

おまけ

Youtubeにアップロードした動画には、間接照明のバックライトが見えるが、それもNeoPixelを使っている。
電源はBright Noteとは全く別系統だ。

f:id:kaname_m:20200726230805p:plain
青いバックライトもNeoPixel

これはLEDコントローラとリモコンが付属していて、リモコンを使って好きな色に光らせることができる。便利。

マイコンで電子ピアノのMIDI信号を読み取る

概要

先日Maker Faire Tokyo 2020(MFT2020)に先日記事にしたピアノ演奏可視化装置で応募したら、この度めでたく出展できることとなった。初出展なのでとても楽しみだ。新型コロナが収束していることを願うばかりである。

せっかくなので、MFT2020までこのピアノ演奏可視化装置の技術的な解説記事を細々と書いていこうと思う。 ピアノ演奏可視化装置の概要は以下の記事参照。

westgate-lab.hatenablog.com

今回は、マイコンで電子ピアノから出力されるMIDI信号を読み取る話だ。
マイコンにはESP32を使っているが、基本的にはどのようなマイコンでもやり方は同じである。

MIDIとは

MIDI(ミディ、Musical Instrument Digital Interface)は、電子楽器の演奏データを機器間で転送・共有するための共通規格である。(Wikipediaより)

古くからある規格で、現行の電子ピアノの大体はMIDI1.0に従った信号を出していると思われる。私の所有するRolandのFP-7もMIDI1.0の信号を出力する。
MIDI1.0の規格書はMIDI企画委員会のウェブサイトからダウンロード可能だ。
(余談だが、今年2月にMIDI2.0が策定された。今後電子ピアノのMIDIはMIDI2.0に準拠してくのかもしれない。なお、MIDI2.0は後方互換性があるとのこと)

MIDI自体は幅広い使い方のできるプロトコルのため、全てを解説することはできないが、電子ピアノの演奏に限って言うと、信号自体は非常に単純である。

ピアノには通常白鍵・黒鍵合わせて88鍵ある。
MIDI1.0では、それぞれに鍵盤に対し21~108までのノート番号が振られている。また、弾いたときの強さとしてベロシティというパラメータがある。

f:id:kaname_m:20200711154534p:plain
ノート番号(MIDI1.0規格書より)

f:id:kaname_m:20200711154635p:plain
ベロシティ(音の強弱、MIDI規格書1.0より)

MIDI出力では、ピアノを弾くと弾いたタイミングで都度「どの鍵盤を弾いたか(ノート番号)」「どれくらいの強さで弾いたか(ベロシティ)」の信号が出力される。 しかも、その信号はUARTで読むことができるため、マイコンで簡単に入力・処理することが可能だ。

なお、演奏情報が「都度」出力されるため、MIDIでは厳密には同時に鍵盤が押されたことを表現できない。だが、人間は厳密に和音を同時に弾くことは難しいし、MIDI信号自体は人間の知覚に対して十分高速なので、和音のズレを意識することはまずない。

MIDIを読み取るハードウェア

MIDI信号は、基本的に31.25kbpsの非同期式シリアル通信だ。マイコンのUARTで簡単に読み取ることができる。
ただし、マイコン側とピアノ側は電気的な絶縁が必要なため、オプトアイソレータ(フォトカプラ)で絶縁する。
以下が、MIDI標準ハードウェア回路図だ。MIDIにはOUT, IN, THRUがあるが、今回のケースではピアノの演奏をマイコンに入力することになるため、MIDI INの部分を見れば良い。

f:id:kaname_m:20200502143703p:plain
MIDI標準ハードウェア回路図(MIDI1.0規格書より)

図の通り、フォトカプラで電気的に絶縁されていることがわかる。回路自体は非常に単純なので、このとおり組めばMIDI信号は読めるのだが、ひとつ気をつける点はフォトカプラの周波数特性だ。MIDI信号の31.25kbpsは一般的なフォトカプラでは応答が追いつかず、正しく信号が読み取れない場合がある。
そのため、高速フォトカプラを使う必要がある。ピアノ演奏可視化装置では、MIDI規格書中に記載のあったTLP513を使った。(規格書が古いため、記載されているフォトカプラはほとんど廃盤になっている。TLP513も在庫限りということだ)

www.sengoku.co.jp

これをESP32のUART2に接続する(RXD2はIO16)。

f:id:kaname_m:20200711150651p:plain
MIDI入力回路(ESP32)

ちなみに、ESP32につながず、直接Teraterm等で信号を読み取ると、例えば以下のようなデータが入力される。これは、ピアノのドミソを順番に押して、全部押したらぱっと離したときのデータだ。

[2020-02-01 12:09:01.899] F8
[2020-02-01 12:09:01.921] F8
[2020-02-01 12:09:01.944] F8
[2020-02-01 12:09:01.964] F8
[2020-02-01 12:09:01.992] F8
[2020-02-01 12:09:01.992] 90   // ノートオン、Ch1
[2020-02-01 12:09:01.992] 3C   // ド(0x3C)を押した
[2020-02-01 12:09:01.992] 32   // 強さ0x32
[2020-02-01 12:09:02.012] F8
[2020-02-01 12:09:02.037] F8
[2020-02-01 12:09:02.060] F8
[2020-02-01 12:09:02.085] F8
[2020-02-01 12:09:02.105] F8
[2020-02-01 12:09:02.129] F8
[2020-02-01 12:09:02.139] FE
[2020-02-01 12:09:02.151] F8
[2020-02-01 12:09:02.173] F8
[2020-02-01 12:09:02.196] F8
[2020-02-01 12:09:02.211] 90    // ノートオン、Ch1
[2020-02-01 12:09:02.211] 40    // ミを押した
[2020-02-01 12:09:02.211] 3B    // 強さ0x3B
[2020-02-01 12:09:02.219] F8
[2020-02-01 12:09:02.242] F8
[2020-02-01 12:09:02.265] F8
[2020-02-01 12:09:02.290] F8
[2020-02-01 12:09:02.314] F8
[2020-02-01 12:09:02.338] F8
[2020-02-01 12:09:02.361] F8
[2020-02-01 12:09:02.381] F8
[2020-02-01 12:09:02.387] FE
[2020-02-01 12:09:02.403] F8
[2020-02-01 12:09:02.426] F8
[2020-02-01 12:09:02.431] 90   // ノートオン、Ch1
[2020-02-01 12:09:02.431] 43   // ソを押した
[2020-02-01 12:09:02.431] 4B // 強さ0x4B
[2020-02-01 12:09:02.449] F8
[2020-02-01 12:09:02.473] F8
[2020-02-01 12:09:02.499] F8
[2020-02-01 12:09:02.526] F8
[2020-02-01 12:09:02.546] F8
[2020-02-01 12:09:02.569] F8
[2020-02-01 12:09:02.591] F8
[2020-02-01 12:09:02.615] F8
[2020-02-01 12:09:02.641] F8
[2020-02-01 12:09:02.641] FE
[2020-02-01 12:09:02.662] F8
[2020-02-01 12:09:02.684] F8
[2020-02-01 12:09:02.702] 80   // ノートオフ、Ch1
[2020-02-01 12:09:02.702] 43 // ソを離した
[2020-02-01 12:09:02.702] 7C   // 強さ(無効値) 
[2020-02-01 12:09:02.709] F8
[2020-02-01 12:09:02.716] 80   // ノートオフ、Ch1
[2020-02-01 12:09:02.716] 3C // ドを離した
[2020-02-01 12:09:02.716] 6D // 強さ(無効値)
[2020-02-01 12:09:02.721] 80   // ノートオフ、Ch1
[2020-02-01 12:09:02.721] 40 // ミを離した
[2020-02-01 12:09:02.721] 6B   // 強さ(無効値)
[2020-02-01 12:09:02.728] F8
[2020-02-01 12:09:02.751] F8
[2020-02-01 12:09:02.774] F8
[2020-02-01 12:09:02.801] F8

このように、演奏時にはノートオンイベント(押した)ノートオフイベント(離した)が発生する。
また、それらのイベントには2つのパラメータ(ノート番号、ベロシティ)が付随することがわかる。 なお、ノートオフ時のベロシティは無効値だ。ノートオフはベロシティ0のノートオンで出力される場合がある。
0xF8は同期用の信号なので、今回は無視する。4分音符あたり24の割当で出力される。
0xFEはハートビート的な役割。定期的に出力され、これが来ないと断線したなどと判断するのに使う。今回は無視。

MIDIを読み取るソフトウェア

上述の通り、MIDI信号は31.25kbpsのUARTとして読み取れる。
今回ESP32を用いているが、UART0は基本的にデバッグポートとして使っているので、MIDIにはUART2を使う。ArduinoのExampleに”MultiSerial”なるものがあるので、それを参考に使う。以下はMultiSerialの簡単な例。これは9600bpsだが、31250bpsに設定すればMIDI信号を入出力できる。

void setup() {
  // initialize both serial ports:
  Serial.begin(9600);
  Serial2.begin(9600);
}

void loop() {
  
  // read from port 0, send to port 0:
  if (Serial.available()) {
    int inByte = Serial.read();
    Serial.write(inByte);
  }
    
  // read from port 2, send to port 0:
  if (Serial2.available()) {
    int inByte = Serial2.read();
    Serial.write(inByte);
  }
}

UART2はESP32ではIO16(RXD)、IO17(TXD)を使う。MIDI OUTはしないので、RXD(IO16)のみ使う。

また、Arduino環境が使えるマイコンであれば、MIDI信号処理にはArduino MIDI Libraryが便利だ。

github.com

今見ると、結構頻繁にアップデートされているようである。私はv4.3.1を使った。インストールする際はzipでダウンロードし、Arduino IDEからライブラリをzip形式でインストールする。
Arduino MIDI Libraryの使い方については以下の記事が非常に詳しい。

qiita.com

ベーシックな使い方としては、MIDI.read()でイベントを検出、ノートオン・ノートオフに対応する処理を行う、といった使い方だ。以下の例ではマルチタスクを使ってMIDI処理タスクを生成している。

MIDIイベントを受信した場合は、MIDI.getData1()でノート番号、MIDI.getData2()でベロシティを取得できる。また、MIDI ChはMIDI.getChannel()だ。Roland FP-7の場合は自分で弾くとCh.1で、プリセット曲をMIDI OUTに出力するとCh.4に割り当てられていたりする。

ピアノ演奏可視化装置では、特にベロシティは使っておらず、MIDI.getData1()で得られるノート番号のみを可視化に用いている。

#include <MIDI.h>

#define TASKPRI_MIDI 0
#define TASKCORE_MIDI 0

MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, MIDI);
TaskHandle_t h_MidiTask;

void prvMidiTask(void *pvParameters) {
    while (1) {
        if (MIDI.read()) {
            switch (MIDI.getType()) {
                case midi::NoteOn:    // 鍵盤を押した
                    disp.noteOnScore(MIDI.getData1(), MIDI.getData2(),
                                     MIDI.getChannel());     // 鍵盤を押した場合の処理
                    break;

                case midi::NoteOff:    // 鍵盤を離した
                    disp.noteOff(MIDI.getData1(), MIDI.getData2(),
                                 MIDI.getChannel());     // 鍵盤を離した場合の処理
                    break;

                case midi::ControlChange:  // ペダルを踏むとControlChangeイベントが発生するが、今回は使わない
                    break;

                default:
                    break;
            }
        } else {
            delay(1);
        }
    }
}

void setup() {
    Serial.begin(115200);
    MIDI.begin(MIDI_CHANNEL_OMNI);
    xTaskCreatePinnedToCore(prvMidiTask, "MidiTask", 4096, NULL, TASKPRI_MIDI,
                            &h_MidiTask, TASKCORE_MIDI);
}

void loop() {
    delay(1);
}

まとめ

マイコンMIDI信号を読み取る方法について述べた。
高速フォトカプラを入手することろだけがやや特殊だが、それ以外は通常のUART通信である。MIDIを読み取りさえしてしまえば、あとはソフトウェアでどうとでも料理できてしまう。

次はピアノ演奏可視化装置+NeoPixelあたりの話でも。

3Dプリンタで我が家専用チューブ調味料スタンドを作成する

概要

家の冷蔵庫のチューブ調味料ゾーンがあまりにも混沌としていたので、3Dプリンタ我が家専用のオーダーメイドチューブ調味料スタンドを作って整理整頓した話。

背景

料理にチューブ調味料は欠かせない。

特に、私はカレーを作るのが趣味でよくカレー粉やスパイスを使って作るのだが、しょうがやにんにくをすりおろすのがだんだん面倒になってきて、最近はチューブ調味料を使っている。
しかし、こうした調味料が増えてくるとその整理整頓が課題になる。
特にチューブ調味料の場合、中身が少なくなってくると逆さにして保管するわけだが、チューブ形状によっては逆さにすると不安定なものも多い。

結果、先日までの我が家のチューブ調味料及びドレッシング等のゾーンはこの状態。(汚くて申し訳ない)

f:id:kaname_m:20200524134249j:plain

生にんにくは倒れ、一つが倒れると他もいいやとばかりに生しょうがが倒れ、なぜか安定しているはずの奥の味ポンまで倒れている。 冷蔵庫の扉部分に収納されるため、扉の開閉の際にも倒れる力が加わり、固定されていないとこうした細長い形状のものは倒れてしまうのだ。

ちなみに、探してみるとチューブ調味料整理のためのホルダーが売っていたりする。

薬味チューブホルダー クリアー

薬味チューブホルダー クリアー

  • メディア: ホーム&キッチン

別にこれを使ったっていいのだが、せっかくなので今回は我が家のレギュラー調味料専用のスタンドを作成してみる。

オーダーメイドチューブ調味料スタンド

特に難しい問題はないのだが、順番としては

  1. チューブのサイズ計測

  2. チューブの配置決定

  3. 3Dモデル設計、印刷

となる。

チューブのサイズ計測

我が家のレギュラーチューブ調味料は、「生しょうが」「生わさび」「生にんにく」「マンゴーチャツネ」この4つである。
マンゴーチャツネはあまり聞かないかもしれないが、カレーに入れるとおいしいのだ。

さて、チューブ調味料だが、メーカーや中身によってサイズは様々である。
そのため、ひとつひとつノギスで計測していく。

チューブスタンドではおもにフタ部分を固定するため、フタの中で最も太い、根元部分を計測する。
例えばマンゴーチャツネのフタは、最も太い部分で22.5mm程度だ。

f:id:kaname_m:20200524135231j:plain

その他も同様に計測すると、結果は以下の通りであった。

生にんにく、マンゴーチャツネ:約22.5mm
生しょうが:約29mm
生わさび:約33mm

チューブの配置決定

サイズが計測できたら、次に配置を決めていく。

冷蔵庫の調味料置き場の面積はそれほど広いわけではいので、なるべくきっちり詰める必要がある。
また、手前に小さい調味料がある方が取りやすいだろう。

隣と調味料が干渉しない間隔を計測する。

f:id:kaname_m:20200524135932j:plain

こうして我が家にとって最適な配置を決定していく。

こうして決まったのがこの配置である。(上が冷蔵庫奥側)

f:id:kaname_m:20200524140442p:plain

手前に小さめの調味料2つ、奥側に大きめの調味料2つ。手前にはもう1つ分スペースがあるので、それは予備とした。

3Dモデルの設計、印刷

さて、ここまでできたら後はCADで設計するのみである。

といっても形自体は非常に単純なので設計はすぐに終わる。
完成図がこちら。

f:id:kaname_m:20200524140619p:plain

奥の方に薄板が延びているのは、そこに重量のあるビン調味料を置くことでスタンド自体の安定性を上げる目的だ。
スタンド自身は樹脂で軽いので、それだけだと倒れてしまう可能性がある。
また、チューブの入れ口を適度に面取りすることで、チューブを入れやすくしている。

これを3Dプリンタで印刷する。サイズが大きいので、4~5時間かかった。
我が家ではFlashForgeのAdventurer3を使っている。

f:id:kaname_m:20200524140940j:plain

できあがったスタンドに実際にチューブ調味料を入れてみる。

f:id:kaname_m:20200524141333j:plain

うん。ピッタリである。

さらに、これを冷蔵庫に入れてみる。

f:id:kaname_m:20200524141041j:plain

スタンド自体も冷蔵庫のサイズに合わせて作ったので、ピッタリである。良い。

まとめ

冷蔵庫のチューブ調味料が混沌とする問題を、専用のスタンドを3Dプリンタで作ることで解決した。
非常に小ネタではあるが、精神衛生上良い影響がある気がする。

このスタンドを作ったのは2週間ほど前だが、この記事を書いた今日現在もチューブは倒れることなく冷蔵庫で立っている。

EagleとFusion360を連携させて回路設計から筐体設計まで一気通貫で行う

概要

回路設計ソフトウェアであるEagleと、3DCADソフトウェアのFusion360を連携させる方法について。
特に、Eagle-Fusion360間の部品の3Dパッケージの連携方法について述べる。

背景

今や、ホビーユースであればEagleも、Fusion360も無料で利用することができる。とてもいい時代だ。(Autodeskありがとう)
特に、EagleがAutodeskに吸収されてから、EagleとFusion360の連携機能が強化されてきた。
今では簡単にEagleで設計した回路を3Dの状態でFusion360にエクスポートし、自分の基板専用の筐体をそのまま設計することができる。
さらに、3Dプリンタが家にあれば、その場で筐体を印刷することも可能だ。

例えば、こんなものができる。
先日記事にしたピアノ演奏可視化装置の回路を例に取ってみる。

westgate-lab.hatenablog.com

Eagleで回路設計をし、

f:id:kaname_m:20200509170524p:plain

それをFusion360に取り込み、

f:id:kaname_m:20200509170533p:plain

f:id:kaname_m:20200509170539p:plain

それに合わせて筐体を設計できてしまう。

f:id:kaname_m:20200509170551p:plain

筐体のモデリングができたら、3Dプリンタで専用ケースも作れてしまう。

f:id:kaname_m:20200606190641j:plain

フタも印刷すれば専用デバイスの出来上がりだ。

f:id:kaname_m:20200606190644j:plain

ただ、3Dの情報を含めきちんと両者を連携させるには、3DパッケージをEagleのライブラリに正しく取り込む必要がある。
この記事では、Eagleに3Dパッケージを取り込み、Fusion360と連携させる方法について述べる。

Eagleで3Dパッケージを扱う場合、既存の3Dパッケージをダウンロードして使う方法と、自分で3DCADを使って3Dパッケージを作成し、それをEagleに取り込む方法がある。

既存の3Dパッケージをダウンロードして使う場合

Eagleにも一部の表面実装コンデンサなど、デフォルトで3Dパッケージが入っているデバイスもあるが、大半は3Dパッケージが存在しない。そのため、自分で用意する必要がある

今回は、ピアノ演奏可視化でも使っているESP32-DevkitCボードを題材にする。

まず最初に3Dパッケージをインターネットからダウンロードするか、自分で作成するかの分岐がある。
ESP32-DevkitCのようなメジャーなモジュールはたいてい誰かが3Dパッケージを作ってくれているので、それを探して使わせてもらうことにする。

3Dパッケージを探す

3Dパッケージの検索には、以下のサイトが便利だ。

www.snapeda.com

grabcad.com

まずは、既存の3Dパッケージが存在しないか、上のようなサイトを使って検索することになる。
ESP32-DevkitCボードについては、例えば「ESP32」と検索してつらつら見ていくと、ESP32-DEVKITC-32Dのパッケージが存在する。

www.snapeda.com

上のページを見ていただくとわかるように、ありがたいことにシンボルとフットプリント(.lbr)・3Dモデル(.step)の全てがダウンロードできる。
SnapEDAはアカウントが必要なので、アカウント登録後、シンボル・フットプリント・3Dモデルをダウンロードする。

f:id:kaname_m:20200509170556p:plain
ESP32-DEVKITC-32Dの2Dデータ(SnapEDAより)

f:id:kaname_m:20200509170559p:plain
ESP32-DEVKITC-32Dの3Dデータ(SnapEDAより)

なお、今回はシンボル・フットプリントもない状態から始めたため、運良く見つかった上のライブラリを使わせてもらったが、3Dモデルはあってもライブラリがない、という場合もある。(GrabCADは3Dモデルのみ)
そうした場合は自分でシンボルやフットプリントをEagleで新規作成する必要がある。
Eagleの詳しい使い方は、以下の書籍が詳しい。
Eagleを使って回路設計する人は1冊持っておくと便利である。

EAGLEによるプリント基板製作の素

EAGLEによるプリント基板製作の素

  • 作者:後閑 哲也
  • 発売日: 2009/11/27
  • メディア: 単行本(ソフトカバー)

シンボル・フットプリントをEagleに読み込む

まずは、Eagleに2Dデータ(シンボル・フットプリント)、つまりダウンロードした.lbrファイルを読み込む

EagleとFusion360を連携させる場合、シンボルや3Dパッケージなどの部品データは全てAutodeskのクラウドLIBRARY.IO)上に保存する必要がある。(ローカルにあるライブラリは連携に使えない)
まずは、EagleのControl Panel上のLibrariesにあるアカウント名のフォルダを右クリックすると、"View on web"とあるので、これを選択し、LIBRARY.IOの自分のアカウントページを開く。

f:id:kaname_m:20200509173846p:plain

LIBRARY.IOのマイページ上に”Import Library”があり、”Upload File”とあるので、そこから先程ダウンロードした.lbrファイルを選択する。

すると、その下の”Library from (アカウント名)”のところにアップロードしたライブラリが表示されるはずだ。

f:id:kaname_m:20200509174033p:plain

次に、LIBRARY.IOにアップロードしたライブラリを、Eagleにダウンロードする。

EagleのControl Panelの左ペインにあるLibrariesを右クリック、Open Library Managerを選択すると以下の画面になるので、AvailableタブでESP32などと検索すると、先程アップロードしたライブラリが現れる。(以下の画像ではローカルに保存した別のESP32のライブラリが出ているが、ここでは無視する)

f:id:kaname_m:20200509170612p:plain

ダウンロードマークがついているライブラリを選択、右下のUseを押すとライブラリがダウンロードされ、Control Panel上でも確認できるようになる。

f:id:kaname_m:20200509170616p:plain

3Dデータ(STEPファイル)をライブラリに取り込む

次に、先程ダウンロードしたSTEPファイルをこのライブラリに関連付ける。
ダウンロードした"ESP32-DECKITC-32.lbr"を開くと、ライブラリのデバイス一覧が開ける。
フットプリントやシンボルは正しく表示されるが、3Dパッケージはまだ取り込んでいないため、ただの四角い板のようなモデルになっている。

f:id:kaname_m:20200509170620p:plain

それでは、ここに先程のSTEPファイルを取り込む。

関連付けたい3Dパッケージ名を右クリック、Editを選ぶと以下のパッケージ編集画面が開く。

f:id:kaname_m:20200509170625p:plain

f:id:kaname_m:20200509170628p:plain

まだ3Dパッケージが反映されていないので、板状のモデルが配置されている。
ここで、上の”Upload”から先程のStepファイルを選ぶ。するとLIBRARY.IOのマイアカウント上にStepファイルが取り込まれ、編集中のライブラリにも自動で取り込まれる。
(すでにStepファイルがアップロード済みの場合は、”Remove 3D Model"で板状モデルを削除し、”Add"からモデルを選択しても良い)

f:id:kaname_m:20200509170633p:plain
UploadしたSTEPファイルが取り込まれた状態

3Dパッケージの位置・向きの調整

さて、向きや位置をここから調整する。

3Dモデルをクリックすると向きや位置を調整できる。
まずは向きを90度変更し、Z軸で高さを調整する。

(2020年6月6日追記)
ピアノ演奏可視化装置で使ったESP-WROOM-32の基板厚さが1.6mmなのに対し、今回使ったモデルは基板厚みが0.9mmだった。
そのためZ軸をぴったり合わせると0.7mmZ方向にずれてしまい、後の筐体設計のときにUSBコネクタ位置がずれてしまう。
そのため、このモデルを使ってESP-WROOM-32を模擬する際は、0.7mmオフセットさせるのが良い。

f:id:kaname_m:20200509170638p:plain

下から見てピン位置がぴったりになるようXY軸を調整。

f:id:kaname_m:20200509170642p:plain

位置調整が完了したら、OKをクリックし、保存する。

f:id:kaname_m:20200509170647p:plain

EagleのLibrary画面に戻るので、Ctrl+Sで保存。すると、「Managed Libraryが更新されたからLibraryメニューのCreate New Versionを選択してね」と出る。
忠告に従いLibrary→Create New Versionを選択。するとここの変更内容がLIBRARY.IOにアップロードされる。
(このとき、3Dパッケージのプレビュー画面が歯車マークのままの場合があるが、Fusion360では正しく反映されるので、そのまま進んで構わない)

実際に基板を設計してみる

さて、ここまでで作成した3Dパッケージ入のESP32-DEVKITCボードを使って回路を設計してみる。
といってもFusion360との連携の試しなので、まずは回路上にただESP32-DEVKITCを置いただけだ。

f:id:kaname_m:20200509170651p:plain
Eagleで基板上に1つだけESP32-DEVKITCを配置する

基板設計が終わったら、Eagleの基板設計エディタの右端に「FUSION 360」というボタンがあるので、それをクリックすると、Fusion360との同期画面がポップアップされる。

f:id:kaname_m:20200509170704p:plain

一番下に"Push to Fusion..."とあるので、これをクリック。どうやら内部ではGitのようなバージョン管理が行われているようだ。
すると既存のモデルにリンクさせるか?と聞かれる。今回は新しいモデルなので、”Create new Fusion 360 design”を選択する。次にPush先のプロジェクトを選択し、Pushする。(これが少し時間がかかる)

f:id:kaname_m:20200509170654p:plain

Pushが終了したら、Fusion360に移る。
Pushしたプロジェクトを見てみると、先程Pushした基板の3Dモデルがあるはずだ。

f:id:kaname_m:20200509172006p:plain

3Dモデルも正しく反映されている。良い良い。

Fusion360へ基板をPushした後、連携が切れてしまった場合

Fusion360やEagleを再起動すると、デザイン同士のリンクが切れてしまうようである。
一度Pushして3DモデルをFusion360に取り込んだのに、Eagle基板設計エディタのFUSION 360をクリックすると以下の画面が出てくる場合がある。

f:id:kaname_m:20200510231351p:plain

このときは、すでにあるデザインに再度リンクすれば良いので、上の"Link to an existing Fusion 360 design"を選択し、リンクしたい既存のデザインを選択する。すると再度Pushできるようになる。

回路の修正を反映する

一発で設計終了すればいいが、実際には回路を何度も修正することになるだろう。
その場合、Fusion360に反映させるにはその度に先程のPushをすれば良い。

例えば、ボードの位置が気に食わなかったので、基板の左側に移動したとする。

f:id:kaname_m:20200509170716p:plain

すると、画面右の”FUSION 360"が緑色になり、要Pushのステータスになるので、先程と同様の手順でPushする。
同期画面では、修正がFusion360の3Dモデルに未反映のため”OUT OF SYNC”となっている。

f:id:kaname_m:20200509170719p:plain

再度Push to Fusionし、Fusion360に戻ると「モデルが更新されました」と出るので”更新”をクリック(これがない場合でも、一度スケッチを閉じて再度開けば更新される)。

f:id:kaname_m:20200509170519p:plain

ボードが基板左側に正しく移動したことが反映された。素晴らしい。

今回は例として1デバイスのみの場合を示したが、同じ作業を抵抗やコンデンサ、その他の部品に対して行うことで頭に示したような回路の3Dモデルが作成できる。

自前で3Dモデルを作成する場合

マイナーな部品の場合はネット上に3Dモデルがないため、自分でモデルを作る必要がある。
シンボルやフットプリントがない場合はそこから自分で作る必要があるが、それはすでにあるものとする。

今回は、秋月電子で売っているこのブザー(PB04-SE12HPR)を例にとってみる。

f:id:kaname_m:20200510231406j:plain
PB04-SE12HPR(秋月電子HPより)

電子ブザー 14mm PB04−SE12HPR: パーツ一般 秋月電子通商-電子部品・ネット通販

流れとしては、以下の通り。

  • データシートに合わせて3DモデルをFusion360で作成する。
  • STEPファイル形式でエクスポートする。

以降は上述したダウンロードして使う場合の、LIBRARY.IOにインポートするとこから同じである。

データシートを入手する

上で述べた3Dモデルのサイトを散々探してどうにも見つからない、となれば、外形の情報を得るため、データシートを入手する。
秋月電子の商品ページから飛べるデータシートの中に、パッケージの情報が含まれている。

f:id:kaname_m:20200510231412p:plain
ブザーのパッケージ図(データシートより)

この情報に従い、Fusion360でモデルを作成する。
どこまで詳細にモデリングするかは好みだが、最低限端子部分の寸法と縦横高さが合っていれば問題ないだろう。今回はボディの色と端子の色はざっくり合わせてみた。

f:id:kaname_m:20200510231415p:plain

f:id:kaname_m:20200510231341p:plain

STEPファイル形式でエクスポートする

さて、モデルができあがったらそれをエクスポートする
Fusion360のデザイン画面からファイル→エクスポートで.stepを選択し任意の場所にエクスポートする。

ここから先は上述の”3Dデータ(STEPファイル)をライブラリに取り込む”以降と同じだ。

f:id:kaname_m:20200510231355p:plain

ライブラリをアップデートする場合

ライブラリそのもの、例えばシンボルやフットプリント、3Dモデルの位置調整などを行った場合はライブラリをアップデートし、それを反映する必要がある。

例えば、先程作成した3Dパッケージの位置を微修正する場合は、まず該当する3Dパッケージが含まれるライブラリをEagleで開き、 3Dパッケージ→Editでパッケージの編集画面に入る。
編集画面で先程と同様に位置・向きを修正し、終わったらOKで保存。
Eagleのライブラリ画面に戻ると3Dパッケージを登録したときと同様に「ライブラリが更新されたから”Create new version”してね」と出るので、Library→Create new versionでライブラリのバージョンを上げる。

ライブラリのアップデートが終わったら、次にこの変更をEagleの設計に反映する必要がある。 Eagleの回路または基板エディタでLibrary→Update allで使用しているライブラリを含めてアップデートする。

f:id:kaname_m:20200510231403p:plain

アップデートが正しく完了すると、またFusion360に反映しなさいということでエディタ右側のFUSION360ボタンが緑色になるので、同様にPushする。

f:id:kaname_m:20200510231400p:plain

Fusion360に戻り、デザインを更新すると、修正されたモデルが反映されているはずだ。(デザインが開かれている場合は、一度閉じて再度開くこと)

まとめ

回路設計ソフトウェアのEagleと3DCADソフトウェアのFusion360を連携させて回路基板の3Dモデルを作成する方法を述べた。
これを使うと回路設計をEagleで行い、Fusion360で取り込んでその基板に合わせた筐体設計が可能になる3Dプリンタを持っていればその場で専用の筐体を印刷することも可能である。

ピアノ演奏を可視化する装置作ってみた(それを使って弾いてみた)

概要

私は電子工作と別にピアノも趣味なのだが、最近は有名どころのクラシックを弾いている。
例えば、ショパンベートーヴェン・リストなどだ。

ただ弾くだけでも楽しいのだが、演奏が音だけでなく視覚でも楽しめると良いかな、と思い、ピアノ演奏を可視化する装置(以下、ピアノディスプレイ)を作ってみた。

こんな感じ。

また、このピアノディスプレイはMaker Faire Kyoto Online 2020にもオンライン出展した。この記事ではその製作過程を述べる。

背景

よくYoutubeでクラシックピアノ演奏動画を見たりするのだが、ある日Rousseau氏の動画を見かけた。

www.youtube.com

Rousseau氏はピアノの演奏とその演奏を可視化した動画を組み合わせて非常にきれいな作品を投稿している。
どうやら、動画の上半分は記録したMIDIをもとに動画を生成し、上下で動画を組み合わせているようである。

めっちゃきれいやん、と思い、早速自分でも作ってみることにした。
ただ、動画で後から組み合わせるのではなく、リアルタイムに可視化するディスプレイとした。

CADでハードウェア設計

Fusion360を使って設計をすすめる。
今回、ピアノの実寸大でディスプレイを開発するため、横幅は1mを超えるサイズになる。

f:id:kaname_m:20200502143222p:plain

鍵盤それぞれに対し、15個のフルカラーLEDを配置した。(後述:NeoPixel)
ピアノの鍵盤は88鍵あるので、全部で1320個のフルカラーLEDを使用している。 また、光らせた時に見栄えが良くなるよう、前面にはスモークアクリル板を設置している。

f:id:kaname_m:20200502143434p:plain

ちなみに、Fusion360レンダリング機能を使うといい感じの画像も作れる。それっぽいそれっぽい。

f:id:kaname_m:20200502143504j:plain

できるだけスタイリッシュな見た目にするため、極力前面にはネジなどの機構部品が見えないようにした。
アクリルの四隅の支持材も同様にFusion360で設計して、3Dプリンタで印刷した。

実際に作っていく

ハードウェア設計が終わったら、実際にものを集めて作っていく。

まずは、鍵盤サイズのアクリル板を用意する。

f:id:kaname_m:20200502143442j:plain

アクリル板の上にNeoPixelのLEDストリングを切って貼っていく。
今回使用したのはWS2812の300個/5mのものだ。

この際、鍵盤の寸法にピッタリ合うよう、コンマ1mm単位で位置を調整する必要がある。

f:id:kaname_m:20200502143449j:plain

LEDストリングを貼り終えたら、全てのストリング1つ1つ配線していく。

このとき注意しなければいけないのは、電源ラインである。
LEDストリングは接続数が増えていくと電流量が結構ハンパないことになるため、単純にストリングを全接続すると末端のLEDはまともに光らなくなる。
例えば、今回使用しているNeoPixel(WS2812B)は、LEDを白色最大光量にすると大体1個50mA消費する。
両手で10鍵押し続けると、15x10=150個のLEDが光り、単純計算で7.5Aの電流が流れることになる。

もちろん、実際には白色には光らせないのでもう少し電流量は少なくなるのだが、単純にLEDストリングをつなげると、末端のLEDに供給されるまでに電圧降下してしまい、以下のツイートのようにまともに光らなくなる。

というわけで、このピアノディスプレイでは銅テープをバスバーとしてディスプレイの上下に配置し、そこからそれぞれのLEDストリングに電源を供給することとした。

f:id:kaname_m:20200502143452j:plain

銅テープは5mm幅のものを用いた。

3M  銅箔テープ No.CU-35C 5mm幅 x 20m

3M 銅箔テープ No.CU-35C 5mm幅 x 20m

  • メディア: Tools & Hardware

ちなみに、LEDストリングの信号ラインをつなげるために合計87本の短い単線をつなげた。これが一番疲れた気がする・・・。

f:id:kaname_m:20200502143500j:plain

ソフトウェア

さて、次にソフトウェアの話。
今回はESP32を使って開発した。

MIDIをESP32で読み取る

ピアノディスプレイでは、電子ピアノのMIDI OUTをESP32で読み取っている。

ArduinoMIDIを解析する方法としては、以下の記事が詳しい。

qiita.com

MIDI OUTは、基本的には31.25kbpsのUART信号なので、マイコンで簡単に読み取ることができる。
ただ、出力信号とマイコンの入力はアイソレートする必要があり、高速フォトカプラが必要だ。

f:id:kaname_m:20200502143703p:plain
MIDI標準ハードウェア(MIDI1.0規格書より)

このあたりのハードウェア仕様はMIDI1.0規格書に詳しく記載されている。
規格書には、MIDIに適合する高速フォトカプラのリストも記載されている。

NeoPixel

みんな大好きNeoPixel。 使い方についてはネット上に多く記事が存在するので、ここでは多くは書かないが、今回はFastLEDライブラリを使用した。

github.com

NeoPixelのライブラリとしては、FastLEDの他にAdafruit_NeoPixelがあるが、Adafruitのライブラリは以下のツイートのようにLEDを多数接続した際に正しく点灯しないため、ピアノディスプレイのように多数のLEDを接続する場合はFastLEDがオススメだ。

マルチタスク

ピアノディスプレイでは1320個のNeoPixelを制御しているが、それだけたくさんのLEDをつなげるとその制御だけで(時間的に)大半のCPUリソースを食ってしまい、他の処理(MIDI入力など)ができなくなる。

そこで、ESP32はデュアルコアなので、1つのCPUをNeoPixelの制御に、もう1つのCPUをMIDI処理などに割り当てMIDIの取りこぼしやLEDの表示ミスなどがないようにする。

ESP32(M5Stack)を使ったマルチタスクの方法については以下の過去記事参照。

westgate-lab.hatenablog.com

最終形態

ディスプレイ部分まで完成したところで、Maker Faire Kyoto Online 2020に出展した。

ピアノの各鍵盤をHSV色空間の色相に割り当てると、こんな感じにゲーミングピアノのようになる。

2020年5月2日のMaker Faire Kyoto Online 2020に向けて最低限のところまで開発したため、まだ回路などは完成していない。(ブレッドボードがディスプレイの裏にぶら下がっている)

他にも、いろんな光らせ方の実装やHIDデバイスと連携させたシーケンスの実装など、色々やりたいことはあるのだが、それらは別途記事にしていきたいと思う。

さぁ、ピアノを目で楽しもう。

3Dプリンタでネジ用ボスを印刷する

概要

3Dプリンタ(FlashForge Adventurer3)でネジ用ボスを印刷する方法などの備忘録。

背景

電子工作で何らか完成品を作ろうとすると、基板を収める筐体が必要になる場合が多い。
昔はタカチのプラスチックケースを選んで、ケースに合わせて基板を設計していたものだ。
だが、今は3Dプリンタがある。3Dプリンタがあれば、基板に合わせて筐体を作ることもできる。

筐体に基板を収める時に必要なのが、基板や蓋固定のネジ用のボスである。
例えば、タカチのTWシリーズでは以下の写真のようなボスがついている。

f:id:kaname_m:20200418210403p:plain
ネジ用ボス(赤枠、他も同様)。写真はタカチのTWシリーズ

ネットで調べるといくつか同じようなことを試している先駆者がいる。

voltechno.com

ohmic-electronics.hatenablog.com

いくつか調べるとインサートナットを使っているものもあった。

今回手持ちのFlashForge Adventurer3を使って、M3の小ねじとタッピングネジを対象にボスを作成した。
結果、PLA樹脂で十分な強度のボスが作成できることを確認した。
備忘録を兼ねて、ここに記録する。

環境

  • 3Dプリンタ:FlashForge Adventurer3
  • 素材:PLA
  • ネジ:M3x10なべ小ねじ、タッピングネジ

テストボード作成

ボスの試験用のボードをFusio360で設計する。
ボスの外径は6mm, 7mm, 8mmの3種類。下穴の内径は全て2.5mmである。
(手持ちのAdventurer3だと、内径2.5mmで印刷すると仕上がりは2.0mmくらいになる)

f:id:kaname_m:20200418211441p:plain
ボス試験用ボード。ボス外径6, 7, 8mmが各3つずつ

テストボード印刷

3Dプリンタで印刷する。このときのポイントは充填率を100%にすること。(デフォルトは15%)
Adventurer3のスライサーソフトFlashprintだと、スライス設定の その他のオプション→充填率から設定可能。

f:id:kaname_m:20200418211951p:plain

後加工

下穴径2.5mmで3Dプリンタで印刷すると若干小さくなって2.0mmくらいになるので、金属にやる場合と同じようにΦ2.5で下穴を開ける。

f:id:kaname_m:20200418212400j:plain

下穴を開けたら、なべ小ねじ用ボスに対しては、M3タップを切る。
樹脂の積層が剥がれたり割れたりということはなかった。

f:id:kaname_m:20200418212403j:plain

ネジ締め

なべ小ねじは、タップを切ってしまえば普通に問題なく締めることができる。
どの外径のボスであっても、スプリングワッシャが潰れる力で締めても樹脂はびくともしない。

タッピングネジはタップを切るよりも強い力でネジを切りながら締めていくことになるが、これに関してもどのボス外径でも問題なく締めることができた。

f:id:kaname_m:20200418212406j:plain

この後、最も外径6mmの最も細いボスに対して、なべ小ねじを限界まで締め上げる破壊試験を行った。
結果、1mmちょっと樹脂を潰しながら進んでいった後、鉄のネジの頭がナメた。
下の画像の手前右がそれである。ネジ下の樹脂が潰れているのがわかるだろうか。

f:id:kaname_m:20200418212353j:plain

PLAでもきちんと下穴開けて、タップを切れば十分な強度のボスができるようである。

まとめ

3Dプリンタ(FlashForge Adventurer3)でPLA樹脂を使ってM3ネジ用のボスを印刷した。
印刷するときは充填率100%で印刷し、 なべ小ねじに関しては金属と同様下穴を開けて、タップを切ればM3に対して外径6mmのボスでも十分な強度が出ることを確認した。
タッピングネジに関しても、同様にΦ2.5mmの下穴を開ければ、問題なくネジを切れることを確認した。

これでEagleとFusion3603Dプリンタを連携させた回路・基板・筐体設計が捗る捗る。

M5Stackからスマホ/PCにCO2濃度上昇を通知する(Pushbullet)

概要

先日の記事で紹介した、M5StackによるCO2濃度モニタはただ数値とグラフを表示するだけだった。
このままではCO2濃度が高いのか低いのか、人が忘れないようにチェックする必要がある。

westgate-lab.hatenablog.com

そこで今回は、それに改良を加えCO2濃度が一定レベルを超えたらスマホ/PCに通知を送るようにした。
通知にはPushbulletを使っているが、M5StackからPushbulletを使って通知を送る方法について述べる。

ソースコードGithubで公開しているので、記事を読むのが面倒な人はこちらを参照のこと。 (この記事に対応するソースコードのバージョンはv2.0である)

github.com

最終的にはPushbulletのアプリを通じてこんな感じにスマホへプッシュ通知される。PushbulletにPC(Chrome拡張)を登録しておけば、PCにも同時に通知される。

f:id:kaname_m:20200405153127j:plain

スマホ・PCへのプッシュ通知はPushbulletが便利

Pushbulletは、様々な端末同士をつないでチャットしたりメッセージや写真を送ったり通知を共有するサービスである。
情報は、テキスト・URL・写真など何でも良い。

www.pushbullet.com

スマホとPC間のちょっとしたデータの共有に便利なので、電子工作に限らず私はよく使っている。
また、APIが豊富に用意されているため、インターネットに繋がってHTTPが使えるデバイスからも利用可能である。

以前ブログに書いた「RaspberryPiで再配達を撲滅するシステム」でも、スマホ/PCへの通知にPushbulletを使っている。

westgate-lab.hatenablog.com

M5Stackからプッシュ通知を送る

さて、ここからM5Stackからスマホ・PCへPushbulletを使ってプッシュ通知を行う方法を述べる。

Pushbulletアカウント、Access Tokenの取得

この後は、すでにPushbulletのアカウントを持ち、Access Tokenを取得している前提とする。
Access Tokenの取得方法については以下の過去記事参照。

westgate-lab.hatenablog.com

プッシュ通知タイミングの設計

今回は、以下のしきい値でプッシュ通知を送ることとした。

  • 1000ppmを超えたら、注意レベルとし、換気を促す。

  • 2000ppmを超えたら、危険レベルとし、速やかな換気を要請する。

しきい値付近では値がふらつくことが予想されるため、一度通知をプッシュしたら一定時間は通知をしないようにした。

ソースコード

ソースコードは全てGithub上で公開している。詳細はそちらを参照のこと。

github.com

なお、この記事では詳細は書いていないが、Ambientを使ったセンサ値のログも同時に実装している。ESPによるAmbientの使い方は公式チュートリアルが豊富にあるのでそちらを参照のこと。 以下では、ソースコードのうち特に重要な点について抜粋して述べる。

まず、Pushbulletではhttpsを使うため、WiFi.hに加えてWiFiClientSecure.hが必要である。

#include <M5Stack.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>

const char *ssid = "YOUR-WIFI-SSID";        // write your WiFi SSID (2.4GHz)
const char *password = "YOUR-WIFI-PASSWORD";     // write your WiFi password
WiFiClientSecure secureClient;
#define PB_APIKEY "YOUR-PUSHBULLET-API-KEY"      // write your Pushbullet API key

ESP32は2.4GHzのWiFiのみ対応しているので、SSIDには2.4GHzの方を記述すること。

setup()内で、WIFIをセットアップしておく。Pushbullet用関数(後述)はWIFIさえセットアップしておけば、他にセットアップは必要ない。

    // Wifi setup
    M5.Lcd.print("WiFi setup...");
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
        Serial.print(".");
    }
    M5.Lcd.println("done");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.print(WiFi.localIP());

次に、loop()内でセンサの値を取得したら、前回のCO2濃度と比較し、濃度しきい値を超えたらユーザへ通知を送るnotifyUser()、後述)。
co2_ppmにCO2濃度が格納されているものとする。

ここでは、CO2濃度をNORMAL, CAUTION, WARNINGの三段階に分け、前回取得時よりもレベルが上がっていたら通知を出している。
ただし、一度通知を出すと通知がpauseされ(pause_notify_[caution|warning])、一定時間通知は出されない。また、プログラム開始からも一定時間は通知を出さない(起動直後の値のふらつき対策)。

#define CO2_CAUTION_PPM 1000
#define CO2_WARNING_PPM 2000

int notify_timer_caution = 0;
int notify_timer_warning = 0;
bool pause_notify_caution = true;
bool pause_notify_warning = true;
#define PAUSE_LENGTH 600 // do not notify PAUSE_LENGTH [s] once notified

enum
{
    LEVEL_NORMAL,
    LEVEL_CAUTION,
    LEVEL_WARNING
};

int co2_level_last = LEVEL_NORMAL;

// (中略)
void loop()
{
    if (airSensor.dataAvailable())
    {
        // get sensor data(前回記事参照)
        co2_ppm = airSensor.getCO2();

        int co2_level_now;
        // check co2 level
        if (co2_ppm < CO2_CAUTION_PPM)
            co2_level_now = LEVEL_NORMAL;
        else if (co2_ppm < CO2_WARNING_PPM)
            co2_level_now = LEVEL_CAUTION;
        else
            co2_level_now = LEVEL_WARNING;

        // notify user when co2 level exceed threshold
        if (co2_level_now > co2_level_last)
        {
            if (co2_level_now == LEVEL_CAUTION && !pause_notify_caution)
            {
                if(notifyUser(co2_level_now)){
                    Serial.println("notifyUser(): CAUTION");
                }else{
                    Serial.println("notifyUser(): failed!");
                }
                pause_notify_caution = true;
            }
            if (co2_level_now == LEVEL_WARNING && !pause_notify_warning)
            {
                if(notifyUser(co2_level_now)){
                    Serial.println("notifyUser(): WARNING");
                }else{
                    Serial.println("notifyUser(): failed!");
                }                
                pause_notify_warning = true;
            }
        }

        co2_level_last = co2_level_now;
    }

    delay(SENSOR_INTERVAL_S * 1000); // SENSOR_INTERVAL_S [秒]ごとにセンサ値取得

    // 一度通知を出したら一定時間通知を出さないためのタイマー
    if (pause_notify_caution)
    {
        notify_timer_caution += SENSOR_INTERVAL_S;
        Serial.printf("notify_timer_caution: %d\n", notify_timer_caution);
        if (notify_timer_caution > PAUSE_LENGTH)
        {
            notify_timer_caution = 0;
            pause_notify_caution = false;
            Serial.println("notify_timer_caution set false");
        }
    }
    if (pause_notify_warning)
    {
        notify_timer_warning += SENSOR_INTERVAL_S;
        Serial.printf("notify_timer_warning: %d\n", notify_timer_warning);
        if (notify_timer_warning > PAUSE_LENGTH)
        {
            notify_timer_warning = 0;
            pause_notify_warning = false;
            Serial.println("notify_timer_warning set false");
        }
    }
}

最後に、ユーザへ通知をプッシュする関数は以下の通りである。

PushbulletへのHTTP通信は、コード中にも記載の通りこちらのサイトを使わせていただいた。

PushbulletはREST APIが利用でき、HTTP POSTでプッシュ通知を実装できる。
通知中の情報としては、title(通知タイトル、ここではCO2 Monitor)、body(通知文)を与えられる。

// reference: https://fipsok.de/Esp32-Webserver/push-Esp32-tab
bool pushbullet(const String &message)
{
    const char *APIKEY{PB_APIKEY};
    const uint16_t timeout{5000};
    const char *HOST{"api.pushbullet.com"};
    String messagebody = R"({"type": "note", "title": "CO2 Monitor", "body": ")" + message + R"("})";
    uint32_t broadcastingTime{millis()};
    if (!secureClient.connect(HOST, 443))
    {
        Serial.println("Pushbullet connection failed!");
        return false;
    }
    else
    {
        secureClient.printf("POST /v2/pushes HTTP/1.1\r\nHost: %s\r\nAuthorization: Bearer %s\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n%s\r\n", HOST, APIKEY, messagebody.length(), messagebody.c_str());
        Serial.println("Push sent");
    }
    while (!secureClient.available())
    {
        if (millis() - broadcastingTime > timeout)
        {
            Serial.println("Pushbullet Client Timeout !");
            secureClient.stop();
            return false;
        }
    }
    while (secureClient.available())
    {
        String line = secureClient.readStringUntil('\n');
        if (line.startsWith("HTTP/1.1 200 OK"))
        {
            secureClient.stop();
            return true;
        }
    }
    return false;
}

bool notifyUser(int level)
{
    const char *title = "CO2 Monitor";
    char body[100];

    switch (level)
    {
    case LEVEL_CAUTION:
        sprintf(body, "CO2 exceeded %d ppm. Ventilate please.", CO2_CAUTION_PPM);
        return pushbullet(body);

    case LEVEL_WARNING:
        sprintf(body, "CO2 exceeded %d ppm. Ventilate immediately.", CO2_WARNING_PPM);
        return pushbullet(body);

    default:
        return false;
    }
}

ここまでできれば、CO2が一定レベルを超えるとプッシュ通知される。

スマホへは記事トップのように通知されるが、PC(Windows10)の場合は以下のような感じである。

f:id:kaname_m:20200405154931p:plain

まとめ

前回作成したCO2濃度モニタに、Pushbulletを使ったプッシュ通知機能を実装した。
これでより意識的に換気できるだろう。

さぁ、空気を入れ替えよう。