Android USB host read from device - android

I'm trying to get some data out of a USB device connected to my Android phone that is on host mode. I'm able to send data to it, but reading fails.
I've looked at several examples and tried all I could but I don't have any experience in USB communication, although by now I know a little, and I've been stuck on this longer that I care to admit.
I'm not very familiar with the endpoint configuration, but I know is that my device uses a CDC type communication method and both the output (from phone to device) and input are registered.
Here's the whole class that manages the USB connection with the only device that is connected to the phone, it's not finished by any means, but I'd like to get that reading part to work before I go any further.
public class UsbCommunicationManager
{
static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
UsbManager usbManager;
UsbDevice usbDevice;
UsbInterface intf = null;
UsbEndpoint input, output;
UsbDeviceConnection connection;
PendingIntent permissionIntent;
Context context;
byte[] readBytes = new byte[64];
public UsbCommunicationManager(Context context)
{
this.context = context;
usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
// ask permission from user to use the usb device
permissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
context.registerReceiver(usbReceiver, filter);
}
public void connect()
{
// check if there's a connected usb device
if(usbManager.getDeviceList().isEmpty())
{
Log.d("trebla", "No connected devices");
return;
}
// get the first (only) connected device
usbDevice = usbManager.getDeviceList().values().iterator().next();
// user must approve of connection
usbManager.requestPermission(usbDevice, permissionIntent);
}
public void stop()
{
context.unregisterReceiver(usbReceiver);
}
public String send(String data)
{
if(usbDevice == null)
{
return "no usb device selected";
}
int sentBytes = 0;
if(!data.equals(""))
{
synchronized(this)
{
// send data to usb device
byte[] bytes = data.getBytes();
sentBytes = connection.bulkTransfer(output, bytes, bytes.length, 1000);
}
}
return Integer.toString(sentBytes);
}
public String read()
{
// reinitialize read value byte array
Arrays.fill(readBytes, (byte) 0);
// wait for some data from the mcu
int recvBytes = connection.bulkTransfer(input, readBytes, readBytes.length, 3000);
if(recvBytes > 0)
{
Log.d("trebla", "Got some data: " + new String(readBytes));
}
else
{
Log.d("trebla", "Did not get any data: " + recvBytes);
}
return Integer.toString(recvBytes);
}
public String listUsbDevices()
{
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
if(deviceList.size() == 0)
{
return "no usb devices found";
}
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
String returnValue = "";
UsbInterface usbInterface;
while(deviceIterator.hasNext())
{
UsbDevice device = deviceIterator.next();
returnValue += "Name: " + device.getDeviceName();
returnValue += "\nID: " + device.getDeviceId();
returnValue += "\nProtocol: " + device.getDeviceProtocol();
returnValue += "\nClass: " + device.getDeviceClass();
returnValue += "\nSubclass: " + device.getDeviceSubclass();
returnValue += "\nProduct ID: " + device.getProductId();
returnValue += "\nVendor ID: " + device.getVendorId();
returnValue += "\nInterface count: " + device.getInterfaceCount();
for(int i = 0; i < device.getInterfaceCount(); i++)
{
usbInterface = device.getInterface(i);
returnValue += "\n Interface " + i;
returnValue += "\n\tInterface ID: " + usbInterface.getId();
returnValue += "\n\tClass: " + usbInterface.getInterfaceClass();
returnValue += "\n\tProtocol: " + usbInterface.getInterfaceProtocol();
returnValue += "\n\tSubclass: " + usbInterface.getInterfaceSubclass();
returnValue += "\n\tEndpoint count: " + usbInterface.getEndpointCount();
for(int j = 0; j < usbInterface.getEndpointCount(); j++)
{
returnValue += "\n\t Endpoint " + j;
returnValue += "\n\t\tAddress: " + usbInterface.getEndpoint(j).getAddress();
returnValue += "\n\t\tAttributes: " + usbInterface.getEndpoint(j).getAttributes();
returnValue += "\n\t\tDirection: " + usbInterface.getEndpoint(j).getDirection();
returnValue += "\n\t\tNumber: " + usbInterface.getEndpoint(j).getEndpointNumber();
returnValue += "\n\t\tInterval: " + usbInterface.getEndpoint(j).getInterval();
returnValue += "\n\t\tType: " + usbInterface.getEndpoint(j).getType();
returnValue += "\n\t\tMax packet size: " + usbInterface.getEndpoint(j).getMaxPacketSize();
}
}
}
return returnValue;
}
private void setupConnection()
{
// find the right interface
for(int i = 0; i < usbDevice.getInterfaceCount(); i++)
{
// communications device class (CDC) type device
if(usbDevice.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA)
{
intf = usbDevice.getInterface(i);
// find the endpoints
for(int j = 0; j < intf.getEndpointCount(); j++)
{
if(intf.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_OUT && intf.getEndpoint(j).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
{
// from android to device
output = intf.getEndpoint(j);
}
if(intf.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_IN && intf.getEndpoint(j).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
{
// from device to android
input = intf.getEndpoint(j);
}
}
}
}
}
private final BroadcastReceiver usbReceiver = new BroadcastReceiver()
{
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if(ACTION_USB_PERMISSION.equals(action))
{
// broadcast is like an interrupt and works asynchronously with the class, it must be synced just in case
synchronized(this)
{
if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false))
{
setupConnection();
connection = usbManager.openDevice(usbDevice);
connection.claimInterface(intf, true);
// set flow control to 8N1 at 9600 baud
int baudRate = 9600;
byte stopBitsByte = 1;
byte parityBitesByte = 0;
byte dataBits = 8;
byte[] msg = {
(byte) (baudRate & 0xff),
(byte) ((baudRate >> 8) & 0xff),
(byte) ((baudRate >> 16) & 0xff),
(byte) ((baudRate >> 24) & 0xff),
stopBitsByte,
parityBitesByte,
(byte) dataBits
};
connection.controlTransfer(UsbConstants.USB_TYPE_CLASS | 0x01, 0x20, 0, 0, msg, msg.length, 5000);
}
else
{
Log.d("trebla", "Permission denied for USB device");
}
}
}
else if(UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action))
{
Log.d("trebla", "USB device detached");
}
}
};
}
I keep getting -1 from the read() method which indicates some kind of error, it always times out. Maybe the problem comes from the connection configuration, I've tried several (read: trial and error) and none worked, surprisingly I don't need any configuration to send data to the device.
Edit
It must also be noted that the cable I'm using is micro-USB to micro-USB and it only works in one way, that is my device is powered by my phone only when the plug A connected to phone and plug B connected to device, not the other way around... it seems very strange. The fact that I'm able to send data and not receive when plugged the right way remains.
EDIT 2
I found that somebody else had the same problem but it seems he wasn't able to solve it.
EDIT 3
I finally found the solution on this page:
Another major oversight is that there is no mechanism for the host to notify the device that there is a data sink on the host side ready to accept data. This means that the device may try to send data while the host isn't listening, causing lengthy blocking timeouts in the transmission routines. It is thus highly recommended that the virtual serial line DTR (Data Terminal Ready) signal be used where possible to determine if a host application is ready for data.
So the DTR signal was mandatory and all I had to do was to add this to the interface configuration:
connection.controlTransfer(0x21, 0x22, 0x1, 0, null, 0, 0);
EDIT 4
If anybody is interested I finished the project and it's open source and published on my GitHub account. It's not stable all the time though (see the notes) and I don't plan working on it anymore, but it works. Feel free to use it for your own projects.

