//■シンギングボウルマシーン 2021/07/08 //2つのボリュームで、回転数と押付力を設定 //トグルスイッチで、ハンマリングモードとスロースタートモードの切り替え //スロースタートの時は、特定の音圧になるまで回転数を上げて時間を短縮します //特定の音圧に到達したら以後はフィードバックなして、定速回転させます。 //スタート停止は本体のプッシュswと、buletoothにて。 //blue toothでON/OFF出来る様にした //bluetoothを動作させる時は本体基板上のジャンパを付けてね //ただしスケッチ転送の際は、bluetoothの出力とケンカするのでジャンパを外します。 //音圧レベルの目標値として60を設定(値は根拠なし、カットアンドトライにて決定) //回転中は常時フィードバックさせようと思ったけれど、僅かな揺らぎがあり気になるので //目標値を超えたら制御を切って、定速回転に切り替えることにした #include Servo servo1; const int VR1 = 0;//アームの押付力設定用のボリューム const int VR2 = 1 ;//回転速度設定用のボリューム const int SERVO = 2 ; const int MOTOR = 3 ; const int SW1 = 4;//スタート、ストップ用のスイッチ const int SW2 = 5;//モード切替スイッチ const int PHOT_SW =6; //原位置検出用フォトインターラプタ const int OFF = HIGH; const int ON = LOW; const int KAITEN = true; const int TEISHI = false; int kaiten_flag;//回転中か停止中かを示すフラグ int data[300]; int pointer = 0; const int MIC = 2; const int LED = 13;//表示用 const int CENTER = 337; //マイク入力の中央値 const int ADJUST = -23; //補正値 const int GAIN = 1; //ゲイン const int LEVEL = 0; //スレショルドレベル これを越えたら検出フラグを立てる const int MOKUHYOUCHI = 60; //増速計算時に目標とする音圧レベル60という値は特に根拠はないです。 //運用時の音量と、音が出始めるまでの時間、音が出始めて増速が終了した時の回転数のギャップ、等からカットアンドトライで決めています。 const int THRESHOLD =30; //増速時に増速処理を終わる閾値 int average; //音圧の平均値 unsigned int sum = 0;//マイク入力データを平均化する際の合計 最大255x200=51000が格納される //「int」だと符号が反転しちゃう場合があるので。 int start_up = 1 ; // スロースタートモードでの立ち上がり時を識別するフラグ。 スタートボタンを押した1回目だけ増速して、いったん音圧が設定値をこえたら //定速に切り替えるために使用 void setup() { int i; Serial.begin(9600);//シリアル通信開始、転送速度は9600ビット/秒 servo1.attach(SERVO); pinMode( LED, OUTPUT); pinMode( SW1, INPUT_PULLUP); pinMode( SW2, INPUT_PULLUP); pinMode( PHOT_SW, INPUT_PULLUP); analogWrite( MOTOR, 0); //アーム回転停止 servo1.write(110); //アーム初期位置(離れている) delay(500); onnatsu_syokika(); //音圧データの初期化 kaiten_flag = TEISHI; //回転フラグ初期化 } void loop() { int i; int mic_in,mic_data; int old_data; int blt_data; //blue tooth データ読み込み用変数 int blt_sw; //blue tooth 仮想スイッチ int temp; //rev_out 計算用 int rev_out; //スロースタート時の回転数 int rev_data; //回転数取り込み用 //■blue tooth関連 シリアルモニタでデータを見る時にはここをコメントアウトします blt_data = Serial.read(); //blue tooth シリアル通信ポート読み込み if( blt_data != -1 ){ //データが来ていないと「-1」が帰ってくる blt_sw = 1; // !=-1 データが来ていれば スイッチが押されたことになる do{ }while( Serial.read() != -1 ); //データバッファをクリア(-1が帰ってくるまで読みだす) }else{ blt_sw = 0; } //■blue tooth関連 ここまで if( digitalRead( SW2 )== ON ){//■スロースタートモード if ( (digitalRead( SW1 ) == ON ) || ( blt_sw == 1 )){ //スタートスイッチが押された if( kaiten_flag == TEISHI){ //回転していなければアームを接触させる sessyoku(); //ゆっくりくっつける 既にくっついていれば動かない kaiten_flag = KAITEN; }else if( kaiten_flag == KAITEN){ //回転していれば停止処理 analogWrite( MOTOR, 0);//回転停止 hanasu(); genten(); kaiten_flag = TEISHI; start_up = 1; onnatsu_syokika(); //音圧データをクリア } }else if( kaiten_flag == KAITEN ){ //スイッチが押されていなくて、回転フラグが立っていれば、通常回転 //1023/5 * 3.3 = 675 5Vだと0~1023だけど3.3Vなので換算して0~675になる //675の中央値が337になる。中央値より小さい値を上に折り返えして、全波整流状態にする。 mic_in = analogRead( MIC ); mic_data = abs( mic_in - CENTER + ADJUST); //さらにこの数値の範囲を統一するためにmap関数で、0~255に換算する。 mic_data = map( mic_data, 0, 337, 0, 255 ); old_data = data[ pointer ]; //旧データを待避 data[ pointer ] = mic_data; //ポインタが示す配列に新しいデータを入れる sum = sum - old_data; //sum(合計)から旧データ分を引く sum = sum + mic_data; //sumに新しいデータ分を加算 average = sum / 300; //平均値計算 pointer++ ; if( pointer > 299 ){ //ポインタが300になったら0に戻す pointer = 0; } rev_data = vr2_read(); //回転数読込 if( THRESHOLD < average ){ start_up = 0; //音圧が40を超えたらスタートアップ終了 } if( start_up ==1 ){ //特定の音量になるまで、設定値より回転数をあげまする。 //どのくらいあげるかを計算しています。 temp = 10 + (MOKUHYOUCHI - average)*10/MOKUHYOUCHI ; //int で計算するために10倍している。 rev_out = rev_data * temp /10 ; //回転数の設定値に係数をかけてから、1/10します。 //設定値の1~2倍の値になる。 }else{ rev_out = rev_data; //スタートアップじゃなければ、設定回転数をそのまま出力 } kaiten(vr1_read(), rev_out ); //回転出力 //■シリアルプロッタ用 buletoothを使う時はここをコメントアウトします。 // Serial.print( 100 ); // Serial.print( ","); // Serial.print( 0 ); // Serial.print( ","); // Serial.print( rev_out ); // Serial.print( ","); // Serial.print(vr2_read()); // Serial.print( ","); // Serial.print( average ); // Serial.print( ","); // Serial.println( start_up *100 ); delay(10); //上の部分をコメントアウトしたときは、ここを有効にしてタイミングを補正します } }else{ //■ハンマリングモード if (( digitalRead( SW1 ) == ON ) || ( blt_sw == 1 )){ //スタートスイッチが押された if( kaiten_flag == TEISHI){ //回転していない状態なら鐘突き・接触・回転 delay(500); gong(); kaiten(vr1_read(), vr2_read() ); //通常回転 sessyoku(); kaiten_flag = KAITEN; }else if( kaiten_flag == KAITEN){ //回転中なら回転停止・アーム離脱 analogWrite( MOTOR, 0);//回転停止 hanasu(); genten(); kaiten_flag = TEISHI; } }else if( kaiten_flag == KAITEN ){ //スイッチは押されていないけど、回転中なら回転動作を繰り返す kaiten(vr1_read(), vr2_read() ); //いちいち呼び出さなくても回転はするけれど、ボリュームの値を常に反映させるため } } } void hanasu(){ //びよよ~んって離すと風情がないのでゆっくり離します。 int vr1in; int i; vr1in = vr1_read(); //アーム押し付け力読み取り for( i = vr1in; i <= 110 ; i++ ){ servo1.write(i); delay(50); } } void sessyoku(){ // ビビリ音が出ない様に押しつけます(ちょっとビビりますが)。 int vr1in; int i; vr1in = vr1_read();//押付力設定ボリューム読み取り for( i = 110; i >= vr1in ; i-- ){ servo1.write(i); delay(50); } } void kaiten(int oshituke,int kaiten){ //回転サブ 引数:押付力、回転数 servo1.write( oshituke ); //アーム押し付け analogWrite( MOTOR, kaiten); //モータ回転 } int vr1_read(){ //押付力設定のボリューム読み取り。出力範囲は70~110° int vr_in; vr_in = analogRead( VR1 ); vr_in = 1023 - vr_in; //ボリュームを時計回りに回すと押付力が増える様に値を反転させる return map( vr_in ,0,1023,70,110); } int vr2_read(){ //回転速度設定のボリューム読み取り。出力範囲は0~255 int vr_in; vr_in = analogRead( VR2 ); return map( vr_in ,0,1023,0,255); } void gong(){ //鐘突き(値はカットアンドトライで決めました) servo1.write(110); servo1.write(95); delay(100); servo1.write(110); delay(500); } void genten(){//原位置に戻る アームは離れている前提 kaiten(110, vr2_read()); do{ }while( digitalRead( PHOT_SW ) == ON ) ;//インターラプタがON(光が遮られる)するまで回転 analogWrite( MOTOR, 0);//回転停止 } void onnatsu_syokika(){ //音圧の平均を求めるための配列を初期化する。セットアップ時と停止時に実行したいので関数化 int i; for( i=0 ; i < 300 ; i++ ){ //配列初期化 data[i] = 0; } sum = 0; }