2026年2月19日木曜日

AIと相談してコレクタで、古いPCのリナックスサーバーに方向検知をRFID読み取りで記録してみた


簡単にRFIDできるツールとして、マニカ コレクタがあります。

 https://www.hayato.info/collector.htm 

です。

前回は「プロセスを起動する」を使いましたが、今回は「Webサーバへ投げる」です。


「サーバへ投げる」のところで、
nec/cgi-bin/collect/add.cgi?PC=REUSE1&TAG=[タグID]&YYYYMMDD=[YYYYMMDD]&HHMMSS=[HHMMSS]&ANT=[アンテナNO]

を設定しました。(サーバーPCの名前は「nec」でPCのhostsに設定してあります。)


MANICAコレクタは、HTTPまたはHTTPSにてGETメソッドで送信することができます。

なので、送信先のWebサーバーに対して自分のPC名(PC=REUSE1)と、コレクタが置き換えてくれる値を設定します。

(引数の書き方はAIに相談すると教えてくれます。)


さらにAIに相談しました。

「会社には、もう古くなって誰も使わないWindows7のPCがあります。これをWebサーバーにしたいのですが良い案はありますか?」

と質問すると、

AIは「Ubuntu Linux(ウブンツ リナックス)」という無償ソフトに入れ替えればすれば快適にWebサーバーを作れます。」

と回答があったので試すことにしました。

ということで、「どうやってインストールするのですか?」「試したらこういうエラーになったのですが原因を推測できますか?」「なるほど、それを試して見ます!」ということを、繰り返していたら動くようになりました。


Webサーバーでは、

(1)HTTPでGETメソッドでデータを受け取ったら、受け取ったデータをテキストファイルに追記する。

(2)次に、その履歴データから最新の20行を抜き出す。

(3)次に、上記(2)のデータの秒単位の時刻経過とアンテナ番号とEPCを判断する。

(4)次に、上記(3)の方向検出データでhtmlファイルを書き換える。

(5)htmlファイルでは、ブラウザに対して、数秒置きに再度読み取らせる。

ということをしたいです。

と、AIに相談すると、かくかくしかじか推奨です。と回答してくれます。

上記の(5)についてはhtmlファイルにて

<head>
<meta charset="utf-8">
<meta http-equiv="Refreshcontent="1">
<title>LIST - MANICAコレクタ</title></head>
</head>

書けば、ほっておいても、PCではブラウザが読み直してくれるので、最新のデータを表示できます。

まぁ簡単!

上記(1)から(4)については、AIに質問して試してみて、巧く行かなかったらまたAIに質問して、を地道に繰り返します。

AIに相談してWebサーバーで動かすスクリプトも作成しました。


すると、できました!


左のウィンドウはMANICAコレクタのログ画面です。

右のウィンドウはWebブラウザで状況表示しています。

コレクタが読取ると、Webブラウザ画面にすぐに反映されています。


今は使っていない古いPCと、お金のかからないリナックス。

コレクタが動くPCと組み合わせて便利に使うことができました。


AIに相談して作った、Webサーバーで動かしたスクリプトも紹介しておきますね。

名前はadd.cgiです。

#!/usr/bin/perl
#@TOP マニカコレクタ用のCGI

#@使うモジュール
use CGI;
use Time::Piece;
use utf8;

#@ここから始まり。
my $qq = new CGI;
my($bContinue)=1;

#@EPCと製品名を紐づけるグローバル変数
my(%TagInfo)=(); # EPCと詳細情報を紐づけるデータ
$TagInfo{'000000000000000000000230'}="治具(一般的)";
$TagInfo{'000000000000000000000231'}="治具(その1)";
$TagInfo{'000000000000000000000232'}="治具(その2)";
$TagInfo{'000000000000000000000233'}="治具(その3)";
$TagInfo{'000000000000000000000234'}="治具(その4)";
$TagInfo{'000000000000000000000235'}="治具(その5)";
$TagInfo{'000000000000000000000000'}="治具(一般的)";

#@エラーログを開く
open $efh, '>> ', "error.txt" ; # エラーログを開く
flock($efh,2) or die; # Ubuntu Linuxだと、ブロックされて待ちになるな、、。
print $efh "start. ----------\n"; # エラーログに追加

