I try to read PVT(Position,Velocity,Time) record from an Etrex 10 Garmin GPS that its USB port is connected to Android tablet(Samsung Galaxy Tab S,Model SM-T705 supporting USB host function).First I used a Windows application to simulate this communication and I monitored USB sent/received packets.According to Garmin documentation(Garmin GPS Interface Specification,USB Addendum),these data packets was sent and received successfully(all numbers are in hexadecimal):
(PC to GPS,start session packet) 00 00 00 00 05 00 00 00 00 00 00 00
(GPS to PC,Acknowledge starting session) 00 00 00 00 06 00 00 00 04 00 00 00 0b ba aa e7
this is part of section 6 of this document that show how host and device(GPS) do communication : ..........................
6-Sample Communication Session
This section provides a sample communication session between a USB host and a Garmin USB GPS. Messages below are presented in chronological order. All data is presented in HEX format. Bulk Out (USB Transport Layer - Start Session) The host sends this first to the Garmin USB device, to tell it to prepare for a transfer. 00 00 00 00 05 00 00 00 00 00 00 00 Interrupt In (USB Transport Layer - Session Started) The device responds to the host, letting the host know the device is prepared for a conversation. 00 00 00 00 06 00 00 00 04 00 00 00 37 C4 E4 AB
...........................
I tried to code this communication in an Android application.In Android codes I can recognize GPS device when connected to tablet.there are 3 end-points.One Bulk_Out ,another Bulk-in and the third one Interrupt-in.When I send start session packet over Bulk-out end point,I don't receive Acknowledge packet over Interrupt(or even Bulk_in) endpoint.here is the code in Xamarin Studio using MONO library :
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
}
}
And this is the braodcast receiver class
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);
}
}
}
}
}
}
}
}
}
when I connect GPS to tablet and run application,fist I receive some packets with 00 content,then I tap command button and application sends start-session-packet,the BulkTransfer function returns 12 means it sent 12 bytes successfully but after that no any received byte in broad cast class.The received zero bytes at the start of application show that broadcast calss works in a rigth way,but I can't find why no acknowlege from GPS.It seems that GPS doesn't receive the command bytes even function returns a number greater than zero.Should I send any command over Control endpoint to GPS?there is no any documentation from Garmin about this.Can you tell me what's wrong with this code or procedure? Any help or guide line is highly appreciated
here is a snap shot of android app,as I described in comment at 2016/9/4
and this is the code I acheived this step:
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;
}