PC・Android間のBluetooth通信
PCに繋いだセンサ情報をandroidに送信するプログラムを作る機会があったので,そのまとめ.
システムの要件としては以下の様になります.
- Android側は元々Bluetoothでデータをやり取りする部分があり,そのデータをセンサ情報で置き換えるため通信にはBluetoothを使用
- PCはVAIOで,元からBluetoothを搭載しているので,それを使用.
- センサのライブラリがC++のコードで用意されていたので,PC側はC++を使用
- PC側のソケット通信には最もメジャーであろうSocketライブラリを使用
- サーバーはPC側で立て,Androidから接続
やったことある人には簡単じゃないかと思われるかもしれませんが,自分は通信まわりのプログラムがとても苦手で,今回も案の定大苦戦を強いられました・・・
全体のおおまかな流れとしては,以下の様になります.
通信用のSocketを開くまで
下準備
まずBluetoothのペアリングを行います.この部分に関してはBluetoothドングルを用いた場合など,個別のケースによるので割愛.
次に通信用のCOMポートの設定を行います(これはひょっとしたら必要ないかもしれませんが).今回用いたVAIOPCの場合,Android側でUUIDを指定してソケットを開くとBluetoothの設定から接続先のCOMポートの追加ができました.USBでつなぐ場合は自動的に割り振られると思います(確認はしてないけど).
socket部分のコード
ここまでで下準備は終了.ここからはコードに移ります.
Android側のプログラムに関しては,公式のsampleのBluetoothChatの通信部分を用いました.一部変更点としては,UUIDとしてSPP(SerialPortServiceClass_UUID)を使用し,BluetoothChatService.javaのMY_UUIDを変更します.通信のUUIDに関して詳しく知りたい方はここを参考に.
変更前
// Unique UUID for this application
private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
変更後
// Unique UUID for this application
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
次にPC側です.この部分はSPP通信用Bluetooth Socket Serverを参考にしました.コードは重要な部分のみ抜粋.
接続用のsocketを開き,androidからの接続を待って通信用のsocketを開く部分のコード.
SOCKET sockRecv;
SOCKADDR_BTH sockAddrBth = {0};
sockRecv = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if (INVALID_SOCKET == sockRecv) {
return false;
}
sockAddrBth.addressFamily = AF_BTH; // For Bluetooth
sockAddrBth.port = BT_PORT_ANY;
if (SOCKET_ERROR == bind(sockRecv, (SOCKADDR *) &sockAddrBth, sizeof(sockAddrBth)))
{
closesocket(sockRecv);
return false;
}
int size = sizeof(sockAddrBth);
getsockname(sockRecv, (SOCKADDR *) &sockAddrBth, &size);
CSADDR_INFO info = {0};
info.LocalAddr.lpSockaddr = (LPSOCKADDR) &sockAddrBth;
info.LocalAddr.iSockaddrLength = sizeof(sockAddrBth);
info.iSocketType = SOCK_STREAM;
info.iProtocol = BTHPROTO_RFCOMM;
WSAQUERYSET set = {0};
set.dwSize = sizeof(WSAQUERYSET); // Must be set to sizeof(WSAQUERYSET)
set.dwOutputFlags = 0; // Not used
set.lpszServiceInstanceName = "Server"; // Recommended.
set.lpServiceClassId = (LPGUID)&SerialPortServiceClass_UUID; // !ここのUUID(00001101-0000-1000-8000-00805F9B34FB)はAndroid側とマッチしている必要がある!
set.lpVersion = NULL; // Not used.
set.lpszComment = NULL; // Optional.
set.dwNameSpace = NS_BTH; // Must be NS_BTH.
set.lpNSProviderId = NULL; // Not required.
set.lpszContext = NULL; // Not used.
set.dwNumberOfProtocols = 0; // Not used.
set.lpafpProtocols = NULL; // Not used.
set.lpszQueryString = NULL; // not used.
set.dwNumberOfCsAddrs = 1; // Must be 1.
set.lpcsaBuffer = &info; // Pointer to a CSADDR_INFO.
set.lpBlob = NULL; // Optional.
if (WSASetService(&set, RNRSERVICE_REGISTER, 0) != 0) {
return false;
}
listen(sockRecv, 0);
SOCKADDR_BTH sab2;
int ilen = sizeof(sab2);
// 通信用のsocketを開く.このsocketで通信を行う.
acceptedSocket = accept(sockRecv, (SOCKADDR *)&sab2, &ilen);
// 接続用のsocketは必要ないので閉じる
closesocket(sockRecv);
上記コードのacceptedSocketは保持しておき,通信にも用います.
Androidにデータを送信
ここまでくればあと少し・・・のはずが,ここでまさかのどん詰まり.数時間悩みました・・・
Socketを通してデータを送信するのは簡単で,sendの関数一つの呼び出しで可能です.何故詰まったかと言うと,Android側でデコードしたデータが全然違うという問題が解決しなかったためです.
ちなみに,sendは,send(SOCKET s, const char* buf, int len, int flags)の形式を取り,bufの部分で配列などを変換して送信します.今回はintの配列を送信するため,int * -> char * の変換をしました.
Android側のデコードは他でも使っていて恐らく間違っていない(というか正しかった)と考え,エンコードの際にどうすればよいのかを延々と調べては試すという繰り返しでした・・・
このやり方に関する記事は結構あるのですが,単純に(char *) (intの配列)という風に型変換すればいいと書いてあるものが多く,試しましたがダメ.さらにint * -> byte * -> char * の変換等も色々試しましたが,一向に解決せず.途方に暮れていたところで一筋の光が.
stackoverflowのこの記事によると,htonを噛ませてネットワークバイトオーダーに変換する必要があるとのこと.
最終的には以下のコードで送信できました.arrayが送信する配列,intは送信するデータ数(配列の長さと等価)です.
分かってれば一時間もかからずできたものに結局2日もかかってしまい,本当に通信周りの知識をつけないとまずいと感じました・・・
Eclipse Android SDK Content Loader hangs at 0% 対応のbat作ってみた
Eclipse Android SDK Content Loader hangs at 0% の対応
Eclipse Android SDK Content Loader hangs at 0% まとめ - kkbnart's diary
の続きでbatファイルを作ってみた.
bat自体作るのほぼ初めてだったので,簡単なんだけど以外と時間かかった・・・
やることは非常に単純で以下の通り
- %USERPROFILE%.androidに移動
- ddms.cfgを削除
- cacheの中身を削除
実際の処理は以下の通りです.ファイルチェック,出力を多少しているのでちょっと長いです.間違いがあるかもしれないので,使う際は自己責任でお願いします.また間違いに気づいた方は指摘もお願いします.
別ドライブから実行する場合は"cd /d ~"かな?
@echo off
rem This bat file works to resolve Eclipse Android SDK Content Loader hanging problem.
rem Change directory to userprofile.android
cd %USERPROFILE%\.android%
echo %CD%
rem search and delete file "ddms.cfg"
set DEL_FILE="ddms.cfg"
IF EXIST %DEL_FILE% (
del %DEL_FILE%
echo Delete file ddms.cfg
) ELSE (
echo !!CAUTION!!
echo file does not exist. Please Delete Manually
GOTO END
)
rem search and delete files in cache
set CACHE_DIR="cache"
IF EXIST %CACHE_DIR% (
del %CACHE_DIR%\%
echo Delete files in cache
) ELSE (
echo !!CAUTION!!
echo file does not exist. Please Delete Manually
GOTO END
)
rem finish process
:END
pause
exit
cygwinでssh接続
Eclipse Android SDK Content Loader hangs at 0% まとめ
EclipseでAndroid開発をしている人なら経験があろう,Eclipse起動時にContents Loader が0%で止まってしまい,作業ができなくなってしまう悪名高きバグ.あのときのイライラ感と言ったら・・・
あまりにも頻繁に起こるので,対処法をまとめてみた.
-
Run Eclipse -Clean
一番簡単な対処法.プラグイン追加後などに,起動オプションとして"-clean"を指定する.調べると大抵出て来るけど,自分自身はこれで解決したことが一回もない. -
adb kill-server, adb start-server
Android Debug Bridgeの再起動コマンド.上同様自分はこれで解決したことなし. -
delete all files in .projects
かなり有効な解決手段.workspace.metadata.plugins\org.eclipse.core.resources.projectsのフォルダ内の全てのファイルを削除し,eclipseを再起動するとほぼ確実に解決する.しかし,その代償としてworkspace以外のディレクトリにあるプロジェクトの参照が消えるので,削除する前に.projects内のファイルのバックアップを取っておき,eclipse再起動後にファイルを元に戻す必要がある.
この方法は長い間使ってきたが,確実に解決するけど面倒臭い -
delete cache, ddms.cfg
最近見つけた対処法.%USERPROFILE%.android内にあるcacheフォルダの中身とddms.cfgファイルを削除し,再起動.非常にスマートな解決法で,この方法が一番おすすめ.stack overflowでも賞賛のコメントが大量についており,素晴らしい対処法であることが伺える.
総括
とりあえず4の対処法でok.それでだめなら3.
これで皆が救われることを祈る.
参考: http://stackoverflow.com/questions/13489141/eclipse-hangs-at-the-android-sdk-content-loader
Windows8が再起動できなくなった時の対応
先日のこと,自分の使っているASUSのノートPCで,HD容量が大分圧迫されていると警告が出たので,ディスククリーンアップを実行.この際,windows updateにもチェックを入れたのだが,処理が一行に終了しないため,キャンセルして再起動.すると・・・
windowsを修復できないとの文字が・・・トラブルシューティング画面が出てきてしまい,色々策を講じるも起動できない状態に・・・
こんなこと初めてだったので,パニック状態.とりあえずデータをサルベージしなければと思い,ググってみてそれらしい方法が見つかった.どうやら,windows8のインストールディスクから起動し,外付けのHDにデータをコピーすればよさそうと分かった(実はここで既に勘違いをしていたのだが).しかし,このPCには内蔵のDVDドライブがないので,USBの物をつなぎ,そこから起動することに.
再度問題発生.Biosの設定変える必要があり,Bootの優先順位でUSBのDVDドライブを一番にしなければらないが,その方法が分からない.
まぁここら辺は常識っちゃ常識だけど,PCごとにBIOS違ったりするから手順も違うことを知らなかったので.ここで結構悩んだ後,いい動画が見つかったため解決.
https://www.youtube.com/watch?v=X3GFZm9at6g
これでやっとインストールディスクから立ち上げられる・・・!ここまでで数時間かかったので,なんかやり遂げた感.そして次のコピーの段階へ.どうやらプリインストールのWindows(WindowsPE)でコマンドプロンプトを立ち上げ,そこでxcopyのコマンドで丸ごとドライブの中身をコピーすれば良さそうと分かった.
ここでプロンプトを立ち上げて気づく.これってわざわざインストールディスクから起動しなくてもできたのでは・・・実はこのPCは最初からでwin8が入っていたためか,初めからコマンドプロンプトは立ち上げられたのだった・・・ここら辺に気づかないのは本当に情けない・・・恐らく,最初から入っていない場合は必要な場合もあるのかな・・・
本当に時間の無駄でした.まぁちょっとここら辺の知識ついたかな・・・まぁその後無事データを回収して,なんとか事なきを得たものの,PCはほとんど初期化してしまったため,OfficeやらEclipseやらは入れ直しになり,今現在も修復中でございます.
ちなみになぜ起動しなくなったかの原因は不明だけど,どうやらディスククリーンアップでwindowsアップデートはチェックをつけない方がいいらしい.今度からは気を付けよ・・・ga