我有一个蓝牙服务器,它使用 bleno 并向客户端返回可用 Wifi 网络的列表。的代码readCharacteristic
看起来基本上是这样的:
class ReadCharacteristic extends bleno.Characteristic {
constructor(uuid, name, action) {
super({
uuid: uuid,
properties: ["read"],
value: null,
descriptors: [
new bleno.Descriptor({
uuid: "2901",
value: name
})
]
});
this.actionFunction = action;
}
onReadRequest(offset, callback) {
console.log("Offset: " + offset);
if(offset === 0) {
const result = this.actionFunction();
result.then(value => {
this.actionFunctionResult = value;
const data = new Buffer.from(value).slice(0,bleno.mtu);
console.log("onReadRequest: " + data.toString('utf-8'));
callback(this.RESULT_SUCCESS, data);
}, err => {
console.log("onReadRequest error: " + err);
callback(this.RESULT_UNLIKELY_ERROR);
}).catch( err => {
console.log("onReadRequest error: " + err);
callback(this.RESULT_UNLIKELY_ERROR);
});
}
else {
let data = new Buffer.from(this.actionFunctionResult);
if(offset > data.length) {
callback(this.RESULT_INVALID_OFFSET, null);
}
data = data.slice(offset+1, offset+bleno.mtu);
console.log(data.toString('utf-8'));
callback(this.RESULT_SUCCESS, data);
}
}
}
(我已经尝试过 data = data.slice(offset+1, offset+bleno.mtu);
并且喜欢这个data = data.slice(offset+1);
)
客户端是读取此特性的 Android 应用程序。
用于阅读的 Android 部分如下所示:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.requestMtu(256);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
mFancyShowCaseView.show();
gatt.close();
scanForBluetoothDevices();
}
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "Can't set mtu to: " + mtu);
} else {
Log.i(TAG, "Connected to GATT server. MTU: " + mtu);
Log.i(TAG, "Attempting to start service discovery:" +
mWifiProvisioningService.discoverServices());
}
}
@Override
// New services discovered
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "ACTION_GATT_SERVICES_DISCOVERED");
BluetoothGattService wifiProvisioningService = gatt.getService(WIFI_PROVISIONING_SERVICE_UUID);
BluetoothGattCharacteristic currentConnectedWifiCharacteristic = wifiProvisioningService.getCharacteristic(WIFI_ID_UUID);
BluetoothGattCharacteristic availableWifiCharacteristic = wifiProvisioningService.getCharacteristic(WIFI_SCAN_UUID);
// Only read the first characteristic and add the 2nd one to a list as we have to wait
// for the read return before we read the 2nd one.
if (!gatt.readCharacteristic(currentConnectedWifiCharacteristic)) {
Log.e(TAG, "Error while reading current connected wifi name.");
}
readCharacteristics.add(availableWifiCharacteristic);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
// Result of a characteristic read operation
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
UUID characteristicUUID = characteristic.getUuid();
if (WIFI_ID_UUID.equals(characteristicUUID)) {
Log.d(TAG, "HEUREKA we found the current wifi name: " + new String(characteristic.getValue()));
final String currentWifiName = new String(characteristic.getValue());
runOnUiThread(new Runnable() {
@Override
public void run() {
((TextView) findViewById(R.id.currentWifiTxt)).setText(currentWifiName);
findViewById(R.id.currentWifiTxtProgress).setVisibility(View.GONE);
}
});
} else if (WIFI_SCAN_UUID.equals(characteristicUUID)) {
Log.d(TAG, "HEUREKA we found the wifi list: " + new String(characteristic.getValue()));
List<String> wifiListArrayList = new ArrayList<>();
try {
JSONObject wifiListRoot = new JSONObject(characteristic.getStringValue(0));
JSONArray wifiListJson = wifiListRoot.getJSONArray("list");
for (int i = 0; i < wifiListJson.length(); i++) {
wifiListArrayList.add(wifiListJson.get(i).toString());
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
return;
}
final String[] wifiList = new String[wifiListArrayList.size()];
wifiListArrayList.toArray(wifiList);
runOnUiThread(new Runnable() {
@Override
public void run() {
((ListView) findViewById(R.id.availableWifiList)).setAdapter(new ArrayAdapter<String>(mContext, R.layout.wifi_name_list_item, wifiList));
findViewById(R.id.currentWifiTxtProgress).setVisibility(View.GONE);
}
});
} else {
Log.i(TAG, "Unexpected Gatt vale: " + new String(characteristic.getValue()));
}
if (readCharacteristics.size() > 0) {
BluetoothGattCharacteristic readCharacteristic = readCharacteristics.get(0);
if (!gatt.readCharacteristic(readCharacteristic)) {
Log.e(TAG, "Error while writing descriptor for connected wifi");
}
readCharacteristics.remove(readCharacteristic);
}
}
}
MTU 调整为 256 字节。我在阅读列表时反映在服务器上。调用本身工作正常并返回列表,但如果列表包含超过600个字节,则在 Android 上只有 600 个字节可用。我以某种方式确定 JS 服务器发送了所有数据,但由于某种原因,Android 客户端仅接收或缓存 600 个字节,这似乎不正确。
我发现了这篇文章:Android BLE - 外设 | onCharacteristicRead 返回错误值或其一部分(但重复)
还有这个: Android BLE - 如何以块的形式读取大特征值(使用偏移量)?
但两者都没有解决我的问题。我知道在开始下一次读取之前我需要等待一次读取返回,并且在继续读取数据之前我需要等到 MTU 被写入。据我所知,这反映在您在上面看到的来源中。我有点迷失在这里。
任何想法都是高度赞赏的。
非常感谢