You can use UsbSerial Lib of from https://github.com/mik3y/usb-serial-for-android
My example code:
UsbManager usbManager = null;
UsbDeviceConnection connection = null;
UsbSerialDriver driver = null;
UsbSerialPort port = null;
usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
// Open a connection to the first available driver.
for (UsbSerialDriver usd : availableDrivers) {
UsbDevice udv = usd.getDevice();
if (udv.getVendorId()==0x067B || udv.getProductId()==2303){
driver = usd;
break;
}
}
connection = usbManager.openDevice(driver.getDevice());
port = driver.getPorts().get(0);
driver.getDevice().
}
if (connection == null) return;
try{
port.open(connection);
port.setParameters(4800, UsbSerialPort.DATABITS_8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
try{
byte buffer[] = new byte[250];
//Log.d("GPS_REQ", "->");
int numBytesRead = port.read(buffer, 500); //5000;
}catch (Exception e) {
Log.d("GPS_ERR", e.getLocalizedMessage());
}

Related

Autoredict from HTTPS with ESP8266

I need to configure ESP for auto redirection.
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <DNSServer.h>
#include <ESP8266mDNS.h>
#ifndef APSSID
#define APSSID "WIFI"
#define APPSK "12345678"
#endif
const char *softAP_ssid = APSSID;
const char *softAP_password = APPSK;
char* ssid = "YOUR WIFI";
char* password = "AND PASSWORD";
const char *myHostname = "esp8266_wifi";
// DNS server
const byte DNS_PORT = 53;
DNSServer dnsServer;
// Web server
ESP8266WebServer server(80);
/* Soft AP network parameters */
//IPAddress apIP(192, 168, 4, 1);
IPAddress apIP(8, 8, 8, 8);
IPAddress netMsk(255, 255, 255, 0);
/** Should I connect to WLAN asap? */
boolean connect;
/** Last time I tried to connect to WLAN */
unsigned long lastConnectTry = 0;
/** Current WLAN status */
unsigned int status = WL_IDLE_STATUS;
/** Is this an IP? */
boolean isIp(String str) {
for (size_t i = 0; i < str.length(); i++) {
int c = str.charAt(i);
if (c != '.' && (c < '0' || c > '9')) {
return false;
}
}
return true;
}
/** IP to String? */
String toStringIp(IPAddress ip) {
String res = "";
for (int i = 0; i < 3; i++) {
res += String((ip >> (8 * i)) & 0xFF) + ".";
}
res += String(((ip >> 8 * 3)) & 0xFF);
return res;
}
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
boolean captivePortal() {
if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname) + ".local")) {
Serial.println("Request redirected to captive portal");
server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true);
server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
server.client().stop(); // Stop is needed because we sent no content length
return true;
}
return false;
}
void handleNotFound() { // В случае ошибки
if (captivePortal()) {
return;
}
String message = F("File Not Found\n\n");
message += F("URI: ");
message += server.uri();
message += F("\nMethod: ");
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += F("\nArguments: ");
message += server.args();
message += F("\n");
for (uint8_t i = 0; i < server.args(); i++) {
message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n");
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send(404, "text/plain", message);
}
void connectWifi() {
Serial.println("Connecting as wifi client...");
WiFi.disconnect();
WiFi.begin(ssid, password);
int connRes = WiFi.waitForConnectResult();
Serial.print("connRes: ");
Serial.println(connRes);
}
void handleRoot(){
String page = "<h1>Hello ESP WORLD</h1>";
server.send(200, "text/html", page);
}
void setup() {
delay(1000);
Serial.begin(9600);
Serial.println();
Serial.println("Configuring access point...");
/* You can remove the password parameter if you want the AP to be open. */
WiFi.softAPConfig(apIP, apIP, netMsk);
WiFi.softAP(softAP_ssid, softAP_password);
delay(500); // Without delay I've seen the IP address blank
Serial.print("AP IP address: ");
Serial.println(WiFi.softAPIP());
/* Setup the DNS server redirecting all the domains to the apIP */
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(DNS_PORT, "*", apIP);
/* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
server.on("/", handleRoot);
server.on("/generate_204", handleRoot); //Android captive portal. Maybe not needed. Might be handled by notFound handler.
server.on("/fwlink", handleRoot); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
server.onNotFound(handleNotFound);
server.begin(); // Web server start
Serial.println("HTTP server started");
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID
}
void loop() {
if (connect) {
Serial.println("Connect requested");
connect = false;
connectWifi();
lastConnectTry = millis();
}
{
unsigned int s = WiFi.status();
if (s == 0 && millis() > (lastConnectTry + 60000)) {
/* If WLAN disconnected and idle try to connect */
/* Don't set retry time too low as retry interfere the softAP operation */
connect = true;
}
if (status != s) {
Serial.print("Status: ");
Serial.println(s);
status = s;
if (s == WL_CONNECTED) {
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
if (!MDNS.begin(myHostname)) {
Serial.println("ERR MDNS!");
} else {
MDNS.addService("http", "tcp", 80);
}
} else if (s == WL_NO_SSID_AVAIL) {
WiFi.disconnect();
}
}
if (s == WL_CONNECTED) {
MDNS.update();
}
}
// Do work:
//DNS
dnsServer.processNextRequest();
//HTTP
server.handleClient();
}
I found a way for http. But how I do it with HTTPS? If I go to https links I get "Connection attempt failed". What should I use? How to solve?
I tried:
ESP: Auto login/accept message by OS with redirect to page like public WIFI portals access points
https://github.com/espressif/arduino-esp32/blob/master/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino

