Need some help with Android LE

I'm in the process of moving my implementation over to the CanBUS Triple's LE stack and, still being new to LE, have a couple questions.

Included below is my main activity and all is well up until the point I want to write/read to serial characteristic.

All I ever receive in this example is a stream of bytes 0x1 and 0x10 (integer 1 and integer 16).

I know my receive loop is VERY sloppy but at this point I'm just trying to confirm ability to send/receive data. I'll likely subscribe to the serial characteristic's read notify option once I figure this initial piece out.

Shouldn't I be getting all kinds of data back as a result of sending 0x1 0x10 0x1 ? Perhaps I've got send/receive all messed up.

Any pointers would be greatly appreciated.

--- see updated code below ---

I've made significant progress with this activity. I'm now able to communicate with the CBT's UART. This updated example discovers the CBT, connects to it, subscribes to its notification characteristic, sends an 0x01 0x01 command, reads the response, and then sets the BT filter (0x04 0x01 0x0028 0x0028) for a known good register and filter value I use over USB (0x03 0x01 0x01 0x0028 0x0028).

Unfortunately I never receive anything beyond the initial status response. I have the exact same results using LightBlue for OS X.

<pre><code>package com.shellware.genesisconnect;

import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends Activity {

private final static UUID CLIENT_CHARACTERISTIC_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private final static UUID BLE_SERVICE_UUID = UUID.fromString("7a1fb359-735c-4983-8082-bdd7674c74d2");
private final static UUID RECEIVE_NOTIFY_CHARACTERISTIC_UUID = UUID.fromString("B0D6C9FE-E38A-4D31-9272-B8B3E93D8658");
private final static UUID RECEIVE_INDICATE_CHARACTERISTIC_UUID = UUID.fromString("B0D6C9FE-E38A-4D31-9272-B8B3E93D8657");

private Context context;

boolean filterSet = false;

private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothDevice bluetoothDevice;
private BluetoothGatt bluetoothGatt;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    context = this;
    setContentView(R.layout.activity_main);
}

@Override
protected void onResume() {
	super.onResume();

	bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
	bluetoothAdapter = bluetoothManager.getAdapter();
	
	if (bluetoothAdapter != null && !bluetoothAdapter.isEnabled()) {
	    Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);   
	    startActivityForResult(enableIntent, 1);		    
	} else {
		if (bluetoothAdapter == null) {
			Toast.makeText(context, "No Bluetooth adapter found", Toast.LENGTH_SHORT).show();
		} else {
		    final boolean stat = bluetoothAdapter.startLeScan(leScanCallback);				
			Toast.makeText(context, String.format("Boothtooth LE Scan Started - %b", stat), Toast.LENGTH_SHORT).show();
		}
	}
}

@Override
protected void onPause() {
	super.onPause();
			
	if (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
		Toast.makeText(context, "Stopping LE Scan", Toast.LENGTH_SHORT).show();
		bluetoothAdapter.stopLeScan(leScanCallback);

		if (bluetoothGatt != null) {
			Toast.makeText(context, "Disconnecting GATT", Toast.LENGTH_SHORT).show();
			bluetoothGatt.disconnect();

			Toast.makeText(context, "Closing GATT", Toast.LENGTH_SHORT).show();
			bluetoothGatt.close();
		}
	}
}

private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
	@Override
	public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
		
		if (device.getName().contains("CANBus Triple")) {
			
			runOnUiThread(new Runnable() {
	               @Override
	               public void run() {
	       			Toast.makeText(context, String.format("Found %s - %s",device.getName(), device.getAddress()), Toast.LENGTH_SHORT).show();
	               }
	           });
							
			bluetoothDevice = device;
			bluetoothAdapter.stopLeScan(leScanCallback);
			
			bluetoothGatt = bluetoothDevice.connectGatt(context, false, btleGattCallback);
		}
	}
};

