Windows BLE GattValueChangedEventArgs.Characteristic.Value missing data - c#

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.

Related

C# - Grabbing data from serialport becomes too slow above 200kb/s

I created a wpf application to control and monitor a sensor invented at my university. I connect to the device with 2 comports of which one is exclusively for sending data from the device. The device encodes each dataset to 5 bytes and starts sending them as soon as I give it the order via the other comport. Internally the sensor has a buffer of ~40 kilobytes.
Problem:
It works very will until a sampling rate above 25kHz (25000 times 5 bytes each second) is chosen. Then data seems to be lost and the device sends an error, that the internal buffer ran full (which explains the lost data)
I tried several approaches without success yet, the latest was trying to apply this solution: https://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport
I fail to understand what determines how often I receive an event that grabs the data from the serialport. The problem doesn't change whether my program is fast or slow .. always ~25kHz = ~200kBytes/s
Code:
private SerialPort _comPortData;
_comPortData.DataReceived += new SerialDataReceivedEventHandler(PortDataReceived);
The Connect() function, stripped-down:
void connect()
{
comPort.BaudRate = 115200;
comPort.DataBits = 8;
comPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), "One");
comPort.Parity = (Parity)Enum.Parse(typeof(Parity), "None");
comPort.PortName = port;
comPort.Open();
Console.WriteLine(comPort.PortName + " opened | Baud Rate: " + comPort.BaudRate);
}
The (edited) PortDataReceived function:
private void PortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (!_comPortData.IsOpen) return;
// Obtain the number of bytes waiting in the port's buffer
int numberOfBytes = _comPortData.BytesToRead;
// Create a byte array buffer to hold the incoming data
byte[] buffer = new byte[numberOfBytes];
// Read the data from the port asynchronously and store it in the buffer
await _comPortData.BaseStream.ReadAsync(buffer, 0, numberOfBytes);
}

Missing bytes while reading the bytes from C# serial port class

I wrote down some app in C# to read the bytes from (USB-SerialPort) payment terminal.
But I can see the bytes are missing/overridden in my application while I read from the read-Buffer of windows serial port.
I have following piece of code for opening the port and reading the bytes( either from ReadExisting or ReadByteArray) from serial port when the DataReceivedEvent gets triggered.
//Open the port
m_port.Open();
//attach the event handler
m_port.ErrorReceived += OnSerialErrorReceived;
m_port.DataReceived += OnSerialDataReceived;
public void OnSerialDataReceived(object sender ,SerialDataReceivedEventArgs serialDataArgs)
{
//Thread.Sleep(20);
//int numberOfBytesToRead = m_port.BytesToRead;
//byte[] readByteArray = new byte[m_port.BytesToRead];
//m_port.Read(readByteArray, 0, readByteArray.Length);
string readData = m_port.ReadExisting();
ParseWLinkProtocolMsgToHexStringArray(Encoding.UTF8.GetBytes(readData));
}
But when I use the Thread.Sleep(20ms) before read the data in OnSerialDataReceived method , I have no missing bytes in my data.
Also I have not set any other properties on the serial port instance except the following.
BaudRate,StopBits,Parity and DataBits.
Can some one please suggest me any other alternative way with out applying the time delay in my application.

C# Converting a byte array from BT LE device

I am using a Nordic Thingy:52 to record environmental data in a UWP app and have followed the example in the Windows Universal Sample apps to connect to BT LE devices.
So far I have been able to connect to the device to retrieve service and characteristic information but when receiving the actual data from the sensors I can't manage to convert the byte array into usable data.
async void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
// An Indicate or Notify reported that the value has changed.
var reader = DataReader.FromBuffer(args.CharacteristicValue);
byte[] input = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(input);
}
When checking the contents of the byte array you can see that something has been received but I'm stuck when it comes to knowing how to convert this array to useful data.
Code to read the byte array
Data specification for data sent by the device
From the document we can see the definition of pressure data:
5 bytes contains one int32 for integer part and one uint8 for decimal part. Uint is hPa.
You get a string like this:
Int32 pressureInteger = BitConverter.ToInt32(input, 0); //252-3-0-0
string pressureString = pressureInteger.ToString() + "." + input[4].ToString() + "hPa";
The string will be "1020.28hPa"
More reference "BitConverter Class" and note little-endian/big-endian.

How to write data to USB HID device in Android with only a single input endpoint

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

Bluetooth api for surface pro 3 windows 8.1

I have a bluetooth button from Radius networks. The builtin - "add a bluetooth device" finds it every time.
I need the api or a stack that I can use to do from my app. I am doing this in c#. the library 32 feet is not compatible
To enumerate RFCOMM Bluetooth devices attached to a device, do:
var DEVICE_ID = new Guid("{00000000-0000-0000-0000-000000000000}"); //Enter your device's RFCOMM service id (try to find it on manufactorer's website
var services = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(
RfcommDeviceService.GetDeviceSelector(
RfcommServiceId.FromUuid(DEVICE_ID)));
To connect to a the first available device, do:
if (services.Count > 0)
{
var service = await RfcommDeviceService.FromIdAsync(services[0].Id);
//Open a socket to the bluetooth device for communication. Use the socket to communicate using the device's API
var socket = new StreamSocket();
await socket.ConnectAsync(service.ConnectionHostName, service.ConnectionServiceName, SocketProtectionLevel
.BluetoothEncryptionAllowNullAuthentication); //Substitue real BluetoothEncryption
}
To send data to the device and read data back, do:
var BYTE_NUM = 64 as UInt32; //Read this many bytes
IInputStream input = socket.InputStream;
IOutputStream output = socket.OutputStream;
var inputBuffer = new Buffer();
var operation = input.ReadAsync(inputBuffer, BYTE_NUM, InputStreamOptions.none);
while (!operation.Completed) Thread.Sleep(200);
inputBuffer = operation.GetResults();
var resultReader = DataReader.FromBuffer(inputBuffer);
byte[] result = new byte[BYTE_NUM];
resultReader.ReadBytes(result);
resultReader.Dispose();
//Do something with the bytes retrieved. If the Bluetooth device has an api, it will likely specify what bytes will be sent from the device
//Now time to give some data to the device
byte[] outputData = Encoding.ASCII.GetBytes("Hello, Bluetooth Device. Here's some data! LALALALALA");
IBuffer outputBuffer = outputData.AsBuffer(); //Neat method, remember to include System.Runtime.InteropServices.WindowsRuntime
operation = output.WriteAsync(outputBuffer);
while (!operation.Completed) Thread.Sleep(200);
await output.FlushAsync(); //Now the data has really been written
This will work for all RFCOMM (normal) bluetooth devices, if your device uses Bluetooth Low Energy please use the corresponding GATT classes.

Categories

Resources