1

USBポートがAndroidタブレット(Samsung Galaxy Tab S、USBホスト機能をサポートするモデルSM-T705)に接続されているEtrex 10 Garmin GPSからPVT(Position、Velocity、Time)レコードを読み取ろうとします。最初にWindowsアプリケーションを使用しましたこの通信をシミュレートするために、USB の送受信パケットを監視しました。Garmin のドキュメント (Garmin GPS Interface Specification、USB Addendum) によると、これらのデータ パケットは正常に送受信されました (すべての数値は 16 進数です)。

(PC から GPS、セッション開始パケット) 00 00 00 00 05 00 00 00 00 00 00 00

(GPS から PC、セッション開始の確認) 00 00 00 00 06 00 00 00 04 00 00 00 0b ba aa e7

これは、ホストとデバイス (GPS) が通信を行う方法を示すこのドキュメントのセクション 6 の一部です。

6サンプル通信セッション

このセクションでは、USB ホストと Garmin USB GPS 間のサンプル通信セッションを提供します。以下のメッセージは時系列で表示されます。すべてのデータは HEX 形式で表示されます。Bulk Out (USB Transport Layer - Start Session) ホストはこれを最初に Garmin USB デバイスに送信し、転送の準備をするよう伝えます。00 00 00 00 05 00 00 00 00 00 00 00 割り込み (USB トランスポート層 - セッション開始) デバイスはホストに応答し、デバイスが会話の準備ができていることをホストに知らせます。00 00 00 00 06 00 00 00 04 00 00 00 37 C4 E4 AB

...................................

Android アプリケーションでこの通信をコーディングしようとしました。Android コードでは、タブレットに接続すると GPS デバイスを認識できます。3 つのエンドポイントがあります。 Bulk-out エンドポイントを介したセッション パケット、Interrupt (または Bulk_in) エンドポイントを介した Acknowledge パケットを受信しません。MONO ライブラリを使用した Xamarin Studio のコードは次のとおりです。

public class MainActivity : Activity
{
private UsbManager m_USBManager = null;
private UsbDevice m_GPS = null;
private UsbInterface m_USBInterface = null;
private UsbEndpoint m_BulkOut = null;
private UsbEndpoint m_BulkIn = null;
private UsbEndpoint m_InterruptIn = null;
private UsbDeviceConnection m_USBConnection = null;

public static string ACTION_USB_PERMISSION = "GarminEtrex10.USB_PERMISSION";

private TextView m_TxMsg;
private ListView m_LvTxRxRows;

private USBReceiver m_USBRx;
PendingIntent m_PermissionIntent;

public List<TxRxRow> m_TxRxRows = new List<TxRxRow> ();

protected override void OnCreate (Bundle bundle)
{
    base.OnCreate (bundle);

    SetContentView (Resource.Layout.Main);

    this.m_PermissionIntent = PendingIntent.GetBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);

    Button button = FindViewById<Button> (Resource.Id.myButton);
    this.m_TxMsg = FindViewById<TextView> (Resource.Id.TxMsg);
    this.m_LvTxRxRows = FindViewById<ListView> (Resource.Id.LvTxRx);

    this.m_USBManager = (UsbManager) this.GetSystemService (Context.UsbService);
    this.m_USBRx = new USBReceiver (this, this.m_USBManager);
    IntentFilter filter = new IntentFilter (ACTION_USB_PERMISSION);
    RegisterReceiver (this.m_USBRx, filter);

    button.Click += new EventHandler (this.ButtonClicked);
}

