編集履歴一覧に戻る
Alaのアイコン画像

Ala が 2020年07月05日16時16分00秒 に編集

初版

タイトルの変更

+

Leafony AI03A MIC&VR&LED 動作確認

タグの変更

+

Leafony

+

PlatformIO

本文の変更

+

# はじめに 本稿では、Leafony の Extention キットにある [AI03A MIC&VR&LED](https://trillion-node.org/leaf-technical-data/ai-i-o/#AI03A) の動作確認を簡単に行います。 というのも、Leafony のキット内リーフを公式のサンプルスケッチを使って試していましたが、残念なことにこのリーフのサンプルスケッチが削除されていました。([マイクとボリュームを使ってLEDを点灯](https://trillion-node.org/example/mic-vr-led/)) 仕方がないので自分でこのリーフの動作サンプルをやっていくことにします。 [SpecSheet](https://github.com/Leafony/HW-Design-Files/blob/master/AI03A_MIC%26VR%26LED/AI03A_MIC%26VR%26LED_SpecSheet_JA.pdf) より、MEMSマイク、ボリューム、LEDが搭載されています。この3つを使用してみます。 コードについては ino ではなく、基本的に Platform IO で C++ ファイルで記載します。 リーフの組み立てについては [マイクとボリュームを使ってLEDを点灯](https://trillion-node.org/example/mic-vr-led/) と同様の構成となります。 |リーフ| |-| |AI03A MIC & VR & LED| |AZ01A USB| |AP01A AVR MCU| |AV01A CR2032 (電池)| # LED ピン番号さえわかれば [LED点滅](https://trillion-node.org/example/sketch-blink/) などを参考にすれば問題ありません。 SpecSheet をみて確認します。しかし記載に差異(?)がありました。 実際に試してみたところ **D6~D11** が正となります。 2-1.ブロック図ではD6~D11がLED。 ![キャプションを入力できます](https://camo.elchika.com/7bea8b04bce4d5d763f3ed34306b0f34e1cd9f01/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64646164633439392d313236372d343236312d616134342d3432613338633335383132332f38373133333036312d373463632d346637632d613230362d643135393530366131336632/) 2-5.ピンアウトでは、D3~D8がLED駆動と書かれています。 ![キャプションを入力できます](https://camo.elchika.com/e2b7d30aceaadf9defcd48a8c436916a624c8e72/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64646164633439392d313236372d343236312d616134342d3432613338633335383132332f61316631663366302d303530652d343739332d616136362d306434316536336230666361/) LEDの動作確認コードは以下のようになります。 ```C++ #include <Arduino.h> // 定数定義 #define LED1 6 #define LED2 7 #define LED3 8 #define LED4 9 #define LED5 10 #define LED6 11 void setup() { // ピンの入出力設定 pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); pinMode(LED6, OUTPUT); // LED 初期設定: LOW 出力 (OFF) digitalWrite(LED1, LOW); digitalWrite(LED2, LOW); digitalWrite(LED3, LOW); digitalWrite(LED4, LOW); digitalWrite(LED5, LOW); digitalWrite(LED6, LOW); // LED 点灯テスト uint8_t LED_GROUP[6] = {LED1, LED2, LED3, LED4, LED5, LED6}; // 1つずつ点滅確認 for(uint8_t led : LED_GROUP) { digitalWrite(led, HIGH); delay(500); digitalWrite(led, LOW); delay(500); } // 全点灯 for(uint8_t led : LED_GROUP) { digitalWrite(led, HIGH); } delay(1000); // 全消灯 for(uint8_t led : LED_GROUP) { digitalWrite(led, LOW); } } void loop() { } ``` # マイク 次にマイクの確認です。 2-5.ピンアウトより、A2 がマイク出力信号(アナログ) となっています。 とりあえずアナログらしいので [analogRead](http://www.musashinodenpa.com/arduino/ref/index.php?f=0&pos=2113) で値をとってみました。 ここで問題として、シリアルモニタで確認する場合、ディレイを大きめに取らないと出力が早すぎて変化をみることができませんが、あまりディレイを大きくとりすぎると変化をとり逃しすぎてイマイチよくわかりませんでした。 解決策としては、Arduinoの公式ツールにシリアルプロッタなるものがあることを知ったのでそれを利用しました。シリアルモニタの出力値をグラフとしてプロットしてくれるものになります。(Arduinoのツールにあります。) これを利用することで簡単にどのように変化しているかみることができます。 ![キャプションを入力できます](https://camo.elchika.com/7207f32926cddb61ec5313f657a55ec6dac561bc/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64646164633439392d313236372d343236312d616134342d3432613338633335383132332f33636361396237312d393735382d346133622d393565382d613135336337363235613739/) ```C++ // 変数 uint16_t micData; void setup() { // シリアル通信の開始 Serial.begin(115200); // 略 } void loop() { micData = analogRead(A2); Serial.print("mic :"); Serial.println(micData); delay(2); } ``` 以下のようにプロットされて確認できます。 | delay2ms | delay500ms | | ----------------------------------------------------------- | ----------------------------------------------------------- | | ![キャプションを入力できます](https://camo.elchika.com/e2bc7bc088fe564cae8beeff9b95d87b255e3afc/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64646164633439392d313236372d343236312d616134342d3432613338633335383132332f65386438346536302d643032382d346230362d613535612d363937353939383831346434/)| ![キャプションを入力できます](https://camo.elchika.com/aab916fd6d2b787c7284e4fe33ab2bf1c34fb10c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64646164633439392d313236372d343236312d616134342d3432613338633335383132332f61303536303162612d373461342d346539382d383438352d636332656632646635653732/) | ## マイクとLED いまいちマイクの出力値の使い方がわからないのですが、サンプルコードの説明に書いてあるように音量に応じてLEDを点滅させてみるようにしてみました。 > マイクにより周囲の音量に応じた数のLEDを点灯させることが出来ます。 ※注意 以降、色々と値をこねくり回したりしていますが、実測から適当にそれっぽく動作するように試していっただけのものになります。このマイクの出力値どう処理すればいいのかわかりません(サンプルコード見たかった・・・)。音の波形とか全くわからないので、ここではあくまでリーフの動作確認とします。 platform.ini にはタイマ割り込みを利用するため MSTimer2 を追加します。 ``` lib_deps = # MSTimer2 https://github.com/PaulStoffregen/MsTimer2 ``` 以下がコードとなります。 ```C++ #include <Arduino.h> #include <MsTimer2.h> // 定数定義 #define LED1 6 #define LED2 7 #define LED3 8 #define LED4 9 #define LED5 10 #define LED6 11 #define MIC_DATASET_MAX 100 // マイクデータ保持最大値 // 変数 uint16_t micData; // マイクの入力値保持 uint16_t micDataset[MIC_DATASET_MAX]; // マイクデータの配列(キュー) uint16_t index; // マイクデータの配列のインデックス uint16_t micLevel; // 0~6に変換したマイクデータ uint32_t micAverage; // マイクの基準値(起動時に平均値をとる) // プロトタイプ宣言 void setup(); void loop(); void Timer1sInterrupt(); void setupDigitalPin(); void testLED(); // 初期化処理 void setup() { setupDigitalPin(); // デジタルピン設定 testLED(); // LED 点灯テスト // シリアル通信の開始 Serial.begin(115200); // マイクの基準算出 uint32_t sum = 0; for(int cnt = 0; cnt < 500; cnt++) { sum += analogRead(A2); } micAverage = sum / 500; Serial.println(micAverage); // タイマー割り込み設定: 1s毎にボリュームでLED点灯 MsTimer2::set(1000, Timer1sInterrupt); MsTimer2::start(); } // メインループ void loop() { micData = analogRead(A2); // MIC_DATASET_MAX 分のキューを作成 micDataset[index %= MIC_DATASET_MAX] = micData; index++; delay(2); } // 1s毎にボリュームでLED点灯割り込み void Timer1sInterrupt() { uint32_t sum = 0; uint32_t max = 0; // キューのデータから平均, 最大値をとる for(uint16_t value : micDataset) { // 基準からの乖離を算出 if(micAverage > value) { value = micAverage - value; } else { value = value - micAverage; } // 最大値 if(max < value) { max = value; } } // 直近の最大値を判定値とする。 uint32_t currentValue = max; // 0 ~ 6 の値に currentValue = constrain(currentValue, 0, 200); micLevel = map(currentValue, 0, 200, 0, 6); // ボリュームレベルにあわせてLEDを点灯 switch (micLevel) { case 0: digitalWrite(LED1, LOW); digitalWrite(LED2, LOW); digitalWrite(LED3, LOW); digitalWrite(LED4, LOW); digitalWrite(LED5, LOW); digitalWrite(LED6, LOW); break; case 1: digitalWrite(LED1, HIGH); digitalWrite(LED2, LOW); digitalWrite(LED3, LOW); digitalWrite(LED4, LOW); digitalWrite(LED5, LOW); digitalWrite(LED6, LOW); break; case 2: digitalWrite(LED1, HIGH); digitalWrite(LED2, HIGH); digitalWrite(LED3, LOW); digitalWrite(LED4, LOW); digitalWrite(LED5, LOW); digitalWrite(LED6, LOW); break; case 3: digitalWrite(LED1, HIGH); digitalWrite(LED2, HIGH); digitalWrite(LED3, HIGH); digitalWrite(LED4, LOW); digitalWrite(LED5, LOW); digitalWrite(LED6, LOW); break; case 4: digitalWrite(LED1, HIGH); digitalWrite(LED2, HIGH); digitalWrite(LED3, HIGH); digitalWrite(LED4, HIGH); digitalWrite(LED5, LOW); digitalWrite(LED6, LOW); break; case 5: digitalWrite(LED1, HIGH); digitalWrite(LED2, HIGH); digitalWrite(LED3, HIGH); digitalWrite(LED4, HIGH); digitalWrite(LED5, HIGH); digitalWrite(LED6, LOW); break; case 6: digitalWrite(LED1, HIGH); digitalWrite(LED2, HIGH); digitalWrite(LED3, HIGH); digitalWrite(LED4, HIGH); digitalWrite(LED5, HIGH); digitalWrite(LED6, HIGH); break; default: break; } } // デジタルピン設定 void setupDigitalPin() { // ピンの入出力設定 pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); pinMode(LED6, OUTPUT); // LED 初期設定: LOW 出力 (OFF) digitalWrite(LED1, LOW); digitalWrite(LED2, LOW); digitalWrite(LED3, LOW); digitalWrite(LED4, LOW); digitalWrite(LED5, LOW); digitalWrite(LED6, LOW); } // LED 点灯テスト void testLED() { uint8_t LED_GROUP[6] = {LED1, LED2, LED3, LED4, LED5, LED6}; // 1つずつ点滅確認 for(uint8_t led : LED_GROUP) { digitalWrite(led, HIGH); delay(500); digitalWrite(led, LOW); delay(500); } // 全点灯 for(uint8_t led : LED_GROUP) { digitalWrite(led, HIGH); } delay(1000); // 全消灯 for(uint8_t led : LED_GROUP) { digitalWrite(led, LOW); } } ``` 初期化処理の setup 内でマイクの基準を算出してますが、音の波形の処理がわかっていないのでここは全く参考にならないと思うのでご注意ください。 とりあえず 500回マイクの値を読みだして平均を保持するようにしています。初期化時は静かに音を出さないようにすることを想定しています。 ```C++ // マイクの基準算出 uint32_t sum = 0; for(int cnt = 0; cnt < 500; cnt++) { sum += analogRead(A2); } micAverage = sum / 500; Serial.println(micAverage); ``` その後、1s毎のタイマー割り込みを設定しています。 ```C++ // タイマー割り込み設定: 1s毎にボリュームでLED点灯 MsTimer2::set(1000, Timer1sInterrupt); MsTimer2::start(); ``` メインループ内では毎周期マイクデータを読みだして100件分のデータを配列に保持しています。 ```C++ micData = analogRead(A2); // MIC_DATASET_MAX 分のキューを作成 micDataset[index %= MIC_DATASET_MAX] = micData; index++; ``` タイマ割り込み関数内では保持している直近100件分のデータから最大値を取り出しています。 このときそのままのデータを使用せず **初期化時に保持していた基準値** からの差を使用しています。理由としては少し前のプロットの図をみていただくと分かると思いますが、音量が上がったとき読みだした値が大きくなるわけではなく振れ幅が大きくなります。 よって初期化時にとっていた平均データから「どれだけ離れているか」という値を利用しています。 直近数件のデータから最大値を取り出している理由については割り込み時点で1回だけの読み出しにすると音を出してもうまく音を出したタイミングのデータを取り切れなかった為です。 ちなみに最初は直近データからの平均を使用しようとしてみたのですが、平均値をとってしまうと大きな音を出し続けなければ変化が丸められてしまった為、最大値に変更しています。 ```C++ // キューのデータから平均, 最大値をとる for(uint16_t value : micDataset) { // 基準からの乖離を算出 if(micAverage > value) { value = micAverage - value; } else { value = value - micAverage; } // 最大値 if(max < value) { max = value; } } // 直近の最大値を判定値とする。 uint32_t currentValue = max; ``` # ボリューム 次はボリューム確認です。 2-5.ピンアウトより、A3 がボリューム出力信号(アナログ) となっています。 SpecSheetをみてもボリュームがどのような値がとれるのかなどがわからなかったので、実際に値をシリアルモニターに出力して試してみます。 結果は、0~929 の値が取れました。 ```C++ uint16_t volumeData; // ボリュームの入力 void loop() { volumeData = analogRead(A3); Serial.println(volumeData); delay(2); } ``` # おわりに 以上で、AI03A MIC&VR&LEDの動作確認としてはおわります。 特にマイクの部分はLEDが音量にあわせて点灯がかわるようにこねくりまわしただけで参考にならないかもしれません。とりあえずマイクを使ってみようくらいの気持ちだったので、読みだしたマイクデータの処理方法がわかりませんでした・・・。 この他のサンプルも色々試してみてそのあたりでひとつ書こうかななどとも思っていたのですが、サンプルコード試していくうちにまさかのサンプルが消えてしまっているリーフがあった為、せっかくなので自分が試した内容をまとめてみました。