У меня есть сервер Bluetooth, который использует bleno и возвращает клиенту список доступных сетей Wi-Fi. Код для 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, прежде чем продолжить чтение данных. Насколько мне известно, это отражено в источнике, который вы видите выше. Я тут как-то потерялся.
Любая идея высоко ценится.
Большое спасибо