protected override void OnResume ()
{
    base.OnResume ();

    bool bHasPermission = false;

    if (this.m_USBManager != null)
    {
        foreach (string strKey in this.m_USBManager.DeviceList.Keys)
        {
            UsbDevice usbd = this.m_USBManager.DeviceList [strKey];
            if (usbd.VendorId == 2334)//Garmin product Id
            {
                this.m_GPS = usbd;
                break;
            }
        }
    }
    else
        this.m_TxMsg.Text = "USB Manager is null";
    if (this.m_GPS != null) 
    {
        this.m_USBManager.RequestPermission (this.m_GPS, this.m_PermissionIntent);
        bHasPermission = this.m_USBManager.HasPermission (this.m_GPS);
        this.m_TxMsg.Text = "Garmin GPS found.";
        if (bHasPermission)
            this.m_TxMsg.Text += " and have permission to use it.";
        else
            this.m_TxMsg.Text += " but not have permission to use it.";
    }
    else
        this.m_TxMsg.Text = "No GPS found.";
    if (bHasPermission) 
    {
        this.m_USBInterface = this.m_GPS.GetInterface (0);
        if (this.m_USBInterface != null) 
        {

            for (int i = 0; i < this.m_USBInterface.EndpointCount; i++)
            {
                UsbEndpoint ep = this.m_USBInterface.GetEndpoint (i);
                if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.Out && this.m_BulkOut == null)
                    this.m_BulkOut = ep;
                if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.In && this.m_BulkIn == null)
                    this.m_BulkIn = ep;
                if (ep.Type == UsbAddressing.XferInterrupt && ep.Direction == UsbAddressing.DirMask && this.m_InterruptIn == null) 
                    this.m_InterruptIn = ep;
            }
        }
    }
}

public void PopulateListView(byte[] bytes, byte port)
{
    TxRxRow row = new TxRxRow (port, bytes);
    this.m_TxRxRows.Add (row);
    TxRxAdapter adp = new TxRxAdapter (this, this.m_TxRxRows);
    this.m_LvTxRxRows.Adapter = adp;
}

public void SetStatus(string strMsg)
{
    this.m_TxMsg.Text = strMsg;
}

public void SetDevice(UsbDevice gps, UsbInterface gpsInterface, UsbDeviceConnection gpsConnection, UsbEndpoint gpsBulkout,UsbEndpoint gpsBultIn,UsbEndpoint gpsInterruptIn)
{
    if (this.m_BulkIn != null)
        return;
    this.m_GPS = gps;
    this.m_USBConnection = gpsConnection;
    this.m_USBInterface = gpsInterface;
    this.m_InterruptIn = gpsInterruptIn;
    this.m_BulkIn = gpsBultIn;
    this.m_BulkOut = gpsBulkout;
}
private void ButtonClicked(object sender, EventArgs e)
{
    if (this.m_BulkOut == null || (this.m_BulkIn == null && this.m_InterruptIn == null)) 
    {
        Toast.MakeText (this, "Could not find right endpoints", ToastLength.Long).Show ();
        return;
    }
    if(this.m_USBConnection ==null)
        this.m_USBConnection = this.m_USBManager.OpenDevice (this.m_GPS);
    if (this.m_USBConnection == null) 
    {
        Toast.MakeText (this, "Can not open USB device", ToastLength.Long).Show ();
        return;
    }

    if (!this.m_USBConnection.ClaimInterface (this.m_USBInterface, true)) 
    {
        Toast.MakeText (this, "Can not claim interface", ToastLength.Long).Show ();
        return;
    }

    int sentBytesCount = 0;

    #region Send Start_Session Packet
    byte[] obuffer = new byte[12];
    for(int i = 0; i < 12; i++)
        obuffer[i] = 0;
    obuffer[4]=5;

    this.m_TxMsg.Text = "Sending Start session packet....";
    sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, 12, 100);

    if (sentBytesCount >= 0) 
    {
        this.m_TxMsg.Text = "Sent bytes = "+sentBytesCount.ToString();
        this.PopulateListView(obuffer,1);
        this.m_USBRx.ReceiveFlag = true;
        this.m_USBConnection.RequestWaitAsync();
    }
    else
    {
        Toast.MakeText (this, "Sent bytes for start session packet = 0.operation failed", ToastLength.Long).Show ();
    }
    #endregion
}
}

そして、これはブロードキャストレシーバークラスです

