FrontPage

2008/09/25からのアクセス回数 8151

SunSPOTを使った用途を考えていたときに、MITの石井さんが提唱されている Tangibleが浮かんだ。認識範囲の短いICTagは、ある地点の道しるべになり、値の読み取り 書き込みができるのでおもしろいことができると思った。

Tagibleの本家の例

http://tangible.media.mit.edu/projects/sensetable/

ICTagの選定

小型で電子工作の容易なICTagリーダライターとして、大信機器のHF-04SRを使うことにしました。

http://www.daishin-kiki.com/products.html

価格も1万円程度で、3.3VでRS-232C/TTLレベルで通信できるところも魅力です。 最近はもっと小さなリーダも見つけた(http://www.neotechkno.co.jp/nt7c/index.html

HF-04SRの対象とするICTagは、ISO15693とMifare規格に対応しており、このなかでI Code SLIの タグを使うことにしました。

ICTagの購入が問題

ICTagの広告でトップにでてくるリンテック株式会社に問い合わせたところ、ICTagの販売は 最低でも100枚単位でないと出荷できないとのことでした。

http://www.lintec.co.jp/e-dept/britem/ictag/products/tsdc_ts.html

営業の方のご厚意でサンプルタグを送ってもらい、HF-04SRでの読み取り確認できました。

ICTagリーダライターの動作確認

いきなりSunSPOTと接続するのは大変なので、シリアル接続でターミナルから操作しました。

秋月のUSB-シリアル変換モジュール

TTLレベルのRS-232C通信をテストする場合、レベルコンバータとUSB-シリアル変換モジュールが 必要ですが、秋月のAE-UM232Rは、USBから電源(5V, 3.3V)を供給し、買ってすぐにブレッドボード に接続できるすぐれもの(これで950円はやすい!)。

5.jpg

MacOSXでシリアル接続

私の使っているMacOSXからシリアル接続できるデバイスは少ないのですが、

  • PL-2303
  • FT232BM

は、MacOSX用のドライバーを公開しているそうです(http://www.zone0.ne.jp/2006/osxserial01.html参照)。

ドライバーのダウンロード

FT232BM用のドライバーをFuture Technology Devices International Ltd. からFTDIUSBSerialDriver_v2_2_10.dmgをダウンロードしました。

Tiger以降でないとだめみたいですから注意してください。

ターミナルソフト

MacOSXで使えるターミナルソフトは、Jerminalを使用することにしました。 先ほどの参照ページのJerminal8095.dmgをダウンロードしました。

接続確認

ターミナルからJerminal(jermコマンド)を以下のオプションで起動します。

$ jerm -b 38400 -p none -d 8 -s 1 /dev/cu.usbserial-A5002yHm 
  • ボーレート 38400
  • パリティなし
  • データ長 8bit
  • ストップビット 1

最後の/dev/cu.cusbserial-xxxxxxは、機種によって変わります。

接続が完了すると以下のような表示でます。

Jerminal v0.8095  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 candy
Type "Ctrl-M ~ ." to exit.
 ispeed 38400 ospeed 38400
 +IGNBRK -BRKINT -IGNPAR -PARMRK -INPCK -ISTRIP -INLCR -IGNCR -ICRNL -IXON
 -IXOFF -IXANY -IMAXBEL
 -OPOST -ONLCR -OXTABS -ONOEOT
 cs8 -CSTOPB +CREAD -PARENB -PARODD +HUPCL +CLOCAL -CCTS_OFLOW -CRTSCTS
 -CRTS_IFLOW -MDMBUF -ECHOKE -ECHOE -ECHO -ECHONL -ECHOPRT -ECHOCTL
 -ISIG -ICANON -ALTWERASE -IEXTEN
 -EXTPROC -TOSTOP -FLUSHO -NOKERNINFO -PENDIN -NOFLSH

ICTagリーダライターとの接続を確認するために、ICTagリーダライターのバージョン表示コマンド(V [CR]) を入力すると、

15693 V03.07

めでたく、ICTag 15693に対応した V03.07であることが表示されました(めでたしめでたし)。

JavaからICTagを制御する

ここでのメインテーマは、ハードをjavaで制御することですから、これからが本題です。

Javaでシリアル通信をする

Javaからシリアルポート、パラレルポートを使用するためのAIPがSunの提供する Java Communications API です。 残念ながらSunの提供するAPIは、SPARC Solaris/x86 Solaris/x86 Linux版のみなのでMacOSXや Windowsでは使えません。

そこで、Java Communications APIに準拠したオープンソースのライブラリRXTXを使用します。

RXTXのサイトからrxtx-2.1-7r2.zipをダウンロードし、解凍します。

  • MACOSX_IDE/ForPackageMaker/install/Library/Extensionsから以下のファイルを取り出します。
    • librxtxSerial.jnlib
    • RXTXcomm.jar

また、RXTXの例題としてprocessing/Gainerのライブラリを使いました。

  • http://gainer.cc/Download/Download からProcessingをダウンロードします。
  • gainer_lib_processing_v1-1-0rc4のlibraries/gainer/libraryから
    • gainer.jar
    • librxtxSerial.so
    • rxtxSerial.dll を取り出します。

RXTXシリアル接続については、

を参考にさせていただきました。

使用するICTag制御コマンド

javaで使用するICTagコマンドは

  • バージョン情報 : V
  • スキャンコマンド: 2XX
  • 連続スキャン開始コマンド: 2XS
  • 読出コマンド: 2R
  • 複数ブロック書込コマンド: 2WM

としました。

各コマンドの書式では

  • UID: タグのUID
  • tt: タグ番号
  • bb: ブロック番号
  • nn: 数値
  • DATA: データ
コマンド書式戻り値成功時戻り値失敗時制約
バージョン情報V[CR]使用できるタグの種類 バージョンなし
スキャン2XX[CR]nn,UID{,UID,UID....}[CR]00[CR]複数タグ検知は最大8枚
連続スキャン2XS[CR]01,UID[CR]中断時None[CR]1文字を送信すると中断する
読出2R,tt,bb,nn[CR]tt,yy,DATA{,tt,yy,DATA...}[CR]00[CR]最大15ブロック
複数ブロック書込2WM,tt,bb,nn,DATA[CR]tt,OK[CR]tt,NG[CR]最大15ブロック

ICTag制御クラスHF04SL

ICTag制御クラスHF04SLに各コマンドの処理を実装します。

最初にICTagのオープンですが、バージョン情報を使って接続先がICTagかどうかをチェックします。今回はISO-15693のICTagを対象としているので、バージョン情報の15693の文字列をキーワードとしました。

	public boolean openICTag(String pname){
		if(openSerialPort(pname)){
			String returnCode="";
			while(returnCode.indexOf("15693") < 0){
				try{
				write("V\r");
				returnCode = readWithTimer(1000);
				}catch(TimeoutException e){
				}catch(IOException e){
				}
			}
			return true;
		}
		return false;
	}

ICTagの読み込みを逐次チェックするよりも連続スキャンを使ってICTagの検出を 待つようにした方が、処理が簡単になります。

連続スキャンの処理は以下のようになります。

	public String contScanICTag() throws IOException {
		write("2XS\r");
		String res = read(3);
		if (res.equals("01,")) {
			String id = read(16);
			read(1);	// skip [CR]
			return (id);
		}
		else {
			read(2); // skip remain None[CR]
			return (null);
		}
	}

連続スキャンのキャンセルはきわめて簡単です。

	public void cancelContScanICTag() throws IOException {
		write("a");
	}

書込もコマンドの仕様通りです。

	public boolean writeICTag(int tagNo, int blokNo, byte[] data) throws IOException {
		int numBlock = (data.length + 3) / 4;
		StringBuffer buf = new StringBuffer();
		buf.append("2WM,");
		buf.append(String.format("%02x,", tagNo));
		buf.append(String.format("%02x,", blokNo));
		buf.append(String.format("%02x,", numBlock));
		for (int i = 0; i < data.length; i++) 
			buf.append(String.format("%02x", data[i]));
		buf.append("\r");
		write(buf.toString());
		
		read(3); // skip tt,
		String result = read(2);
		read(1); // skip [CR]
		return (result.equals("OK"));
	}

残りの読込は、ちょっと長くなりましたが、ほとんど仕様どおりです。

	public String[] readICTag(int tagNo) throws IOException {
		StringBuffer buf = new StringBuffer();
		buf.append("2R,");
		buf.append(String.format("%02d,00,0F\r", tagNo));
		write(buf.toString());
		
		String numStr = read(2);
		if (numStr == "00") {
			return (null);
		}
		else {
			List<String>	list = new ArrayList<String>();
			int numTag = Integer.parseInt(numStr);
			int count = 1;
			while (available() >= 2) {
				if (count++ != 1) {
					numStr = read(2);
				}
				read(1); // skip ','
				String lenStr = read(2);
				read(1); // skip ','
				int len = Integer.parseInt(lenStr, 16);
				String data = read(len*2);
				read(1); // skip ',' or [CR]	
				list.add(data);
			}
			return (list.toArray(new String[0]));
		}		
	}

必要な部品がそろったので、テストをします。

	public static void main(String[] args) {
		HF04SL ictag = new HF04SL();
		try {
			ictag.openICTag("/dev/cu.usbserial-A5002yHm");
			System.out.println(ictag.contScanICTag());
			// Icode SLIは、0ブロックに書き込めない
			System.out.println(ictag.writeICTag(1, 1, "0123456789ABCDEF".getBytes()));
			String[] tagData = ictag.readICTag(01);
			System.out.println(tagData[0]);	
			System.out.println(ictag.hexToString(tagData[0], 4, 16));
		}
		catch (Exception e) {	
			e.printStackTrace();
		}
		finally {
			ictag.closeSerialPort();
		}
	}

実行結果は、

Stable Library
=========================================
Native lib Version = RXTX-2.1-7
Java lib Version   = RXTX-2.1-7
RXTX Warning:  Removing stale lock file. /var/lock/LK.004.011.031
1A8FA410000104E0
true
000000003031323334353637383941424344454630303030000000000000000000000000
740069006F006E0020002000000000000000000000000000
0123456789ABCDEF
closing /dev/cu.usbserial-A5002yHm
Experimental:  JNI_OnLoad called.

正常に動作しました。

サンプルアプリケーション

ICTagのサンプルアプリケーションとして、本にICTagを付け、ISBN番号からAmazonの検索を使って

  • 書籍のタイトル
  • 著者
  • 出版社
  • 総ページ数
  • 出版日
  • 価格

を表示する、ダイアログを作成します。

BookManager.jpg
  1. ICTagがリーダにかざされるとダイアログを表示します。
  2. ISBN番号を入力しISBN Searchボタンを押すと、 アマゾンから書籍の情報を取得し、表示します。
  3. OKボタンで次のICTagの読込まちになります。

Visual Editorを使ってBookDialogを作成する

BookDialogは、EcdlipseのプラグインVisual Editorを使って作成しました。

VisualEditor.jpg

ISBN Searchボタンの処理(AWS検索の処理)

アマゾンの検索は、AWSを使いました。 ここで、検索結果の読込に失敗して情報をうまく取得できません。ネット調べたところ、

にnamespaceを指定する必要があるとのコメントを見つけ、registerNamespaceメソッドでawsを登録 するとうまく読み取ることができました。

	private void searchISBN() {
		String requestUrl = "http://webservices.amazon.co.jp/onca/xml?" 
		+ "Service=AWSECommerceService&SubscriptionId=" + [ここに Access Key IDをセット]"
		+ "&Operation=ItemSearch&ResponseGroup=Medium&SearchIndex=Blended&Keywords="
		+ getIcbnField().getText();

		try {
			DocumentContainer container = new DocumentContainer(new URL(
					requestUrl));
			container.setNamespaceAware(true);
			JXPathContext context = JXPathContext.newContext(container);
			context.registerNamespace("aws",
							"http://webservices.amazon.com/AWSECommerceService/2005-10-05");

			String title = context.getValue("//aws:Title").toString();
			String author = context.getValue("//aws:Author").toString();
			int numOfAuthor = (int)Double.parseDouble(context.getValue("count(//aws:Author)").toString());
		
			for (int i = 1; i < numOfAuthor; i++) {
				String nextAuthor = "//aws:Author[" + (i+1) + "]";
				author = author + ", " + context.getValue(nextAuthor).toString();
			}

			String publisher = context.getValue("//aws:Manufacturer").toString();
			String publicationDate = context.getValue("//aws:PublicationDate").toString();
			String noPages = context.getValue("//aws:NumberOfPages").toString();
			String listPrice = context.getValue("//aws:Amount").toString();
			
			titleArea.setText(title);
			authorArea.setText(author);
			publisherField.setText(publisher);
			publicationDateField.setText(publicationDate);
			noPagesField.setText(noPages);
			listPriceField.setText(listPrice);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

ソースファイル

今回のソースを以下から参照してください。

コメント

この記事は、

選択肢 投票
おもしろかった 0  
そうでもない 0  
わかりずらい 0  

皆様のご意見、ご希望をお待ちしております。


(Input image string)


添付ファイル: fileVisualEditor.jpg 1163件 [詳細] fileBookManager.jpg 1098件 [詳細] fileBookManager.java 1216件 [詳細] fileBookDialog.java 1251件 [詳細] fileHF04SL.java 1433件 [詳細] file5.jpg 1121件 [詳細]

トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-01-08 (木) 15:29:36 (5586d)
SmartDoc