Monitoring beacons that are advertising from my application through AltBeacon library

I'm working on a solution that advertises and scans in the iBeacon format using the AltBeacon library. The concern that i have is that the library scans all the devices which is fine but after parsing through the scanned devices it also tracks the advertising devices that are not advertising from my application. Is there anyway to solve this through using the library? If not what could be the alternate solution to this.
It is very important for me to track the advertising beacons that are only advertising from my application.
This is the code is use while advertising in iBeacon format through the AltBeacon library:
BluetoothManager bluetoothManager =
(BluetoothManager) applicationContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager != null) {
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
BluetoothLeAdvertiser mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
if (mBluetoothLeAdvertiser != null) {
beacon = new Beacon.Builder()
.setId1(userId)
.setId2("1")
.setId3("1")
.setManufacturer(0x004C)
.setTxPower(-75)
.setDataFields(Arrays.asList(new Long[]{0l}))
.build();
beaconParser = new BeaconParser()
.setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
beaconTransmitter = new BeaconTransmitter(InventaSdk.getContext(), beaconParser);
beaconTransmitter.setBeacon(beacon);
}
}
Edit:
Parsing Beacon code:
/**
* Construct a Beacon from a Bluetooth LE packet collected by Android's Bluetooth APIs,
* including the raw Bluetooth device info
*
* #param scanData The actual packet bytes
* #param rssi The measured signal strength of the packet
* #param device The Bluetooth device that was detected
* #return An instance of a <code>Beacon</code>
*/
public Beacon fromScanData(byte[] scanData, int rssi, BluetoothDevice device) {
return fromScanData(scanData, rssi, device, new Beacon());
}
protected Beacon fromScanData(byte[] bytesToProcess, int rssi, BluetoothDevice device, Beacon beacon) {
BleAdvertisement advert = new BleAdvertisement(bytesToProcess);
boolean parseFailed = false;
Pdu pduToParse = null;
int startByte = 0;
ArrayList<Identifier> identifiers = new ArrayList<Identifier>();
ArrayList<Long> dataFields = new ArrayList<Long>();
for (Pdu pdu: advert.getPdus()) {
if (pdu.getType() == Pdu.GATT_SERVICE_UUID_PDU_TYPE ||
pdu.getType() == Pdu.MANUFACTURER_DATA_PDU_TYPE) {
pduToParse = pdu;
LogHelper.d(TAG, "Processing pdu type: "+pdu.getType()+bytesToHex(bytesToProcess)+" with startIndex: "+pdu.getStartIndex()+" endIndex: "+pdu.getEndIndex());
break;
}
else {
LogHelper.d(TAG, "Ignoring pdu type %02X "+ pdu.getType());
}
}
if (pduToParse == null) {
LogHelper.d(TAG, "No PDUs to process in this packet.");
parseFailed = true;
}
else {
byte[] serviceUuidBytes = null;
byte[] typeCodeBytes = longToByteArray(getMatchingBeaconTypeCode(), mMatchingBeaconTypeCodeEndOffset - mMatchingBeaconTypeCodeStartOffset + 1);
if (getServiceUuid() != null) {
serviceUuidBytes = longToByteArray(getServiceUuid(), mServiceUuidEndOffset - mServiceUuidStartOffset + 1, false);
}
startByte = pduToParse.getStartIndex();
boolean patternFound = false;
if (getServiceUuid() == null) {
if (byteArraysMatch(bytesToProcess, startByte + mMatchingBeaconTypeCodeStartOffset, typeCodeBytes)) {
patternFound = true;
}
} else {
if (byteArraysMatch(bytesToProcess, startByte + mServiceUuidStartOffset, serviceUuidBytes) &&
byteArraysMatch(bytesToProcess, startByte + mMatchingBeaconTypeCodeStartOffset, typeCodeBytes)) {
patternFound = true;
}
}
if (patternFound == false) {
// This is not a beacon
if (getServiceUuid() == null) {
LogHelper.d(TAG, "This is not a matching Beacon advertisement. (Was expecting "+byteArrayToString(typeCodeBytes)
+ ".The bytes I see are: "+
bytesToHex(bytesToProcess));
} else {
LogHelper.d(TAG, "This is not a matching Beacon advertisement. Was expecting "+
byteArrayToString(serviceUuidBytes)+
" at offset "+startByte + mServiceUuidStartOffset+"and "+byteArrayToString(typeCodeBytes)+
" at offset "+ startByte + mMatchingBeaconTypeCodeStartOffset + "The bytes I see are: "
+ bytesToHex(bytesToProcess));
}
parseFailed = true;
beacon = null;
} else {
LogHelper.d(TAG, "This is a recognized beacon advertisement -- "+
byteArrayToString(typeCodeBytes)+"seen");
LogHelper.d(TAG, "Bytes are: "+ bytesToHex(bytesToProcess));
}
if (patternFound) {
if (bytesToProcess.length <= startByte+mLayoutSize && mAllowPduOverflow) {
// If the layout size is bigger than this PDU, and we allow overflow. Make sure
// the byte buffer is big enough by zero padding the end so we don't try to read
// outside the byte array of the advertisement
LogHelper.d(TAG, "Expanding buffer because it is too short to parse: "+bytesToProcess.length+", needed: "+(startByte+mLayoutSize));
bytesToProcess = ensureMaxSize(bytesToProcess, startByte+mLayoutSize);
}
for (int i = 0; i < mIdentifierEndOffsets.size(); i++) {
int endIndex = mIdentifierEndOffsets.get(i) + startByte;
if (endIndex > pduToParse.getEndIndex() && mIdentifierVariableLengthFlags.get(i)) {
LogHelper.d(TAG, "Need to truncate identifier by "+(endIndex-pduToParse.getEndIndex()));
// If this is a variable length identifier, we truncate it to the size that
// is available in the packet
int start = mIdentifierStartOffsets.get(i) + startByte;
int end = pduToParse.getEndIndex()+1;
if (end <= start) {
LogHelper.d(TAG, "PDU is too short for identifer. Packet is malformed");
return null;
}
Identifier identifier = Identifier.fromBytes(bytesToProcess, start, end, mIdentifierLittleEndianFlags.get(i));
identifiers.add(identifier);
}
else if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
parseFailed = true;
LogHelper.d(TAG, "Cannot parse identifier "+i+" because PDU is too short. endIndex: " + endIndex + " PDU endIndex: " + pduToParse.getEndIndex());
}
else {
Identifier identifier = Identifier.fromBytes(bytesToProcess, mIdentifierStartOffsets.get(i) + startByte, endIndex+1, mIdentifierLittleEndianFlags.get(i));
identifiers.add(identifier);
}
}
for (int i = 0; i < mDataEndOffsets.size(); i++) {
int endIndex = mDataEndOffsets.get(i) + startByte;
if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
LogHelper.d(TAG, "Cannot parse data field "+i+" because PDU is too short. endIndex: " + endIndex + " PDU endIndex: " + pduToParse.getEndIndex()+". Setting value to 0");
dataFields.add(new Long(0l));
}
else {
String dataString = byteArrayToFormattedString(bytesToProcess, mDataStartOffsets.get(i) + startByte, endIndex, mDataLittleEndianFlags.get(i));
dataFields.add(Long.decode(dataString));
}
}
if (mPowerStartOffset != null) {
int endIndex = mPowerEndOffset + startByte;
int txPower = 0;
try {
if (endIndex > pduToParse.getEndIndex() && !mAllowPduOverflow) {
parseFailed = true;
LogHelper.d(TAG, "Cannot parse power field because PDU is too short. endIndex: " + endIndex + " PDU endIndex: " + pduToParse.getEndIndex());
}
else {
String powerString = byteArrayToFormattedString(bytesToProcess, mPowerStartOffset + startByte, mPowerEndOffset + startByte, false);
txPower = Integer.parseInt(powerString)+mDBmCorrection;
// make sure it is a signed integer
if (txPower > 127) {
txPower -= 256;
}
beacon.mTxPower = txPower;
}
}
catch (NumberFormatException e1) {
// keep default value
}
catch (NullPointerException e2) {
// keep default value
}
}
}
}
if (parseFailed) {
beacon = null;
}
else {
int beaconTypeCode = 0;
String beaconTypeString = byteArrayToFormattedString(bytesToProcess, mMatchingBeaconTypeCodeStartOffset+startByte, mMatchingBeaconTypeCodeEndOffset+startByte, false);
beaconTypeCode = Integer.parseInt(beaconTypeString);
// TODO: error handling needed on the parse
int manufacturer = 0;
String manufacturerString = byteArrayToFormattedString(bytesToProcess, startByte, startByte+1, true);
manufacturer = Integer.parseInt(manufacturerString);
String macAddress = null;
String name = null;
if (device != null) {
macAddress = device.getAddress();
name = device.getName();
}
beacon.mIdentifiers = identifiers;
beacon.mDataFields = dataFields;
beacon.mRssi = rssi;
beacon.mBeaconTypeCode = beaconTypeCode;
if (mServiceUuid != null) {
beacon.mServiceUuid = (int) mServiceUuid.longValue();
}
else {
beacon.mServiceUuid = -1;
}
beacon.mBluetoothAddress = macAddress;
beacon.mBluetoothName= name;
beacon.mManufacturer = manufacturer;
beacon.mParserIdentifier = mIdentifier;
beacon.mMultiFrameBeacon = extraParsers.size() > 0 || mExtraFrame;
}
return beacon;
}
Scan callbacks:
private ScanCallback getNewLeScanCallback() {
if (leScanCallback == null) {
leScanCallback = new ScanCallback() {
#MainThread
#Override
public void onScanResult(int callbackType, ScanResult scanResult) {
LogHelper.d(TAG, "got record");
List<ParcelUuid> uuids = scanResult.getScanRecord().getServiceUuids();
if (uuids != null) {
for (ParcelUuid uuid : uuids) {
LogHelper.d(TAG, "with service uuid: "+uuid);
}
}
try {
LogHelper.d("ScanRecord", "Raw Data: " + scanResult.toString());
LogHelper.d("ScanRecord", "Device Data Name: " + scanResult.getDevice().getName() + "Rssi: " + scanResult.getRssi() + "Address: " + scanResult.getDevice().getAddress() + "Service uuid: " + scanResult.getScanRecord().getServiceUuids());
}catch (Exception e){
LogHelper.d("ScanRecord",e.getMessage());
e.printStackTrace();
}
mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
scanResult.getRssi(), scanResult.getScanRecord().getBytes());
if (mBackgroundLScanStartTime > 0) {
LogHelper.d(TAG, "got a filtered scan result in the background.");
}
}
#MainThread
#Override
public void onBatchScanResults(List<ScanResult> results) {
LogHelper.d(TAG, "got batch records");
for (ScanResult scanResult : results) {
mCycledLeScanCallback.onLeScan(scanResult.getDevice(),
scanResult.getRssi(), scanResult.getScanRecord().getBytes());
}
if (mBackgroundLScanStartTime > 0) {
LogHelper.d(TAG, "got a filtered batch scan result in the background.");
}
}
#MainThread
#Override
public void onScanFailed(int errorCode) {
Intent intent = new Intent("onScanFailed");
intent.putExtra("errorCode", errorCode);
LocalBroadcastManager.getInstance(CycledLeScannerForLollipop.this.mContext).sendBroadcast(intent);
switch (errorCode) {
case SCAN_FAILED_ALREADY_STARTED:
LogHelper.e(TAG, "Scan failed: a BLE scan with the same settings is already started by the app");
break;
case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
LogHelper.e(TAG, "Scan failed: app cannot be registered");
break;
case SCAN_FAILED_FEATURE_UNSUPPORTED:
LogHelper.e(TAG, "Scan failed: power optimized scan feature is not supported");
break;
case SCAN_FAILED_INTERNAL_ERROR:
LogHelper.e(TAG, "Scan failed: internal error");
break;
default:
LogHelper.e(TAG, "Scan failed with unknown error (errorCode=" + errorCode + ")");
break;
}
}
};
}
return leScanCallback;
}
The general approach to filter for “your” beacons is to see an an identifier prefix that is common to all your beacons. You then tell if it is your beacon by filtering on beacons that match this identifier prefix.
Two ways to do the filtering:
A) Software filtering after scan results come in.
With this approach, you wait until you parse the beacons and then use an if statement to see if the beacon identifiers match your prefix. If not, do not process it. The Android Beacon Library has this as a built-in feature by using Region objects to provide matching patterns for “your” beacons.
// replace uuid with your own
beaconManager.startRangingBeaconsInRegion(new Region("matchOnlyMyBeacons", Identifier.parse(“2F234454-CF6D-4A0F-ADF2-F4911BA9”)), null, null));
beaconManager.addRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
// only beacons matching the identifiers in the Region are included here
}
});
Since you are mot using the library as a whole but copying some of its code, you may have to build similar logic yourself like this:
// replace the uuid with yours below
if (beacon.getID1().equals(Identifier.parse(“2F234454-CF6D-4A0F-ADF2-F4911BA9”)){
// only process matching beacons here
}
This is a simple approach as very flexible. It works well in cases where your app runs only in the foreground or in the background when usually there are few BLE devices around that are not of interest.
The disadvantage is that it can burn cpu and battery if lots of beacons are around that are not of interest.
B) Use hardware scan filters
Android 6+ APIs allow you to put similar matching functions into the Bluetooth chip itself so all scan callbacks you get already match the identifier prefix. This is us less taxing on CPU and battery but has disadvantages:
Not all devices support this, though most devices built since 2018 do.
Hardware filters are a limited resource. If other apps take all of them up, you will not get scan results.
Filters are inflexible. If even a single byte of the advertisement prefix doesn’t match (commonly due to a differing manufacturer code) you will not get a scan result.
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setServiceUuid(null);
byte[] filterBytes = new byte[]{
/* 0215 are the start of iBeacon. Use beac for AltBeacon */
(byte) 0x02, (byte) 0x15,
// These bytes are your 16 byte proximityUUID (ID1)
(byte) 0x2F, (byte) 0x23, (byte) 0x44, (byte) 0x54, (byte) 0xCF, (byte) 0x6D, (byte) 0x4A, (byte) 0x0F, (byte) 0xAD, (byte) 0xF2, (byte) 0xF4, (byte) 0x91, (byte) 0x1B, (byte) 0xA9, (byte) 0xFF, (byte) 0xA6
};
byte[] maskBytes = new byte[]{
/* Make this the same length as your filter bytes, and set every value to 0xff to match all bytes */
(byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff
};
builder.setManufacturerData((int) 0x004c /* apple for iBeacon, use 0x0118 for AltBeacon */, filterBytes, maskBytes);
ScanFilter[] scanFilters = new ScanFilter[] { builder.build() };
scanner.startScan(scanFilters, scanSettings, scanCallback);

BulkTransfer & Android USB API

I have a program in which I attempt to attach my android device to a webcam via USB. I'm having trouble with a few things, namely properly transferring data. I've tried using bulkTransfer and there seems to be no recognition of it being used. I've been trying to find examples that may assist me such as here but none are helping me - their structure seems to be better than mine but whenever I switch my program crashes on load.
I'm fairly confident my bytes declaration is also incorrect and I should be somehow forwarding my data there, but I'm unsure how. Any help in terms of how to data transfer and how to structure my code would be appreciated.
Some declarations:
private byte[] bytes = {1,2};
private static int TIMEOUT = 0;
private boolean forceClaim = true;
In On Create:
UsbDevice device = (UsbDevice) getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE);
UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()) {
device = deviceIterator.next();
PendingIntent mPermissionIntent = PendingIntent.getBroadcast(this,0,new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
mUsbManager.requestPermission(device, mPermissionIntent);
UsbDeviceConnection connection = mUsbManager.openDevice(device);
Log.d("CAM Connection", " " + connection);
Log.d("CAM UsbManager", " " + mUsbManager);
Log.d("CAM Device", " " + device);
UsbInterface intf = device.getInterface(0);
Log.d("CAM_INTF Interface!!!!", " " + intf );
UsbEndpoint endpoint = intf.getEndpoint(0);
Log.d("CAM_END Endpoint", " " + endpoint );
connection.claimInterface(intf, forceClaim);
StringBuilder sb = new StringBuilder();
if(connection.bulkTransfer(endpoint,bytes,bytes.length,TIMEOUT) < 2)
Log.d("test", "");
//Log.d("BULK", ""+ connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT)); //do in another thread
}
Additional relevant code:
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(EXTRA_PERMISSION_GRANTED, false)) {
if(device != null){
//call method to set up device communication
}
}
else {
Log.d("Deny:", "permission denied for device " + device);
}
}
}
}
};
one of your problem is on finding endpoints. endpoint0 is for controlling task and you should find the appropriate IN and OUT endpoints in your code.
UsbInterface usbInterfaceTemp = null;
usbInterface = null;
endpointIN = null;
endpointOUT = null;
for (int i = 0; i < usbGotPermiDVC.getInterfaceCount(); i++) {
usbInterfaceTemp = usbGotPermiDVC.getInterface(i);
if (usbInterfaceTemp.getEndpointCount() >= 2) {
for (int j = 0; j < usbInterfaceTemp.getEndpointCount(); j++) {
UsbEndpoint usbEndpointTemp = usbInterfaceTemp.getEndpoint(j);
if (usbEndpointTemp.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (usbEndpointTemp.getDirection() == UsbConstants.USB_DIR_IN) {
endpointIN = usbEndpointTemp;
} else if (usbEndpointTemp.getDirection() == UsbConstants.USB_DIR_OUT) {
endpointOUT = usbEndpointTemp;
}
}
}
}
}
if (endpointIN != null && endpointOUT != null) {
usbInterface = usbInterfaceTemp;
}
if (usbInterface == null) {
return;
}
usbDeviceConnection = usbManager.openDevice(usbSelectedDevice);
if (!(usbDeviceConnection != null && usbDeviceConnection.claimInterface(usbInterface, true))) {
usbDeviceConnection = null;
return;
}
usbDeviceConnection.controlTransfer(0x21, 34, 0, 0, null, 0, 0);
usbDeviceConnection.controlTransfer(0x21, 32, 0, 0, new byte[]{(byte) 0x80,
0x25, 0x00, 0x00, 0x00, 0x00, 0x08}, 7, 0);
Toast.makeText(getApplicationContext(), "Device opened and Interface claimed!", Toast.LENGTH_SHORT).show();
in which usbGotPermiDVC is the device that got the permission to access via USB.