public class USBReceiver : BroadcastReceiver
{
private UsbManager m_USBMngr = null;
private Context m_Activity = null;
private UsbEndpoint m_BulkIN = null;
private UsbEndpoint m_InterruptIN = null;
private UsbDeviceConnection m_USBConnection=null;

public bool ReceiveFlag = false;

public USBReceiver(Context activity, UsbManager mngr) 
{
    this.m_Activity = activity;
    this.m_USBMngr = mngr;
}
public override void OnReceive (Context context, Intent intent)
{
    String action = intent.Action;
    (this.m_Activity as MainActivity).SetStatus ("Receiving activity=" + intent.Action + "...");
    if (action.Equals (MainActivity.ACTION_USB_PERMISSION)) 
    {
        lock (this) 
        {
            UsbDevice device = (UsbDevice)intent.GetParcelableExtra (UsbManager.ExtraDevice);
            bool bHasPermission = intent.GetBooleanExtra (UsbManager.ExtraPermissionGranted, false);
            //For the first time initialize MainActivity local parameters
            if (this.m_USBConnection == null) 
            {
                if (device != null && bHasPermission) 
                {
                    if (device.VendorId != 2334)
                        return;
                    this.m_USBConnection = this.m_USBMngr.OpenDevice (device);
                    UsbInterface ui = device.GetInterface (0);
                    UsbEndpoint epBulkOut = null;
                    for (int i = 0; i < ui.EndpointCount; i++) 
                    {
                        UsbEndpoint ep = ui.GetEndpoint (i);
                        if (ep.Type == UsbAddressing.XferInterrupt && ep.Direction == UsbAddressing.In)
                            this.m_InterruptIN = ep;
                        if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.In)
                            this.m_BulkIN = ep;
                        if (ep.Type == UsbAddressing.XferBulk && ep.Direction == UsbAddressing.Out)
                            epBulkOut = ep;
                    }
                    (this.m_Activity as MainActivity).SetDevice (device, ui, this.m_USBConnection, epBulkOut, this.m_BulkIN, this.m_InterruptIN);
                }
            }
            if (this.m_BulkIN != null) 
            {
                int maxPacketSize = this.m_BulkIN.MaxPacketSize;
                byte[] buff = new byte[maxPacketSize];
                int bytesRead = this.m_USBConnection.BulkTransfer (this.m_BulkIN, buff, maxPacketSize, 4000);
                if (bytesRead > 0) 
                {
                    (this.m_Activity as MainActivity).PopulateListView (buff, 2);
                    ReceiveFlag = false;
                }
            }
            if (this.m_InterruptIN != null) 
            {
                UsbRequest req = new UsbRequest ();
                if (req.Initialize (this.m_USBConnection, this.m_InterruptIN)) 
                {

                    int maxsize = this.m_InterruptIN.MaxPacketSize;
                    Java.Nio.ByteBuffer buff = Java.Nio.ByteBuffer.Allocate (maxsize);
                    if (req.Queue (buff, maxsize)) 
                    {
                        if (this.m_USBConnection.RequestWait () == req)
                        {
                            byte[] bb = new byte[maxsize];
                            buff.Get (bb, 0, buff.Remaining ());
                            if (bb.Length > 0) 
                            {
                                (this.m_Activity as MainActivity).PopulateListView (bb, 3);
                            }
                        }
                    }
                }
            }
        }
    }
}
}

GPS をタブレットに接続してアプリケーションを実行すると、最初に 00 コンテンツのパケットを受信し、コマンド ボタンをタップしてアプリケーションが開始セッション パケットを送信すると、BulkTransfer 関数が 12 を返しますブロードキャストクラスのバイトです。アプリケーションの開始時に受信したゼロ バイトは、ブロードキャスト コールが正しい方法で機能していることを示していますが、GPS からの確認応答がない理由がわかりません。GPS は機能していてもコマンド バイトを受信して​​いないようです0 より大きい数値を返します。コントロール エンドポイントを介して GPS にコマンドを送信する必要がありますか?これに関する Garmin のドキュメントはありません。このコードまたは手順の問題点を教えてください。ヘルプやガイドラインは大歓迎です