#@データファイルを開き、http通信で受け取ったデータ記録する。
my($fh);
my($pf_name)=sprintf("log.txt");
if($bContinue){
    open $fh, '>> ', $pf_name ;
    flock($fh,2) or die; # Ubuntu Linuxだと、ブロックされて待ちになるな、、。
}

if($bContinue){ #@受け取ったデータを記録する。
    # PC,ANT,YYYYMMDD,HHMMSS,TAG
    print $fh $qq->param("PC").",";
    print $fh $qq->param("ANT").",";
    print $fh $qq->param("YYYYMMDD").",";
    print $fh $qq->param("HHMMSS").",";
    print $fh $qq->param("TAG")."\n";
    close $fh; #閉じる。
}

#@データを記録するグローバル変数配列を宣言する。
my(@aPC)=();
my(@aANT)=();
my(@aYYYYMMDD)=();
my(@aHHMMSS)=();
my(@aTAG)=();
my(@aMove)=(); #移動方向を記録する。

#@データを記録する
if($bContinue){
    $/ = undef;
    my($tmpFName)="tmp.$$.txt"; #print $efh "tmpf=".$tmpFName."\n";
    system("tac ".$pf_name." | head -20 >$tmpFName"); #最新データをピックアップ。
    open $fh2, '< ', $tmpFName ;
    my($buf)=<$fh2>;
    close $fh2;
    unlink($tmpFName);
    
    my(@aBuf)=split(/\n/,$buf);
    for (my $m=0; $m < @aBuf; $m++ ){ #行単位
	my(@aLine)=split(/,/,$aBuf[$m]); #カンマ区切り単位
	push(@aPC,$aLine[0]);
	push(@aANT,$aLine[1]);
	push(@aYYYYMMDD,$aLine[2]);
	push(@aHHMMSS,$aLine[3]);
	push(@aTAG,$aLine[4]);
	push(@aMove," "); #数を合わせるため、空行を入れる。
    }
}

#@記録されたデータから、移動方向を判断する。
if($bContinue){
    my $aNum=@aPC; #print $efh "aNum=".$aNum."\n";
    if ( $aNum == 0 ) {
	# データが空っぽならNOP
    }
    else{
	for(my $m=0; $m < $aNum; $m++){ #print $efh '$m='.$m."\n";
	    if($m == ($aNum -1)){
		# 最後のデータは、ひとつ後の行とは差分をとれない。	    }
	    }
	    else{ # PCがどこなのか、今のところチェックしない。
		#直近の同じEPCのタグを探す。見つからない可能性はある。
		#print $efh "m=".$m.":";
		my($mm)=0;
		for($mm=$m+1; $mm < ($aNum-1); $mm++){ # $mmを設定。
		    if($aTAG[$m] eq $aTAG[$mm]){
			#print $efh "aNum=".$aNum.":TAG ID HIT. mm=".$mm.":[".$aTAG[$m]."][".$aTAG[$mm]."] last\n";
			last;
		    }
		}
		#print $efh " mm=".$mm."\n";
		if($aTAG[$m] eq $aTAG[$mm]){ # タグのIDは同じか?
		    #print $efh "[".$aYYYYMMDD[$m].$aHHMMSS[$m]."][".$aYYYYMMDD[$mm].$aHHMMSS[$mm]."]\n";
		    my $t  = Time::Piece->strptime($aYYYYMMDD[$m].$aHHMMSS[$m],     '%Y%m%d%H%M%S');
		    my $t2 = Time::Piece->strptime($aYYYYMMDD[$mm].$aHHMMSS[$mm], '%Y%m%d%H%M%S');
		    my $diff = $t - $t2;

		    #print $efh "[".$aYYYYMMDD[$m].$aHHMMSS[$m]."][".$aYYYYMMDD[$mm].$aHHMMSS[$mm]."]\n";
		    #print $efh "diff sec = ".$diff->seconds."\n";
		    if ($diff->seconds < 20){ # 間隔の最大値は、この秒数とする。
		     	if($aANT[$m] eq $aANT[$mm]){ #print $efh "移動なし"."\n";
			    $aMove[$m]="移動なし";
		     	}
		     	elsif($aANT[$m] eq "1"){ #print $efh "持ち込み"."\n";
			    $aMove[$m]="持ち込み";
		     	}
		     	elsif($aANT[$m] eq "2"){ #print $efh "持ち出し"."\n";
			    $aMove[$m]="持ち出し";
		     	 }
		     	else{ #print $efh "空間転移"."\n";
			    $aMove[$m]="空間転移";
		     	}
		    }
		    else{#print $efh "タイムアウト"."\n";
			$aMove[$m]="";
		    }
		}
	    }
	}
    }
}

