編集リクエストは記事の情報を改善するために、内容の変更を依頼できる仕組みです。
受理するかどうかは自由ですので、発信したい情報に近づくかどうかを意識して、記事編集の判断のご協力をお願いします。
本文の変更
# 作品概要 この作品は誰でも簡単にかっこよく弾けるギターとなっています。昨今の新型コロナウイルスの影響でおうち時間が増え、楽器を始める人が増えました。その中でもギターを始めたという人は多くいます。 しかし、ギターは初心者が挫折しやすい楽器になっています。その原因はギターの難しさにあり、ギターの難しさは右手と左手にそれぞれあります。 ![ギターの演奏方法](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つのモードを実装しました。 ### コードモード @[twitter](https://twitter.com/teamSOTA655/status/1752206623618343293) ### ビギナーモード @[twitter](https://twitter.com/teamSOTA655/status/1752212003312332804) ### マニュアルモード @[twitter](https://twitter.com/teamSOTA655/status/1752211584158708034) # 回路図 全体回路図は以下のようになります。 ![全体回路図](https://camo.elchika.com/567896399b0785e38afb3b0a8bb44fa318269bdd/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f32646364366231642d663238612d343730312d613037632d343036383334643632343834/) タッチセンサの値を読み取るためのタッチセンサ制御回路は複数のシフトレジスタ部とコンパレータ部から構成されており、タッチ部に手が触れているかの判定をしています。 1番目のシフトレジスタ部の回路は以下のようになっています。シフトレジスタを用いることでタッチ部の金属線に一本ずつ順番に波形を送っています。ダイオードを入れることにより複数個所押しても識別可能となっています。 ![1番目のシフトレジスタ部](https://camo.elchika.com/cc726fde46ee326cd5296bf15f2c8b7365bd793f/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f34646238376361352d643239342d343562362d383332652d363665633564333136323161/) 2~11番目のシフトレジスタ部は以下のようになっています。k番目のシフトレジスタのSER端子はk-1番目のシフトレジスタのQH’端子と接続します。k番目のシフトレジスタのQH'端子はk+1番目のシフトレジスタのSER端子と接続します。このようにし複数のシフトレジスタを連結しています。 ![k番目のシフトレジスタ部](https://camo.elchika.com/89a4ded6832b045873a18a3694350dcc7bc367e5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f38616334643264342d633765342d346637382d386137392d616637343461303533303665/) コンパレータ部は以下のようになっています。コンパレータ部ではシフトレジスタから出力された波形の読み取りをしています。-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/) 組み立てた結果内部はこのようになっています。 # ソースコード SPRESENSEとPC側のプログラムを分けて解説します。 ## SPRESENSE SPRESENSEではマルチコアを使用しています。 ### MainCore MainCoreでは音源の再生・停止、スイッチの読み取り、SubCoreから受信した情報の処理をしています。音源の再生・停止はSDカードに入れた音源に番号を登録し、番号を指定することで再生可能となっています。 モードスイッチより演奏音源・モードを切り替えることができます。 SubCore3から受信した文字列から音源番号を羅列したコード譜を作成します。SubCore2から受信したタッチセンサの値から各モードでのコード音源を検索します。 SubCore1から受信した加速検出と加速方向より、音源を決定し、音を再生します。 音源の再生は〇〇を利用しています。
### SubCore1 SubCore1ではタッチセンサの触れている場所を検出しています。
1つずつタッチセンサに触れていった場合、タッチセンサの値はそれぞれ以下の図のように変化します。そこで、触れていないときの値より、閾値以上離れている場所を読み取り、5frame中3 frame反応している場所をタッチセンサが触れていると検出します。同時押しも読み取り可能です。
1つずつタッチセンサに触れていった場合、タッチセンサの値はそれぞれ以下の図のように変化します。触れていないときの値より、閾値以上離れている場所を読み取り、5フレーム中3 フレーム反応している場所をタッチセンサが触れていると検出します。同時押しも読み取り可能です。 @[twitter](https://twitter.com/teamSOTA655/status/1752250531324838008) ![タッチセンサの読み取り結果](https://camo.elchika.com/a0dea8548f2a42e09da0d986ef99936111c0cd8c/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39623663353863312d333232362d346231632d393863342d3663383564623566303566622f39666235356238322d666335362d343030642d613866392d343835313666633265393531/)
### SubCore2 SubCore2ではエンコーダにより、ホイールの加速を検出します。エンコーダのa相、b相が変化するステップ時間を読み取り、ステップ時間の変化から加速を読み取ります。割り込み処理を利用し、読み飛ばしを防止しています。
@[twitter](https://twitter.com/teamSOTA655/status/1752218361768157367) ![キャプションを入力できます](https://camo.elchika.com/d4e5f483d6223c7889c510c0097a92ed381e8500/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39623663353863312d333232362d346231632d393863342d3663383564623566303566622f38366436396631652d383735302d346362372d623439312d356237366232306230363239/)
### SubCore3 SubCore3ではwifiモジュールからの文字列を取得します。 ## PC側 あああ ```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 : oooo 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() ``` img1、img2は以下を使用 ![画面遷移](https://camo.elchika.com/f0ebc40a6d6fa1d407221c576b49b45ee556b727/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f36343733303230352d343863322d343639352d393862332d643461393361663634393638/) # 演奏動画 エレキでも演奏できます @[twitter](https://twitter.com/teamSOTA655/status/1752212235164975559)