Arduinoとの通信1の動作確認がまだの場合は、そちらを先に行ってください。
先のサンプルでは、PTBの機能をほとんど利用していませんでしたが、次にご紹介するサンプルでは、圧力センサーを押さえると画面上の円(PTBの関数で描画されたもの)が大きくなります。
左側に描画される円がセンサー1から入力されたもので、右側に描画される円がセンサー2から入力されたものです。
またサンプル1では、送受信の開始時にのみ許可信号(文字A)を送っていましたが、今回のサンプルではデータを受信したいタイミングで必ずパソコンからArduinoに対して許可信号を送るようにしています。(ハンドシェイク、フロー制御と呼ばれます)この理由は、Flipなどが実行されるわずかな時間のあいだに、バッファーにデータが蓄積されるのを防ぐためです。こちらの記事も参考になります。
Arduinoのスケッチです。
// 2つのセンサーをA0とA1に接続const int SENSOR1 = 0; // アナログ入力 A0const int SENSOR2 = 1; // アナログ入力 A1int sen1 = 0; // A0から読み込んだデータint sen2 = 0; // A1から読み込んだデータint inByte = 0; // バッファーから読み込んだ1バイト分のデータint SA = 0; // Serial.available();int flag = 0; // flagが1のときはパソコン(Matlab)にデータを送信void setup() { Serial.begin(38400); // 通信速度(bits/sec)はお使いの環境に合わせてください。 Serial.print("A\n"); // Arduinoの準備が整ったことをパソコンに通知}void loop() { SA = Serial.available(); if (SA > 0) { // バッファーにデータがたまっていたら inByte = Serial.read(); // バッファーのデータを1バイト読み込む(バッファーは空になる) if (inByte == 65) {// Matlabから'A'が送られていたら flag = 1; } else { flag = 0; } } if (flag == 1){ flag = 0; // サンプル1からの変更点 // アナログデータの読み取り sen1 = analogRead(SENSOR1); sen2 = analogRead(SENSOR2); // パソコンへのデータの送信 Serial.print(SA, DEC); Serial.print(","); Serial.print(inByte, DEC); Serial.print(","); Serial.print(sen1, DEC); Serial.print(","); Serial.print(sen2, DEC); Serial.print("\n"); //終端子 delay(10); // サンプリング間隔(ms)。チャタリング防止にもなっている。 }}Matlabのソースコードです。
function serialsample2% 実験参加者名の入力SubName = input('Name? ', 's'); % 名前をたずねるif isempty(SubName) % 名前の入力がなかったらプログラムを終了 return;end;% 出力ファイルの上書き確認を行うSaveFileName=[SubName '.csv']; % 出力ファイル名if exist(SaveFileName, 'file') % すでに同じ名前のファイルが存在していないかの確認 resp=input([SaveFileName 'はすでに存在します。上書きをしてよい場合は y を入力してエンターキーを押してください。'], 's'); if ~strcmp(resp,'y') disp('プログラムを強制終了しました。') return endend% 呼び出しておいたほうがよい関数たち。AssertOpenGL; KbName('UnifyKeyNames'); %OSで共通のキー配置にするListenChar(2); % Matlabに対するキー入力を無効GetSecs;WaitSecs(0.1);%HideCursor;openFlag = 0;% 背景色 bgColor = [128 128 128]; %RGBの値% 文字色strColor = [0 0 0];fontSize = 20; % 文字サイズ(整数)try % 刺激を呈示するディスプレイ(ウィンドウ)の設定 screenNumber = max(Screen('Screens')); % デバッグ用。ウィンドウでの呈示 %[windowPtr, windowRect] = Screen('OpenWindow', screenNumber, bgColor, [10 50 1200 1000]); % 実験用。フルスクリーン [windowPtr, windowRect] = Screen('OpenWindow', screenNumber, bgColor); % 1フレームの時間 (inter flame interval) ifi = Screen('GetFlipInterval', windowPtr); % 画面の中央の座標 [centerX, centerY] = RectCenter(windowRect); %------------------------------ % フォント設定 if IsWin %Screen('TextFont', windowPtr, 'メイリオ'); Screen('TextFont', windowPtr, 'Courier New'); end; if IsOSX % DrawHighQualityUnicodeTextDemoを参照。 allFonts = FontInfo('Fonts'); foundfont = 0; for idx = 1:length(allFonts) %if strcmpi(allFonts(idx).name, 'Hiragino Mincho Pro W3') if strcmpi(allFonts(idx).name, 'Hiragino Kaku Gothic Pro W3') foundfont = 1; break; end end if ~foundfont error('Could not find wanted japanese font on OS/X !'); end Screen('TextFont', windowPtr, allFonts(idx).number); end; Screen('TextSize', windowPtr, fontSize); % シリアルポートオブジェクト myserial = serial('/dev/tty.usbmodemfa1311', 'BaudRate', 38400) fopen(myserial); myserial.Status; get(myserial) % 出力ファイルを開く Fid = fopen(SaveFileName, 'wt'); openFlag = 1; fprintf(Fid, '%s\n', SubName); fprintf(Fid, '%s\n', datestr(now, 'yy-mmdd-HH:MM')); fprintf(Fid, '%s\n', ''); % 出力ファイルの見出し fprintf(Fid, '%s\n', '通し番号,BA,データ数,バイト数,SA,inByte,Sen1,Sen2,タイムラグ'); % Arduinoとの通信を確立するため、データ'A'を受け取るのを待つ。 while myserial.BytesAvailable == 0 end; startChar = fscanf(myserial); % 入力バッファーからデータを読み込み %whos('startChar') %double(startChar) if startChar ~= 'A' error('通信を確立できませんでした。') end; for i = 1:2 while myserial.BytesAvailable fread(myserial, myserial.BytesAvailable); % バッファーを空に。 WaitSecs(0.5); end; DrawFormattedText(windowPtr, double('スペースキーを押すとシリアル通信を開始します。'), 'center', 'center', strColor); Screen('Flip', windowPtr); while KbCheck; end; % いずれのキーも押していないことを確認 while 1 % while 文の中をぐるぐる回ります。 [ keyIsDown, keyTime, keyCode ] = KbCheck; % キーが押されたか、そのときの時間、どのキーか、の情報を取得する if keyIsDown if keyCode(KbName('SPACE')) % break; % while文を抜ける。 end; while KbCheck; end; end; end; cnt = 1; baseTime = GetSecs; while 1 fprintf(myserial, '%c', 'A'); % Arduinoに送信許可を送る。(終端子を含まない形で、1バイト送る) onTime = GetSecs; % 送信を許可した時間 while myserial.BytesAvailable == 0 % Arduinoからデータを受け取るまでループする。 end; %-------------------------------- % カンマ区切りのデータなどの扱い rawdata = fscanf(myserial); % 終端子が読み込まれるまで待つ。 timelag = GetSecs - onTime; tmpCell = textscan(rawdata, '%d%d%d%d', 'delimiter', ','); % Arduinoから送られてくるデータは4種類(%dの数に注意) [SA, inByte, sen1, sen2] = deal(tmpCell{:}) dist = 300; % 2つの円の間隔 radius = double(sen1)/2; % 左の円の半径(センサー1からの入力)doubleで倍精度に変換する必要あり。 tmpRect = [centerX - dist - radius, centerY - radius, centerX - dist + radius, centerY + radius]; Screen('FillOval', windowPtr, [255 255 255], tmpRect); radius = double(sen2)/2; % 右の円の半径(センサー2からの入力)doubleで倍精度に変換する必要あり。 tmpRect = [centerX + dist - radius, centerY - radius, centerX + dist + radius, centerY + radius]; Screen('FillOval', windowPtr, [255 255 255], tmpRect); Screen('Flip', windowPtr); % BA = BytesAvailable。これが大きな値だと読み込んだデータに時間の遅れがあるということ。 % inByteが65のとき、これは「A」のASCIIコードを表している。 % inByteが10のとき、これは終端子「LF」のASCIIコードを表している。 info = whos('rawdata'); fprintf(Fid, '%d,%d,%d,%d,%d,%d,%d,%d,%f\n', cnt, myserial.BytesAvailable, info.size(2), info.bytes, SA, inByte, sen1, sen2, timelag*1000); cnt = cnt + 1; if GetSecs - baseTime > 5 break; end; end; Screen('Flip', windowPtr); %fprintf(myserial, '%c', 'B'); % Arduinoにデータの送信を停止させる。('A'ではなく'B'を送っていることに注意) fprintf(Fid, '\n'); cnt % 読み込んだデータ(行)の数 %fprintf('計測を終了しました。\n'); %WaitSecs(2); % 少し待ち時間をはさまないと次のブロックにデータが残っている。 end; DrawFormattedText(windowPtr, double('実験は終わりです。'), 'center', 'center', strColor); Screen('Flip', windowPtr); KbWait([], 3); %終了処理 fclose(Fid); % ファイルを閉じる。 fclose(myserial); delete(myserial); clear myserial; Screen('CloseAll'); ShowCursor; ListenChar(0); catch % 以下はプログラムを中断したときのみ実行される。 %if exist('Fid', 'var') % ファイルを開いていたら閉じる。 if openFlag fclose(Fid); disp('fclose'); end; fclose(myserial); delete(myserial); clear myserial; Screen('CloseAll'); ShowCursor; ListenChar(0); psychrethrow(psychlasterror);end