#@htmlのテーブル部分を作成する。
my($tableStr)=""; #@テーブル部分を作成する。
if($bContinue){
    my $aNum=@aPC; #print $efh "aNum=".$aNum."\n";
    if ( $aNum == 0 ) {
	# データが空っぽならNOP
    }
    else{
	$tableStr="";
	$tableStr .= '<table border="1">'."\n";;
	$tableStr .= "<tr><th>移動方向</th><th>年月日</th><th>時分秒</th>";
	$tableStr .= "<th>読取PC</th><th>アンテナ</th><th>EPC</th><th>タグ情報</th></tr>\n";
	for(my $m=0; $m < $aNum; $m++){ #print $efh '$m='.$m."\n";
	    $tableStr .= "<tr>";
	    $tableStr .= "<td>".$aMove[$m]."</td>";
	    $tableStr .= "<td>".$aYYYYMMDD[$m]."</td>";
	    $tableStr .= "<td>".$aHHMMSS[$m]."</td>";
	    $tableStr .= "<td>".$aPC[$m]."</td>";
	    $tableStr .= "<td>".$aANT[$m]."</td>";
	    $tableStr .= "<td>".$aTAG[$m]."</td>";
	    if ( exists($TagInfo{$aTAG[$m]}) ){
		$tableStr .= "<td>".$TagInfo{$aTAG[$m]}."</td>";
	    }
	    else{
		$tableStr .= "<td>"."未登録タグ"."</td>";
	    }
	    $tableStr .= "</tr>\n";
	}
	$tableStr .= '</table>';
    }
}

#@htmlファイルを更新する。
my($htmlStr)=""; #@html全体を作ってhtmlファイルを更新する。
if($bContinue){
    my($t)=1; # htmlファイルの再読み込み間隔秒
    $htmlStr = $htmlStr. "<html>\n";
    $htmlStr = $htmlStr. "<head>\n";
    $htmlStr = $htmlStr. '<meta charset="utf-8">'."\n";
    $htmlStr = $htmlStr. "<meta http-equiv=\"Refresh\" content=\"$t\">"."\n";
    $htmlStr = $htmlStr. "<title>LIST - MANICAコレクタ</title></head>\n";
    $htmlStr = $htmlStr. "</head>\n";
    $htmlStr = $htmlStr. "<body>\n";
    $htmlStr = $htmlStr. '<h1>MANINCAコレクタ 方向検出の最新読取情報</h1>';
    $htmlStr = $htmlStr. "最新のデータが".$t."秒ごとに再度読み込みされます。"."\n";
    $htmlStr = $htmlStr. "<hr>\n";
    $htmlStr = $htmlStr.$tableStr."<br>\n";
    $htmlStr = $htmlStr. "<hr>\n";
    $htmlStr = $htmlStr. "</body>\n";
    $htmlStr = $htmlStr. "</html>\n";

    my($fhHtml);
    open $fhHtml, '> ', '/var/www/html/collect/direct.html' ;
    flock($fhHtml,2) or die; # Ubuntu Linuxだと、ブロックされて待ちになるな、、。
    print $fhHtml $htmlStr;
    close $fhHtml;
}

#@エラーログを閉じる。
print $efh "end. ----------\n"; # ログファイルに追加
close $efh; # エラーログを閉じる

