我尝试从 Etrex 10 Garmin GPS 读取其 USB 端口连接到 Android 平板电脑(三星 Galaxy Tab S,型号 SM-T705 支持 USB 主机功能)的 PVT(位置、速度、时间)记录。首先我使用了一个 Windows 应用程序为了模拟这种通信,我监控了 USB 发送/接收的数据包。根据 Garmin 文档(Garmin GPS 接口规范,USB 附录),这些数据包已成功发送和接收(所有数字均为十六进制):
(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
这是本文档第 6 节的一部分,展示了主机和设备(GPS)如何进行通信:......................
6-示例交流会话
本节提供 USB 主机和 Garmin USB GPS 之间的示例通信会话。以下消息按时间顺序显示。所有数据均以 HEX 格式显示。批量输出(USB 传输层 - 开始会话)主机首先将其发送到 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,另一个 Bulk-in,第三个 Interrupt-in。当我发送开始时通过批量输出端点的会话数据包,我没有通过中断(甚至 Bulk_in)端点接收到确认数据包。这是 Xamarin Studio 中使用 MONO 库的代码:
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 的数据包,然后我点击命令按钮,应用程序发送 start-session-packet,BulkTransfer 函数返回 12 表示它成功发送了 12 个字节,但之后没有收到任何内容广播类中的字节。应用程序开始时收到的零字节表明广播类以正确的方式工作,但我找不到 GPS 没有确认的原因。似乎 GPS 甚至没有接收到命令字节返回一个大于零的数字。我应该通过控制端点向 GPS 发送任何命令吗?Garmin 没有关于此的任何文档。您能告诉我此代码或程序有什么问题吗?任何帮助或指导线都非常感谢
这是 android 应用程序的快照,正如我在 2016/9/4 的评论中所述
这是我在这一步中实现的代码:
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;
}