USBConnection nullPointerException

I try to send command to my USB custom device. I think I set all properly - for example I can get ID of device, so Android "sees" it. However , when I try to send it command I get null pointer at line :
connection.bulkTransfer(usbEndpointOut,send,send.length,SEND_TIMEOUT);
Endpoints are set correctly (I've checked it on log). This is my class. Please, help.
mTestButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> usbDevices = usbManager.getDeviceList();
UsbDevice device;
if (usbDevices != null) {
boolean keep = true;
for (Map.Entry<String, UsbDevice> entry : usbDevices.entrySet()) {
device = entry.getValue();
int deviceVID = device.getVendorId();
int devicePID = device.getProductId();
if (deviceVID != 0x1d6b || (devicePID != 0x0001 || devicePID != 0x0002 || devicePID != 0x0003)) {
Toast.makeText(MainActivity.this, "ID: " + device.getDeviceId()
, Toast.LENGTH_SHORT).show();
Log.e(TAG, "onCreate: " + device.getDeviceId());
UsbInterface usbInterface = null;
UsbEndpoint usbEndpointIn = null;
UsbEndpoint usbEndpointOut = null;
for (int i = 0; i < device.getInterfaceCount(); i++) {
usbInterface = device.getInterface(i);
//l("Interface[" + i + "] -> " + usbInterface);
if (usbInterface != null) {
for (int j = 0; j < usbInterface.getEndpointCount(); j++) {
UsbEndpoint usbEndpoint = usbInterface.getEndpoint(j);
//l("Endpoint[" + j + "] -> " + usbEndpoint);
if (usbEndpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (usbEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
//l("Found input!");
usbEndpointIn = usbEndpoint;
} else {
//l("Found output!");
usbEndpointOut = usbEndpoint;
}
if (usbEndpointIn != null && usbEndpointOut != null) {
break;
}
}
}
if (usbEndpointIn != null && usbEndpointOut != null) {
break;
}
} else {
//l("Interface was null");
}
}
connection = usbManager.openDevice(device);
connection.bulkTransfer(usbEndpointOut, send, send.length, SEND_TIMEOUT);
keep = false;
} else {
{
connection = null;
device = null;
}
if (!keep)
break;
}
}
}
}
});
}
private static final byte[] send = new byte[]{
(byte) 0xDA, (byte) 0xAD, // const
(byte) 0x02, (byte) 0x74, (byte) 0x00, // com
(byte) 0xBF, (byte) 0xDB // last
};
First of all, the loop which searches usbEndpointIn and usbEndpointOut might fail and then yields null. So, you should call bulkTransfer only if you have valid endpoints.
But the main thing is that you missed to claim the interface that you want to communicate with via bulkTransfer:
if (usbEndpointOut != null) {
connection = usbManager.openDevice(device);
if (connection != null) {
if (connection.claimInterface(usbInterface, true) == true) {
connection.bulkTransfer(usbEndpointOut, send, send.length, SEND_TIMEOUT);
keep = false;
}
}
}

