shirachan が 2024年01月31日03時21分14秒 に編集
コメントアウトを削減
本文の変更
# 作品概要 この作品は、誰でも簡単にかっこよく弾けるギターです。昨今の新型コロナウイルスの影響によるおうち時間の増加をきっかけに、楽器の演奏にチャレンジする人が増えました。とりわけ、多くの方が挑戦する楽器としてギターが挙げられます。 しかし、ギターは初心者が挫折しやすい楽器でもあります。その原因は、左右の手で異なる働きを求められる、ギターの演奏方法の難しさにあります。 ![ギターの演奏方法](https://camo.elchika.com/a4e8946484779e24715a91bb752076b761d6f6b7/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f61663832613166392d633662342d346465612d393835662d353431636563633966313862/) ギターの演奏では、左手で指板上の弦を押さえることによって鳴らす音を決定します。複数弦を押さえるとき、この押さえ方は「コード」と呼ばれ、Am、Em7、Csus4などの名前が付いています。しかし、コードは代表的なものだけでも約160種類あるため、コードを暗記して弦を押すことは初心者にとってとても大変です。 また、右手は弦を弾くことにより音を鳴らします。音を鳴らすには弦が均一に鳴るように弾く必要があり、習得には時間がかかります。 そこで、このような課題を解決し、簡単にかっこよく演奏ができるギター「らくラクギター」を作成しました。 # 構成 らくラクギターの構成は以下の図のようになっています。 ![らくラクギターの構成](https://camo.elchika.com/1ab5256cf282ecca5ac3a08963261f85a74c247b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f38616138386237322d666339622d346465352d386335612d323931623361633136643031/) ### タッチセンサ ![タッチセンサ](https://camo.elchika.com/6a9fb9a035571e53e4447a8cf3221126ddcde761/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f31336337643530652d633836362d343835322d393966622d653431646437613434643230/) 左手でタッチセンサを押さえることで、ギターで鳴らす音を決めます。指板部分のセンサは弦を模しており、合計88個の金属線一本一本が個別のセンサになっています。タッチセンサは静電容量式で、人が金属線に触れたときに生じる波形の遅延時間を読み取ることで、押下の判定をしています。 ![タッチセンサの仕組み](https://camo.elchika.com/549e93b37431215457f7afec33579a9ecd3b6daf/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f36353834336631342d353832392d346664382d613335382d393033363361393064376664/) ### ホイール ![ホイール拡大](https://camo.elchika.com/f1c5c86c7b119acf86f8e9477ecafefe67cacfb2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f65353765393734662d353430392d346366352d393266342d323132383537356437616566/) 右手で弦を弾くようにホイールを回すことで音が鳴ります。ホイールの回転軸にはエンコーダが付いており、ホイールの回転を読み取ることで音の鳴らし方を決定します。 ### モードスイッチ ![モードスイッチ](https://camo.elchika.com/ea836d99c050b7be339446ccae372b4e9e07b972/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f36333430396336612d343964302d343464342d623266352d393661333534363135646530/) 下記で解説するモードの切り替えと音源の切り替えをします。音源の切り替えではアコースティックギターの音とエレキギターの音の切り替えができるようになっています。 ### スピーカー ここから音が鳴ります。 # 機能 本作品は、本物のギターと同じように左手でタッチセンサを押さえ、右手でホイールを回すことで演奏します。演奏には演奏者のレベルに合わせた3つのモードを実装しました。 ### コードモード コードモードでは、左手でタッチする部分と鳴らすコードを対応付け、1か所を押さえるだけで任意のコードを鳴らせます。これにより、普通のギターと同様の弦を押さえる格好で簡単に演奏ができるようになっています。コードと押さえる位置の対応は以下のようになっています。 ![コードモードの押さえる位置](https://camo.elchika.com/0a4dd0178e66236e2647f59873ff54bda946be96/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f30366265633139632d343632362d343636652d383336352d323830313731323563653739/) 例えば、Dmというコードは3か所押さえる必要がありますが、コードモードでは1か所を押さえるだけで演奏ができます。 ![例:Dm](https://camo.elchika.com/aac674045a919df032202d540cd18f871d38b176/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f32643163616131622d386366352d343436332d383366632d393534623434633835313332/) @[twitter](https://twitter.com/teamSOTA655/status/1752206623618343293) ### ビギナーモード ビギナーモードはホイールを回すだけで演奏ができるモードです。 SPRESENSEをWi-Fiのアクセスポイントにし、PCと接続します。PCからSPRESENSEにコード譜を送信することで、コード譜の曲が演奏できます。コード譜は以下のようなtxtファイルになっており中にコードが書かれています。 ```txt:ドライフラワー_さび.txt C G D B7 Em7 C G D D#dim Em7 C G B7 Em7 Am7 Cm D G ``` @[twitter](https://twitter.com/teamSOTA655/status/1752212003312332804) ### マニュアルモード 本物のギターと同じコードの押さえ方で演奏ができるモードになっています。 @[twitter](https://twitter.com/teamSOTA655/status/1752211584158708034) # システム図 全体のシステム図は以下の通りです。 ![システム図](https://camo.elchika.com/74c71f67c929eadd4a3cc559e0f00a9cd68f961c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f38303166663062612d323466392d343237302d393465322d623437316462373532626461/) # 回路図 全体回路図は以下のようになります。 ![全体回路図](https://camo.elchika.com/567896399b0785e38afb3b0a8bb44fa318269bdd/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f32646364366231642d663238612d343730312d613037632d343036383334643632343834/) タッチセンサの値を読み取るためのタッチセンサ制御回路は、複数のシフトレジスタ部とコンパレータ部から構成されており、指板部の金属線への接触を判定します。 1番目のシフトレジスタ部の回路は以下のようになっています。シフトレジスタを用いることで、指板部の金属線に一本ずつ順番に波形を送信します。また、ダイオードを入れることで、複数箇所の押下も識別可能です。 ![1番目のシフトレジスタ部(※クリックで拡大)](https://camo.elchika.com/8d018f6afe5d8f19873cace31ff0744c373ea5a0/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f32653331363866342d376162362d346465662d626464632d653762656136663830636335/) 2~11番目のシフトレジスタ部は以下のようになっています。k番目のシフトレジスタのSER端子はk-1番目のシフトレジスタのQH’端子と接続します。k番目のシフトレジスタのQH'端子はk+1番目のシフトレジスタのSER端子と接続します。このようにし複数のシフトレジスタを連結しています。 ![k番目のシフトレジスタ部(※クリックで拡大)](https://camo.elchika.com/5af52197ee7691316483832554b7810f8ef66549/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f37333931303839352d373262392d343165352d613062302d306538373435303161333135/) コンパレータ部は以下のようになっています。コンパレータ部ではシフトレジスタから出力された波形の読み取りをしています。-INPUTA端子に接続されている可変抵抗でタッチセンサの感度の調整ができるようになっています。コンパレータからの出力であるOUTPUTA端子の状態をSPRESENSEで読み取っています。 ![コンパレータ部](https://camo.elchika.com/6fb7ab72bdfcdd9a0fd26c4c079a8228a17345d9/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f33333037373861632d306432352d346436662d396263622d386135363732396364393731/) # 部品・作成 使用した部品は以下の通りです。 | 部品 | 使用個数 | 備考 | |:-----------------------------------:|:--------:|:--------------------------------------------:| | SONY SPRESENSE メインボード | 1 | | | SONY SPRESENSE 拡張ボード | 1 | | | SPRESENSE Wi-Fi Add-onボード is110B | 1 | | | LM393 | 1 | | | 2SC1815 | 1 | | | 74HC595 | 11 | | | PAM8403ステレオデジタルアンプボード | 1 |音の増幅に使用。他のもので代用可能| | ダイオード1S4 | 88 | | | 抵抗4.7kΩ | 1 | | | 抵抗100kΩ | 88 | | | 抵抗470kΩ | 1 | | | 可変抵抗10kΩ | 1 | | | コンデンサ0.1µF | 11 | | | スピーカーPS95-8 | 1 | | | 32GB microSDカード | 1 | | | エンコーダKY-040 | 1 | | | L字型ピンヘッダ(1x8) | 11 | | | L字型ピンヘッダ(2x10) | 3 | | | DIP8P ICソケット | 1 | | | DIP16P ICソケット | 11 | | | 3.5φ3極プラグ P-92 | 1 | | | QIコネクタハウジング 2P | 7 | | | QIコネクタハウジング 3P | 1 | | | QIコネクタハウジング 5P | 1 | | | QIコネクタハウジング 6P | 6 | | | QIコネクタハウジング 8P | 11 | | | QIコネクタ(オス) | 適宜 | 配線に使用 | | QIコネクタ(メス) | 適宜 | 配線に使用 | | ケーブル | 適宜 | | | ステンレス線 | 適量 | | | 指板部基板 | 1 | 自作基板 | | タッチセンサ制御基板 | 3 | 自作基板 | | ブレッドボード | 1 | 電源線の分岐に使用。他のもので代用可能 | | ギター組み立てキット | 1 | | | MDF板(910x450x5.5) | 1 | 表板を強化するために使用 | | 端材 | 2 | 回転軸を支えるために使用。他のもので代用可能 | | スペーサ ねじ呼びM3 長さ10mm | 15 | | | M3ナット | 7 | | | とめねじ付きベアリングTB6000ZZ | 2 | | | カップリングCPLS20-6-10 | 1 | | | 回転軸SFMR10-230 | 1 | | | スライドスイッチms-12aas1 | 3 | | | ボルト CSH-ST-M4-14 | 4 | | | ナット FNTLP-STU-M4 | 4 | | | なべ小ねじ CSPTRT-SUS-M2.3-8 | 4 | | ギター本体はギター組み立てキットから作成しました。 ![組み立て中の写真](https://camo.elchika.com/d2064c0dcf36a435a14fc0d627111fd2f6130d03/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f33306364356164322d623335392d343265362d386162322d393938346561663162656263/) ネックの裏にはギターを握ったときに手のひらが触れるようにGND線を通しています。GND線を通さないとタッチセンサが上手く反応しません。 ![GND線](https://camo.elchika.com/4750602c26f504a1a3ebfc2f0a144ff80091a20f/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f63386261353139342d303463622d343665322d396636312d336639643661353232383530/) ホイールは3Dプリンタで印刷しました。 ![ホイール](https://camo.elchika.com/135165840639968beedc09aa13022d63a024e0d0/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f35656335346637302d313831362d343632612d386536312d393935326430633234633339/) タッチセンサ制御回路と指板部は別途基板を作成しました。 ![タッチセンサ制御回路基板](https://camo.elchika.com/a3a02e5742d132fdab8e903ddebee93c1f7cc921/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f65343038353232632d333138392d346537342d393964332d366637363636393361613234/) ![はんだ付けした写真](https://camo.elchika.com/aef4aa31974eb18011d0dd062256890170d35c6d/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f33656262653231322d353762662d346139302d383563652d343930656361633766356562/) ![指板部基板](https://camo.elchika.com/cff9723e39b8eb8c3966051a4754e626f19aca18/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f61623135323838642d363765302d346165632d396631352d363064363866373936313131/) ![はんだ付けした写真](https://camo.elchika.com/598bf0af15b514655906b18a45e25290fe083953/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f39396331323230342d323430322d346236332d393665332d346633623432663835343837/) 組み立てた結果内部はこのようになっています。 ![内部の写真1](https://camo.elchika.com/478dbf24f6ca6191143574d315c80ac635b9d240/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f66613637653938632d363536622d346262312d626535622d363262356666373362303332/) ![内部の写真2](https://camo.elchika.com/c0311bcb7605a4faee2161e63c40605c72982be3/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f64306265336539322d313461652d346137352d393536342d363266396664373561386163/) # ソースコード SPRESENSEとPC側のプログラムを分けて解説します。 ## SPRESENSE SPRESENSEではマルチコアを使用しています。Spresense Referense Board バージョン2.5.1の環境下で動かしました。 また、各コードの音源はCakewalk by BandLabを用いて作成しました。プラグイン(ギターの音)はAmple Guitar M v3.7のトライアルバージョン(アコースティック)とAmple Guitar LP v3.7のトライアルバージョン(エレキ)を使いました。作成した音源はSDカード内に保存し、SPRESENSEの拡張ボードに挿入します。 ### MainCore MainCoreでは音源の再生・停止、スイッチの読み取り、SubCoreから受信した情報の処理をしています。音源の再生・停止はSDカードに入れた音源に番号を登録し、番号を指定することで再生可能となっています。 モードスイッチより演奏音源・モードを切り替えることができます。 SubCore3から受信した文字列から音源番号を羅列したコード譜を作成します。SubCore2から受信したタッチセンサの値から各モードでのコード音源を検索します。 SubCore1から受信した加速検出と加速方向より、音源を決定し、音を再生します。 ```arduino:MainCore ////////////////////////////// Main Core ////////////////////////////////////////////////////////////////////////////////////////// #ifdef SUBCORE // SubCore指定時にはエラー #error "Core selection is wrong!!" #endif #include <MP.h> int subcore1 = 1; int subcore2 = 2; int subcore3 = 3; #include <SDHCI.h> #include <Audio.h> #include <vector> #include <stdint.h> AudioClass *theAudio; File myFile; bool ErrEnd = false; // error callback static void audio_attention_cb(const ErrorAttentionParam *atprm) { printf("************Attention!!!!!**********\n"); //theAudio->stopPlayer(AudioClass::Player0); if (atprm->error_code == AS_ATTENTION_CODE_INFORMATION) { printf("*****************INFORMATION*******************\n"); ErrEnd = true; } if (atprm->error_code == AS_ATTENTION_CODE_WARNING) { printf("****************WARNING*******************\n"); ErrEnd = true; } if (atprm->error_code == AS_ATTENTION_CODE_ERROR) { printf("*****************ERROR*******************\n"); ErrEnd = true; } if (atprm->error_code == AS_ATTENTION_CODE_FATAL) { printf("*****************FATAL*******************\n"); ErrEnd = true; } } class PlayGuitar { public: // wavファイル読込用 struct Item { int16_t note; String path; }; PlayGuitar(const Item *table, size_t table_length); void setup(); void begin(); void end(); bool sendNoteOn(int16_t note,int16_t volume); void sendNoteOff(); //void sendNoteEnd(); void update(); private: // class SDClass sd; WavContainerFormatParser theParser; // wavの1つのデータ struct PcmData { int16_t note;//番号 String path; uint32_t offset; uint32_t size; }; std::vector<PcmData> pcm_table_; // param int samplingrate = 192000; AsClkMode AS_CLKMODE = AS_CLKMODE_HIRES;//AS_CLKMODE_NORMAL or AS_CLKMODE_HIRES int bit = 16;//16 or 24 int channel = 1; int max_buffer = 24000; uint8_t s_buffer[720]; uint8_t sc_store_frames = 10;// 最初に読み込むframe数 uint8_t sc_store_frames_loop = 1;//loopで読み込むframe数 // sendNoteOn Off End updateで使う size_t supply_size = 0; uint32_t s_remain_size = 0; bool started = false; bool carry_over = false; bool file_open(int16_t note); }; // wavのnote,pathをpcm_table_に書き込む PlayGuitar::PlayGuitar(const Item *table, size_t table_length) { printf("PlayGuitar Constructor\n"); for (size_t i = 0; i < table_length; i++) { PcmData pcm_data; pcm_data.note = table[i].note; pcm_data.path = table[i].path; pcm_table_.push_back(pcm_data); } } // wavのoffsetとsizeをpcm_table_に書き込む void PlayGuitar::setup() { printf("PlayGuitar setup\n"); while (!sd.begin()) { printf("please Insert SD card"); } for (size_t i = 0; i < pcm_table_.size(); i++) { fmt_chunk_t fmt; // sd cardからWavContainerFormatParserでwav情報読み込////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////み handel_wav_parser_t *handle = (handel_wav_parser_t *)theParser.parseChunk(("/mnt/sd0/"+pcm_table_[i].path).c_str(), &fmt); if (handle == NULL) { printf("Wav parser error note %d\n",pcm_table_[i].note); exit(1); } printf("framerate %d = %d\n",i,fmt.rate); printf("bit %d = %d\n",i,fmt.bit); pcm_table_[i].offset = handle->data_offset; pcm_table_[i].size = handle->data_size; theParser.resetParser((handel_wav_parser *)handle); } printf("note, path, offset, size\n"); for (size_t i = 0; i < pcm_table_.size(); i++) { printf("%3d, %s, %d, %d\n",pcm_table_[i].note, pcm_table_[i].path.c_str(), pcm_table_[i].offset,pcm_table_[i].size); } } //Audioの初期設定 void PlayGuitar::begin() { theAudio = AudioClass::getInstance(); theAudio->begin(audio_attention_cb);// HWとの接続 theAudio->setRenderingClockMode(AS_CLKMODE); theAudio->setPlayerMode(AS_SETPLAYER_OUTPUTDEVICE_SPHP, max_buffer,max_buffer);// 出力先,player1 maxbuffer,player2 maxbuffer //sampling,16or24,mono or stereo err_t err = theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_WAV, "/mnt/sd0/BIN",samplingrate,bit,channel); if (err != AUDIOLIB_ECODE_OK) { printf("Player0 initialize error\n"); exit(1); } printf("initialization Audio Library\n"); } // noteからwavファイルを検索し、 // wavファイルのoffsetの位置から読み込み、s_remain_sizeをset bool PlayGuitar::file_open(int16_t note) { // noteからwavファイルを検索 int table_index_ = -1; for (size_t i = 0; i < pcm_table_.size(); i++) { if (note == pcm_table_[i].note) { table_index_ = i; break; } } myFile.close(); if (table_index_ < 0) { printf("error: note is nothing(note=%d)\n", note); return false; } //printf("play %s"pcm_table_[table_index_].path.c_str()); myFile = sd.open(pcm_table_[table_index_].path.c_str()); if (!myFile) { printf("error: file open error(%s)\n", pcm_table_[table_index_].path.c_str()); return false; } //offsetの位置から読込 //wavファイルのsizeをset myFile.seek(pcm_table_[table_index_].offset); s_remain_size = pcm_table_[table_index_].size; return true; } // note番号と一致するものを最初のフレーム分、読み取る // volumeも決まる // started をset bool PlayGuitar::sendNoteOn(int16_t note,int16_t volume) { // reset supply_size = 0; carry_over = false; // 開始時のみファイルの読み込み if(!file_open(note)) { printf("file can't read"); return false; } // 入力が変化したらここのloopから抜け出せると良い for (uint8_t i = 0; i < sc_store_frames; i++) { supply_size = myFile.read(s_buffer, (s_remain_size < sizeof(s_buffer)) ? s_remain_size : sizeof(s_buffer)); s_remain_size -= supply_size; //Serial.print("remain_size="); //Serial.println(s_remain_size); int err = theAudio->writeFrames(AudioClass::Player0, s_buffer, supply_size); //Serial.print("first write"); // 開始時のみチェック if (err != AUDIOLIB_ECODE_OK) { Serial.println("SUDIOLIB_ECODE_OK"); break; } // 終了 if (s_remain_size == 0) { break; } } theAudio->setVolume(volume); theAudio->startPlayer(AudioClass::Player0); started = true; //puts("Play!"); return true; } // startedの時、数フレーム分読み取る // s_remain_sizeが0になったら終了し、sendNoteOff(startedをreset) void PlayGuitar::update() { if(started) { for (uint8_t i = 0; i < sc_store_frames_loop; i++) { if(!carry_over) { supply_size = myFile.read(s_buffer, (s_remain_size < sizeof(s_buffer)) ? s_remain_size : sizeof(s_buffer)); s_remain_size -= supply_size; } carry_over = false; //sbufferのsupply_size(読み取れた量)をFIFOに変換 int err = theAudio->writeFrames(AudioClass::Player0, s_buffer, supply_size); // bufferが満タン if (err == AUDIOLIB_ECODE_SIMPLEFIFO_ERROR) { //Serial.println("SUDIOLIB_ECODE_SIMPLEFIFO_ERROR"); carry_over = true; break; } else { //Serial.println("not full"); } //Serial.println("s_remain_size ="); //Serial.println(s_remain_size); // 終了 if (s_remain_size == 0) { //Serial.print("remain_size =0"); sendNoteOff(); break; } } } } // 再生をstopし、startedをreset void PlayGuitar::sendNoteOff() { if(started) { theAudio->stopPlayer(AudioClass::Player0,AS_STOPPLAYER_NORMAL); started = false; } } //void PlayGuitar::sendNoteEnd() //{ // if(started) // { // theAudio->stopPlayer(AudioClass::Player0); // started = false; // } //} // 終了 void PlayGuitar::end() { theAudio->setReadyMode(); theAudio->end(); printf("Exit player\n"); exit(1); } ////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const PlayGuitar::Item table[437] = { {999,"A/free_u.wav"}, // setup.wav {-1,"A/free_u.wav"}, // A/free_u.wav {-11,"A/Caug_u.wav"}, {-12,"A/Cdim_u.wav"}, {-13,"A/CM_u.wav"}, {-14,"A/Cm__u.wav"}, {-15,"A/C7_u.wav"}, {-16,"A/Csus4_u.wav"}, {-17,"A/CM7_u.wav"}, {-18,"A/Cm7__u.wav"}, {-19,"A/C7sus4_u.wav"}, {-21,"A/C#aug_u.wav"}, {-22,"A/C#dim_u.wav"}, {-23,"A/C#M_u.wav"}, {-24,"A/C#m__u.wav"}, {-25,"A/C#7_u.wav"}, {-26,"A/C#sus4_u.wav"}, {-27,"A/C#M7_u.wav"}, {-28,"A/C#m7__u.wav"}, {-29,"A/C#7sus4_u.wav"}, {-31,"A/Daug_u.wav"}, {-32,"A/Ddim_u.wav"}, {-33,"A/DM_u.wav"}, {-34,"A/Dm__u.wav"}, {-35,"A/D7_u.wav"}, {-36,"A/Dsus4_u.wav"}, {-37,"A/DM7_u.wav"}, {-38,"A/Dm7__u.wav"}, {-39,"A/D7sus4_u.wav"}, {-41,"A/D#aug_u.wav"}, {-42,"A/D#dim_u.wav"}, {-43,"A/D#M_u.wav"}, {-44,"A/D#m__u.wav"}, {-45,"A/D#7_u.wav"}, {-46,"A/D#sus4_u.wav"}, {-47,"A/D#M7_u.wav"}, {-48,"A/D#m7__u.wav"}, {-49,"A/D#7sus4_u.wav"}, {-51,"A/Eaug_u.wav"}, {-52,"A/Edim_u.wav"}, {-53,"A/EM_u.wav"}, {-54,"A/Em__u.wav"}, {-55,"A/E7_u.wav"}, {-56,"A/Esus4_u.wav"}, {-57,"A/EM7_u.wav"}, {-58,"A/Em7__u.wav"}, {-59,"A/E7sus4_u.wav"}, {-61,"A/Faug_u.wav"}, {-62,"A/Fdim_u.wav"}, {-63,"A/FM_u.wav"}, {-64,"A/Fm__u.wav"}, {-65,"A/F7_u.wav"}, {-66,"A/Fsus4_u.wav"}, {-67,"A/FM7_u.wav"}, {-68,"A/Fm7__u.wav"}, {-69,"A/F7sus4_u.wav"}, {-71,"A/F#aug_u.wav"}, {-72,"A/F#dim_u.wav"}, {-73,"A/F#M_u.wav"}, {-74,"A/F#m__u.wav"}, {-75,"A/F#7_u.wav"}, {-76,"A/F#sus4_u.wav"}, {-77,"A/F#M7_u.wav"}, {-78,"A/F#m7__u.wav"}, {-79,"A/F#7sus4_u.wav"}, {-81,"A/Gaug_u.wav"}, {-82,"A/Gdim_u.wav"}, {-83,"A/GM_u.wav"}, {-84,"A/Gm__u.wav"}, {-85,"A/G7_u.wav"}, {-86,"A/Gsus4_u.wav"}, {-87,"A/GM7_u.wav"}, {-88,"A/Gm7__u.wav"}, {-89,"A/G7sus4_u.wav"}, {-91,"A/G#aug_u.wav"}, {-92,"A/G#dim_u.wav"}, {-93,"A/G#M_u.wav"}, {-94,"A/G#m__u.wav"}, {-95,"A/G#7_u.wav"}, {-96,"A/G#sus4_u.wav"}, {-97,"A/G#M7_u.wav"}, {-98,"A/G#m7__u.wav"}, {-99,"A/G#7sus4_u.wav"}, {-101,"A/Aaug_u.wav"}, {-102,"A/Adim_u.wav"}, {-103,"A/AM_u.wav"}, {-104,"A/Am__u.wav"}, {-105,"A/A7_u.wav"}, {-106,"A/Asus4_u.wav"}, {-107,"A/AM7_u.wav"}, {-108,"A/Am7__u.wav"}, {-109,"A/A7sus4_u.wav"}, {-111,"A/A#aug_u.wav"}, {-112,"A/A#dim_u.wav"}, {-113,"A/A#M_u.wav"}, {-114,"A/A#m__u.wav"}, {-115,"A/A#7_u.wav"}, {-116,"A/A#sus4_u.wav"}, {-117,"A/A#M7_u.wav"}, {-118,"A/A#m7__u.wav"}, {-119,"A/A#7sus4_u.wav"}, {-121,"A/Baug_u.wav"}, {-122,"A/Bdim_u.wav"}, {-123,"A/BM_u.wav"}, {-124,"A/Bm__u.wav"}, {-125,"A/B7_u.wav"}, {-126,"A/Bsus4_u.wav"}, {-127,"A/BM7_u.wav"}, {-128,"A/Bm7__u.wav"}, {-129,"A/B7sus4_u.wav"}, {1,"A/free_d.wav"}, // A/free_d.wav {11,"A/Caug_d.wav"}, {12,"A/Cdim_d.wav"}, {13,"A/CM_d.wav"}, {14,"A/Cm__d.wav"}, {15,"A/C7_d.wav"}, {16,"A/Csus4_d.wav"}, {17,"A/CM7_d.wav"}, {18,"A/Cm7__d.wav"}, {19,"A/C7sus4_d.wav"}, {21,"A/C#aug_d.wav"}, {22,"A/C#dim_d.wav"}, {23,"A/C#M_d.wav"}, {24,"A/C#m__d.wav"}, {25,"A/C#7_d.wav"}, {26,"A/C#sus4_d.wav"}, {27,"A/C#M7_d.wav"}, {28,"A/C#m7__d.wav"}, {29,"A/C#7sus4_d.wav"}, {31,"A/Daug_d.wav"}, {32,"A/Ddim_d.wav"}, {33,"A/DM_d.wav"}, {34,"A/Dm__d.wav"}, {35,"A/D7_d.wav"}, {36,"A/Dsus4_d.wav"}, {37,"A/DM7_d.wav"}, {38,"A/Dm7__d.wav"}, {39,"A/D7sus4_d.wav"}, {41,"A/D#aug_d.wav"}, {42,"A/D#dim_d.wav"}, {43,"A/D#M_d.wav"}, {44,"A/D#m__d.wav"}, {45,"A/D#7_d.wav"}, {46,"A/D#sus4_d.wav"}, {47,"A/D#M7_d.wav"}, {48,"A/D#m7__d.wav"}, {49,"A/D#7sus4_d.wav"}, {51,"A/Eaug_d.wav"}, {52,"A/Edim_d.wav"}, {53,"A/EM_d.wav"}, {54,"A/Em__d.wav"}, {55,"A/E7_d.wav"}, {56,"A/Esus4_d.wav"}, {57,"A/EM7_d.wav"}, {58,"A/Em7__d.wav"}, {59,"A/E7sus4_d.wav"}, {61,"A/Faug_d.wav"}, {62,"A/Fdim_d.wav"}, {63,"A/FM_d.wav"}, {64,"A/Fm__d.wav"}, {65,"A/F7_d.wav"}, {66,"A/Fsus4_d.wav"}, {67,"A/FM7_d.wav"}, {68,"A/Fm7__d.wav"}, {69,"A/F7sus4_d.wav"}, {71,"A/F#aug_d.wav"}, {72,"A/F#dim_d.wav"}, {73,"A/F#M_d.wav"}, {74,"A/F#m__d.wav"}, {75,"A/F#7_d.wav"}, {76,"A/F#sus4_d.wav"}, {77,"A/F#M7_d.wav"}, {78,"A/F#m7__d.wav"}, {79,"A/F#7sus4_d.wav"}, {81,"A/Gaug_d.wav"}, {82,"A/Gdim_d.wav"}, {83,"A/GM_d.wav"}, {84,"A/Gm__d.wav"}, {85,"A/G7_d.wav"}, {86,"A/Gsus4_d.wav"}, {87,"A/GM7_d.wav"}, {88,"A/Gm7__d.wav"}, {89,"A/G7sus4_d.wav"}, {91,"A/G#aug_d.wav"}, {92,"A/G#dim_d.wav"}, {93,"A/G#M_d.wav"}, {94,"A/G#m__d.wav"}, {95,"A/G#7_d.wav"}, {96,"A/G#sus4_d.wav"}, {97,"A/G#M7_d.wav"}, {98,"A/G#m7__d.wav"}, {99,"A/G#7sus4_d.wav"}, {101,"A/Aaug_d.wav"}, {102,"A/Adim_d.wav"}, {103,"A/AM_d.wav"}, {104,"A/Am__d.wav"}, {105,"A/A7_d.wav"}, {106,"A/Asus4_d.wav"}, {107,"A/AM7_d.wav"}, {108,"A/Am7__d.wav"}, {109,"A/A7sus4_d.wav"}, {111,"A/A#aug_d.wav"}, {112,"A/A#dim_d.wav"}, {113,"A/A#M_d.wav"}, {114,"A/A#m__d.wav"}, {115,"A/A#7_d.wav"}, {116,"A/A#sus4_d.wav"}, {117,"A/A#M7_d.wav"}, {118,"A/A#m7__d.wav"}, {119,"A/A#7sus4_d.wav"}, {121,"A/Baug_d.wav"}, {122,"A/Bdim_d.wav"}, {123,"A/BM_d.wav"}, {124,"A/Bm__d.wav"}, {125,"A/B7_d.wav"}, {126,"A/Bsus4_d.wav"}, {127,"A/BM7_d.wav"}, {128,"A/Bm7__d.wav"}, {129,"A/B7sus4_d.wav"}, {-1001,"E/free_u.wav"},// E/free_u.wav {-1011,"E/Caug_u.wav"}, {-1012,"E/Cdim_u.wav"}, {-1013,"E/CM_u.wav"}, {-1014,"E/Cm__u.wav"}, {-1015,"E/C7_u.wav"}, {-1016,"E/Csus4_u.wav"}, {-1017,"E/CM7_u.wav"}, {-1018,"E/Cm7__u.wav"}, {-1019,"E/C7sus4_u.wav"}, {-1021,"E/C#aug_u.wav"}, {-1022,"E/C#dim_u.wav"}, {-1023,"E/C#M_u.wav"}, {-1024,"E/C#m__u.wav"}, {-1025,"E/C#7_u.wav"}, {-1026,"E/C#sus4_u.wav"}, {-1027,"E/C#M7_u.wav"}, {-1028,"E/C#m7__u.wav"}, {-1029,"E/C#7sus4_u.wav"}, {-1031,"E/Daug_u.wav"}, {-1032,"E/Ddim_u.wav"}, {-1033,"E/DM_u.wav"}, {-1034,"E/Dm__u.wav"}, {-1035,"E/D7_u.wav"}, {-1036,"E/Dsus4_u.wav"}, {-1037,"E/DM7_u.wav"}, {-1038,"E/Dm7__u.wav"}, {-1039,"E/D7sus4_u.wav"}, {-1041,"E/D#aug_u.wav"}, {-1042,"E/D#dim_u.wav"}, {-1043,"E/D#M_u.wav"}, {-1044,"E/D#m__u.wav"}, {-1045,"E/D#7_u.wav"}, {-1046,"E/D#sus4_u.wav"}, {-1047,"E/D#M7_u.wav"}, {-1048,"E/D#m7__u.wav"}, {-1049,"E/D#7sus4_u.wav"}, {-1051,"E/Eaug_u.wav"}, {-1052,"E/Edim_u.wav"}, {-1053,"E/EM_u.wav"}, {-1054,"E/Em__u.wav"}, {-1055,"E/E7_u.wav"}, {-1056,"E/Esus4_u.wav"}, {-1057,"E/EM7_u.wav"}, {-1058,"E/Em7__u.wav"}, {-1059,"E/E7sus4_u.wav"}, {-1061,"E/Faug_u.wav"}, {-1062,"E/Fdim_u.wav"}, {-1063,"E/FM_u.wav"}, {-1064,"E/Fm__u.wav"}, {-1065,"E/F7_u.wav"}, {-1066,"E/Fsus4_u.wav"}, {-1067,"E/FM7_u.wav"}, {-1068,"E/Fm7__u.wav"}, {-1069,"E/F7sus4_u.wav"}, {-1071,"E/F#aug_u.wav"}, {-1072,"E/F#dim_u.wav"}, {-1073,"E/F#M_u.wav"}, {-1074,"E/F#m__u.wav"}, {-1075,"E/F#7_u.wav"}, {-1076,"E/F#sus4_u.wav"}, {-1077,"E/F#M7_u.wav"}, {-1078,"E/F#m7__u.wav"}, {-1079,"E/F#7sus4_u.wav"}, {-1081,"E/Gaug_u.wav"}, {-1082,"E/Gdim_u.wav"}, {-1083,"E/GM_u.wav"}, {-1084,"E/Gm__u.wav"}, {-1085,"E/G7_u.wav"}, {-1086,"E/Gsus4_u.wav"}, {-1087,"E/GM7_u.wav"}, {-1088,"E/Gm7__u.wav"}, {-1089,"E/G7sus4_u.wav"}, {-1091,"E/G#aug_u.wav"}, {-1092,"E/G#dim_u.wav"}, {-1093,"E/G#M_u.wav"}, {-1094,"E/G#m__u.wav"}, {-1095,"E/G#7_u.wav"}, {-1096,"E/G#sus4_u.wav"}, {-1097,"E/G#M7_u.wav"}, {-1098,"E/G#m7__u.wav"}, {-1099,"E/G#7sus4_u.wav"}, {-1101,"E/Aaug_u.wav"}, {-1102,"E/Adim_u.wav"}, {-1103,"E/AM_u.wav"}, {-1104,"E/Am__u.wav"}, {-1105,"E/A7_u.wav"}, {-1106,"E/Asus4_u.wav"}, {-1107,"E/AM7_u.wav"}, {-1108,"E/Am7__u.wav"}, {-1109,"E/A7sus4_u.wav"}, {-1111,"E/A#aug_u.wav"}, {-1112,"E/A#dim_u.wav"}, {-1113,"E/A#M_u.wav"}, {-1114,"E/A#m__u.wav"}, {-1115,"E/A#7_u.wav"}, {-1116,"E/A#sus4_u.wav"}, {-1117,"E/A#M7_u.wav"}, {-1118,"E/A#m7__u.wav"}, {-1119,"E/A#7sus4_u.wav"}, {-1121,"E/Baug_u.wav"}, {-1122,"E/Bdim_u.wav"}, {-1123,"E/BM_u.wav"}, {-1124,"E/Bm__u.wav"}, {-1125,"E/B7_u.wav"}, {-1126,"E/Bsus4_u.wav"}, {-1127,"E/BM7_u.wav"}, {-1128,"E/Bm7__u.wav"}, {-1129,"E/B7sus4_u.wav"}, {1001,"E/free_d.wav"},// E/free_d.wav {1011,"E/Caug_d.wav"}, {1012,"E/Cdim_d.wav"}, {1013,"E/CM_d.wav"}, {1014,"E/Cm__d.wav"}, {1015,"E/C7_d.wav"}, {1016,"E/Csus4_d.wav"}, {1017,"E/CM7_d.wav"}, {1018,"E/Cm7__d.wav"}, {1019,"E/C7sus4_d.wav"}, {1021,"E/C#aug_d.wav"}, {1022,"E/C#dim_d.wav"}, {1023,"E/C#M_d.wav"}, {1024,"E/C#m__d.wav"}, {1025,"E/C#7_d.wav"}, {1026,"E/C#sus4_d.wav"}, {1027,"E/C#M7_d.wav"}, {1028,"E/C#m7__d.wav"}, {1029,"E/C#7sus4_d.wav"}, {1031,"E/Daug_d.wav"}, {1032,"E/Ddim_d.wav"}, {1033,"E/DM_d.wav"}, {1034,"E/Dm__d.wav"}, {1035,"E/D7_d.wav"}, {1036,"E/Dsus4_d.wav"}, {1037,"E/DM7_d.wav"}, {1038,"E/Dm7__d.wav"}, {1039,"E/D7sus4_d.wav"}, {1041,"E/D#aug_d.wav"}, {1042,"E/D#dim_d.wav"}, {1043,"E/D#M_d.wav"}, {1044,"E/D#m__d.wav"}, {1045,"E/D#7_d.wav"}, {1046,"E/D#sus4_d.wav"}, {1047,"E/D#M7_d.wav"}, {1048,"E/D#m7__d.wav"}, {1049,"E/D#7sus4_d.wav"}, {1051,"E/Eaug_d.wav"}, {1052,"E/Edim_d.wav"}, {1053,"E/EM_d.wav"}, {1054,"E/Em__d.wav"}, {1055,"E/E7_d.wav"}, {1056,"E/Esus4_d.wav"}, {1057,"E/EM7_d.wav"}, {1058,"E/Em7__d.wav"}, {1059,"E/E7sus4_d.wav"}, {1061,"E/Faug_d.wav"}, {1062,"E/Fdim_d.wav"}, {1063,"E/FM_d.wav"}, {1064,"E/Fm__d.wav"}, {1065,"E/F7_d.wav"}, {1066,"E/Fsus4_d.wav"}, {1067,"E/FM7_d.wav"}, {1068,"E/Fm7__d.wav"}, {1069,"E/F7sus4_d.wav"}, {1071,"E/F#aug_d.wav"}, {1072,"E/F#dim_d.wav"}, {1073,"E/F#M_d.wav"}, {1074,"E/F#m__d.wav"}, {1075,"E/F#7_d.wav"}, {1076,"E/F#sus4_d.wav"}, {1077,"E/F#M7_d.wav"}, {1078,"E/F#m7__d.wav"}, {1079,"E/F#7sus4_d.wav"}, {1081,"E/Gaug_d.wav"}, {1082,"E/Gdim_d.wav"}, {1083,"E/GM_d.wav"}, {1084,"E/Gm__d.wav"}, {1085,"E/G7_d.wav"}, {1086,"E/Gsus4_d.wav"}, {1087,"E/GM7_d.wav"}, {1088,"E/Gm7__d.wav"}, {1089,"E/G7sus4_d.wav"}, {1091,"E/G#aug_d.wav"}, {1092,"E/G#dim_d.wav"}, {1093,"E/G#M_d.wav"}, {1094,"E/G#m__d.wav"}, {1095,"E/G#7_d.wav"}, {1096,"E/G#sus4_d.wav"}, {1097,"E/G#M7_d.wav"}, {1098,"E/G#m7__d.wav"}, {1099,"E/G#7sus4_d.wav"}, {1101,"E/Aaug_d.wav"}, {1102,"E/Adim_d.wav"}, {1103,"E/AM_d.wav"}, {1104,"E/Am__d.wav"}, {1105,"E/A7_d.wav"}, {1106,"E/Asus4_d.wav"}, {1107,"E/AM7_d.wav"}, {1108,"E/Am7__d.wav"}, {1109,"E/A7sus4_d.wav"}, {1111,"E/A#aug_d.wav"}, {1112,"E/A#dim_d.wav"}, {1113,"E/A#M_d.wav"}, {1114,"E/A#m__d.wav"}, {1115,"E/A#7_d.wav"}, {1116,"E/A#sus4_d.wav"}, {1117,"E/A#M7_d.wav"}, {1118,"E/A#m7__d.wav"}, {1119,"E/A#7sus4_d.wav"}, {1121,"E/Baug_d.wav"}, {1122,"E/Bdim_d.wav"}, {1123,"E/BM_d.wav"}, {1124,"E/Bm__d.wav"}, {1125,"E/B7_d.wav"}, {1126,"E/Bsus4_d.wav"}, {1127,"E/BM7_d.wav"}, {1128,"E/Bm7__d.wav"}, {1129,"E/B7sus4_d.wav"} }; /////////////////////////////////////receive code list(Subcore 3)/////////////////////////////////////////////////////// #define MSGLEN 200 struct MyPacket { volatile int status; /* 0:ready, 1:busy */ char message[MSGLEN]; }; std::vector<int16_t> number(char* str) { char *ptr; std::vector<int16_t> v; v.clear(); // _を区切りに文字列を分割 // 1回目 ptr = strtok(str, "_"); v.push_back(atoi(ptr)); //printf("ptr = %s\n",ptr); // 2回目以降 while(ptr != NULL) { // strtok関数により変更されたNULLのポインタが先頭 ptr = strtok(NULL, "_"); // ptrがNULLの場合エラーが発生するので対処 if(ptr != NULL) { v.push_back(atoi(ptr)); //printf("ptr,i =%s,%d\n", ptr,i); } } return v; } /////////////////////////////////////////// Main ////////////////////////////////////////////////////////////////////// #define Swich_12 (12)//音源 #define Swich_13 (13)//beginner_mode PlayGuitar inst(table, 437); void setup() { // シリアル通信設定 Serial.begin(2000000); inst.setup(); inst.begin(); pinMode(Swich_12, INPUT); pinMode(Swich_13, INPUT); // マルチコア起動 int ret1 = 0; int ret2 = 0; int ret3 = 0; ret1 = MP.begin(subcore1); ret2 = MP.begin(subcore2); ret3 = MP.begin(subcore3); MP.RecvTimeout(MP_RECV_POLLING); // 起動エラー if (ret1 < 0) printf("MP.begin 1 error = %d\n", ret1); if (ret2 < 0) printf("MP.begin 2 error = %d\n", ret2); if (ret3 < 0) printf("MP.begin 3 error = %d\n", ret3); printf("start\n"); inst.sendNoteOn(999,-100); } bool speed_up = false; uint32_t rcvdata2; long sub2_time = 0; bool beginner_mode_started = false; std::vector<int16_t> code_list; uint16_t list_count = 0; int16_t note = 10000; int16_t volume = 0; void loop() { //////////////////////加速判定 (subcore 2)////////////////////////// long now_time = micros(); double receive_time_million = now_time - sub2_time; double receive_time = receive_time_million/1000000; if(receive_time>0.08) { int ret2; int8_t rcvid; MP.RecvTimeout(MP_RECV_POLLING); ret2 = MP.Recv(&rcvid, &rcvdata2, subcore2); // SubCore2からmsg受信 if (ret2 < 0) { //printf("encorder can"); } else { sub2_time = micros(); printf("Encorder data=%d \n", rcvdata2); //音量の変更 if(rcvdata2 == 1||rcvdata2 == 2) { volume = 50; } else { volume = -100; } speed_up = true; } } ///////////////////////receive code list (subcore 3)////////////////////////// MP.RecvTimeout(MP_RECV_POLLING); int ret3; int8_t msgid; MyPacket *packet; /* Receive message from SubCore */ ret3 = MP.Recv(&msgid, &packet, subcore3); if (ret3 > 0) { //printf("%s\n", packet->message); char* note_message = packet->message; code_list = number(note_message); printf("size = %d\n",code_list.size()); for(int i=0;i< code_list.size();i++) { printf("%d,",code_list[i]); } printf("\n"); /* status -> ready */ packet->status = 0; inst.sendNoteOff(); inst.sendNoteOn(1001,-100); } /////////////////////////////////////////////////////////////// bool beginner_mode = digitalRead(Swich_13); if(beginner_mode_started == true&&beginner_mode == false) { beginner_mode_started = false; } if(speed_up) { //////////// beginner mode /////////////////////////////////////// if(beginner_mode) { printf("beginner mode = %d\n",beginner_mode_started); if(code_list.size()>0) { if(!beginner_mode_started) { list_count = 0; note = code_list[list_count]; beginner_mode_started = true; } else { list_count = list_count+1; if(list_count == code_list.size()) { list_count = 0; } note = code_list[list_count]; } } } /////////////// code or manual mode (subcore 1)////////////////////////// else { int ret1; uint32_t rcvdata = 1; int8_t rcvid = 1; ret1 = MP.Send(rcvid,rcvdata,subcore1); if(ret1<0) printf("fail connect SubCore1\n"); else { //Serial.println("to 1 send success"); MP.RecvTimeout(MP_RECV_BLOCKING); ret1 = MP.Recv(&rcvid, &rcvdata, subcore1); // SubCore1からmsg受 if(ret1<0) printf("can't receive SubCore1\n"); else { note = rcvdata; printf("from SubCore1 data %d\n",note); } } } ////////////////// 音を鳴らす ////////////////////// if(note == 10000) { } else { // elekiの場合 bool Acoustic = digitalRead(Swich_12); if(!Acoustic) { note = note + 1000; } //ダウンストロークの場合 if(rcvdata2 == 2||rcvdata2 == 3) { note = -note; } printf("play_number= %d\n",note); inst.sendNoteOff(); inst.sendNoteOn(note,volume); } speed_up = false; } inst.update(); return; } ``` ### SubCore1 SubCore1では、タッチセンサの触れている場所を検出しています。 以下の動画のように1つずつタッチセンサに触れていった場合、タッチセンサの値はそれぞれ以下の図のように変化します。 @[twitter](https://twitter.com/teamSOTA655/status/1752250531324838008) ![タッチセンサの読み取り結果](https://camo.elchika.com/4c740b4034705b202e2c5bfd6f23fd669e57668a/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f63363864633033382d383131612d343835302d616530302d373763653630313137356130/) 触れていないときの値と触れたときの差が閾値以上である場所を読み取り、これが5回中3回検出されたとき、その場所のタッチセンサに触れていると判定します。同時押しも読み取り可能です。 ```arduino:SubCore1 #if (SUBCORE != 1) // SubCoreの指定 #error "Core selection is wrong!!" #endif #include <MP.h> #include <vector> using namespace std; class Touch_Sensor { private: //pin番号 const uint8_t SRCLK = 2; const uint8_t RCLK = 3; const uint8_t G = 4; const uint8_t SER = 5; const uint8_t input_pin = 6; public: struct Touch_data { uint8_t note; vector<uint8_t> touch_number; }; int8_t touch_num = 88; //初期設定 void setup(); //filter処理 void filter(); //タッチセンサ処理 void get_sensor(bool setup); //押されたタッチセンサ番号 vector<uint8_t> touch_number; uint8_t filter_k = 5; // 総フレーム数 uint8_t filter_threshold= 3; // このフレーム数反応した場所をtouch_number_filterに追加 vector<uint8_t> touch_number_k[5]; // 各フレームで反応したタッチセンサの場所 vector<uint8_t> touch_number_filter; }; void Touch_Sensor::setup() { pinMode(SRCLK, OUTPUT);//SRCLK pinMode(RCLK, OUTPUT);//RCLK pinMode(SER, OUTPUT);//SER pinMode(G, OUTPUT);//G pinMode(input_pin, INPUT);//input_pin for(uint8_t j = 0; j < 5; j++) { get_sensor(true); } for (uint8_t k=0; k<filter_k; k++) { touch_number_k[k].clear(); } Serial.println("touch sensor setup end"); } // 数フレームののタッチセンサの値で押弦判断 void Touch_Sensor::filter() { static uint8_t touch_k[88]={0}; touch_number_filter.clear(); // 最初のフレームでタッチした場所を-1引く for(uint8_t j = 0; j<touch_number_k[0].size(); j++) { for(uint8_t i = 0; i < touch_num; i++) { if(touch_number_k[0][j]==i) touch_k[i] = touch_k[i]-1; } } // 前に詰める for(uint8_t k=0; k < (filter_k-1); k++) { touch_number_k[k].clear(); for (uint8_t j=0; j< touch_number_k[k+1].size(); j++) { touch_number_k[k].push_back(touch_number_k[k+1][j]); } } // 最新のフレームでタッチされた場所 get_sensor(false); touch_number_k[filter_k-1].clear(); for (uint8_t j=0; j< touch_number.size(); j++) { touch_number_k[filter_k-1].push_back(touch_number[j]); } // 最新のフレームでタッチした場所を1足す for(uint8_t j = 0; j<touch_number_k[filter_k-1].size(); j++) { for(uint8_t i = 0; i < touch_num; i++) { if(touch_number_k[filter_k-1][j]==i) touch_k[i] = touch_k[i]+1; } } // 各フレームでのタッチした位置確認用 for (uint8_t k=0; k<filter_k; k++) { printf("touch_number %d:",k+1); for (uint8_t j=0; j<touch_number_k[k].size();j++) { printf("%d,",touch_number_k[k][j]); } printf("\n"); } // 閾値(filter_threshold)を超えた場所をtouch_number_filterへ for(uint8_t i = 0; i < touch_num; i++) { if(touch_k[i] >= filter_threshold) touch_number_filter.push_back(i); } } void Touch_Sensor::get_sensor(bool setup) { //タッチセンサの値 static uint8_t touch_flag[88]; //閾値 static uint8_t no_touch[88] = {0}; // static uint8_t no_touch_u[88] = {0}; //タッチセンサの初期化 for(uint8_t i = 0; i < touch_num; i++) { touch_flag[i] = 0; } touch_number.clear(); delayMicroseconds(150); // 入力状態をHIGHにする digitalWrite(SER, HIGH); for (uint8_t i=0; i<touch_num; i++) { // 閾値決定のための変数 uint8_t sub_threshold = 1; // シフトレジスタをシフトさせてSERピンの状態を記憶させる digitalWrite(SRCLK, LOW); delayMicroseconds(2); digitalWrite(SRCLK, HIGH); // シフトレジスタの状態をストレージレジスタへ反映させる digitalWrite(RCLK, LOW); delayMicroseconds(2); digitalWrite(RCLK, HIGH); //出力を有効にする digitalWrite(G, LOW); while(digitalRead(input_pin) != HIGH) { if (setup) { sub_threshold++; } else { touch_flag[i]++; } } if (setup) { // printf("%d",i); //最大値を閾値とする if(sub_threshold > no_touch[i]){ no_touch[i] = sub_threshold; }
// if(sub_threshold < no_touch_u[i]){ // no_touch_u[i] = sub_threshold; // }
} else { uint8_t threshold_u = (no_touch[i]/4)*5; uint8_t threshold_s = (no_touch[i]/4)*2; if((touch_flag[i] > threshold_u)||(touch_flag[i]<threshold_s))// no_touchが30のとき 7.5 60のとき15 { touch_number.push_back(i); }
// if(touch_flag[i]>(5/4)*no_touch[i]||touch_flag[i]<(3/4)*no_touch_u[i]) // { // touch_number.push_back(i); // }
} delayMicroseconds(50); //出力を無効にする digitalWrite(G, HIGH); // 放電するまで待つ delayMicroseconds(200); // 以降の入力状態をLOWにする digitalWrite(SER, LOW); } // 閾値(no_touch)確認用 printf("no_touch\n"); for(int i=0;i<88;i++){ printf("%d, ",no_touch[i]); } printf("\n"); // タッチセンサの値確認用 for(int i=0;i<88;i++){ printf("%d, ",touch_flag[i]); } printf("\n"); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const uint16_t code_mode_size = 109; const Touch_Sensor::Touch_data code_mode[109] = { {1, {}}, {11, {0}}, {12, {1}}, {13, {2}}, {14, {3}}, {15, {4}}, {16, {5}}, {17, {2,4}}, {18, {3,4}}, {19, {4,5}}, {21, {6}}, {22, {7}}, {23, {8}}, {24, {9}}, {25, {10}}, {26, {11}}, {27, {8,10}}, {28, {9,10}}, {29, {10,11}}, {31, {12}}, {32, {13}}, {33, {14}}, {34, {15}}, {35, {16}}, {36, {17}}, {37, {14,16}}, {38, {15,16}}, {39, {16,17}}, {41, {18}}, {42, {19}}, {43, {20}}, {44, {21}}, {45, {22}}, {46, {23}}, {47, {20,22}}, {48, {21,22}}, {49, {22,23}}, {51, {24}}, {52, {25}}, {53, {26}}, {54, {27}}, {55, {28}}, {56, {29}}, {57, {26,28}}, {58, {27,28}}, {59, {28,29}}, {61, {30}}, {62, {31}}, {63, {32}}, {64, {33}}, {65, {34}}, {66, {35}}, {67, {32,34}}, {68, {33,34}}, {69, {34,35}}, {71, {36}}, {72, {37}}, {73, {38}}, {74, {39}}, {75, {40}}, {76, {41}}, {77, {38,40}}, {78, {39,40}}, {79, {40,41}}, {81, {42}}, {82, {43}}, {83, {44}}, {84, {45}}, {85, {46}}, {86, {47}}, {87, {44,46}}, {88, {45,46}}, {89, {46,47}}, {91, {48}}, {92, {49}}, {93, {50}}, {94, {51}}, {95, {52}}, {96, {53}}, {97, {50,52}}, {98, {51,52}}, {99, {52,53}}, {101, {54}}, {102, {55}}, {103, {56}}, {104, {57}}, {105, {58}}, {106, {59}}, {107, {56,58}}, {108, {57,58}}, {109, {58,59}}, {111, {60}}, {112, {61}}, {113, {62}}, {114, {63}}, {115, {64}}, {116, {65}}, {117, {62,64}}, {118, {63,64}}, {119, {64,65}}, {121, {66}}, {122, {67}}, {123, {68}}, {124, {69}}, {125, {70}}, {126, {71}}, {127, {68,70}}, {128, {69,70}}, {129, {70,71}} }; ///////////////////////////// const uint16_t manual_mode_size = 225; const Touch_Sensor::Touch_data manual_mode[225] = { {1, {}}, {11, {3,4,8,13}}, {11, {42,51,52,56}}, {12, {13,20,22,27}}, {12, {40,42,45}}, {13, {4,8,13}}, {13, {13,14,15,16,17,26,27,28}}, {14, {2,4,13}}, {14, {13,14,15,16,17,22,26,27}}, {14, {42,43,44,45,46,47,55,56}}, {15, {4,8,13,15}}, {15, {13,14,15,16,17,26,28}}, {16, {4,5,13,14}}, {16, {13,14,15,16,17,26,27}}, {17, {8,13}}, {17, {42,46,50,51}}, {18, {13,14,15,16,17,22,26}}, {18, {42,43,44,45,46,47,55}}, {19, {13,14,15,16,17,26,34}}, {19, {42,43,44,45,46,47,55,57}}, {21, {9,10,14,19}}, {21, {53,57,58,62}}, {22, {19,26,28,33}}, {22, {46,48,51}}, {23, {19,20,21,22,23,32,33,34}}, {23, {48,49,50,51,52,53,57,61,62}}, {24, {19,20,21,22,23,28,32,33}}, {24, {48,49,50,51,52,53,61,62}}, {25, {19,20,21,22,23,32,34}}, {25, {48,49,50,51,52,53,57,61}}, {26, {19,20,21,22,23,32,33,40}}, {26, {48,49,50,51,52,53,61,62,63}}, {27, {19,20,21,22,23,27,32,34}}, {27, {48,52,56,57}}, {28, {3,8,19}}, {28, {19,20,21,22,23,28,32}}, {28, {48,49,50,51,52,53,61}}, {29, {19,20,21,22,23,32,40}}, {29, {48,49,50,51,52,53,61,63}}, {31, {11,15,16}}, {31, {54,63,64,68}}, {32, {3,5}}, {32, {25,32,34,39}}, {33, {9,11,16}}, {33, {25,29,38,39,40}}, {34, {5,9,16}}, {34, {25,26,27,28,29,34,38,39}}, {35, {4,9,11}}, {35, {25,26,27,28,29,38,40}}, {36, {9,16,17}}, {36, {25,26,27,28,29,38,39}}, {37, {9,10,11}}, {37, {25,26,27,28,29,33,38,40}}, {38, {4,5,9}}, {38, {25,26,27,28,29,34,38}}, {39, {4,9,17}}, {39, {25,26,27,28,29,38}}, {41, {2,17}}, {41, {21,22,26,31}}, {41, {65,69,70,74}}, {42, {31,38,40,45}}, {42, {58,60,63}}, {43, {15,17,22,26,31}}, {43, {31,32,33,34,35,44,45,46}}, {44, {31,32,33,34,35,40,44,45}}, {44, {60,61,62,63,64,65,73,74}}, {45, {31,32,33,34,35,44,46}}, {45, {60,61,62,63,64,65,69,73}}, {46, {31,32,33,34,35,44,45,52}}, {46, {60,61,62,63,64,65,73,74,75}}, {47, {31,32,33,34,35,39,44,46}}, {47, {60,64,68,69}}, {48, {31,32,33,34,35,40,44}}, {48, {60,62,63,64}}, {49, {31,32,33,34,35,44,52}}, {49, {60,61,62,63,64,65,73,75}}, {51, {3,4,8,13}}, {51, {37,47,51,52}}, {52, {8,15,17}}, {52, {37,44,46,51}}, {53, {3,7,8}}, {53, {37,38,39,40,41,50,51,52}}, {54, {7,8}}, {54, {37,38,39,40,41,46,50,51}}, {55, {3,7}}, {55, {37,38,39,40,41,50,52}}, {56, {7,8,9}}, {56, {37,38,39,40,41,50,51}}, {57, {2,3,7}}, {57, {37,38,39,40,41,45,50,52}}, {58, {7}}, {58, {26,27,28,37,39}}, {59, {7,9}}, {59, {37,38,39,40,41,50}}, {61, {0,9,10,14}}, {61, {33,34,38,43}}, {62, {3,5,14}}, {62, {43,50,52,57}}, {63, {0,1,2,3,4,5,9,13,14}}, {63, {43,44,45,46,47,56,57,58}}, {64, {0,1,2,3,4,5,13,14}}, {64, {43,44,45,46,47,52,56,57}}, {65, {0,1,2,3,4,5,9,19}}, {65, {43,44,45,46,47,56,58}}, {66, {0,1,2,3,4,5,13,14,15}}, {66, {43,44,45,46,47,56,57}}, {67, {0,4,8,9}}, {67, {43,44,45,46,47,51,56,58}}, {68, {0,1,2,3,4,5,13}}, {68, {32,33,34,43,45}}, {69, {0,1,2,3,4,5,13,15}}, {69, {43,44,45,46,47,56}}, {71, {11,15,16,20}}, {71, {39,40,44,49}}, {72, {4,6,9}}, {72, {49,56,58,63}}, {73, {6,7,8,9,10,11,15,19,20}}, {73, {49,50,51,52,53,62,63,64}}, {74, {6,7,8,9,10,11,19,20}}, {74, {49,50,51,52,53,58,62,63}}, {75, {10,15,20}}, {75, {6,7,8,9,10,11,15,19}}, {75, {49,50,51,52,53,62,64}}, {76, {6,7,8,9,10,11,19,20,21}}, {76, {49,50,51,52,53,62,63}}, {77, {6,10,14,15}}, {77, {49,50,51,52,53,57,62,64}}, {78, {6,7,8,9,10,11,19}}, {78, {49,50,51,52,53,58,62}}, {79, {6,7,8,9,10,11,19,21}}, {79, {49,50,51,52,53,62}}, {81, {2,7,12,17}}, {81, {12,21,22,26}}, {82, {10,12,15}}, {82, {55,62,64,69}}, {83, {7,12,17}}, {83, {12,13,14,15,16,17,21,25,26}}, {84, {1,12}}, {84, {12,13,14,15,16,17,25,26}}, {84, {55,56,57,58,59,64,68,69}}, {85, {5,7,12}}, {85, {12,13,14,15,16,17,21,25}}, {86, {4,12,13,17}}, {86, {12,13,14,15,16,17,25,26,27}}, {87, {7,11,12}}, {87, {12,13,14,15,16,17,20,21,25}}, {88, {12,13,14,15,16,17,25}}, {88, {44,45,46,55,57}}, {89, {4,5,12,13}}, {89, {12,13,14,15,16,17,25,25}}, {91, {23,27,28,32}}, {91, {51,52,56,61}}, {92, {16,18,21}}, {92, {61,68,70,75}}, {93, {18,19,20,21,22,23,27,31,32}}, {93, {61,62,63,64,65,74,75,76}}, {94, {18,19,20,21,22,23,31,33}}, {94, {61,62,63,64,65,70,74,75}}, {95, {18,19,20,21,22,23,27,31}}, {95, {61,62,63,64,65,74,76}}, {96, {18,19,20,21,22,23,31,32,33}}, {96, {61,62,63,64,65,74,75}}, {97, {18,22,26,27}}, {97, {61,62,63,64,65,69,74,76}}, {98, {18,19,20,21,22,23,31}}, {98, {61,62,63,64,65,70,74}}, {99, {18,19,20,21,22,23,31,33}}, {99, {61,62,63,64,65,74}}, {101, {5,9,10,14}}, {101, {24,33,34,38}}, {102, {2,4,9}}, {102, {22,24,27}}, {103, {8,9,10}}, {103, {24,25,26,27,28,29,33,37,38}}, {104, {4,8,9}}, {104, {24,25,26,27,28,29,37,38}}, {105, {8,10}}, {105, {24,25,26,27,28,29,33,37}}, {106, {8,9,16}}, {106, {24,25,26,27,28,29,37,38,39}}, {107, {3,8,10}}, {107, {24,28,32,34}}, {108, {4,8}}, {108, {24,25,26,27,28,29,37}}, {109, {8,16}}, {109, {24,26,28,29,37,39}}, {111, {35,39,40,44}}, {111, {63,64,68,73}}, {112, {1,8,10,15}}, {112, {28,30,33}}, {113, {1,2,3,4,5,14,15,16}}, {113, {30,31,32,33,34,35,39,43,44}}, {114, {1,2,3,4,5,10,14,15}}, {114, {30,31,32,33,34,35,43,44}}, {115, {1,2,3,4,5,14,16}}, {115, {30,31,32,33,34,35,39,43}}, {116, {1,2,3,4,5,14,15,22}}, {116, {30,31,32,33,34,35,43,44,45}}, {117, {1,2,3,4,5,9,14,16}}, {117, {30,34,38,39}}, {118, {1,2,3,4,5,10,14}}, {118, {30,31,32,33,34,35,43}}, {119, {1,2,3,4,5,14,23}}, {119, {30,31,32,33,34,35,43,45}}, {121, {2,7,17}}, {121, {36,45,46,50}}, {122, {7,14,16,21}}, {122, {34,36,39}}, {123, {7,8,9,10,11,20,21,22}}, {123, {36,40,41,45,49,50}}, {124, {7,8,9,10,11,16,20,21}}, {124, {36,37,38,39,40,41,49,50}}, {125, {2,7,9,11}}, {125, {7,8,9,10,11,20,22}}, {125, {36,37,38,39,40,41,45,49}}, {126, {7,8,11}}, {126, {7,8,9,10,11,20,21,28}}, {126, {36,37,38,39,40,41,49,50,51}}, {127, {7,8,9,10,11,15,20,22}}, {127, {36,40,44,45}}, {128, {7,9,11}}, {128, {7,8,9,10,11,16,20}}, {129, {7,8,9,11}}, {129, {7,8,9,10,11,20,28}}, {129, {36,37,38,39,40,41,49,51}} }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Touch_Sensor touch_sensor; #define Swich_11 (11) int8_t ret1; void setup() { // シリアル通信設定 Serial.begin(2000000); while (!Serial); touch_sensor.setup(); pinMode(Swich_11, INPUT); // // マルチコア起動 ret1 = MP.begin(); MP.RecvTimeout(MP_RECV_POLLING); // 起動エラー if (ret1 < 0) printf("SubCore start error = %d\n", ret1); } void loop() { uint16_t note = 10000;
///////////////////////////////////////// filter なし用/////////////////////////////////////////////////////// // touch_sensor.get_sensor(false); // printf("touch_number:"); // for(uint8_t i=0;i<touch_sensor.touch_number.size();i++) // { // printf("%d,",touch_sensor.touch_number[i]); // } // printf("\n"); // // //対応した音の番号の出力 // bool Code_mode = digitalRead(Swich_11); // if(Code_mode) // { // for(uint8_t i=0;i<code_mode_size;i++) // { // if(touch_sensor.touch_number.size() == code_mode[i].touch_number.size() && // equal(touch_sensor.touch_number.cbegin(),touch_sensor.touch_number.cend(),code_mode[i].touch_number.cbegin())) // { // note = code_mode[i].note; // break; // } // // } // } // else // { // for(uint8_t i=0;i<manual_mode_size;i++) // { // if(touch_sensor.touch_number.size() == manual_mode[i].touch_number.size() && // equal(touch_sensor.touch_number.cbegin(),touch_sensor.touch_number.cend(),manual_mode[i].touch_number.cbegin())) // { // note = manual_mode[i].note; // break; // } // } // }
////////////////////////////////////////// filter用 ////////////////////////////////////////////////////// touch_sensor.filter(); printf("touch_number_filter:"); for(uint8_t i=0;i<touch_sensor.touch_number_filter.size();i++) { printf("%d,",touch_sensor.touch_number_filter[i]); } printf("\n"); // 対応した音の番号の出力 bool Code_mode = digitalRead(Swich_11); if(Code_mode) { for(uint8_t i=0;i<code_mode_size;i++) { if(touch_sensor.touch_number_filter.size() == code_mode[i].touch_number.size() && equal(touch_sensor.touch_number_filter.cbegin(),touch_sensor.touch_number_filter.cend(),code_mode[i].touch_number.cbegin())) { note = code_mode[i].note; break; } } } else { for(uint8_t i=0;i<manual_mode_size;i++) { if(touch_sensor.touch_number_filter.size() == manual_mode[i].touch_number.size() && equal(touch_sensor.touch_number_filter.cbegin(),touch_sensor.touch_number_filter.cend(),manual_mode[i].touch_number.cbegin())) { note = manual_mode[i].note; break; } } } ///////////////////////////////////////////////////////////////////////////////////////////// int8_t msgid; uint32_t msgdata; /* Echo back */ ret1 = MP.Recv(&msgid, &msgdata); // MainCoreからmsg受信 if (ret1 < 0) { //printf("SubCore1 fail connect Main = %d\n", ret1); } else { msgdata = note; ret1 = MP.Send(msgid, msgdata); //msg送信エラー if (ret1 < 0) printf("SubCore 1 send error = %d\n", ret1); else printf("SubCore1 send: id=%d data=%d\n", msgid, msgdata); } return; } ``` ### SubCore2 SubCore2ではエンコーダにより、ホイールの加速を検出します。エンコーダのa相、b相が変化するステップ時間を読み取り、ステップ時間の変化から加速を読み取ります。割り込み処理を利用し、読み飛ばしを防止しています。以下の動画のようにホイールを回転させると、加速度は図のように変化します。3つの図は縦軸のスケールが異なっており、閾値を赤線の位置としたとき、6回転させたことを読み取れます。 @[twitter](https://twitter.com/teamSOTA655/status/1752218361768157367) ![ホイールを回転させたときの加速度(※クリックで拡大)](https://camo.elchika.com/f8ca99b92821398570614b952df24ec72ae1e506/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f35656463636262312d343834612d343965352d613965392d653835366633653733373837/) ```arduino:SubCore2 ///////////////////////// SubCore2 ////////////////////////////////////////////////////////////////// #if (SUBCORE != 2) // SubCoreの指定 #error "Core selection is wrong!!" #endif #include <MP.h> // pin設定と回転方向 int pinA = 9; //CLK int pinB = 10;//DT const int CW = 1; const int CCW = -1; // update で使う int multiplication = 4;//逓倍 int resolution = 19;//分解能 int a_now=0; int b_now=0; int a_pri=0; int b_pri=0; // calculate_step_dtで使う bool first = true; double step_angular; int count = 0;//エンコーダカウント int direction_pri; // dtで使う double dt[6]; long t_now =0; long t_pri=0; // flagで使う(加速度判定) double ddt_now; // updateとflagで使う(速度判定) double flag_time = 0; bool velocity_check = false; int velocity_count; // param double threshold = 0.1; //加速判定閾値0.008 double flag_to_flag_time = 0.2; //加速判定後の次の加速までの最低時間[s] bool velocity_use = false; //速度判定行うかどうか double check_limit = 0.04; //加速判定後速度計測にかける時間[s] double velocity_count_threshold = 4;//check_limitでhighspeedと判定される最低ステップ数 int section_direction;// int theta_now = 0; bool highspeed = false; void encorder_setup(); void encorder_update(); void caluculate_step_dt(int direction_now); void dt_latest_update(); void dt_stock_update(); void flag(); void encorder_setup() { pinMode(pinA,INPUT); pinMode(pinB,INPUT); step_angular = 360/(resolution*multiplication); for(int i=0;i<6;i++) { dt[i]=0; } } void caluculate_step_dt(int direction_now) { if(first) { first = false; dt_latest_update();//最新ステップ変化時間を更新 section_direction = direction_now; } else { //Serial.print(direction_pri); //Serial.print(","); //Serial.print(direction_now); if(direction_pri != direction_now)// 同じ区間を行き来していたら { //Serial.print("direction"); dt_latest_update();//最新ステップ変化時間のみを更新 section_direction = section_direction + direction_now; } else { //Serial.print("#"); if(section_direction != 0)// 前回の角度変位が有効であれば(2ステップ同方向に変化した) { dt_stock_update(); flag(); } else// 前回の角度変位が無効なら(結果的に現在のステップしか変化していない) { //Serial.print("&"); dt_latest_update();//最新ステップ変化時間のみを更新 } section_direction = direction_now; } } direction_pri = direction_now; //count = count + direction_now; //theta_now = count*step_angular; } void encorder_update() { a_now = digitalRead(pinA); b_now = digitalRead(pinB); if(a_now != a_pri&&b_now != b_pri) { //Serial.print("skip"); } if(a_now != a_pri) { if(a_now != b_now){ //Serial.print("+a"); caluculate_step_dt(CW); } else if(a_now == b_now){ //Serial.print("-a"); caluculate_step_dt(CCW); } } if(b_now != b_pri){ if(b_now == a_now){ //Serial.print("+b"); caluculate_step_dt(CW); } else if(b_now != a_now){ //Serial.print("-b"); caluculate_step_dt(CCW); } } a_pri=a_now; b_pri=b_now; if(velocity_use) { if(velocity_check) { long time_now = micros(); double check_time_million=time_now-flag_time; double check_time = check_time_million/1000000; if(check_limit < check_time) { if(velocity_count < velocity_count_threshold-1) { Serial.println("0"); highspeed = false; } else if(velocity_count >= velocity_count_threshold) { Serial.println("1"); highspeed = true; } else { Serial.println("0.5"); } velocity_check = false; //return true; } } //return false; } else { if(velocity_check) { velocity_check = false; //Serial.print("FLAG!!!!!!!!!!!!!!!!"); //return true; } //return false; } } void dt_stock_update() { for(int i=0;i<5;i++) { dt[i] = dt[i+1]; } t_pri = t_now; t_now = micros(); double dt_million = t_now - t_pri; dt[5] = dt_million/1000000; //Serial.print(","); //Serial.print(dt[5],5); } void dt_latest_update() { t_now = micros(); double dt_million = t_now - t_pri; dt[5] = dt_million/1000000; //Serial.print(","); //Serial.print(dt[5],5); } void flag() { //Serial.println(""); if(!velocity_check) { ddt_now = (dt[0]-dt[4])+(dt[1]-dt[5]); //Serial.print(ddt_now,5); if (ddt_now >= threshold) { long now_time = micros(); double flag_to_now_time = (now_time-flag_time)/1000000; //Serial.println(flag_to_now_time); if(flag_to_now_time > flag_to_flag_time) { flag_time = micros(); velocity_check = true; velocity_count =0; check_limit = check_limit - dt[5]; send_maincore(); } } } else { velocity_count = velocity_count + 1; } } void send_maincore() { int ret2; int8_t msgid = 0; uint32_t msgdata = 0; Serial.print("flag!!!!!!!!!!!!!!!!!!"); if(section_direction>0 && !highspeed) msgdata = 0; else if(section_direction>0 && highspeed) msgdata = 1; else if(section_direction<0 && highspeed) msgdata = 2; else if(section_direction<0 && !highspeed) msgdata = 3; // printf("%d",msgdata); //printf("SubCore2 send: id=%d data=%d\n", msgid, msgdata); ret2 = MP.Send(msgid, msgdata); // // msg送信エラー if (ret2 < 0) printf("Subcore2 MP.Send error %d\n", ret2); } void setup() { int ret2 = 0; Serial.begin(2000000); while (!Serial); encorder_setup(); attachInterrupt(pinA,encorder_update,CHANGE);//3.1の場合はfalseと書かないとチャタリング処理される attachInterrupt(pinB,encorder_update,CHANGE); //マルチコア起動 ret2 = MP.begin(); MP.RecvTimeout(MP_RECV_POLLING); // 起動エラー if (ret2 < 0) printf("SubCore2 MP error = %d\n", ret2); } void loop() {
// int ret2; // int8_t msgid = 0; // uint32_t msgdata = 0; // if(encorder.update()) // { // Serial.print("flag!!!!!!!!!!!!!!!!!!"); // if(encorder.section_direction>0 && !encorder.highspeed) // msgdata = 0; // else if(encorder.section_direction>0 && encorder.highspeed) // msgdata = 1; // else if(encorder.section_direction<0 && encorder.highspeed) // msgdata = 2; // else if(encorder.section_direction<0 && !encorder.highspeed) // msgdata = 3; // // printf("%d",msgdata); // //printf("SubCore2 send: id=%d data=%d\n", msgid, msgdata); // // ret2 = MP.Send(msgid, msgdata); // // // msg送信エラー // // if (ret2 < 0) // // printf("MP.Send error Subcore2 %d\n", ret2); // } // return;
} ``` ### SubCore3 SubCore3ではWi-Fiで接続されたPCから送信されたコード譜の受信をしています。プログラム内では、SPRESENSEをWi-Fiアクセスポイントとして立ち上げています。 ```arduino:SubCore3 #if (SUBCORE != 3) // SubCoreの指定 #error "Core selection is wrong!!" #endif #include <GS2200Hal.h> #include <GS2200AtCmd.h> #include <TelitWiFi.h> #include "config.h" #include <MP.h> #include <vector> #define CONSOLE_BAUDRATE 2000000 // ここに受信したデータが入る extern uint8_t ESCBuffer[]; extern uint32_t ESCBufferCnt; ///////////////////////////////////////////////////////////////////////// char* pc_msg; #define MSGLEN 200 #define MY_MSGID 10 struct MyPacket { volatile int status; /* 0:ready, 1:busy */ char message[MSGLEN]; }; MyPacket packet; TelitWiFi gs2200; TWIFI_Params gsparams; ///////////////////////////////////////////////////////////////////////// void setup() { /* initialize digital pin LED_BUILTIN as an output. */ pinMode(LED0, OUTPUT); digitalWrite( LED0, LOW ); // turn the LED off (LOW is the voltage level) Serial.begin( CONSOLE_BAUDRATE ); // talk to PC /* Initialize SPI access of GS2200 */ Init_GS2200_SPI_type(iS110B_TypeC);//typeCに変更 /* Initialize AT Command Library Buffer */ gsparams.mode = ATCMD_MODE_LIMITED_AP;//アクセスポイントモード
//gsparams.mode = ATCMD_MODE_STATION;//ステーションモード
gsparams.psave = ATCMD_PSAVE_DEFAULT; if( gs2200.begin( gsparams ) ){ ConsoleLog( "GS2200 Initilization Fails" ); while(1); } /* GS2200 runs as AP *///アクセスポイントモード if( gs2200.activate_ap( AP_SSID, PASSPHRASE, AP_CHANNEL ) ){ ConsoleLog( "WiFi Network Fails" ); while(1); }
/* GS2200 Association to AP *///ステーションモード /*if( gs2200.activate_station( AP_SSID, PASSPHRASE ) ){ ConsoleLog( "Association Fails" ); while(1); }*/
digitalWrite( LED0, HIGH ); // turn on LED memset(&packet, 0, sizeof(packet)); int ret3; ret3 = MP.begin(); MP.RecvTimeout(MP_RECV_BLOCKING); // 起動エラー if (ret3 < 0) printf("SubCore3 start error = %d\n", ret3); delay(50); } // the loop function runs over and over again forever void loop() { ATCMD_RESP_E resp; char server_cid = 0, remote_cid=0; ATCMD_NetworkStatus networkStatus; uint32_t timer=0; resp = ATCMD_RESP_UNMATCH; ConsoleLog( "Start TCP Server"); resp = AtCmd_NSTCP( TCPSRVR_PORT, &server_cid); if (resp != ATCMD_RESP_OK) { ConsoleLog( "No Connect!" ); delay(2000); return; } if (server_cid == ATCMD_INVALID_CID) { ConsoleLog( "No CID!" ); delay(2000); return; } while( 1 ) { // ConsoleLog( "Waiting for TCP Client"); if( ATCMD_RESP_TCP_SERVER_CONNECT != WaitForTCPConnection( &remote_cid, 5000 ) ){ continue; } ConsoleLog( "TCP Client Connected"); // Prepare for the next chunck of incoming data WiFi_InitESCBuffer(); // Start the echo server int loopcnt=0; while( loopcnt++<100000 ){ while( Get_GPIO37Status() ){ resp = AtCmd_RecvResponse(); loopcnt=0; if( ATCMD_RESP_BULK_DATA_RX == resp ){ if( Check_CID( remote_cid ) ) { //ConsolePrintf( "Received : %s\r\n", ESCBuffer+1 ); pc_msg = ESCBuffer+1; printf("pc_msg : %s\n",pc_msg); int ret3; if (packet.status == 0) { packet.status = 1; snprintf(packet.message, MSGLEN, "%s",pc_msg); ret3 = MP.Send(MY_MSGID, &packet); if (ret3 < 0) { printf("MP.Send error = %d\n", ret3); } } if( ATCMD_RESP_OK != AtCmd_SendBulkData( remote_cid, ESCBuffer+1, ESCBufferCnt-1 ) ) { // Data is not sent, we need to re-send the data ConsolePrintf( "Sent Error : %s\r\n", ESCBuffer+1 ); delay(10); } } WiFi_InitESCBuffer(); } } } } } ``` config.shでWi-Fiアクセスポイントの設定をしています ```config:config.sh /* * config.h - WiFi Configration Header * * This work is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, but without any warranty; * without even the implied warranty of merchantability or fitness for a particular * purpose. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this work; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CONFIG_H_ #define _CONFIG_H_ /*-------------------------------------------------------------------------* * Configration *-------------------------------------------------------------------------*/ #define AP_SSID "linksys" #define PASSPHRASE "0123456789" #define AP_CHANNEL 6 #define TCPSRVR_PORT "10001" #endif /*_CONFIG_H_*/ ``` ## PC側 PC側ではコード譜をSPRESENSEに送信します。PCはWi-FiアクセスポイントになっているSPRESENSEに接続します。送信したいコード譜の書かれたtxtファイルを選択し、SPRESENSEに送信します。以下のようなGUIになっており操作しやすくなっています。 ![画面遷移](https://camo.elchika.com/6b1ec89f9e29eebd79fb9e8d95bca1e86bab4aca/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f35363830363635662d656238652d343935362d613133652d353563333234336639326235/) ```python:PC側のソースコード import PySimpleGUI as sg import sys import requests import re import os url = "http://192.168.1.99:10001" name = "team_SOTA" conversion_table = [[11,"Caug"], [12,"Cdim"], [13,"CM"], [14,"Cm"], [15,"C7"], [16,"Csus4"], [17,"CM7"], [18,"Cm7"], [19,"C7sus4"], [21,"C#aug"], [22,"C#dim"], [23,"C#M"], [24,"C#m"], [25,"C#7"], [26,"C#sus4"], [27,"C#M7"], [28,"C#m7"], [29,"C#7sus4"], [31,"Daug"], [32,"Ddim"], [33,"DM"], [34,"Dm"], [35,"D7"], [36,"Dsus4"], [37,"DM7"], [38,"Dm7"], [39,"D7sus4"], [41,"D#aug"], [42,"D#dim"], [43,"D#M"], [44,"D#m"], [45,"D#7"], [46,"D#sus4"], [47,"D#M7"], [48,"D#m7"], [49,"D#7sus4"], [51,"Eaug"], [52,"Edim"], [53,"EM"], [54,"Em"], [55,"E7"], [56,"Esus4"], [57,"EM7"], [58,"Em7"], [59,"E7sus4"], [61,"Faug"], [62,"Fdim"], [63,"FM"], [64,"Fm"], [65,"F7"], [66,"Fsus4"], [67,"FM7"], [68,"Fm7"], [69,"F7sus4"], [71,"F#aug"], [72,"F#dim"], [73,"F#M"], [74,"F#m"], [75,"F#7"], [76,"F#sus4"], [77,"F#M7"], [78,"F#m7"], [79,"F#7sus4"], [81,"Gaug"], [82,"Gdim"], [83,"GM"], [84,"Gm"], [85,"G7"], [86,"Gsus4"], [87,"GM7"], [88,"Gm7"], [89,"G7sus4"], [91,"G#aug"], [92,"G#dim"], [93,"G#M"], [94,"G#m"], [95,"G#7"], [96,"G#sus4"], [97,"G#M7"], [98,"G#m7"], [99,"G#7sus4"], [101,"Aaug"], [102,"Adim"], [103,"AM"], [104,"Am"], [105,"A7"], [106,"Asus4"], [107,"AM7"], [108,"Am7"], [109,"A7sus4"], [111,"A#aug"], [112,"A#dim"], [113,"A#M"], [114,"A#m"], [115,"A#7"], [116,"A#sus4"], [117,"A#M7"], [118,"A#m7"], [119,"A#7sus4"], [121,"Baug"], [122,"Bdim"], [123,"BM"], [124,"Bm"], [125,"B7"], [126,"Bsus4"], [127,"BM7"], [128,"Bm7"], [129,"B7sus4"]] def make_data(txt_data): send_data = '' reshape_data = [] # 正規表現を使ってコード文字列だけを抽出 extract_data = re.findall(r'[A-Z][M,a-z,7,#]*', txt_data) # Mを追加 for s in extract_data: if len(re.findall(r'[M,a-z,7]', s)) == 0: reshape_data.append(s + 'M') else: reshape_data.append(s) # 完全一致検索を使ってコードを番号に変換 for s in reshape_data: for c in conversion_table: if c[1] == s: send_data = send_data + str(c[0]) + '_' return reshape_data, send_data def Window1(): # 画面レイアウト col1 = [[sg.Image(filename=resource_path('img1.png'))]] col2 = [[sg.Button('はじめる' ,font=('yu Gothic UI',18),size=(12,1)), sg.Button('終了', font=('yu Gothic UI',18),size=(12,1))]] layout1 = [[sg.Column(col1, justification='c')], [sg.Column(col2, justification='c')]] # 1番目の画面を開く window = sg.Window("らくラクギター chord sender", layout1, resizable=True, size=(600, 600)) event1, values1 = window.read() if event1 == '終了': sys.exit() if event1 == 'はじめる': pass window.close() return event1, values1 def Window2(): # 画面レイアウト col1 = [[sg.Image(filename=resource_path('img2.png'))]] col2 = [[sg.Text('PC と らくラクギターをWi-Fiで接続してください',font=('yu Gothic UI',18),size=(34,1))]] col3 = [[sg.Text('SSID : linksys PATH : 0123456789',font=('yu Gothic UI',12),size=(28,1))]] col4 = [[sg.Button('OK' ,font=('yu Gothic UI',18),size=(12,1)), sg.Button('終了', font=('yu Gothic UI',18),size=(12,1))]] layout2 = [[sg.Column(col1, justification='c')], [sg.Column(col2, justification='c')], [sg.Column(col3, justification='c')], [sg.Column(col4, justification='c')]] # 2番目の画面を開く window = sg.Window("らくラクギター chord sender", layout2, resizable=True, size=(600, 600)) event2, values2 = window.read() if event2 == '終了': sys.exit() if event2 == 'OK': pass window.close() return event2, values2 def Window3(): col1 = [[sg.Text('らくラクギターに送信するコード譜を選択してください',font=('yu Gothic UI',18),size=(48,1))]] col2 = [[sg.Text("ファイル",font=('yu Gothic UI',18),size=(6,1)), sg.InputText(font=('yu Gothic UI',12),size=(38,1)), sg.FileBrowse(key="file1",font=('yu Gothic UI',18),size=(6,1))]] col3 = [[sg.Submit("決定",font=('yu Gothic UI',18),size=(12,1)), sg.Cancel("キャンセル",font=('yu Gothic UI',18),size=(12,1))]] layout3 = [[sg.Column(col1, justification='l')], [sg.Column(col2, justification='l')], [sg.Column(col3, justification='c')]] # 3番目の画面を開く window = sg.Window("らくラクギター chord sender", layout3, resizable=True, size=(600, 600)) event3, values3 = window.read() window.close() return event3, values3 def Window4(txt_filepath): # txtファイルを開いてデータの読み取り f = open(txt_filepath, 'r', encoding='UTF-8') txt_data = f.read() print(txt_data) reshape_data, send_data = make_data(txt_data) f.close() col1 = [[sg.Text('このコード譜を送信しますか?',font=('yu Gothic UI',18),size=(24,1))]] col2 = [[sg.Text(txt_filepath, text_color='black', background_color='white', font=('yu Gothic UI',12),size=(54,1))]] col3 = [[sg.Column([[sg.Text(txt_data, text_color='black', background_color='white')]], size=(550, 250), scrollable=True, background_color='white')]] col4 = [[sg.Button('Spresenseに送信' ,font=('yu Gothic UI',18),size=(20,1)), sg.Button('キャンセル', font=('yu Gothic UI',18),size=(20,1))]] layout4 = [[sg.Column(col1, justification='c')], [sg.Column(col2, justification='c')], [sg.Column(col3, justification='c')], [sg.Column(col4, justification='c')]]
# layout4 = [[sg.Text('送信しますか')], # [sg.Text(txt_filepath)], # [sg.Text(txt_filepath, text_color='black', background_color='white')], # [sg.Column([[sg.Text(txt_data, text_color='black', background_color='white')]], size=(245, 115), scrollable=True, background_color='white')], # [sg.Column([[sg.Text(reshape_data, text_color='black', background_color='white')]], size=(245, 115), scrollable=True, background_color='white')], # [sg.Column([[sg.Text(send_data, text_color='black', background_color='white')]], size=(245, 115), scrollable=True, background_color='white')], # [sg.Button('Spresenseに送信'), sg.Button('キャンセル')]]
# 4番目の画面を開く window = sg.Window("らくラクギター chord sender", layout4, resizable=True, size=(600, 600)) event4, values4 = window.read() if event4 == 'キャンセル': pass # sys.exit() if event4 == 'Spresenseに送信': spr = requests.post(url, data=send_data, timeout=(4.0,7.5)) window.close() return event4, values4 def Window5(): col1 = [[sg.Text('送信しました',font=('yu Gothic UI',18),size=(10,1))]] col2 = [[sg.Button('OK' ,font=('yu Gothic UI',18),size=(20,1))]] layout5 = [[sg.Column(col1, justification='c')], [sg.Column(col2, justification='c')]] # 5番目の画面を開く window = sg.Window("らくラクギター chord sender", layout5, resizable=True, size=(600, 600)) event5, values5 = window.read() window.close() return event5, values5 def resource_path(relative_path): if hasattr(sys, '_MEIPASS'): return os.path.join(sys._MEIPASS, relative_path) return os.path.join(os.path.abspath("."), relative_path) def main(): sg.theme('LightBrown4') event1, values1 = Window1() print('event1*****') print(event1) print('values1*****') print(values1) event2, values2 = Window2() print('event2*****') print(event2) print('values2*****') print(values2) event3, values3 = Window3() print('event3*****') print(event3) print('values3*****') print(values3) event4, values4 = Window4(values3['file1']) print('event4*****') print(event4) print('values4*****') print(values4) event5, values5 = Window5() print('event5*****') print(event5) print('values5*****') print(values5) if __name__ == "__main__": main() ``` # 演奏動画 エレキギターの音でも演奏できます。 @[twitter](https://twitter.com/teamSOTA655/status/1752335671141048520) # おわりに 今回は、SPRESENSEで誰でも簡単に演奏できるギターを作成しました。製作者の中には楽器の知識がない者もいましたが、製作を通じて楽器に興味を持つことができました。このような、ものづくりの経験を通じて、少しでも楽器に興味を持つ人が増えるとうれしいです。