关键字:esp、调试、串口、74880
时间:2019年8月

前言

ESP-01开发板是基于ESP8266芯片的8针脚微型开发板。

调试口

虽然ESP-01开发板总共只有8个针脚,但有两串口可以使用,Uart0和Uart1。Uart0有TX和RX两个针脚,Uart1只有TX针脚。调试信息通过Uart0输出。

串口设置

ESP-01开发板串口默认使用的波特率是74880,是一个特殊的波特率,通常串口工具都无法选择这个波特率。

推荐工具1

miniterm.py,命令行指定波特率。

root@debian:~# chmod 777 miniterm.py
root@debian:~# miniterm.py /dev/ttyUSB0 74880

miniterm.py内容见参考文献

推荐工具2

minicom,需要手动修改配置文件。

root@debian:~# vim /etc/minicom/minirc.dfl
# Machine-generated file - use "minimum -s" to change parameter.
pu port          /dev/ttyUSB0
pu baud rate     74880
pu bits          8
pu parity        N
pu stopbits      1
:wq!
root@debian:~# minicom

参考

https://blog.csdn.net/ustccw/article/details/85797692

注意事项

1、用户权限需要添加android.permission.INTERNET
2、不能在主线程上发送http请求,因为主线程是UI控制线程

manifests配置权限

<manifest>
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

子线程代码

@Override
public void onClick(View view) {
	new Thread() {
		public void run() {
			try {
				URL url = new URL("https://www.baidu.com");
				HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
				httpURLConnection.setRequestMethod("GET");
				httpURLConnection.setDoInput(true);
				httpURLConnection.setDoOutput(false);
				httpURLConnection.setConnectTimeout(30000);
				httpURLConnection.setReadTimeout(30000);
				httpURLConnection.connect();
				BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), "utf-8"));
				String line;
				StringBuilder stringBuilder = new StringBuilder();
				String response;
				while ((line = bufferedReader.readLine()) != null) {
					stringBuilder.append(line);
				}
				response = stringBuilder.toString();
				bufferedReader.close();
				httpURLConnection.disconnect();
		   } catch (Exception e) {
			   e.printStackTrace();
		   }
		}
	}.start();
}
...

关键字:android、ble、蓝牙、gatt
时间:2019年06月

前言

阅读本文前,需要熟悉Activity的开发,例如:onCreate()、onResume()等。

基本概念

经典蓝牙

通迅前需要手机在设置中填写密钥配对

通过建立socket连接发送数据

BLE

4.0以上版本均为BLE

BLE以Characteristic作为数据传输通道获取数据

每个蓝牙设备可提供多个service

每个service由多个Characteristic组成

每个Characteristic又有多个Descriptor

BLE开发的关键点

通过manager获取adapter,BluetoothManager.getAdapter()

通过adapter获取scannser,BluetoothAdapter.getBluetoothLeScanner()

启动scanner产生回调,BluetoothLeScanner.startScan( ScanCallback )

通过回调函数中的result获取device,ScanCallback.onScanResult( ScanResult ) { ScanResult.getDevice() }

通过device获取gatt,BluetoothDevice.connectGatt( BluetoothGattCallback )

连接成功的回调中启动discoverServices, BluetoothGattCallback.onConnectionStateChange( BluetoothGatt ){ BluetoothGatt.discoverServices() }

发现服务回调中获取service,BluetoothGattCallback.onServicesDiscovered( BluetoothGatt ){ gatt.getServices() }

通过service获取characteristic,BluetoothGattService.getCharacteristics()

修改characteristic获取数据方式 BluetoothGatt.setCharacteristicNotification( characteristic, true | false )

发送读characteristic数据请求,BluetoothGatt.readCharacteristic( characteristic )

在读到数据的回调中获取数据,BluetoothGattCallback.onCharacteristicRead( characteristic ) { characteristic.getValue() }

manifests设置权限

manifests文件中,需要增加蓝牙权限和定位权限。蓝牙权限需要解释,定位权限如果没有,会扫描不到任何蓝牙设备。

<manifest xmlns:...
	...
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    ...
</manifests>

获取定位授权

在高版本Android系统中,定位需要用户授权才能使用。如果用户未授权,仍然扫描不到任何蓝牙设备。获取用户授权有两种方法。

方法一

在Android设置中找到定位服务,找到App,允许获取位置。

方法二

在程序中弹出对话框,提示用户是否允许。

protected void onResume() {
	...
	if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
	ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                    1);
	}
	...
}