Arduino and android USB host : openDevice method returns null

I am developing an application to interface an arduino duemilanove board with an android 4.1.1 smartphone over an USB interface using android’s USB host APIs. I have used the USB View application for windows to find the USB descriptors of the arduino duemelanove board. And by using these descriptors I could find the endpoints and interface on android platform. The descriptors are as follows:
Device Descriptor:
bcdUSB: 0x0200
bDeviceClass: 0x00
bDeviceSubClass: 0x00
bDeviceProtocol: 0x00
bMaxPacketSize0: 0x08 (8)
idVendor: 0x0403 (Future Technology Devices International Limited)
idProduct: 0x6001
bcdDevice: 0x0600
iManufacturer: 0x01
0x0409: "FTDI"
iProduct: 0x02
0x0409: "FT232R USB UART"
0x0409: "FT232R USB UART"
iSerialNumber: 0x03
0x0409: "A9GJFH5T"
bNumConfigurations: 0x01
Configuration Descriptor:
wTotalLength: 0x0020
bNumInterfaces: 0x01
bConfigurationValue: 0x01
iConfiguration: 0x00
bmAttributes: 0xA0 (Bus Powered Remote Wakeup)
MaxPower: 0x2D (90 Ma)
Interface Descriptor:
bInterfaceNumber: 0x00
bAlternateSetting: 0x00
bNumEndpoints: 0x02
bInterfaceClass: 0xFF
bInterfaceSubClass: 0xFF
bInterfaceProtocol: 0xFF
iInterface: 0x02
0x0409: "FT232R USB UART"
0x0409: "FT232R USB UART"
Endpoint Descriptor:
bEndpointAddress: 0x81
Transfer Type: Bulk
wMaxPacketSize: 0x0040 (64)
bInterval: 0x00
Endpoint Descriptor:
bEndpointAddress: 0x02
Transfer Type: Bulk
wMaxPacketSize: 0x0040 (64)
bInterval: 0x00
The application detects the device and displays all the descriptors in the text views assigned. But the problem is- when I try opening the device using the UsbManager.openDevice(UsbDevice) method of the UsbManager Class of the android’s USB host APIs , I find that the method returns a null value in the UsbDeviceConnection variable
This is the thread where all the methods for opening the UsbConnection are implemented
private class SetupThread extends Thread {
public void run() {
String devParam = "";
String intfepParam = "";
// display device descriptor
devParam = ardParam.devinfoRet(arduino);
devDscrptor.setText(devParam);
intf = ardParam.findIntf(arduino);
if (intf != null) {
intfepParam = ardParam.intfPara(intf);
log1.setText("Interface found");
epIn = ardParam.findepIn(intf);
epOut = ardParam.findepOut(intf);
if (epIn != null && epOut != null) {
log2.setText("Both endpoints found");
intfepParam += ardParam.epPara(epIn);
intfepParam += ardParam.epPara(epOut);
intfepDscrptor.setText(intfepParam);
// just checking if Usbdevice arduino is null
if (arduino != null) {
// this is where the problem comes
mConnection = mUsbManager.openDevice(arduino);
conxnInfo.setText("Arduino not null");
}
if (mConnection != null) {
mConnection.claimInterface(intf, false);
conxnInfo.setText("Connection opened");
}
}
else {
if ((epIn == null) && (epOut == null)) {
intfepDscrptor.setText(intfepParam);
log2.setText("Both endpoints null");
}
else if ((epIn == null) && (epOut != null)) {
intfepParam += ardParam.epPara(epOut);
intfepDscrptor.setText(intfepParam);
log2.setText("epIn null");
}
else {
intfepParam += ardParam.epPara(epIn);
intfepDscrptor.setText(intfepParam);
log2.setText("epOut null");
}
}
} else {
log1.setText("Interface is null");
}
}
}
ardParam is an object of ArduinoParams class and the code for ArduinoParams class is as follows:
package com.example.arduinobasic;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
public class ArduinoParams {
public String devinfoRet(UsbDevice device) {
String DevInfo = "";
DevInfo += "Device Name:" + device.getDeviceName();
DevInfo += "Device Id:" + device.getDeviceId();
DevInfo += "Product Id:" + device.getProductId();
DevInfo += "Vendor Id:" + device.getProductId();
DevInfo += "Device Class:" + device.getDeviceClass();
DevInfo += "Device Subclass" + device.getDeviceSubclass();
DevInfo += "Device Protocol:" + device.getDeviceProtocol() + "\n";
return DevInfo;
}
public UsbInterface findIntf(UsbDevice device) {
UsbInterface intf = null;
for (int i = 0; i < device.getInterfaceCount(); i++) {
if (device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC
&& device.getInterface(i).getInterfaceSubclass() == 255
&& device.getInterface(i).getInterfaceProtocol() == 255) {
intf = device.getInterface(i);
}
}
return intf;
}
public UsbEndpoint findepIn(UsbInterface intf) {
UsbEndpoint epin = null;
for (int i = 0; i < intf.getEndpointCount(); i++) {
if ((intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)
&& (intf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
epin = intf.getEndpoint(i);
}
}
return epin;
}
public UsbEndpoint findepOut(UsbInterface intf) {
UsbEndpoint epout = null;
for (int i = 0; i < intf.getEndpointCount(); i++) {
if ((intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_OUT)
&& (intf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
epout = intf.getEndpoint(i);
}
}
return epout;
}
public String intfPara(UsbInterface intf) {
String intfPara = "";
intfPara += "Interface id:" + intf.getId();
intfPara += "Interface Class:" + intf.getInterfaceClass();
intfPara += "Interface Subclass:" + intf.getInterfaceSubclass();
intfPara += "Interface Protocol:" + intf.getInterfaceProtocol() + "\n";
return intfPara;
}
public String epPara(UsbEndpoint ep) {
String epPara = "";
epPara += "Endpoint Address:" + ep.getAddress();
epPara += "Endpoint Attributes:" + ep.getAttributes();
epPara += "Endpoint Direction" + ep.getDirection();
epPara += "Endpoint Number:" + ep.getEndpointNumber();
epPara += "Endpoint max pckt size :" + ep.getMaxPacketSize();
epPara += "Endpoint Interval :" + ep.getInterval();
epPara += "Endpoint Type:" + ep.getType() + "\n";
return epPara;
}
}
Sorry for using a lot of if-else statements.I hope some one will help me out with this problem. Thanks in advance.

Categories

Resources