低功耗蓝牙:未检测到 RFDuino
Bluetooth Low Energy: RFDuino is not detected
我正在尝试编写一个应用程序以在 Android Phone 和 RFDuino 之间通过 BLE 建立基本通信。 RFDuino 只是做广告,可以被 "BLE Scanner" 应用检测到。
下面是 activity 的代码草案,它应该检测 BLE 设备。
问题是,未检测到任何设备。调试日志显示,方法 "onLeScan" 甚至没有被调用一次...
我 运行 没有想法,如果有任何提示我将不胜感激 :-)
package de.tuhh.et5.serialcommunicator;
import (...)
public class BLEActivity extends AppCompatActivity {
private static final long SCAN_PERIOD = 10000;
private int REQUEST_ENABLE_BT = 1;
public final static String TAG = "BLE_Activity"; // Tag for the LOG
private BluetoothAdapter ble_adapter;
private boolean scanning;
private Handler ble_handler;
private BLE_DeviceListAdapter BLE_DeviceListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
d("on Create");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ble);
TabHost tabHost=(TabHost)findViewById(R.id.tabHost);
tabHost.setup();
TabHost.TabSpec t1=tabHost.newTabSpec("Tab1");
t1.setContent(R.id.tab1_layout);
t1.setIndicator("Devices");
tabHost.addTab(t1);
BLE_DeviceListAdapter = new BLE_DeviceListAdapter();
ListView ble_list = (ListView)findViewById(R.id.BLE_DeviceListView);
ble_list.setAdapter(BLE_DeviceListAdapter);
ble_handler = new Handler();
// get a bluetooth adapter from the bluetooth manager
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
ble_adapter = bluetoothManager.getAdapter();
// Check if BLE is supported by the Android device
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
// Enable bluetooth
if (ble_adapter == null || !ble_adapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
// menu stuff
@Override
public boolean onCreateOptionsMenu(Menu menu) {
d("menu inflates");
// Inflate the menu; this adds items to the action bar if it is present.
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.ble_menu, menu);
if (scanning) {
menu.findItem(R.id.scanning_start).setVisible(false);
} else {
menu.findItem(R.id.scanning_start).setVisible(true);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.scanning_start:
d("scan pressed");
BLE_DeviceListAdapter.clear();
ble_scan(true);
break;
}
return true;
}
private void ble_scan(final boolean en){
d("Scan start");
// stopLeScan and startLeScan are deprecated since Android 5.0, but still work.
// to offer a compatibility down to Android 4.3 the old methods are used
if (en){
ble_handler.postDelayed(new Runnable() {
@Override
public void run() {
d("scan timer finished");
scanning = false;
ble_adapter.stopLeScan(ble_callback);
invalidateOptionsMenu();
}
},SCAN_PERIOD);
scanning = true;
ble_adapter.startLeScan(ble_callback);
}else{
scanning = false;
ble_adapter.stopLeScan(ble_callback);
}
invalidateOptionsMenu();
}
private BluetoothAdapter.LeScanCallback ble_callback = new BluetoothAdapter.LeScanCallback(){
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord){
d("onLeScan");
runOnUiThread(new Runnable(){
@Override
public void run(){
BLE_DeviceListAdapter.addDevice(device);
BLE_DeviceListAdapter.notifyDataSetChanged();
}
});
}
};
// This class lists the elements in an ble device entry as defined in "ble_device_entry.cml"
// It is needed in the BLE_DeviceListAdapter class
private class ViewHolder {
TextView name;
TextView device_id;
TextView device_adress;
}
// #####################################################
// private Class BLE_DeviceListAdapter
//
// Adapter to handle found BLE Devices
// #####################################################
private class BLE_DeviceListAdapter extends BaseAdapter{
private ArrayList<BluetoothDevice> ble_devices; // holds found devices
private LayoutInflater ble_inflater; // layout inflater
public BLE_DeviceListAdapter(){
super();
// Init
ble_devices = new ArrayList<>();
ble_inflater = BLEActivity.this.getLayoutInflater();
}
// method to add a device
public void addDevice(BluetoothDevice ble_device){
// device is added, if it is not already in the list
if (!ble_devices.contains(ble_device)){
ble_devices.add(ble_device);
}
}
// Mandatory, returns device at position ...
@Override
public BluetoothDevice getItem(int position){
return ble_devices.get(position);
}
// clear list..
public void clear(){
ble_devices.clear();
}
// Mandatory: Not needed, therefore just a dummy
@Override
public long getItemId(int i) {
return i;
}
// Mandatory: returns size of list
@Override
public int getCount() {
return ble_devices.size();
}
// Mandatory: creates and returns view with a device entry
@Override
public View getView(int i, View view, ViewGroup viewGroup ){
ViewHolder viewHolder; // create view holder
// if the view does not already exist:
if (view == null){
view = ble_inflater.inflate(R.layout.ble_device_entry, null);// inflate view
viewHolder = new ViewHolder(); // create a ViewHolder
// Connenct the ViewHolder elements to layout elements
viewHolder.name = (TextView)findViewById(R.id.BLE_sName);
viewHolder.device_adress = (TextView)findViewById(R.id.BLE_sAdress);
viewHolder.device_id = (TextView)findViewById(R.id.BLE_sID);
view.setTag(viewHolder); // Add the ViewHolder as a Tag to the view
}else{
// if the view already exists
viewHolder = (ViewHolder) view.getTag(); // get Tag from view to populate ViewHolder
}
// get BLE Device
Log.d("test", "hier so....");
BluetoothDevice device = ble_devices.get(i);
// get device name
final String deviceName = device.getName();
// assign device name to ViewHolder Object
if (deviceName != null && deviceName.length() > 0){
viewHolder.device_id.setText(deviceName);
}else viewHolder.device_id.setText(R.string.unknown_ble_device);
// assign more values
viewHolder.device_adress.setText(device.getAddress());
viewHolder.name.setText(R.string.unknown_ble_device);
d("before return view");
return view;
}
}
// #####################################################
// End Adapter
// #####################################################
public void d(Object msg) {
Log.d(TAG, ">==< " + msg.toString() + " >==<");
}
public void e(Object msg) {
Log.e(TAG, ">==< " + msg.toString() + " >==<");
}
}
扫码我觉得还行。
一些要检查的东西:
您是否debug/log检查选项菜单是否正常工作并且ble_scan()
被调用?
你 debug/log 检查过 startLeScan()
returns true
了吗?
如果未找到设备,则不会调用 onLeScan()
回调。
因为你扫描了 10 秒,RFDuino 应该经常做广告自己 enough/for 足够长的时间,至少一个广告周期总是发生在 10 秒内 window。
所有 Android 版本都需要这些权限:
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
另外Android6.x(Marhsmallow)以后还需要:
android.permission.ACCESS_COARSE_LOCATION
如果您编写的代码仅针对 6.x 及更高版本,您需要使用新的运行时权限模型而不是基于旧清单文件的模型。
在某些设备上 运行 Android 6.x 系统范围 "Location" 设置必须打开。参见:
然后当然 some of the older discussions 可能会有一些有用的提示。
好的,看来我只是搞砸了权限。我没有该位置的运行时权限。我认为如果不排除目标 Android 6.xx ,基于清单的许可就足够了。现在我遇到了视图持有者和其他东西的问题,但那是另一个故事:-)。
无论如何感谢@MarkusKauppinen 的解决方案!
我正在尝试编写一个应用程序以在 Android Phone 和 RFDuino 之间通过 BLE 建立基本通信。 RFDuino 只是做广告,可以被 "BLE Scanner" 应用检测到。
下面是 activity 的代码草案,它应该检测 BLE 设备。
问题是,未检测到任何设备。调试日志显示,方法 "onLeScan" 甚至没有被调用一次...
我 运行 没有想法,如果有任何提示我将不胜感激 :-)
package de.tuhh.et5.serialcommunicator;
import (...)
public class BLEActivity extends AppCompatActivity {
private static final long SCAN_PERIOD = 10000;
private int REQUEST_ENABLE_BT = 1;
public final static String TAG = "BLE_Activity"; // Tag for the LOG
private BluetoothAdapter ble_adapter;
private boolean scanning;
private Handler ble_handler;
private BLE_DeviceListAdapter BLE_DeviceListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
d("on Create");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ble);
TabHost tabHost=(TabHost)findViewById(R.id.tabHost);
tabHost.setup();
TabHost.TabSpec t1=tabHost.newTabSpec("Tab1");
t1.setContent(R.id.tab1_layout);
t1.setIndicator("Devices");
tabHost.addTab(t1);
BLE_DeviceListAdapter = new BLE_DeviceListAdapter();
ListView ble_list = (ListView)findViewById(R.id.BLE_DeviceListView);
ble_list.setAdapter(BLE_DeviceListAdapter);
ble_handler = new Handler();
// get a bluetooth adapter from the bluetooth manager
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
ble_adapter = bluetoothManager.getAdapter();
// Check if BLE is supported by the Android device
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
// Enable bluetooth
if (ble_adapter == null || !ble_adapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
// menu stuff
@Override
public boolean onCreateOptionsMenu(Menu menu) {
d("menu inflates");
// Inflate the menu; this adds items to the action bar if it is present.
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.ble_menu, menu);
if (scanning) {
menu.findItem(R.id.scanning_start).setVisible(false);
} else {
menu.findItem(R.id.scanning_start).setVisible(true);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.scanning_start:
d("scan pressed");
BLE_DeviceListAdapter.clear();
ble_scan(true);
break;
}
return true;
}
private void ble_scan(final boolean en){
d("Scan start");
// stopLeScan and startLeScan are deprecated since Android 5.0, but still work.
// to offer a compatibility down to Android 4.3 the old methods are used
if (en){
ble_handler.postDelayed(new Runnable() {
@Override
public void run() {
d("scan timer finished");
scanning = false;
ble_adapter.stopLeScan(ble_callback);
invalidateOptionsMenu();
}
},SCAN_PERIOD);
scanning = true;
ble_adapter.startLeScan(ble_callback);
}else{
scanning = false;
ble_adapter.stopLeScan(ble_callback);
}
invalidateOptionsMenu();
}
private BluetoothAdapter.LeScanCallback ble_callback = new BluetoothAdapter.LeScanCallback(){
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord){
d("onLeScan");
runOnUiThread(new Runnable(){
@Override
public void run(){
BLE_DeviceListAdapter.addDevice(device);
BLE_DeviceListAdapter.notifyDataSetChanged();
}
});
}
};
// This class lists the elements in an ble device entry as defined in "ble_device_entry.cml"
// It is needed in the BLE_DeviceListAdapter class
private class ViewHolder {
TextView name;
TextView device_id;
TextView device_adress;
}
// #####################################################
// private Class BLE_DeviceListAdapter
//
// Adapter to handle found BLE Devices
// #####################################################
private class BLE_DeviceListAdapter extends BaseAdapter{
private ArrayList<BluetoothDevice> ble_devices; // holds found devices
private LayoutInflater ble_inflater; // layout inflater
public BLE_DeviceListAdapter(){
super();
// Init
ble_devices = new ArrayList<>();
ble_inflater = BLEActivity.this.getLayoutInflater();
}
// method to add a device
public void addDevice(BluetoothDevice ble_device){
// device is added, if it is not already in the list
if (!ble_devices.contains(ble_device)){
ble_devices.add(ble_device);
}
}
// Mandatory, returns device at position ...
@Override
public BluetoothDevice getItem(int position){
return ble_devices.get(position);
}
// clear list..
public void clear(){
ble_devices.clear();
}
// Mandatory: Not needed, therefore just a dummy
@Override
public long getItemId(int i) {
return i;
}
// Mandatory: returns size of list
@Override
public int getCount() {
return ble_devices.size();
}
// Mandatory: creates and returns view with a device entry
@Override
public View getView(int i, View view, ViewGroup viewGroup ){
ViewHolder viewHolder; // create view holder
// if the view does not already exist:
if (view == null){
view = ble_inflater.inflate(R.layout.ble_device_entry, null);// inflate view
viewHolder = new ViewHolder(); // create a ViewHolder
// Connenct the ViewHolder elements to layout elements
viewHolder.name = (TextView)findViewById(R.id.BLE_sName);
viewHolder.device_adress = (TextView)findViewById(R.id.BLE_sAdress);
viewHolder.device_id = (TextView)findViewById(R.id.BLE_sID);
view.setTag(viewHolder); // Add the ViewHolder as a Tag to the view
}else{
// if the view already exists
viewHolder = (ViewHolder) view.getTag(); // get Tag from view to populate ViewHolder
}
// get BLE Device
Log.d("test", "hier so....");
BluetoothDevice device = ble_devices.get(i);
// get device name
final String deviceName = device.getName();
// assign device name to ViewHolder Object
if (deviceName != null && deviceName.length() > 0){
viewHolder.device_id.setText(deviceName);
}else viewHolder.device_id.setText(R.string.unknown_ble_device);
// assign more values
viewHolder.device_adress.setText(device.getAddress());
viewHolder.name.setText(R.string.unknown_ble_device);
d("before return view");
return view;
}
}
// #####################################################
// End Adapter
// #####################################################
public void d(Object msg) {
Log.d(TAG, ">==< " + msg.toString() + " >==<");
}
public void e(Object msg) {
Log.e(TAG, ">==< " + msg.toString() + " >==<");
}
}
扫码我觉得还行。
一些要检查的东西:
您是否debug/log检查选项菜单是否正常工作并且ble_scan()
被调用?
你 debug/log 检查过 startLeScan()
returns true
了吗?
如果未找到设备,则不会调用 onLeScan()
回调。
因为你扫描了 10 秒,RFDuino 应该经常做广告自己 enough/for 足够长的时间,至少一个广告周期总是发生在 10 秒内 window。
所有 Android 版本都需要这些权限:
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
另外Android6.x(Marhsmallow)以后还需要:
android.permission.ACCESS_COARSE_LOCATION
如果您编写的代码仅针对 6.x 及更高版本,您需要使用新的运行时权限模型而不是基于旧清单文件的模型。
在某些设备上 运行 Android 6.x 系统范围 "Location" 设置必须打开。参见:
然后当然 some of the older discussions 可能会有一些有用的提示。
好的,看来我只是搞砸了权限。我没有该位置的运行时权限。我认为如果不排除目标 Android 6.xx ,基于清单的许可就足够了。现在我遇到了视图持有者和其他东西的问题,但那是另一个故事:-)。
无论如何感谢@MarkusKauppinen 的解决方案!