#@呼び出すPCが判断できるように応答を返す。
#もし応答しないと、コレクタはWebサーバーの内部障害として認識する。
print "Content-Type: text/html\n\n";
print "<html><head><title>response</title></head>\n";
print "<body>\n";
print "OK\n";
print "</body>\n";
print "</html>\n";

1;

以上です。


2026年2月18日水曜日

最近のレゴと、IoTブロック

同じ「ブロック」だけど、やってることは全然ちがう話


STEAM教育って言葉、ここ数年でよく目にするようになりました。
正直、ちゃんと説明しろと言われると少しあいまいですが、
「なんとなく、これから必要そう」
そんな距離感で気になっている人も多いんじゃないでしょうか。

私もその一人です。

子どもが将来どういう分野に進むかは分かりませんが、
テクノロジーについては、どの分野に行っても
分かっていてマイナスになることはなさそうだなと思っています。


最近のレゴを見ていて思ったこと

最近のレゴ公式ストアを見ると、
完成度の高いセットがかなり増えました。

どれも
「作る → 完成する → 眺める」
までがきれいに設計されていて、
遊びとしてはとても分かりやすいです。

もうすぐリリースされる レゴ スマートプレイシステム も、
組み立て方によって音や動きが変わる仕組みで、
いかにもレゴらしい進化だな、という印象です。

作る楽しさ、集中力、想像力。
このあたりは、やっぱりレゴは強いです。


「IoTブロック」って何だろう、と思って調べた

「IoTブロック」について、少し調べてみました。
試しにそのまま「IoTブロック」で検索してみると、
よく出てくるのが MESH や PLOCO。

どちらも「ブロック」と呼ばれていますが、
レゴのように形を作るブロックとは、
どうも考え方が違いそうです。

調べていくと、
同じブロックでも、
そもそもの役割や遊び方がかなり違う
ということが見えてきました。


MESHは「仕組みをいじる」ブロック

MESHは、

  • センサー
  • 無線
  • アプリ

を組み合わせて、
「こうなったら、こう動く」
というルールを自分で作っていくタイプのブロックです。

完成形が決まっているというより、
ずっと途中、みたいな感じ。

触っていると、

  • 条件を変える
  • 結果が変わる
  • また直す

という流れが自然に出てきます。

レゴのように「完成したら終わり」ではなく、
 動きや仕組みを調整し続ける遊び、という印象です。


PLOCOは、レゴに近い感触だった

一方の PLOCO。

PLOCOは、
  • ブロックを組み立てる
  • その形をスマホで読み取る
  • 数値に変換される
  • 画面上で結果(バトルなど)が出る
という流れです。

自分で条件分岐を作ったり、
処理の流れを設計したりするわけではありません。

なので、
IoTブロックというよりは、
レゴの延長線上にある遊びに近いと感じました。

「作って、読み取って、結果を見る」
という分かりやすさがあります。


同じブロックでも、向いている遊び方は違う

並べてみると、こんな感じです。

  • レゴ/スマートプレイ
    → 作ること自体を楽しむ
  • PLOCO
    → 作った形と結果の関係を楽しむ
  • MESH
    → 仕組みを考えて動かすことを楽しむ

どれが上とか下とかではなく、
そもそも遊びのタイプが違う、というだけの話です。


まとめ(特にオチはないです)

最近のレゴを見ていると、
ブロックと一口に言っても、
ずいぶん役割が分かれてきたなと感じます。

  • 形を作るブロック
  • 結果を返すブロック
  • 仕組みをいじるブロック

どれも同じ「ブロック」ですが、
やっていることはかなり違います。

STEAM教育とか、IoTとか、
大きな言葉で考えると難しくなりますが、
まずは
「何をして遊んでいるか」
くらいの目線で見ると、分かりやすい気がしました。


2026年2月16日月曜日

AndroidのHCEでオフラインスマート名刺

AndroidのHCEでオフラインスマート名刺

NFCを使ったスマート名刺と聞くと、ICカードにURLを埋め込むタイプのものを連想することが多いと思いますが、AndroidのHCE(Host Card based Emulation)を使えば受信側はアプリ不要、完全オフラインで画像つきのプロフィールを送信することができます。

