arduino、Javaドシロウトが書いてみた、はじめてのarduino
とにかくコードが見たいって人は読みとばすと良いよ。ちなみに、Javaもarduinoもズブの初心者なので色々突っ込みどころ満載だよ^^
概要
先日大学院の友人たちと一緒にハッカソン(一日から二日くらい?の短期間で何かアプリケーションのプロトタイプ作ろうぜ!なイベント)に参加した。そこで作ることになったのはティッシュ使用量削減を目的としたアプリ、すなわちSTS(Social Tissue Service) × 育成ゲーム。
arduinoというハードウェアとセンサを用いてデータを取得し活用するという、オフラインとオンラインの連携を目指したアプリとなっている。新卒×学生ハッカソン - スマホアプリを作ろう -の趣旨であるスマホアプリから逸脱した感は否めないが、まあハッカソンなんで作りたいもん作ろうやソフトウェアのみならずハードウェアまで組み込んだ、枠にとらわれない試みといえるだろう。
システム
コード
環境構築
開発環境はWindows7 64bit、JDK7、Eclipse4.2
Javaでarduinoとシリアルポート通信をおこなうにあたり、メモメモ... MacのJavaでAVRとシリアル通信したいを参考に環境構築をおこなった。
手順としては、rxtx-2.1-7-bins-r2.zipをダウンロード、解凍したディレクトリの中にあるRXTXcomm.jarをC:\Program Files (x86)\Java\jre7\lib\extに、Windows\i368-mingw32にあるrxtxParallel.dll、rxtxSerial.dllをC:\Program Files (x86)\Java\jre7\binにコピーした。(32、64bit版のJavaの両方にコピーしてしまったから、うまく動かなかったら追加してみるといいんじゃないかな?)
パスが見つからないとか言われたら、新しく実行環境の構築をすれば良いと思うよb
ディレクトリ構成(関係する部分のみ抜粋)
hackathon `-- src |- デフォルトパッケージ | |- DBHandler.java // DB関連 | |- MusicPlay.java // 音声ファイル再生 | `- main.java // メインの実行ファイル `- wav // 音声ファイル置き場 |- 1.wav .... `- 20.wav
arduino側
sketch_aug25a.ino
// このコードは友人が書いたもので、理解できてない部分も多々あります。 // serial_voltage_byte_sample1 int mage_pin = 2; int mage_value = 0; int val = 0; int seri = 0; void setup() { Serial.begin( 9600 ); // この番号は後で使う } void loop() { mage_value = analogRead( mage_pin ); // 曲げセンサーを通して検知した値が500 ~ 650だったので、0 ~ 100にスケールを合わせてみた val = map(mage_value, 500, 650, 0, 100); if(val >= 40){ // 曲がってると判断 seri = 1; }else if(val < 40){ // 曲がってないと判断 seri = 0; } if (seri > 0) { // 曲がっている場合のみ出力 Serial.print(seri); } delay( 300 ); // 0.3secごとにセンサーのデータを取得する }
Java側
main.java(実行するメインのファイル)
import java.io.*; import gnu.io.*; public class main { public static final void main(String[] args){ try{ // arduino IDE -> ツール -> シリアルポートで確認 CommPortIdentifier portID = CommPortIdentifier.getPortIdentifier( "COM3" ); // Linuxならこんな感じらしいぉ //CommPortIdentifier portID = CommPortIdentifier.getPortIdentifier("/dev/tty.usbserial-A400FMPJ"); // 第一引数はアプリケーション名: sketch_aug25aはスケッチのファイル名 SerialPort port = (SerialPort)portID.open("sketch_aug25a", 5000); // 接続に失敗したら5sec待って再度トライ // 第一引数のポートはスケッチファイルで指定した数字 port.setSerialPortParams(9600, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE); InputStream is = port.getInputStream(); int c; // arduinoからの出力読み込み用 boolean update_flag = false; // DB updateフラグ boolean flag = true; // 無限ループ用フラグ System.out.println("start reading"); int count=0; // 音声ファイル用カウンタ DBHandler dbh = new DBHandler(); // DBへの接続、SQL実行クラス PlayMusic pm = new PlayMusic(); // 音声ファイル再生クラス while(true){ c = is.read(); if (c != -1) { System.out.println(count+ "->" + c); // デバッグ用 // arduinoでは1を出力するはずが、なぜか48 or 49になってる if (c > 48) { // ティッシュ抜き取り update_flag = true; } else { update_flag = false; } if (update_flag) { count++; pm.filename = "wav\\" + count + ".wav"; pm.play(); // 音声ファイル再生 dbh.update(); // update文実行 update_flag = false; } } if (flag == false) { break; } } is.close(); port.close(); System.out.println("finished reading"); } catch(NoSuchPortException e){ System.err.println("Can Not Find Device"); e.printStackTrace(); } catch(PortInUseException e){ System.err.println("Can Not Open Device"); e.printStackTrace(); } catch(UnsupportedCommOperationException e){ System.err.println("Invalid Parameter"); e.printStackTrace(); } catch(IOException e){ e.printStackTrace(); } catch(Exception e){ e.printStackTrace(); } } }
DBHandler.java
import java.sql.*; public class DBHandler { String host = "SERVER_URL:DB_PORT"; // MySQLを使用したので、ポートはデフォルトの3306 String table = "hackathon"; String url = "jdbc:mysql://" + host + "/" + table + "?useUnicode=true&characterEncoding=utf8"; String user = "hohe"; String password = "huge"; public void select() { // DBに保存されたデータ確認用。 try { Class.forName("org.gjt.mm.mysql.Driver"); Connection con = DriverManager.getConnection(url, user, password); System.out.println("接続成功"); String sql = "SELECT * FROM user u JOIN tissue t ON u.id = t.user_id"; Statement statement = con.createStatement(); ResultSet reslut = statement.executeQuery(sql); // SELECT結果の表示 while(reslut.next()){ int id = reslut.getInt("id"); String name = reslut.getString("name"); int current_count = reslut.getInt("current_count"); System.out.print(id + "\t"); System.out.print(name + "\t"); System.out.println(current_count + "\n"); } con.close(); } catch(Exception e){ System.out.println("例外発生:"+ e); } } public void update () { // tissueテーブルのcurrent_countをカウントアップ try { Class.forName("org.gjt.mm.mysql.Driver"); Connection con = DriverManager.getConnection(url, user, password); System.out.println("接続成功"); // ホントはユーザを決め打ちにしたくないけど、プロトタイプということで… String sql = "UPDATE tissue SET current_count = current_count + 1 " + "WHERE user_id = (SELECT id FROM user WHERE name = 'hoge')"; Statement statement = con.createStatement(); int reslut = statement.executeUpdate(sql); // UPDATE実行結果取得(使わないけど) System.out.println("executed"); con.close(); } catch(Exception e){ System.out.println("例外発生:"+ e); } } }
PlayMusic.java
import java.io.File; import java.io.IOException; import javax.sound.sampled.*; public class PlayMusic { String filename; // 再生するwavファイル int count = 0; // ループする回数 int sleep_time = 0; // 再生後待機する時間(sec) not use public void play() { System.out.println("start play"); Clip clip = null; // このファイルが置かれているパスからの相対パスだとうまく読み込めなかった。ので、絶対パスで指定している。 // プロジェクトルートからの相対パスなら良いのかな? // Winの場合、C:hoge\\fuga\\ となる。 バックスラッシュをエスケープする必要が有ることに注意 String wav_file = "ABSOLUTE_PATH" + filename; System.out.println(wav_file); AudioInputStream audioInputStream; try { File soundFile = new File(wav_file); audioInputStream = AudioSystem.getAudioInputStream(soundFile); AudioFormat audioFormat = audioInputStream.getFormat(); DataLine.Info info = new DataLine.Info(Clip.class, audioFormat); clip = (Clip)AudioSystem.getLine(info); clip.open(audioInputStream); clip.loop(count); // countの回数分だけループ(今回は使わないので0を指定。コメントアウトしたらどうなるかは試してない) } catch (UnsupportedAudioFileException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (LineUnavailableException e) { e.printStackTrace(); } // 1秒経過したら終了する // これを書かないと、音声ファイルが再生されない。 // 実際に使用するときは何らかの方法で再生時間を取得しないといけないのかな? try { Thread.sleep(1000); } catch (InterruptedException e) { // 何もしない } clip.stop(); System.out.println("finished playing"); //System.exit(0); // これを実行すると処理が終了する(再生してプログラム終了) } }