private final BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {
 
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
    	super.onCharacteristicChanged(gatt, characteristic);
    	
		runOnUiThread(new Runnable() {
               @Override
               public void run() {
					Toast.makeText(context, new String(characteristic.getValue()), Toast.LENGTH_SHORT).show();
               }});
		
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (!filterSet) {
			// look for stereo DISP button press
			byte[] msg = {0x04, 0x01, 0x0028, 0x0028};
			characteristic.setValue(msg);
			bluetoothGatt.writeCharacteristic(characteristic);
			filterSet = true;
		}
    }
 
    @Override
    public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) { 
    	super.onConnectionStateChange(gatt, status, newState);
    	
        if (newState == BluetoothProfile.STATE_CONNECTED) {
			runOnUiThread(new Runnable() {
	               @Override
	               public void run() {
	       			Toast.makeText(context, "Connected!", Toast.LENGTH_SHORT).show();
	               }
	           });
			
        	bluetoothGatt.discoverServices();
        	
        }
    }
 
    @Override
    public void onServicesDiscovered(final BluetoothGatt gatt, final int status) { 
    	super.onServicesDiscovered(gatt, status);
    	
		BluetoothGattService service = bluetoothGatt.getService(BLE_SERVICE_UUID);
		if (service == null) return;
			
		BluetoothGattCharacteristic notifyCharacteristic = service.getCharacteristic(RECEIVE_NOTIFY_CHARACTERISTIC_UUID);

// BluetoothGattCharacteristic indicateCharacteristic = service.getCharacteristic(RECEIVE_INDICATE_CHARACTERISTIC_UUID);

		BluetoothGattDescriptor notifyDescriptor = notifyCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_UUID);

// BluetoothGattDescriptor indicateDescriptor = indicateCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_UUID);

		bluetoothGatt.setCharacteristicNotification(notifyCharacteristic, true);
		notifyDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
	    bluetoothGatt.writeDescriptor(notifyDescriptor);	
    }

	@Override
	public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
		super.onDescriptorWrite(gatt, descriptor, status);

		byte[] msg = {0x01, 0x01};
		descriptor.getCharacteristic().setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
		descriptor.getCharacteristic().setValue(msg);
		bluetoothGatt.writeCharacteristic(descriptor.getCharacteristic());		

		runOnUiThread(new Runnable() {
               @Override
               public void run() {
       			Toast.makeText(context, "Service configured", Toast.LENGTH_SHORT).show();
               }
           });
	}

	@Override
	public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
		super.onCharacteristicWrite(gatt, characteristic, status);
		
		runOnUiThread(new Runnable() {
               @Override
               public void run() {
       			Toast.makeText(context, "Wrote to Characteristic", Toast.LENGTH_SHORT).show();
               }
           });
	}
};

}</code></pre>

administrators

Hey ShellDude!! Sorry for the delay, I've been busy catching up on life things the last couple weeks. After a solid year of CBT/Kickstarter the life stuff really piles up. :)

I was implementing separate BT filters but went back to just rate limiting the data going to the BT LE module over UART to avoid buffer overruns on the module. Try setting the filter using 0x03 just like you would over USB Serial.

Also checkout the new 0.4.5 firmware that was merged in yesterday. You'll have to flash it with arduino, I haven't build a binary yet. It has improvements for the BT LE UART.

Also something to keep in mind is the MCU will send packets out on the last used interface only. So if you sent a command on USB Serial it will only dump back to USB Serial, same goes for BT.

Let me know if that helps and how far you get, I'll be checking the forum more regularly now. I've been trying to find time to build the Android Wear BT CBT app. That should be a fun one!

Derek,

Thank you so much for the response! It's great to see that your project is still active. The convenience alone it adds is second to none... just need to work out a few more kinks.

With 0.4.4 I tried doing just that (setting an 03 filter) but while the filter worked fine when connected to USB I could never get it to return anything over BLE, ensuring that only Serial1 was connected (I noticed the interoperability between Serial and Serial1).

After spending some time reviewing all the sketch code I was left with the impression that it should just be working. I'll check out 0.4.5 today and report back.

Success!

It took some serious debugging but I figured it out. For some reason (Android only maybe?) LE chokes on processing the SerialCommand::logCommand() activeSerial->write(COMMAND_OK) operation. I'm guessing 0xFF is screwing up the stack somehow.

Anyway I replaced

<code>activeSerial->write(COMMAND_OK);</code>

with

<code>if (activeSerial && Serial) activeSerial->write(COMMAND_OK);</code>

in SerialCommand.h's logCommand() function and I can now filter to my heart's content over BTLE. This was the final barrier to wrapping up my proof of concept. Now the real coding begins!

I recall reading you accept bug reports via github... will head over there now.