2016/9/4 のコメントで説明したように、これは Android アプリのスナップショットです。 ここに画像の説明を入力

そして、これは私がこのステップを達成したコードです:

        private void ButtonClicked(object sender, EventArgs e)
    {
        if (this.m_BulkOut == null || (this.m_BulkIn == null && this.m_InterruptIn == null)) 
        {
            Toast.MakeText (this, "Could not find right endpoints", ToastLength.Long).Show ();
            return;
        }

        if (this.m_USBConnection == null) {
            try {
                this.m_USBConnection = this.m_USBManager.OpenDevice (this.m_GPS);
            } catch {
            }
        }

        if (this.m_USBConnection == null) 
        {
            Toast.MakeText (this, "Can not open USB device", ToastLength.Long).Show ();
            return;
        }

        if (!this.m_USBConnection.ClaimInterface (this.m_USBInterface, true)) 
        {
            Toast.MakeText (this, "Can not claim interface", ToastLength.Long).Show ();
            return;
        } 
        else
            this.PopulateListView (null, (byte)USBOpCode.ClaimInterface);

        int sentBytesCount = 0;

        #region Send Start_Session Packet
        byte[] obuffer = new byte[12];
        byte[] ibuffer = new byte[12];
        for(int i = 0; i < 12; i++)
        {
            obuffer[i] = 0;
            ibuffer[i] = 0;
        }
        obuffer[4]=5;

        UsbRequest req=new UsbRequest();
        if(req.Initialize(this.m_USBConnection,this.m_InterruptIn))
        {
            this.PopulateListView(null,(byte)USBOpCode.InitializeRequest);
            ByteBuffer buff=ByteBuffer.Wrap(ibuffer,0,12);
                //.Allocate(12);
            if(req.Queue(buff, 12))
            {
                this.PopulateListView(null,(byte)USBOpCode.QueueRequest);

                sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, obuffer.Length, 100);
                if(sentBytesCount>0)
                    this.PopulateListView(obuffer,(byte)USBOpCode.BultTransferOut);
                sentBytesCount=0;
                sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, obuffer.Length, 100);
                if(sentBytesCount>0)
                    this.PopulateListView(obuffer,(byte)USBOpCode.BultTransferOut);
                sentBytesCount=0;
                sentBytesCount += this.m_USBConnection.BulkTransfer (this.m_BulkOut, obuffer, obuffer.Length, 100);
                if(sentBytesCount>0)
                    this.PopulateListView(obuffer,(byte)USBOpCode.BultTransferOut);

                if(this.m_USBConnection.RequestWait() == req)
                    this.PopulateListView(null,(byte)USBOpCode.RequestWait);
                if(true)//buff.Position() > 0)
                {

                    //buff.Get(ibuffer,0,12);
                    this.PopulateListView(ibuffer,(byte)USBOpCode.InterruptIn_Received);
                }
            }
            req.Close();
        }
        this.m_USBConnection.ReleaseInterface(this.m_USBInterface);
        this.PopulateListView(null,(byte)USBOpCode.ReleaseInterface);
        return;

}

ここに画像の説明を入力

ここに画像の説明を入力

4

1 に答える 1

1

Garmin デバイスは、Linux では少しトリッキーですが、Android では確かにそうです。これは、(アメリカの) ビジネス戦略のもう 1 つのケースです (クローズド ソース、醜いドキュメントなど)。

これを参照してください: http://wiki.openstreetmap.org/wiki/USB_Garmin_on_GNU/Linux

Linuxでは、「ガーミン」と呼ばれるカーネルドライバーが必要ですlsmod | grep garmin http://northwestspatial.com/wp/?p=162

これは同様の問題のアプリです。作者に連絡してください https://play.google.com/store/apps/details?id=com.carlopescio.sportablet

于 2016-08-29T21:09:28.240 に答える