I have a USB device that I'm attempting to communicate to with my Android 4.1 device using the MonoDroid API, and I've run into some issues setting up a proper connection. First, the steps taken to arrive at what I "think" may be an issue:
Filter my device by vendor and product ID with an intent filter in
my AndroidManifest file. This works well, as when I plug in my
device my app requests to launch by default, so permissions should
correct.
Grab my USB device from an Activity that my intent filter sends the program after discovering said device: UsbDevice device = (UsbDevice)this.Intent.GetParcelableExtra(UsbManager.ExtraDevice);
After checking that there is only one interface present, I grab the associated interface by issuing: UsbInterface intf = device.GetInterface(0);
Check the number of endpoints and grab them. There's 2, as this is an input and output device: UsbEndpoint endpoint_IN = intf.GetEndpoint(0);
UsbEndpoint endpoint_OUT = intf.GetEndpoint(1);
Grab a connection to the device using the UsbManager: UsbDeviceConnection connection = device_manager.OpenDevice(device);
However, and I noticed that the endpoint at index 0 of the interface (endpoint_IN above) has UsbAddressing enumeration type "DirMask", where endpoint_OUT has type "Out"; I'd expect endpoint_IN to be "In", which is not the case. What is "DirMask?" The inline documentation states "Documentation for this section has not yet been entered", and the online docs reflect the same: http://api.xamarin.com/?link=T%3aAndroid.Hardware.Usb.UsbAddressing
Could this be my issue? I'm just not really sure. I tried to implement the rest of the communication procedure, but haven't been able to yield any results. For example, the following code should input a command to receive one reading:
Byte[] sys_command = Encoding.ASCII.GetBytes("!001:SYS?\r");
Java.Nio.ByteBuffer sys_command_buffer = Java.Nio.ByteBuffer.Wrap(sys_command);
Java.Nio.ByteBuffer output_buffer = Java.Nio.ByteBuffer.Allocate(4);
UsbRequest request_out = new UsbRequest();
request_out.Initialize(connection, endpoint_OUT);
connection.ClaimInterface(intf, forceClaim);
request_out.Queue(output_buffer, 4);
connection.BulkTransfer(endpoint_IN, sys_command, sys_command.Length, TIMEOUT);
if (connection.RequestWait() == request_out)
readings.Text = output_buffer.GetFloat(0).ToString();
Any insight?
I had the interface endpoints backwards, all things considered. That is, I was attempting to read/write to the wrong interface.
In case anyone else stumbles on this, the DirMask type means look at the Direction attribute of the endpoint instead of the Type attribute. If the Type is DirMask, the Direction could be UsbAddressing.In or Out.
And the Direction of In is the out endpoint, and vice versa (which I'm guessing is why you had them backwards).
Related
It's let me confused for a long time.
I have two bt audio devices connected on android phone, and I want to switch specific bt audio device using programmatically.
I search keyword about a2dp, media router and audioManager than seems can't do this thing...
I only way found the function is:
BT devices=> A and B connected on an Android phone.
And right now the media play to A and I want to switch to B
Step1: A, B unpair on Android phone.
Step2: A pair on Android phone.
Step3: B pair on Android phone.
The media play output is B,
seems lastest pair Bluetooth device is media play output.
Can anyone give me some advice or direction?
Thanks, guys
Documentation says it can only be connected to a single A2DP device at a time, so you should be able to switch between devices by connecting to device you want to use.
You can connect to device by using BluetoothSockets.
EDIT:
I found other solutions, one which I find reliable. Both of them rely on method reflections.
First one is from this post.
1. Get BluetoothA2dp proxy from serviceListener
bluetoothManager.adapter.getProfileProxy(this, serviceListener, BluetoothProfile.A2DP)
private val serviceListener = object : BluetoothProfile.ServiceListener {
override fun onServiceDisconnected(profile: Int) {
if (profile == BluetoothProfile.A2DP) {
bluetoothA2dp = null
}
}
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) {
if (profile == BluetoothProfile.A2DP) {
handler.post(btSearchRunnable)
bluetoothA2dp = proxy as BluetoothA2dp
}
}
}
Get "connect" method from reflection.
private val connect = BluetoothA2dp::class.java.getDeclaredMethod("connect",BluetoothDevice::class.java)
Invoke method with proxy as first argument and device as second.
connect.invoke(bluetoothA2dp,device)
Other method used reflection to get createRfcommSocket to circumvent issue with one parameter which you can't normally access. I can post link to it, but problem I had with it is that always lagged main thread, always threw me an error and connected unpredictably.
I'm trying to create an OSGi bundle that'd be installed on a eurotech gateway (reliagate 10 05).
This bundle would essentially connect the gateway to a BLE device.
To do so, I use a framework provided by eurotech called Everyware™ Software Framework (ESF) that adds up an extra layer on top of the kura v1.2.0 framework.
The catch is, the BLE device only accepts random static address type.
I managed to connect the gateway manually to the BLE device using the following commands in console:
hcitool -i hci0 lecc --random <BD_ADDR>
then
gatttool -i hci0 -b <BD_ADDR> --interactive
This works fine. The hard part is when I try to do the same thing in code using the ESF/kura framework.
Here's a snippet from a sample I use that I found on this page
public boolean connect(String adapterName) {
this.bluetoothGatt = this.device.getBluetoothGatt();
boolean connected = false;
try {
connected = this.bluetoothGatt.connect(adapterName);
} catch (KuraException e) {
logger.error(e.toString());
}
if (connected) {
this.bluetoothGatt.setBluetoothLeNotificationListener(this);
this.isConnected = true;
return true;
} else {
// If connect command is not executed, close gatttool
this.bluetoothGatt.disconnect();
this.isConnected = false;
return false;
}
}
Here is a list of some objects that the sample uses to scan and establish a connection:
org.eclipse.kura.bluetooth.BluetoothAdapter;
org.eclipse.kura.bluetooth.BluetoothDevice;
org.eclipse.kura.bluetooth.BluetoothGattSecurityLevel;
org.eclipse.kura.bluetooth.BluetoothGattService;
org.eclipse.kura.bluetooth.BluetoothLeScanListener;
org.eclipse.kura.bluetooth.BluetoothService;
org.eclipse.kura.bluetooth.BluetoothDevice;
org.eclipse.kura.bluetooth.BluetoothGatt;
org.eclipse.kura.bluetooth.BluetoothGattCharacteristic;
org.eclipse.kura.bluetooth.BluetoothLeNotificationListener;
So I searched through the api doc but didn't find anything.
Though, one interesting SO post mentions a command code to send to the device.
I found a method in kura framework that might help.
Here's the signature:
void ExecuteCmd(java.lang.String ogf, java.lang.String ocf, java.lang.String parameter)
but I couldn't figure out the OpCode Group Field (ogf) associated to the OpCode Command Field(ocf) in any documentation (I skimmed the ~2300 pages of the Bluetooth 4.0 core spec). If anyone knows where to search... :)
In the end, the question is: is there a way to set the address type to random (as with the hcitool command) with the kura framework ?
Or am I totally misleaded ? :/
Anyway, I'm really new to the kura and ble ecosystems so, sorry if it looks like an obvious thing to do but I feel like I'm running out of inspiration and could totally use a hand!
PS: Congrats if you made it to the end!
Haha lol. Kura seems to just start a gatttool process, send commands in text, and parse the output as its interface...
Here is where it is stated, using the address as parameter: https://github.com/eclipse/kura/blob/0339ac787f90debdfc270c1dee0c16de16ea6f7e/kura/org.eclipse.kura.linux.bluetooth/src/main/java/org/eclipse/kura/linux/bluetooth/util/BluetoothUtil.java#L319. Unfortunately the Kura developers seem to have missed that there is something called Random Address in the BLE standard and I don't see how that could be worked around using the current API.
Okay so for those who find themselves in my position in the future, I just received an answer from the Eurotech support team.
Dear Mr. Carneiro,
[...]
Regarding the random BD_ADDR, this is a configuration of the BLE device.
So, your BLE device is advertising an address of type random, not public, and you should specify the address type on the connection string, as you already did.
Unfortunately, current Kura Bluetooth API doesn't provide a way to specify the type of address into the connection string. We are developing a new set of APIs for BLE that will be available on preview on the next Kura/ESF release, but the Reliagate 10-05 will not support these yet.
I'm trying to write a program that will send MIDI messages to external synthesizers. It does work when using the default synth in Java (1.8 on a mac) and makes a nice ping sound every second when connected to the default synthesizer in Java. As expected this is a boring sound, but it does verify that I'm able to make short midi sequences that are interpreted as sounds by a synth.
I then try to route that signal to an IAC device, so that it can be picked up by other programs (including synthesizers) that can make interesting sounds. However, it seems that none of the events I produce are actually picked up by the IAC device. It doesn't seem like they are sent outside of the JVM, or if they are they are somehow dropped.
My question is simply: Has anyone done this, and can they tell me what they did?
In essence my code does this:
final MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
for (int i = 0; i < infos.length; i++) {
final MidiDevice device = MidiSystem.getMidiDevice(infos[i]);
final String deviceInfo = device.getDeviceInfo().getName();
// My IAC device is called "Bus 1"
if ("Bus 1".equals(deviceInfo)) {
Receiver rcvr = device.getReceiver()
ShortMessage myMsg = new ShortMessage();
// Start playing the note Middle C (60),
// moderately loud (velocity = 93).
myMsg.setMessage(ShortMessage.NOTE_ON, 0, 60, 93);
rcvr.send(myMsg, timeStamp);
}
}
I the test this by connecting the IAC device to an MS-20 soft synth, no sounds are produced. I then connect a "MockMidi" keyboard up to the "Bus 1" IAC device, and I can then play the MS-20 bus. I then try to hook the MockMidi keyboard up to the Bus 1 as an input device, but I don't see any events.
Looks straightforward, but as far as I can tell, no midi messages are ever received at the IAC device when sent from Java, yet the mechanism seems to be working fine both inside java, and outside Java.
I'm stuck, all help will be highly appreciated (and source code will be shared on github when it works (sending to IAC would count as working)). :-)
The documentation for getReceiver() says:
Obtaining a Receiver with this method does not open the device. To be able to use the device, it has to be opened explicitly by calling open().
Is there a way to programatically identify the currently USB working mode?
I mean, some function that would return either if the device is at Host, Device or Accessory mode.
Not the best answer, but once you have a UsbManager you might be able to figure it out. Usually this UsbManager is created using a Context I believe, but it looks like you are switching modes so you hopefully can get a UsbManager instance, m in this case:
UsbManager m = createManagerSomehow
For accessory mode, this only has one callback. If this returns one then you know it is Accessory.
m.getAccessoryList()
So I'm thinking somehting like this might work:
if(m.getAccessoryList().size() > 0)
accessoryMode = true;
And for Host, if you have a UsbDevice device, or String deviceName you could use the same UsbManager m function to see if it contains that device.
if(m.getDeviceList().containsValue(device))
hostMode = true;
or
if(m.getDeviceList().containsKey(deviceName))
hostMode = true;
and I don't know about what Device is, but if none of the above are true then you know it is just a Device. You don't really need this boolean variable below, because you have the other two. It's just here to help with my explanation via state logic.
if(!hostMode && !accessoryMode)
deviceMode = true
Hope this helps. Check out UsbManager for more documentation and just search the page for Host and Accessory.
http://developer.android.com/reference/android/hardware/usb/UsbManager.html
NOTE: I am a little confused when you say you switch the modes in your comment. It conflicts with the Accessory call I make above and might not work, but what I would do then (if you don't need deviceMode) then just check if the usb device is a HOST mode and if not you know it is in application mode...
Is it possible to detect if there are any Chromecast devices on the current WiFi network. I've seen that there's a Cast SDK but I couldn't find anything about searching for devices. Having never worked with this SDK before, it is possible that I may have overlooked it.
Chromecast devices are discoverable using avahi with the type _googlecast._tcp
$ avahi-browse -r _googlecast._tcp
+ eth0 IPv4 Living Room _googlecast._tcp local
= eth0 IPv4 Living Room _googlecast._tcp local
hostname = [Living\032Room.local]
address = [192.168.1.100]
port = [8009]
txt = ["st=0" "fn=Living Room" "ca=5" "ic=/setup/icon.png" "md=Chromecast" "ve=02" "id=c832a30b81ab84a706c82745438fcd64"]
You can perform discovery without using the cast button; you need to use media router from v7 support library (which needs the v7 appcompat support as well) and then get an instance of the media router from your code, define a selector (which is basically a filter to possibly narrow down the devices that you are interested in) and then add a callback to start discovery. As devices are discovered (asynchronously), your callbacks will be called. Take a look at this sample project, specially this class which does exactly what you want.
You can use SSDP (Simple Service Discovery Protocol) in your local network with an ST (Service Type) of:
urn:dial-multiscreen-org:service:dial:1
Your SSDP Query should look like this:
M-SEARCH * HTTP/1.1\r\n
Host: 239.255.255.250:1900\r\n
MAN: "ssdp:discover"\r\n
MX: 3\r\n
ST: urn:dial-multiscreen-org:service:dial:1\r\n\r\n
You will get a list of all devices (IP and Port) that support those service type (like Chromecast does). Then you could pull a xml file from this IP:8008 to check if it really is a Chromecast device:
http://IP:8008/ssdp/device-desc.xml
The Response will look like this:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<URLBase>http://192.168.0.0:8008</URLBase>
<device>
<deviceType>urn:dial-multiscreen-org:device:dial:1</deviceType>
<friendlyName>Chromecast</friendlyName>
<manufacturer>Google Inc.</manufacturer>
<modelName>Eureka Dongle</modelName>
<UDN>uuid:3c7f2a1e-dbd2-f94d-b456-816555a9d1f9</UDN>
<iconList>
<icon>
<mimetype>image/png</mimetype>
<width>98</width>
<height>55</height>
<depth>32</depth>
<url>/setup/icon.png</url>
</icon>
</iconList>
<serviceList>
<service>
<serviceType>urn:dial-multiscreen-org:service:dial:1</serviceType>
<serviceId>urn:dial-multiscreen-org:serviceId:dial</serviceId>
<controlURL>/ssdp/notfound</controlURL>
<eventSubURL>/ssdp/notfound</eventSubURL>
<SCPDURL>/ssdp/notfound</SCPDURL>
</service>
</serviceList>
</device>
</root>
More info about it here:
http://wolfpaulus.com/jounal/mac/chromecasting/
If you want to stream something to your Chromecast, you can get information about the ChromeCast App by calling:
http://IP:8008/apps/ChromeCast
Following the recommended way of Google you don't need to scan for Chromecast devices to provide content from an Android app. You can add a cast button to your Activity by extending it with ActionBarActivity and then adding in onCreateOptionsMenu() an ActionBar option item with
app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
as action.
Here you can find a complete sample on how to send to a Chromecast stick. If you want to scan manually for any reason, you can use the Android MediaRouters API, quote from the docs:
Cast device discovery may be performed using the Android MediaRouter
APIs in the Android Support Library, with compatibility back to
Android 2.1.
A very simple solution is to use com.youview.tinydnssd.DiscoverResolver. Here my implementation:
DiscoverResolver resolver = new DiscoverResolver(ctx, "_googlecast._tcp.", new DiscoverResolverHandler());
In DiscoverResolverHandler() you can handle the result.