I use windows BLE to connect and read from BLE barcode scanner. Everything works fine while the value I receive from the BLE device is only short string. If I scan a barcode which is more than 16 bytes, the GattCharacteristic.ValueChanged event fires multiple times, and each time I receive a chunk from the data. It would not be problem but I don't receive everything and parts are missing. I use identical method on Xamarin.IOS and the event raised once and I receive 1 long string containing the whole data. So it works in Xamarin.IOS but not in winform.net.
It seems windows uses about 16bytes IBuffer for GattCharacteristic.Value and while it raises the event, that buffer gets cleared and replaced with new data.
Is there any way to increase Windows BLE IBuffer size or tell windows to get the whole data together before raise the event?
What I should receive:
Collection: 1000
From: West Midlands
To: Distribution
RaisedBy: holloway
Raised: 29/09/2021
Item: Camargue 572 4m - hometx44cama
SKU: 26479
Identifier: 174435
SubIdentifier: 21642727-6
Qty: 4.27
What I receive:
Collection: 1000
From: West Mid
land To: Distr
edBy: holloway
ised: 29/09/20
21 Item: Camarg
ue 572 4m
Identifier
: 174435
SubIde
ntifier: 21642
4.27
The code:
CurrentConnectedDevice = await BluetoothLEDevice.FromIdAsync(deviceid);
GattDeviceServicesResult result = await CurrentConnectedDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);
var services = result.Services;
GattDeviceService ScannerService = services.Single(d => d.Uuid == ScannerServiceUUID);
var Characteristics = await ScannerService.GetCharacteristicsAsync(BluetoothCacheMode.Uncached);
ScanCharacteristic = Characteristics.Characteristics.Single(c => c.Uuid == ScannerNotifyUUID);
var status = await ScanCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
ScanCharacteristic.ValueChanged += BarcodeScanned;
private void BarcodeScanned(GattCharacteristic sender, GattValueChangedEventArgs args)
{
byte[] data;
CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data);
Console.WriteLine(Encoding.UTF8.GetString(data));
}
In the Buffer constructor (link) you can pass the size of the buffer. In your implementation, you are using args.CharacteristicValue as the buffer. If you can control the value of args.CharacteristicValue then you can increase the buffer size, or you can pass in a new Buffer to the CopyToByteArray method.
I have a USB HID device that I would like to communicate with. I am successfully doing so on Windows using the HidSharp library (link: https://github.com/treehopper-electronics/HIDSharp). My Windows application is developed using the .NET Framework 4.5, C#, and Visual Studio.
I now want to communicate with this same USB HID device from an Android tablet instead of from the Windows desktop. I am encountering some problems doing so. When I have the device plugged in to my tablet, it reports a single interface with a single "read" endpoint. Here is what is reported to me:
Interface #0
Class: Human Interaction Device (0x3)
Endpoint: #0
Address : 0x81 (10000001)
Number : 1
Direction : Inbound (0x80)
Type : Intrrupt (0x3)
Poll Interval : 1
Max Packet Size: 64
Attributes : 000000011
As you can see, it only reports a single endpoint, which is an inbound endpoint. I need to be able to output simple commands to this device, which I was able to do so successfully on Windows using HidSharp.
HidSharp abstracted everything into a single "stream" object that you could read from and write to. Using the Android APIs, there isn't a single "stream" object, but rather there seem to be 3 different ways of reading/writing: bulk transfer, control transfer, and USB Request. I've tried sending out data using all 3, but with seemingly no success.
Any suggestions on what to do? Is there a reason why I could send out data to this device on Windows, but seemingly cannot do so from Android? Is there a way to use a single endpoint as both a read and a write endpoint? Is there something that I am just obviously missing and not understanding?
I am using Xamarin as my development environment (C#, Visual Studio 2017). Since code is always helpful, here is how I am connecting to the device:
int VendorID = 0x04d8;
int ProductID = 0x2742;
UsbManager USB_Manager = null;
UsbDevice USB_Device = null;
UsbDeviceConnection DeviceConnection = null;
UsbInterface DeviceInterface = null;
UsbEndpoint OutputEndpoint = null;
UsbEndpoint InputEndpoint = null;
//Grab the Android USB manager and get a list of connected devices
var USB_Manager = MyMainActivity.ApplicationContext.GetSystemService(Android.Content.Context.UsbService) as Android.Hardware.Usb.UsbManager;
var attached_devices = USB_Manager.DeviceList;
//Find the device in the list of connected devices
foreach (var d in attached_devices.Keys)
{
if (attached_devices[d].VendorId == VendorID && attached_devices[d].ProductId == ProductID)
{
USB_Device = attached_devices[d];
break;
}
}
//Assuming we found the correct device, let's set everything up
if (USB_Device != null)
{
for (int j = 0; j < USB_Device.InterfaceCount; j++)
{
DeviceInterface = USB_Device.GetInterface(j);
for (int i = 0; i < DeviceInterface.EndpointCount; i++)
{
var temp_ep = DeviceInterface.GetEndpoint(i);
if (temp_ep.Type == Android.Hardware.Usb.UsbAddressing.XferInterrupt)
{
if (temp_ep.Direction == Android.Hardware.Usb.UsbAddressing.In)
{
InputEndpoint = temp_ep;
}
if (temp_ep.Direction == Android.Hardware.Usb.UsbAddressing.Out)
{
OutputEndpoint = temp_ep;
}
}
}
}
//Request permission to communicate with this USB device
UsbReceiver receiver = new UsbReceiver();
PendingIntent pending_intent = PendingIntent.GetBroadcast(Game.Activity, 0, new Android.Content.Intent(UsbReceiver.ACTION_USB_PERMISSION), 0);
IntentFilter intent_filter = new IntentFilter(UsbReceiver.ACTION_USB_PERMISSION);
Game.Activity.RegisterReceiver(receiver, intent_filter);
USB_Manager.RequestPermission(USB_Device, pending_intent);
bool has_permission = USB_Manager.HasPermission(USB_Device);
var device_connection = USB_Manager.OpenDevice(USB_Device);
device_connection.ClaimInterface(DeviceInterface, true);
DeviceConnection = device_connection;
}
Next, here is how I attempt to read from the device:
//3 methods of attempting to read from the device
//Method 1:
byte[] inpt = new byte[64];
var request = new UsbRequest();
request.Initialize(DeviceConnection, InputEndpoint);
var byte_buffer = ByteBuffer.Allocate(64);
request.Queue(byte_buffer, 64);
DeviceConnection.RequestWait();
byte_buffer.Rewind();
for(int i = 0; i < 64; i++)
{
inpt[i] = (byte) byte_buffer.Get();
}
//Method 2:
byte[] inpt = new byte[64];
DeviceConnection.BulkTransfer(InputEndpoint, inpt, inpt.Length, 1000);
//Method 3:
byte[] inpt = new byte[64];
DeviceConnection.ControlTransfer(UsbAddressing.In, 0, 0, 0, inpt, 64, 1000);
And finally, here is how I attempt to write data to this device:
//Method 1:
byte[] output_msg; //This variable is assigned elsewhere in the code
DeviceConnection.BulkTransfer(OutputEndpoint, output_msg, output_msg.Length, 30);
//Method 2:
byte[] output_msg; //This variable is assigned elsewhere in the code
DeviceConnection.ControlTransfer(UsbAddressing.Out, 0, 0, 0, output_msg, output_msg.Length, 1000);
//Method 3:
byte[] output_msg; //This variable is assigned elsewhere in the code
var write_request = new UsbRequest();
write_request.Initialize(DeviceConnection, OutputEndpoint);
var byte_buffer_write = ByteBuffer.Wrap(output_msg);
request.Queue(byte_buffer_write, output_msg.Length);
DeviceConnection.RequestWait();
"OutputEndpoint" is typically null because there is no output endpoint, so I often replace "OutputEndpoint" with "InputEndpoint", but with no success.
Any help would be greatly appreciated! Thanks!!!
You are dealing with HID devices which means you should do Interrupt Transfers.
In Android, you should use UsbRequest to perform Interrupt Transfers (as it does Asynchronous NonBlocking IO).
The endpoints are unidirectional and can be used for both inbounds and outbound (but not at the same time)
If the endpoint is inbound then submit the Urb using UsbRequest and queue as you tried before but using empty buffer with expected bufferLength.
The RequestWait will return UsbRequest Object back upon completion.
If the usbRequest.getEndPoint().getDirection() is inbound then your buffer variable will be updated with read buffer from the device.
If the usbRequest.getEndpoint().getDirection() is outbound then you should pass your buffer to write data to the device
I have an specific issue about the Bluetooth BLE API on Windows 10.
At the moment I'm programming a tool on C# (Visual Studio) which connects itself to a given BLE - Device. Currently the connection works perfect and I can read out the ServiceUUIDs and the CharacterUUIDs.
The main Problem is after I try to read the value of the Character its always returns me 00. I heard that implementing a notification will change that and I followed these instructions but they didn't help me.
(Here are the Specifications from bluetooth.com:)
My Code:
//connect to BluetoothDevice
var device = await BluetoothLEDevice.FromIdAsync(address);
//get UUID of Services
var services = await device.GetGattServicesAsync();
if (services != null)
{
foreach (var servicesID in services.Services)
{
//if there is a service thats same like the Battery Service
if (servicesID.Uuid.ToString() == BluetoothBLE.Constants.BATTERY_SERVICE)
{
//updateServiceList is like a console logging in my tool
updateServiceList($"Service: {servicesID.Uuid}");
var characteristics = await servicesID.GetCharacteristicsAsync();
foreach (var character in characteristics.Characteristics)
{
if (Constants.BATTERY_LEVEL == character.Uuid.ToString())
{
updateServiceList("C - UUID: "+ character.Uuid.ToString());
GattReadResult result = await character.ReadValueAsync();
if (result.Status == GattCommunicationStatus.Success)
{
var reader = DataReader.FromBuffer(result.Value);
byte[] input = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(input);
System.Diagnostics.Debug.WriteLine(BitConverter.ToString(input));
}
}
}
}
After Running my Code, the system logs 00. The characterUUID for battery level (0x2A19, from https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.battery_level.xml) was read successfully but the value is strange..
So i'm trying to create a C# code to communicate to a serial device that is conected to my PC. But that device requires a Start Byte to communicate with my code. I try sending it after SerialDevice class is created and i get it's OutputStream but the device just don't respond at all.
The DeviceInformation of that device says that it can't pair. I think it's because the DeviceInformation class request a communication with my device and it don't responde because it lacks the Start byte.
Is there a way for me to create a custom conection with my SerialDevice beside this class?
Or is there a way for me to configure the start byte in the declaration of the SerialDevice class?
Start byte is: STX, 0x02.
Explain more what you've done so far if you need more help!
The device i'm trying to integrate requires that the start byte is as it follows:
#define FINGERPRINT_STARTCODE 0xEF01
This is a C code that is in the docummentation of the device.
As i'm trying to migrate it to C# i'm not being abble to change the start byte and therefore the device doesn't responde to my C# code.
This is me declarating the SerialDevice:
string aqs = SerialDevice.GetDeviceSelector("UART0");
var dis = await DeviceInformation.FindAllAsync(aqs);
UartPort = await SerialDevice.FromIdAsync(dis[0].Id);
UartPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
UartPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
UartPort.BaudRate = 9600;
UartPort.Parity = SerialParity.None;
UartPort.StopBits = SerialStopBitCount.One;
UartPort.DataBits = 8;
DataWriterObject = new DataWriter(UartPort.OutputStream);
DataReaderObject = new DataReader(UartPort.InputStream);
And then i try to send data through the DataWriter with my start byte:
private async Task<int> EnviarPacote(Package package)
{
int sum;
DataWriterObject.WriteBytes(package.start);
DataWriterObject.WriteByte(package.address[0]);
DataWriterObject.WriteByte(package.address[1]);
DataWriterObject.WriteByte(package.address[2]);
DataWriterObject.WriteByte(package.address[3]);
DataWriterObject.WriteInt16(package.type);
int tamanho = 12;
DataWriterObject.WriteInt32(tamanho);
//DataWriterObject.WriteBytes(package.length);
sum = tamanho + package.type;
foreach (var dado in package.data)
{
DataWriterObject.WriteByte(dado);
}
DataWriterObject.WriteInt32(12);
await UartPort.OutputStream.WriteAsync(DataWriterObject.DetachBuffer());
return sum;
}
And it just doesn't work.
I guess i should change something in my SerialDevice declaration so that the class knows how to communicate with my device. But i don't know how.
I'm trying to connect and send a file from android device to a PC or another smartphone with Xamarin.Android via Bluetooth.
Connection is estabilished, but it doesn't send the file. It doesn't seems to work since there are no exceptions.
int bufferSize = (int)sourceStream.Length;
byte[] byteArray=File.ReadAllBytes("/sdcard/test.txt");
BluetoothSocket socket = device.CreateInsecureRfcommSocketToServiceRecord(UUID.FromString("00001105-0000-1000-8000-00805f9b34fb"));
try
{
await socket.ConnectAsync();
Stream oStream = socket.OutputStream;
oStream.Write(byteArray, 0, bufferSize);
}
catch (Exception ex)
{
//some catching
}
Beside that, do you know any tutorial out there?
I do not know what your receiving code looks like, but you can use the built-in Android BlueTooth Intent.ActionSend Sharing app to start the transfer:
var photoAsset = Assets.OpenFd ("BusinessCard.png");
var javaIOFile = new Java.IO.File (photoAsset.ToString ());
var sendIntent = new Intent (Intent.ActionSend);
sendIntent.SetType ("image/*");
sendIntent.SetComponent (new ComponentName ("com.android.bluetooth", "com.android.bluetooth.opp.BluetoothOppLauncherActivity"));
sendIntent.PutExtra (Intent.ExtraStream, Android.Net.Uri.FromFile (javaIOFile));
StartActivity (sendIntent);
Of course the receiver would have to have their BlueTooth on and accept the connection/transfer.