仕組みは単純で、まず送信側のアプリでAIDにType 4 Tagを示す"D2760000850101"を設定します。
これで受信側は送信側をNFCタグと認識するので、OSを問わず、かつアプリ不要で通信できます。

画像を送信するためのキモになるのはvCard(.vcf)という名刺用のファイルフォーマットです。
iOSとAndroidはvCardで連絡先データを管理しています。vCardにはプロフィール画像と氏名、連絡先、会社などの情報を記載することができ、これをNDEFレコードにのせることで受信側は自動で連絡先データとして認識するというながれです。

NDEFレコード上ではvCardはMIMEタイプとして扱います。NDEFヘッダーのTNF(Type Name Format)に 0x02 (MIME Media) を指定し、タイプフィールドに text/vcard をセットすることで、受信側のOSがこれを連絡先データとして解釈します。

// NDEF Message Builder (vCard Record)
fun createVCardNdefMessage(vCardString: String): ByteArray {
    val vCardBytes = vCardString.toByteArray(StandardCharsets.UTF_8)
    val typeBytes = "text/vcard".toByteArray(StandardCharsets.UTF_8)
    val payloadLength = vCardBytes.size

    val output = ByteArrayOutputStream()
    val isShort = payloadLength <= 255
    var header = 0xC2 // MB=1, ME=1, TNF=0x02
    if (isShort) header = header or 0x10
    
    output.write(header)
    output.write(typeBytes.size)

    if (isShort) {
        output.write(payloadLength)
    } else {
        output.write((payloadLength shr 24) and 0xFF)
        output.write((payloadLength shr 16) and 0xFF)
        output.write((payloadLength shr 8) and 0xFF)
        output.write(payloadLength and 0xFF)
    }

    output.write(typeBytes)
    output.write(vCardBytes)
    return output.toByteArray()
}

HostApduService を継承し、以下のシーケンスを実装します。

  • SELECT AID: Type 4 Tag アプリケーションの選択
  • SELECT FILE: CCファイルまたはNDEFファイルの選択
  • READ_BINARY: 選択されたファイルのデータをオフセットに従って返却

特に READ_BINARY では、一度に送信できるバッファサイズ(Le)に制限があるため、大きなvCardデータ(画像付きなど)を送信する場合は、複数回の読み取りリクエストに対して正確にオフセット管理を行う必要があります。

// HostApduService (READ_BINARY Response)
override fun processCommandApdu(commandApdu: ByteArray, extras: Bundle?): ByteArray {
    if (readBinaryApdu(commandApdu)) {
        val offset = getReadBinaryOffset(commandApdu)
        val le = getReadBinaryLe(commandApdu)
        
        val selectedFile = currentSelectedFile ?: return STATUS_FAILED
        if (offset >= selectedFile.size) return STATUS_FAILED
        
        val lenToRead = Math.min(le, selectedFile.size - offset)
        val response = ByteArray(lenToRead + 2)
        System.arraycopy(selectedFile, offset, response, 0, lenToRead)
        
        // Append Success Status (90 00)
        response[lenToRead] = 0x90.toByte()
        response[lenToRead + 1] = 0x00.toByte()
        return response
    }
    // ...
}

実際に動かしました。

vCardのファイルサイズが大きくなりすぎるとおそらくタイムアウトが発生してしまいます。
試したところ30KBあたりが送信できる限界で、20KBを超えると送信するのに5秒ほど時間がかかりました。実際に使うときは相手のスマホのNFCセンサーの位置を把握するためにさらに時間を要するので、10KB程度で収まるようにするのが現実的かと思いました。

Android Beamが廃止されたのもなんとなく頷けますが、異なるOS間で通信できるのは魅力的に思えます。
HCEでもURLのみを飛ばすことができます。そもそもオフラインで通信する必要もほとんどないうえ、URL単体のほうが速くて安定しているのでそのほうが実用性はあります。

URLバージョン

2026年2月13日金曜日

AIと相談してコレクタで、もっと簡単に方向検知をRFID読み取りで記録してみた

簡単にRFIDできるツールとして、マニカ コレクタがあります。

 https://www.hayato.info/collector.htm 

です。