So it stopped working again. Am I only person having BT filtering issues?

I suspected the 0xff thing was too good to be true and now I can't recreate the scenario where i got it to work.

Made some progress tonight. I'm not sure why but logCommand() is very touchy. If I even reference &Serial1 it stops streaming. If I do not provide a heartbeat, (with the btRateLimit enabled) it won't stream.

If I remove btRateLimit completely and ensure I have a filter that removes the bulk of the noise, it works fine.

https://www.youtube.com/watch?v=zlOBfZTUGQw&feature=youtu.be

administrators

For anyone else watching I committed a change to the repo that improved this and Shell is up and running. I'm still working on improving the BT LE stuff and working on some android connectivity.

Shell, I'm so jealous of your Android install. Totally awesome.

Hi Derek

I have been developing on a Nexus4 - Android 5. I can configure and get info back - for example I can get the system info, Dump the EEPROM, get the status of each Canbus returning. But it seems to intermittently cut out or drop the connection. If i get the CBT to dump the EEPROM, it gets about halfway through maybe (can't tell exactly, but there is no closing brace) and then won't respond to any other commands and sometimes needs a reboot before it shows up on scans again.

I am running the latest commit for the firmware (commit a7e063c)

What does it mean when the Blue light is on Solid? When this happens the CBT stops showing up on scans for devices. After that I need to reboot it sometimes before I can get it to show up again. Doesn't show up in LightBlue either when the blue light is solid.

Cheers

I should add my assumption was that the blue light indicated a bluetooth connection (and that would seem to make sense) However it seems to get stuck on even though no other devices can see it or communicate with it.

I've learned quite a bit about Android gatt over the past two weeks.

The various callbacks are not threadsafe. You want to do as little work as possible in them. Leave the heavy lifting to your UI thread and a handler/runnable.

Another possibility, or additional complication, is the amount of data you are trying to transfer.

Blindly forwarding from a 100kbps or greater can connection to BTLE is akin to pushing a watermelon through a garden hose.

Initially try filtering on one specific register... After you ensure you have lughtweight, synchronized if necessary, gatt callbacks.

Thanks Shelldude. At the moment I haven't connected to a can device, soon though :) I want to get reliable communication between my device and the CBT first. I seem to get intermittent behaviour, sometimes I can do consecutive EEPROM dumps (only using that as an example since it is a relatively large chunk of data) and all data comes through no problem and then other times the connection cuts out and then nothing until a reboot. It does seem like buffer issues between the Micro and BLE112 although at this point I am still looking at my code to ensure I am not doing anything stupid. Have you tried doing an EEPROM dump {0x01, 0x02} with yours and if so do you have any issues?

Cheers
Tom

I don't think Derek implemented throttling on the eeprom dump. Take a look inside SerialCommand.

Ah I see. Ok that makes sense then.

There is actually some rate limiting built into the eeprom dump. btDelay() which incorporates a delay of 20ms every 8 bytes.

Only thing is it doesn't work. As far as i can tell the byteCount is not ever incremented even though there is a 'byteCount++' I tried a bunch of different things but the condition (byteCount>8) is never triggered to cause a delay. I know this for sure because i set the delay to 1000 ms and I certainly did not see any delays happening. I reimplemented in a different way which solves the problem and I can reliably dump the Eeprom data. I want to roll a general solution for all writes and then I'll generate a pull request.

[Derek - So I can assign the byteCount variable (say to 10) and then the condition will trigger (as you would expect) but byteCount++ or byteCount +=1 or byteCount = byteCount + 1 don't appear to be working in that context. This makes no sense. Any ideas on that? I've not done any work with these before so maybe its some quirk I don't know about. I did notice that it is defined both as a class member variable and a global variable.]

PS. Its not that I am obsessed with dumping the eeprom data. Its just its an example of a chunk of data that is far larger than the 20 Byte BLE packet size imposed by BLE spec. If I 'button bash' the channelDebug or even the systemDebug commands I can quickly currupt the serial communication between the micro and the BLE module. I suspect this will also extend to messages from a canbus. The current rate limiting only discards packets that arrive too frequently (Derek am I correct saying that).

Sounds like you're past your initial hurdle.

Code like wind... Wait for no one!

Looks like your connection to CANBus Triple was lost, please wait while we try to reconnect.