检查手机是否支持蓝牙

protected void onCreate(Bundle savedInstanceState) {
	...
	if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
		Toast.makeText(this, "不支持BLE", Toast.LENGTH_SHORT).show();
		finish();
	}
	...
}

获取BluetoothManager实例

protected void onCreate(Bundle savedInstanceState) {
	...
	final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
	...

通过BluetoothManager实例获取BluetoothAdapter实例

BluetoothAdapter变量定义为成员变量,便于其他地方使用。

...
private BluetoothAdapter mBluetoothAdapter;
...

protected void onCreate(Bundle savedInstanceState) {
	...
	mBluetoothAdapter = bluetoothManager.getAdapter();
	if (mBluetoothAdapter == null) {
		Toast.makeText(this, "不支持蓝牙", Toast.LENGTH_SHORT).show();
		finish();
	}
	...
} 

判断蓝牙是否开启

检查蓝牙是否打开,没打开则弹框提示用户打开。

protected void onResume() {
	...
	if (!mBluetoothAdapter.isEnabled()) {
		Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
		startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
	}
	...
}

扫描附近的蓝牙设备

扫描需要用到BluetoothLeScanner类,该类实例通过mBluetoothAdapter.getBluetoothLeScanner()获取。
部分文章提到使用LeScanCallback类和startLeScan()方法扫描,该方法已经弃用,不建议使用。

BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
scanner.startScan(new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            BluetoothDevice device = result.getDevice();
            if (!mDevices.contains(device)) {
                mDevices.add(device);
                // mAdapter是和界面上ListView绑定的ArrayAdapter,具体看完整代码。
                mAdapter.add(device.getAddress());
                mAdapter.notifyDataSetChanged();
            }
        }
    }

小结

通过上面几步已经能够扫描到周围的蓝牙设备了,还可以通过ScanResult的getRssi()方法获取设备的信号强度,以及使用BluetoothDevice的getName()方法获取设备名称,但getName()有可能为null,使用时请注意。getName()为空非常不便于用户辨识设备,个人感觉是否为空可能与信号强度有关系,可以改变距离进行尝试。

扫描获取到device对象已经可以进行连接了,但device对象无法序列化,传递很不方便,可以使用device.getAddress()代替device进行参数传递。

其他Activity获取device对象

地址可以作为设备唯一标识进行参数传递,再还原成BluetoothDevice对象使用。

BluetoothManager manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter adapter = manager.getAdapter();
BluetoothDevice device = adapter.getRemoteDevice("43:41:3C:F2:85:EE");

获取BluetoothGatt并进行连接

BluetoothDevice device = adapter.getRemoteDevice("43:41:3C:F2:85:EE");
mBluetoothGatt = device.connectGatt(this, false, mGattCallback); // mGattCallback说明见下文

回调函数

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            Log.d(TAG, "onConnectionStateChange()");
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.i(TAG, "Connected to GATT server.");
                Log.i(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.i(TAG, "Disconnected from GATT server.");
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                List services = gatt.getServices();
                for (BluetoothGattService service : services) {
                    Log.d(TAG, "service: " + service.getUuid().toString());
                    for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {
                        Log.d(TAG, "characteristic: " + characteristic.getUuid().toString());
                        if (characteristic.getUuid().toString().equals("00002a29-0000-1000-8000-00805f9b34fb")) {
                            if ((characteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_READ)>0) {
                                Log.d(TAG, "PROPERTY_READ");
                                mBluetoothGatt.setCharacteristicNotification(characteristic, false);
                                mBluetoothGatt.readCharacteristic(characteristic);
                            }
                            if ((characteristic.getProperties() | BluetoothGattCharacteristic.PROPERTY_NOTIFY)>0) {
                                Log.d(TAG, "PROPERTY_NOTIFY");
                                mBluetoothGatt.setCharacteristicNotification(characteristic, true);
                            }
                        }
                        for (BluetoothGattDescriptor descriptor : characteristic.getDescriptors()) {
                            Log.d(TAG, "descriptor: " + descriptor.getUuid().toString());
                        }
                    }
                }
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            Log.d(TAG, "onCharacteristicRead()");
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.d(TAG, characteristic.getUuid().toString() + " " + new String(characteristic.getValue()));
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            Log.d(TAG, "onCharacteristicChanged()");
        }
    };

结束语

在研究Android的BLE使用时,第一个手机蓝牙硬件存在问题,导致代码未按预期执行,安装其他蓝牙调试App现象相同,后来更换手机后问题解决。