前回はリレーショナルデータベスを使いましたが、もっと簡単に実現したいです。

今回はコレクタにバッチファイルを実行させる方法です。



バッチファイルの作りかたは、知らなくても大丈夫。

AIに相談しました。

例えば「コマンドラインで時刻を取得して、その出力から特定の情報をとりたい。」

と相談すると、実例を交えて回答してくれるので、

コピーすると、やりたいバッチファイルが実現できました。


実行させるバッチファイルは、コレクタからの引数を、ちょこっと加工して新たにテキストファイルを作ります。

バッチファイルは今回も、コレクタからアンテナ番号をもらいます。

このバッチファイル(collog.bat)は、

「時刻情報+EPC情報+アンテナ番号」を入れて、拡張子「.coltxt」として作成します。


コレクタで何回か読み取ると、こいう感じになります。


つぎにエクセルで、このフォルダを監視して「*.coltxt」を、常に監視したい。

監視して、アンテナ番号と時刻の差分の秒数を計算して、ICタグ移動を知りたい。

と、AIに相談すると、

「モードレスのフォームを作り、そのフォームから数秒間隔で監視するのがお勧めです。

そのフォームの中から結果をエクセルシートに書いてはいかがですか。

こういうフォームを作ると実現できます。」

という回答をくれるので、試しに作ったり、エラーを相談したり、改良案を求めたり、

と、AIと一緒に考えました。


すると、実現できました!


左上の画面ではコレクタがバッチファイルを実行している様子が見えます。

下のエクセルでは、モードレスフォームを実行して「監視中」を表示しています。

フォームではコレクタが作ったファイルを監視して、

「持ち込み/持ち出し/移動無し/タイムアウト」を表示しています。

(タイムアウトとは、1回は読んだけど、次の読み取りまでの時間が長すぎた。

だから移動ではなく、移動は判断しないタイムアウトだね。

という意味です。今回の「移動」とは、読んでから10秒ぐらいの間に

再度の読み取りがあった。という考えです。)


エクセルのフォームのレイアウトや作り方は、皆さんが好みで作成されると良いでしょう。

コレクタから情報をもらい、新たにファイルを作成するバッチファイルは、

コレクタの仕様にしたがった呼び出しになりますので、下記にご紹介します。


【コレクタに設定する、collog.batの呼び出し】

C:\RFID\CMD\collog.bat  [タグID]  [アンテナNO] [YYYYMMDD] [HHMMSS]


【collog.bat】

set P=C:\RFID\CMD\

set T=%TIME: =0%

set F=%T:~0,2%%T:~3,2%%T:~6,2%%T:~9,2%

set D=%date:~0,4%%date:~5,2%%date:~8,2%

set EPC=%1%

set ANT=%2%

set YMD=%3%

set HMS=%4%

set OUTF=%P%%D%-%F%-%EPC%-%ANT%.coltxt

echo %OUTF%

echo %EPC%,%ANT%,%YMD%,%HMS% > %OUTF%

exit


ここで終わりです。

2026年2月6日金曜日

AIと相談してコレクタで、方向検知をRFID読み取りで記録してみた

簡単にRFIDできるツールとして、マニカ コレクタがあります。

 https://www.hayato.info/collector.htm 

です。

とても簡単に始められるのは、もちろんですが、実はSQL文を実行することも、さらっと記載されています。

SQL文にコレクタが展開して埋め込むことができるのは、
①タグID:ICタグの電子ID

②アンテナID:複数枚のアンテナをつなげた定置リーダーは何枚目で読んだか

③YYYYMMDD:年月日

④HHMMSS:時分秒

です。


注目なのは②です。定置リーダーは2枚から4枚のアンテナをつなげることができて、コレクタは何枚目で読んだのか分かるのです!

そこで、賢明な貴方は気が付くはずです。

方向の検出に使えるのでは?と。


そこで、AIに相談します。

「私はRFIDタグから①②③④を読み取れるソフトをPCで動かしています。データベースに情報を格納したいのですが、安価なものを紹介してください。」

回答を見ると、SQL Server Expressが良さそうだと思われました。

そこで、AIに質問します。

