I wrote an Android Phone app in Android Studio that connects to a UbloX NINA B1 to send and receive data on the SPS Service.
I'm having issues with the incoming data from the NINA B1 (Peripheral). I'm not sure if I connected correctly and turned the notifications on correctly.
Below is the code where I do the connection to the SPS Service. (Hardcoded to select Group 3 and child 0)
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
// Clicked on a Caracteristic (eg. FIFO or Credits)
public boolean enableNotification() {
// Hardcode for SPS service on NINA B112, can only use this service, other services will not be used
// This groupPosition and childPosition was determined from A_05 code, The selectable list gave these values when selecting SPS service
int groupPosition = 3;
int childPosition = 0;
BluetoothGattCharacteristic characteristic;
characteristic = characteristics_HashMapList.get(services_ArrayList.get(groupPosition).getUuid().toString()).get(childPosition);
// Todo find a better way to do below two lines. This is a temp way to pass characteristic & mBTLE_Service to static variables to use in HomeFragment for sending data
//Copy characteristics of SPS service to static act_characteristic for use by HomeFragment
this.act_characteristic = characteristic;
//Copy mBTLE_Service of SPS service to static act_service for use by HomeFragment
this.act_service = mBTLE_Service;
if (Utils.hasWriteProperty(characteristic.getProperties()) != 0) {
String uuid = characteristic.getUuid().toString();
//act_characteristic.setTitle(uuid);
//act_characteristic.setService(mBTLE_Service);
//act_characteristic.setCharacteristic(characteristic);
//dialog_btle_characteristic.show(getFragmentManager(), "Dialog_BTLE_Characteristic");
} if (Utils.hasReadProperty(characteristic.getProperties()) != 0) {
if (mBTLE_Service != null) {
mBTLE_Service.readCharacteristic(characteristic);
}
} if (Utils.hasNotifyProperty(characteristic.getProperties()) != 0) {
if (mBTLE_Service != null) {
mBTLE_Service.setCharacteristicNotification(characteristic, true);
}
}
return false;
}
Here is where I set the notifications ON, this part I'm not so confident about.
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(getString(R.string.CLIENT_CHARACTERISTIC_CONFIG)));
// Todo: if the indications and notifications is to be used, then mode the indications part back into setCharacteristicNotification() and add a check for Indications, then change below code to set both indication and notify
if (enabled) {
// Enable INDICATION & NOTIFICATION
final byte[] ENABLE_INDICATION_NOTIFICATION = {0x03, 0x00};
descriptor.setValue(ENABLE_INDICATION_NOTIFICATION);
}
else {
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
//ToDo Shorten the delay time so that the Notifications Enable still works
// Delay in milli seconds
int m_sec = 200;
Delay.delay(m_sec, new Delay.DelayCallback() {
#Override
public void afterDelay() {
// Enable Notifications after 200 m_sec Delay
mBluetoothGatt.writeDescriptor(descriptor);
}
});
}
This is where I handle the incoming Bytes, I send a Modbus message from the Phone to NINA B1, then NINA B1 replies with a Modbus message. The Incoming byte count is always < 20 bytes. And I fill the RxBuff until the correct amount of bytes are received AND the Slave Address is correct, then I decode the received message.
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(EXTRA_UUID, characteristic.getUuid().toString());
}
// For all other profiles, writes the data formatted in HEX.
// Only Pack 20 bytes at a time coming from BLE server into data[]
final byte[] rx_Data;
int tempSlavAdr;
rx_Data = characteristic.getValue();
if (rx_Data != null && rx_Data.length > 0) {
// Only handle message if request came from ReadInput_Thread
if (this.request_flag) {
// ToDO: See if other non Modbus data arrives between packets
// Populate RxBuff
for (int i = 0; i < rx_Data.length; i++) {
this.RxBuff[this.WrPtr++] = rx_Data[i];
}
Log.d(TAG,"QTY Bytes in rx_Data = " + (Integer)rx_Data.length);
// Start Decode message only after all expected bytes are received && first message is Slave Address
// Prevent Negative numbers in Slave Address: tempSlavAdr is used in the the next if statement
tempSlavAdr = (byte) this.RxBuff[0];
if (tempSlavAdr < 0) tempSlavAdr += 256;
if ((this.WrPtr == FragmentHome.ExpectedCount_sent) && (tempSlavAdr == slaveAddr)) {
// Copy RxBuff over to Dec_RxBuff
int Dec_RxBuff[] = new int[255];
for (int i = 0; i < this.WrPtr; i++) {
Dec_RxBuff[i] = (this.RxBuff[i]);
Dec_RxBuff[i] += 256;
Dec_RxBuff[i] = Dec_RxBuff[i] & 0xFF;
}
// Clear flag indicating that Thread Send a Request
this.request_flag = false;
message_rx = false;
// Decode ModbusMessage and message Good
if (modbusHandler.DecodeModbusMessage(Dec_RxBuff)){
message_rx = true;
}
// Decode ModbusMessage and message Bad
else{
message_rx = false;
}
// Clear buffers
WrPtr = 0;
Arrays.fill(rx_Data, (byte) 0);
Arrays.fill(RxBuff, (byte) 0);
// Todo What if WrPtr stop short of Expected counter? Do some protection
// WrPtr > ExpectedCount Clear Buffers
} else if (WrPtr > FragmentHome.ExpectedCount_sent) {
// Clear buffers
// HomeFragment.set_Actual(false);
Log.d(TAG,"RX Count to big expected " + FragmentHome.ExpectedCount_sent + " got " + WrPtr);
WrPtr = 0;
Arrays.fill(rx_Data, (byte) 0);
Arrays.fill(RxBuff, (byte) 0);
}
// Todo Use this intent to pass data to a service to Decode Modbus Data (Service not written yet)
// intent.putExtra(EXTRA_DATA, new String(data) + "\n" + Utils.hexToString(data));
}
}
else {
//intent.putExtra(EXTRA_DATA, "0");
}
//sendBroadcast(intent);
}
I still get some data loss, and not sure how to handle this. Phone and NINA B1 is next to one another so distance is not a problem.
Can I get some help on how to set the indications correctly? And why I might lose bytes.
Marinus
Related
I am writing a Xamarin.Android application, but this question is applicable to native Android and BLE in general. I have a write characteristic that I can write to, and it works as long as I don't send more than 600 characters. Anything over 600 characters gets truncated. Looking at my logs, I can see that the text is being split into 20 character packets, and OnCharacteristicWriteRequest is called for each packet, but stops being called after 600 characters. I am testing with 2 Android tablets. My code to write to the characteristic:
public override void OnServicesDiscovered(BluetoothGatt gatt, [GeneratedEnum] GattStatus status)
{
base.OnServicesDiscovered(gatt, status);
try
{
if (status != GattStatus.Success)
{
Log?.Invoke("discover services failed");
return;
}
Log?.Invoke("services discovered");
if(RequestForAddressExists(gatt.Device.Address))
{
lock (_requestsLocker)
{
Java.Util.UUID serviceUuid = GetRequestedServiceUuid(gatt.Device.Address);
Java.Util.UUID characteristicUuid = GetRequestedCharacteristicUuid(gatt.Device.Address);
BluetoothGattCharacteristic characteristic = gatt.GetService(serviceUuid).GetCharacteristic(characteristicUuid);
Log?.Invoke("characterisitic found");
var request = _requests.FirstOrDefault(r => r.DeviceAddress == gatt.Device.Address);
if (characteristic.Properties.HasFlag(GattProperty.Write))
{
Log?.Invoke("writing characteristic...");
string data = ((WriteCharacteristicRequest)request).Data;
characteristic.SetValue($"{data}{Constants.WriteCharacteristicEndDelimiter}");
characteristic.WriteType = GattWriteType.Default;
gatt.WriteCharacteristic(characteristic);
}
else
{
Log?.Invoke("GattProperty not supported");
_requests.Remove(request);
}
}
}
}
catch (Exception e)
{
Log?.Invoke(e.Message);
}
}
public override void OnCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, [GeneratedEnum] GattStatus status)
{
base.OnCharacteristicWrite(gatt, characteristic, status);
if (status != GattStatus.Success)
{
Log?.Invoke($"OnCharacteristicWrite status not success: {status}");
}
else
{
Log?.Invoke("OnCharacteristicWrite success");
}
gatt.Disconnect();
gatt.Close();
lock (_requestsLocker)
{
var r = _requests.FirstOrDefault(x => x.DeviceAddress == gatt.Device.Address);
if (r != null)
{
_requests.Remove(r);
}
}
}
My code to accept the write request:
public override void OnCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, bool preparedWrite, bool responseNeeded, int offset, byte[] value)
{
base.OnCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
Log?.Invoke("OnCharacteristicWriteRequest");
string data = System.Text.Encoding.UTF8.GetString(value);
Log?.Invoke(data);
string characteristicId = new Guid(characteristic.Uuid.ToString()).ToString().ToUpperInvariant();
var record = _writeCharacteristicsReceived.FirstOrDefault(c => c.DeviceAddress == device.Address && c.CharacteristicId.ToUpperInvariant() == characteristicId);
if(record != null)
{
record.Data += data;
}
else
{
record = new CharacteristicWriteReceived()
{
CharacteristicId = characteristicId,
DeviceAddress = device.Address,
Data = data
};
_writeCharacteristicsReceived.Add(record);
}
if (record.Data.EndsWith(Constants.WriteCharacteristicEndDelimiter) == true)
{
Log?.Invoke("end found");
_writeCharacteristicsReceived.Remove(record);
record.Data = record.Data.Substring(0, record.Data.Length - Constants.WriteCharacteristicEndDelimiter.Length); // remove the end delimeter
Log?.Invoke(record.Data);
OnCharacteristicWriteReceived?.Invoke(record);
}
if (responseNeeded)
{
BluetoothGattServer.SendResponse(device, requestId, GattStatus.Success, 0, value);
}
}
public override void OnExecuteWrite(BluetoothDevice device, int requestId, bool execute)
{
// need to override OnExecuteWrite and call SendResponse here as well,
// since the execute packet corresponds to the last ATT packet that the client sends as a "finish" marker,
// and the client expects a response to know that the server accepted the writes
base.OnExecuteWrite(device, requestId, execute);
BluetoothGattServer.SendResponse(device, requestId, GattStatus.Success, 0, new byte[0]);
}
The funny thing is, even when the text is truncated, I still get status == GattStatus.Success in my OnCharacteristicWrite. Why is it being truncated? Is there a maximum number of packets that can be sent?
Both devices are continuously advertising and scanning on BLE while writing to this characteristic...could that cause a problem?
A characteristic value can only be 512 bytes long per specification. Writing a longer value is not allowed, even if apparently some stacks don't enforce it.
When you write a value longer than what fits in the MTU (default 23 bytes minus 3 for header), the sender Bluetooth stack splits it up in multiple chunks (Prepared Write) and then sends an Execute request to commit. For each chunk you have the offset parameter so you know at which offset to write the current chunk.
I am having an issue getting the network latency to be low for my multiplayer shooter game made with Unity. I am using UDP to send player positions from the game client to my amazon server and back to another game client. My game clients are sending 60 byte UDP packets to the amazon server at a rate of 8 packets per second.
When I play the game on two separate iOS devices (iPhone 7 and iPad mini) the network latency is very low and the players are able to see each other move instantly. However, if I run the game on my iPhone 7 and play against another player who is using a samsung galaxy s4 running android, which is a lower powered device, I experience 5 second latency on the android device when receiving player positions from the iOS device. The odd thing is that the iOS device can receive player positions from the android device instantly. The same issue happens when playing on the iPhone 7 against a client on mac, except the iPhone 7 experiences the 5 second latency when receiving player positions from the mac client. The issue also happens when playing on the Samsung Galaxy S4 against a client on mac, where the galaxy s4 experiences the 5 second latency.
I have tried to increase the udp receive buffer size to 16 MB but it didn't change anything.
Here is a sample of my game client code where I send player positions to the amazon server:
void Update(){
// manually constrain rotation because rigidbody constraints don't work
this.transform.rotation = Quaternion.Euler(new Vector3(0, this.transform.rotation.eulerAngles.y, 0));
this.transform.position = new Vector3(this.transform.position.x, boatHeightConstant, this.transform.position.z);
// Speed Boost Handling
if(isSpeedBoosting == true){
tiltFactor = tiltModifier * (VelocityRatio() + 1.0f);
speedBoostTimer += Time.deltaTime;
}
else{
tiltFactor = VelocityRatio() + 1.0f;
}
if(speedBoostTimer >= speedBoostDuration){
isSpeedBoosting = false;
speedBoostTimer = 0f;
endSpeedBoost = true;
}
if(endSpeedBoost == true){
GetComponentInChildren<MainWeapon>().EndFireRateBoost();
endSpeedBoost = false;
}
// Attack Boost Handling
if(isAttackBoosting == true){
attackBoostTimer += Time.deltaTime;
}
if(attackBoostTimer >= attackBoostDuration){
isAttackBoosting = false;
attackBoostTimer = 0f;
endAttackBoost = true;
}
if(endAttackBoost == true){
GetComponentInChildren<MainWeapon>().ResetDamage();
GetComponentInChildren<MainWeapon>().ResetHeatUpRate();
endAttackBoost = false;
}
if (GetComponent<InputManager>().GetInputType() == 0 || GetComponent<InputManager>().GetInputType() == 1 )
{
if (isSpeedBoosting == true)
{
Move(speedModifier);
}
else
{
Move(1f);
}
if (syncTimer <= 0f) {
syncTimer = networkRefreshRate;
SyncTransform ();
} else {
syncTimer -= Time.deltaTime;
}
}
else
{
NetworkMove();
}
}
/// <summary>
/// This function is constantly called to upload the local player's position to the server so the server is
/// aware of this player's movement and can share this local players current position with other players in the game.
/// </summary>
public void SyncTransform() {
if (isLocalPlayer == true && client != null && client.IsConnected()) {
client.SendPlayerTransform(SharedData.storage["userId"], transform.position, transform.rotation, currentLife);
}
}
Here is a sample of the UDP sender class in my game client:
public void SendPlayerTransform(string playerId, Vector3 position, Quaternion rotation, int currentLife) {
Message.Writer writer = new Message.Writer(Message.MessageType.PlayerTransform, udpClient, remoteEndPoint);
// Write the timestamp of this message
writer.WriteLong(DateTime.UtcNow.Ticks);
// write the player id
writer.WriteString(SharedData.storage["userId"]);
// write the position vector
writer.WriteFloatArray(CommonGameFunctions.ConvertVectorToFloatArray(position));
// write the rotation vector
writer.WriteFloatArray(CommonGameFunctions.ConvertQuaternionToFloatArray(rotation));
writer.WriteInt (currentLife);
// Now send the message
writer.Send();
}
Here is a sample of where I receive the UDP messages on the game client:
public int HandleUdpMessages() {
if (udpTimerStarted == false) {
lastUdpMessageReceivedTime = DateTime.Now;
udpTimerStarted = true;
} else if (udpTimerStarted == true && udpClient.Available == 0){
TimeSpan t = DateTime.Now - lastUdpMessageReceivedTime;
if (t.Seconds >= 10f) {
// no message received for last 10 seconds then throw IO exception
//throw new SocketException();
}
}
if (udpClient.Available > 0) {
var messageReader = new Message.Reader (udpClient);
messageReader.BlockingRead (ref localEndPoint, UdpReceiveTimeout);
var messageType = messageReader.ReadMessageTypeUdp ();
lastUdpMessageReceivedTime = DateTime.Now;
Debug.Log ("Received udp message: " + messageType);
switch (messageType) {
// Player position update message
case Message.MessageType.PlayerTransform:
HandlePlayerTransform (messageReader);
break;
case Message.MessageType.PlayerScore:
HandlePlayerScore (messageReader);
break;
case Message.MessageType.RockHealth:
HandleRockHealth (messageReader);
break;
case Message.MessageType.PlayerHealth:
HandlePlayerHealth (messageReader);
break;
case Message.MessageType.ShieldHealth:
HandleShieldHealth (messageReader);
break;
default:
Debug.LogError ("Unhandled message " + messageType);
break;
}
}
return 0;
}
public void HandlePlayerTransform(Message.Reader reader)
{
long timeStamp = reader.ReadLong ();
string playerId = reader.ReadString();
if (playerMessageTimeStamps [playerId].latestPlayerTransform > timeStamp)
return;
Vector3 position = new Vector3();
Quaternion rotation = new Quaternion();
// read position
position = CommonGameFunctions.ConvertFloatArrayToVector3(reader.ReadFloatArray(3));
rotation = CommonGameFunctions.ConvertFloatArrayToQuaternion(reader.ReadFloatArray(4));
// Now update the transform of the right player
Player player = Player.playerTable[playerId];
if (player == null) {
return;
}
player.SetNetworkPostion(position);
player.SetNetworkRotation(rotation);
}
On my server this is the main game loop which runs on its own dedicated thread.
// Now all the players are connected to the server
// We can start the main game loop
while (gameRunning == true) {
HandlePlayersWhoDroppedOut ();
if (PlayersLeftGame () == true) {
DisconnectAllPlayers ();
Debug.LogError ("Player's left match, returning from thread");
return;
} else {
foreach (NetworkPlayer p in participants) {
try {
p.HandleTcpMessages ();
p.HandleUdpMessages ();
} catch (IOException e) {
droppedPlayers.Add (p);
}
}
try {
RespawnRocksIfDestroyed ();
} catch (System.IO.IOException e) {
DisconnectAllPlayers ();
return;
Debug.LogError ("Failed to spawn rocks");
}
}
}
The problem with my code was that I was reading exactly one UDP message in each iteration of the UDP message handler function. I changed my function to read ALL the available UDP messages in the buffer and the lag reduced by 80%. UDP messages queue up in the buffer quicker than the message handler function repeats so this is why the problem was happening.
I have an Android app that is communicating with and arduino board via bluetooth
All the commands are going backward and forwards fine, until I wanted to send a comand of the type
"aT?bb"
from the android app, however when I print it in ardunio I am getting
"aT%3F"
I am logging the command in android and it is formed correctly My quesion is does c++/Arduino handle '?' differently then normal chars?
here is my arduino code->
while(bluetooth.available())
{
char toSend = (char)bluetooth.read();
if(toSend != '\0'){
if (toSend == 'a'){ i=0 ;}
inMsg[i] = toSend;
i++;
}
}
if(i == 5 )
{
// mock sending queries
if(inMsg[2] == '?'){
if(inMsg[1] == 'T'){
bluetooth.write("ty1");Serial.println("");
}else if(inMsg[1] == 'x'){ //normal cycle
bluetooth.write("xx1");
}else if(inMsg[1] == 'X'){ Serial.println(""); //booter
bluetooth.write("XX0");
}else if(inMsg[1] == 'N'){Serial.println(""); //On time
bluetooth.write("on1");
}else if(inMsg[1] == 'F'){ Serial.println(""); //Off time
bluetooth.write("of30");
}else if(inMsg[1] == 'S'){ Serial.println(""); //Speed percent
bluetooth.write("sp30");
}
}
// write to console
for(int j = 0; j < 5; j++){
Serial.write(inMsg[j]);
}
// new line
if(i == 5){Serial.println("");}
i = 0; // reset buffer
}
aT%3F <- this is mal formed
aS133 <- all the other are as I sent them from android
aN169
aF192
aS200
aXXXX
aYYYY
ayYYY
axXXX
my Android Code
...
command = "aT?bb";
writeCommand(command);
...
private void writeCommand(String command)
{
for (BluetoothGattCharacteristic characteristic : characteristics)
{
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) >0)
{
try {
characteristic.setValue(URLEncoder.encode(command, "utf-8"));
gatt.writeCharacteristic(characteristic);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
As pointed out in the comments above it was the URLEncoder that was changing the String. I have now changed this method to
private void writeCommand(String command)
{
for (BluetoothGattCharacteristic characteristic : characteristics)
{
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) >0)
{
characteristic.setValue(command);
gatt.writeCharacteristic(characteristic);
}else{
Log.i(TAG,"non write able");
}
}
}
I'm wondering if anybody can help me figure out what is causing the data I am sending to become corrupt.
My setup is currently an Arduino pro mini with a HM-10 bluetooth module connected (I have also tried HM-11 Module too) and an Android application to receive the bluetooth data.
Module setup: http://letsmakerobots.com/node/38009
If I send data with big enough intervals then the data is fine, but if I send the data continuously I see messages getting mixed up and lost. To test this I send "$0.1,0.2,0.3,0.4,0.5" to the Android application from the Arduino, sometimes the stream of data appears to send fine but other times it is really quite scrambled. Please see the below graphs that demonstrate this:
Good case:
Bad case:
Arduino code:
String inputString = ""; //Hold the incoming data.
boolean stringComplete = false; //Determines if the string is complete.
boolean realtime = false;
void setup()
{
Serial.begin(9600);
delay(500);
Serial.print("AT+START");
delay(500);
}
void loop()
{
if(stringComplete)
{
if(inputString.equals("rStart"))
{
Serial.println("$startACK");
realtime = true;
}
else if(inputString.equals("stop"))
{
Serial.println("$stopACK");
realtime = false;
}
else{
Serial.print(inputString);
}
inputString = "";
stringComplete = false;
}
if(realtime)
{
Serial.println("$0.1,0.2,0.3,0.4,0.5,0.6");
delay(10);
}
}
void serialEvent() {
while (Serial.available())
{
// get the new byte:
char inChar = (char)Serial.read();
if (inChar == '\n')
{
stringComplete = true;
}
else
{
inputString += inChar;
}
}
}
The Android side just receives the data and then parses it in an IntentService:
#Override
protected void onHandleIntent(Intent intent) {
//Incoming command.
String rawData = intent.getStringExtra(DataProcessingIntentService.REQUEST);
//Append our new data to our data helper.
Log.i(this.getClass().getName(), "Previous Raw: (" + DataProcessingHelper.getInstance().getData() + ")");
DataProcessingHelper.getInstance().appendData(rawData);
Log.i(this.getClass().getName(), "New Raw: (" + DataProcessingHelper.getInstance().getData() + ")");
commandStartIndex = DataProcessingHelper.getInstance().getData().indexOf("$");
commandEndIndex = DataProcessingHelper.getInstance().getData().indexOf("\n");
//Set this as the data starting point.
if(commandStartIndex != -1){
DataProcessingHelper.getInstance().offsetData(commandStartIndex);
}
//Ensure that a command has been found and that the end index is after the starting index.
if(commandStartIndex != -1 && commandEndIndex > commandStartIndex){
//Remove the command structure from the command.
command = DataProcessingHelper.getInstance().getData().substring(commandStartIndex+1, commandEndIndex-1); //Remove the \r\n end command.
DataProcessingHelper.getInstance().offsetData(commandEndIndex+1);
if(command.length() > 1){
//Split the data out of the comand.
splitData = command.split(",");
Log.i(this.getClass().getName(), "Broadcasting the processed data. (" + command + ")");
//Broadcast data.
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(DataProcessingIntentService.RESPONSE);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(DataProcessingIntentService.RESPONSE, splitData);
sendBroadcast(broadcastIntent);
}else{
Log.e(this.getClass().getName(), "Command is less than 1 character long!");
}
}
}
Thank you for any help!
I have now figured out what was causing this problem. It appears that BLE only supports a maximum of 20 bytes per a transaction. The time between these transactions is different depending on what you are using. I'm currently using notifications which means that I can send 20 bytes every 7.5 milliseconds maximum. I have opted for 10 milliseconds to be safe. I will now need to look into breaking up packets into 20 bytes maximum to ensure no data corruption.
I am trying to receive data from Arduino to my Android device. I started from here
In the activity part of the application, they did
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(fromUser){
if(sUsbController != null){
sUsbController.send((byte)(progress&0xFF));
}
}
}
In the send function
public void send(byte data) {
mData = data;
synchronized (sSendLock) {
sSendLock.notify();
}
}
And in the UsbRunnable part
private class UsbRunnable implements Runnable {
private final UsbDevice mDevice;
UsbRunnable(UsbDevice dev) {
mDevice = dev;
}
#Override
public void run() {//here the main USB functionality is implemented
UsbDeviceConnection conn = mUsbManager.openDevice(mDevice);
if (!conn.claimInterface(mDevice.getInterface(1), true)) {
return;
}
// Arduino Serial usb Conv
conn.controlTransfer(0x21, 34, 0, 0, null, 0, 0);
conn.controlTransfer(0x21, 32, 0, 0, new byte[] { (byte) 0x80,
0x25, 0x00, 0x00, 0x00, 0x00, 0x08 }, 7, 0);
...
...
conn.bulkTransfer(epOUT, new byte[] { mData }, 1, 0);
So, App took the progress of the seekbar and sent it to the Arduino.
But, I want my app to receive data from Arduino. I guess I need to use bulktransfer function as well. think that I want to save the data to mData variable.
How can I do that?
Using the bulkTransfer method is the way to go. You will need to use the IN endpoint to receive the data. For instance, to get a byte of data from the Arduino use this:
byte[] reply = new byte[1]; // to store data from Arduino
int size = 1; // receive at most 1 byte of data
int timeout = 100; // try to receive data for up to 100 milliseconds
int count = conn.bulkTransfer(epIN, reply, size, timeout);
if(count < 0) {
Log.d("ArduinoUSB", "Failure occurred when receiving from Arduino");
} else {
Log.d("ArduinoUSB", "Received " + count + " bytes: " + Arrays.toString(reply));
}
The data will be stored in reply.