「アンテナが2枚あり、部屋の扉の外側と内側に取り付けてます。RFID読み取りタイミングで、何番目のアンテナで読んだかは識別できます。部屋に持ち込んだ、部屋から持ち出した、を知りたいです。データベースで方法はありますか?」

すると、

「テーブルを2つ作り、逐次読取り結果テーブルと持ち込み持ち出しを記録するテーブル、そしてトリガーを使用すれば出来ます。」

という回答なので、テーブル作成SQL文も作ってもらい、エラーが出たら報告して修正してもらい、と繰り返したら、できました!



コレクタのログ表示では逐次の読み取りが表示され、右のデータベース表示では、「IN」「OUT」が表示されます。

コレクタを使えば手軽に持ち出し/持ち込みが識別できることがわかりました。

仕掛けは機能したので、お客様に勧めるときは、見た目の相談もして、現場で使いやすくアレンジしましょう!


使用した機材は、ノートPCが1台、定置リーダーが1台、アンテナが2枚です。

AIに相談して作成したSQL文も紹介しますね。

【コレクタで実行するSQL文】

--コレクタの時刻文字列をSQL Server用の時刻文字列に変換してインサート。

INSERT INTO dbo.T_RFID (dt,id_ant, id_epc, yyyymmdd, hhmmss, desc_txt)

VALUES (

  DATEADD(SECOND,

      DATEDIFF(SECOND, CONVERT(time(0),'00:00:00'),

               TRY_CONVERT(time(0), STUFF(STUFF('[HHMMSS]',3,0,':'),6,0,':'))),

      TRY_CONVERT(datetime2(0), '[YYYYMMDD]', 112)

  ),

  '[アンテナNO]',

  '[タグID]',

  '[YYYYMMDD]',

  '[HHMMSS]',

  'COLLECTOR'

);


【SQL Server Expressでテーブル作成】

USE [MANICA_01]

GO

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE TABLE [dbo].[T_RFID](

[auto_id] [int] IDENTITY(1,1) NOT NULL,

[dt] [datetime2](0) NULL,

[id_ant] [varchar](2) NOT NULL,

[id_epc] [varchar](96) NOT NULL,

[yyyymmdd] [varchar](8) NOT NULL,

[hhmmss] [varchar](6) NOT NULL,

[desc_txt] [varchar](14) NULL,

PRIMARY KEY CLUSTERED 

(

[auto_id] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

GO


USE [MANICA_01]

GO

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE TABLE [dbo].[T_DIRECT](

[auto_id] [int] IDENTITY(1,1) NOT NULL,

[id_epc_auto_id] [varchar](96) NOT NULL,

[direct] [varchar](4) NOT NULL,

[desc_txt] [varchar](14) NULL,

PRIMARY KEY CLUSTERED 

(

[auto_id] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

GO


【SQL Server Expressでトリガー作成】

USE [MANICA_01]

GO

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE   TRIGGER [dbo].[trg_T_RFID_to_T_DIRECT_5sec]

ON [dbo].[T_RFID]

AFTER INSERT

AS

BEGIN

    SET NOCOUNT ON;

    INSERT INTO dbo.T_DIRECT (id_epc_auto_id, direct, desc_txt)

    SELECT

        i.id_epc,

        CASE

            WHEN i.id_ant IN ('1','01') THEN 'IN'

            WHEN i.id_ant IN ('2','02') THEN 'OUT'

            ELSE 'IN'

        END,

        i.desc_txt

    FROM inserted AS i

    CROSS APPLY (

        SELECT TOP (1)

            r.auto_id,

            r.dt

        FROM dbo.T_RFID r

        WHERE r.id_epc = i.id_epc

          AND r.auto_id < i.auto_id          -- 「直前」の候補に限定(重要ぴょん)

          AND r.dt IS NOT NULL

        ORDER BY r.auto_id DESC              -- ここで降順ぴょん

    ) AS prev

    WHERE

        i.dt IS NOT NULL

        AND DATEDIFF(SECOND, prev.dt, i.dt) BETWEEN 0 AND 8;

END;

GO

ALTER TABLE [dbo].[T_RFID] ENABLE TRIGGER [trg_T_RFID_to_T_DIRECT_5sec]

GO



【コマンドラインで「IN][OUT」表示】

「show_update.bat」の内容


@echo off

setlocal

:loop

sqlcmd -S .\SQLEXPRESS -d MANICA_01 -U manica_user -P "himitu" -W -Q "SELECT TOP (10) * FROM dbo.T_DIRECT ORDER BY auto_id DESC;"

echo ---- %date% %time% ----

timeout /t 3 /nobreak >nul

goto loop


ここで終わりです。


2026年2月3日火曜日

ユニクロのタグを使って、生活はどこまで楽になるか実験してみた|おうち棚卸・家事ラク編


RFIDというと、

「物流」「倉庫」「アパレルの在庫管理」
…みたいな、いかにも業務向けの技術というイメージが強いと思います。

でも、ふと思いました。

これ、もっと生活レベルまで引き下げたらどうなるんだろう?

ちょうど手元にあったのが、ユニクロの服についているあのタグ。
実はこれ、誰でも手に入るRFIDなんですよね。

今回はこれを
「身近に手に入るRFID」として使って、
家庭・家事レベルでどこまで役に立つのかを、
かなり現実的な条件で試してみました。 


床下収納を開けなくていい?
RFIDで“家のストック棚卸”をやってみた


やったことはシンプルです。
  • 常備品などの日用品にタグを紐づけ
  • 在庫切れのものは、タグを「買い足し用ボックス」に入れておく
  • 冷蔵庫・床下収納・パントリーを開けずに買い足すものをチェック
  • 読み取ったデータを家族(旦那)と共有


なぜこれをやろうと思ったか


家事の中で、地味〜にストレスなのがこれ。
  • 床下収納を毎回開けて確認する
  • 「あったっけ?」と曖昧なまま買い物に行く
  • 結果、「もうあるのに買った」が発生する

完璧な在庫管理がしたいわけじゃないんです。

「出かける前に、ざっくり確認できればいい」
それだけなのに、意外と手間がかかる。

そこで思いついたのが、
RFIDで“家のストック棚卸”ができないか?
という、かなり軽いノリの実験でした。


やり方(超シンプル)


難しいことは一切していません。

① タグを紐づける


対象はこのあたり。
  • 冷蔵庫の常備品(牛乳・納豆・豆腐…)
  • いちいち開けるのが面倒な
    床下収納の醤油・みりん・調味料
  • トイレットペーパーなどの日用品

それぞれにユニクロのタグを紐づけました。




マニカエクセルツールのアプリでタグを読み取って入力。
サクサク登録できて、ここは意外とストレスなし。

② ストックがなくなりました


使い切ったら、
買い足しが必要なタグを
適当な入れ物にポイっと放り込むだけ。



管理というより、「仕分け」感覚です。

③ 買い物行く前に読み取り




リーダーでまとめて読み取り。
いちいちメモに書いたり、
スマホに打ち込んだりする必要はありません。

④ 外出先でスマホから確認


外出先からでも、
「今、家に何があるか」をスマホで確認できます。

買い終わったものはその場で削除できるので、
残りの「買い足しが必要なもの」だけがすっきり残ります。


⑤ データを家族と共有




管理画面のリンクを家族に共有しておけば、
いちいち買い物リストを送る手間もなし。

③の読み取り自体を、
家族にお願いするのも全然アリです。


実際どうだったか


まず結論から。

「意外と、使える」

日常の細かい負担って、
 一つひとつは小さいけど、積み重なると結構効きます。

それを減らせるだけで、
生活の快適度ってちゃんと上がるんだな、と。


まとめ




RFIDは
「管理するため」の技術じゃなくて、

「確認作業を減らすため」
くらいの使い方で、ちょうどいい。

「出かける前に一瞬確認できる」
それだけで、家事のストレスはかなり減ります。

これは完璧な在庫管理ではありません。
でも、

“出かける前の確認”としては十分使える。

RFIDは、
家事を効率化する魔法の技術ではなく、

👉 家事の「確認作業」を減らす道具

として考えると、
 一気に生活に近づく技術だと感じました。