显示附近蓝牙设备
This commit is contained in:
@@ -230,6 +230,8 @@ dependencies {
|
|||||||
implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0'
|
implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0'
|
||||||
//蓝牙通讯
|
//蓝牙通讯
|
||||||
implementation 'no.nordicsemi.android:ble:2.11.0'
|
implementation 'no.nordicsemi.android:ble:2.11.0'
|
||||||
|
//espressif官方sdk,用于esp固件的扫描,配网等。
|
||||||
|
implementation 'com.github.espressif:esp-idf-provisioning-android:lib-2.2.3'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import com.ckkj.water.index.entity.WaterDeviceEntity;
|
|||||||
import com.ckkj.water.network.BaseBean;
|
import com.ckkj.water.network.BaseBean;
|
||||||
import com.ckkj.water.utils.manager.ActivityManager;
|
import com.ckkj.water.utils.manager.ActivityManager;
|
||||||
import com.ckkj.water.utils.manager.ThemeManager;
|
import com.ckkj.water.utils.manager.ThemeManager;
|
||||||
|
import com.ckkj.water.utils.manager.WaterDeviceManager;
|
||||||
import com.ckkj.water.utils.show.ToastUtil;
|
import com.ckkj.water.utils.show.ToastUtil;
|
||||||
import com.gyf.immersionbar.ImmersionBar;
|
import com.gyf.immersionbar.ImmersionBar;
|
||||||
import com.maning.mlkitscanner.scan.MNScanManager;
|
import com.maning.mlkitscanner.scan.MNScanManager;
|
||||||
@@ -48,12 +49,14 @@ import com.maning.mlkitscanner.scan.callback.act.MNScanCallback;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import no.nordicsemi.android.ble.observer.ConnectionObserver;
|
||||||
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
|
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
|
||||||
import no.nordicsemi.android.support.v18.scanner.ScanCallback;
|
import no.nordicsemi.android.support.v18.scanner.ScanCallback;
|
||||||
import no.nordicsemi.android.support.v18.scanner.ScanFilter;
|
import no.nordicsemi.android.support.v18.scanner.ScanFilter;
|
||||||
@@ -80,6 +83,7 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
|
|||||||
private BleScanDeviceAdapter bleAdapter;
|
private BleScanDeviceAdapter bleAdapter;
|
||||||
private List<WaterDeviceEntity> mDeviceList = new ArrayList<>();
|
private List<WaterDeviceEntity> mDeviceList = new ArrayList<>();
|
||||||
private Set<String> discoveredMacSet = new HashSet<>();
|
private Set<String> discoveredMacSet = new HashSet<>();
|
||||||
|
private WaterDeviceManager waterDeviceManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DevicePresenter createPresenter() {
|
public DevicePresenter createPresenter() {
|
||||||
@@ -189,8 +193,160 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
scanner.startScan(null, settings, mScanCallback);
|
scanner.startScan(null, settings, mScanCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止 BLE 扫描
|
||||||
|
*/
|
||||||
|
private void stopBleScan() {
|
||||||
|
if (mScanCallback != null) {
|
||||||
|
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
|
||||||
|
scanner.stopScan(mScanCallback);
|
||||||
|
Log.e("TEST", "停止扫描");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接蓝牙设备
|
||||||
|
*/
|
||||||
|
private void connectToDevice(String deviceAddress, String deviceName) {
|
||||||
|
if (waterDeviceManager != null) {
|
||||||
|
// 断开之前的连接
|
||||||
|
waterDeviceManager.disconnect().enqueue();
|
||||||
|
waterDeviceManager.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据地址获取 BluetoothDevice
|
||||||
|
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);
|
||||||
|
if (device == null) {
|
||||||
|
ToastUtil.showShort(mContext, "设备无效");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
waterDeviceManager = new WaterDeviceManager(mContext);
|
||||||
|
|
||||||
|
// 设置数据回调
|
||||||
|
waterDeviceManager.setOnDataReceivedListener(bytes -> {
|
||||||
|
Log.e("TEST", "设备响应: " + Arrays.toString(bytes));
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
// TODO: 处理设备返回的数据,比如 WiFi 配置响应
|
||||||
|
handleDeviceResponse(bytes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 连接设备
|
||||||
|
waterDeviceManager.connect(device)
|
||||||
|
.retry(3, 100) // 重试3次,间隔100ms
|
||||||
|
.done(bluetoothDevice -> {
|
||||||
|
Log.e("TEST", "连接成功: " + deviceName);
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
ToastUtil.showShort(mContext, "连接成功: " + deviceName);
|
||||||
|
// TODO: 连接成功后,发送 WiFi 配置
|
||||||
|
// sendWifiConfig("SSID", "PASSWORD");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.fail((bluetoothDevice, status) -> {
|
||||||
|
Log.e("TEST", "连接失败: " + status);
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
ToastUtil.showShort(mContext, "连接失败,请重试");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.enqueue();
|
||||||
|
|
||||||
|
waterDeviceManager.setConnectionObserver(new ConnectionObserver() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceConnecting(@NonNull BluetoothDevice device) {
|
||||||
|
Log.e("TEST", "onDeviceConnecting");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceConnected(@NonNull BluetoothDevice device) {
|
||||||
|
Log.e("TEST", "onDeviceConnected");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceFailedToConnect(@NonNull BluetoothDevice device, int reason) {
|
||||||
|
Log.e("TEST", "onDeviceFailedToConnect");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceReady(@NonNull BluetoothDevice device) {
|
||||||
|
Log.e("TEST", "onDeviceReady");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDisconnecting(@NonNull BluetoothDevice device) {
|
||||||
|
Log.e("TEST", "onDeviceDisconnecting");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceDisconnected(
|
||||||
|
@NonNull BluetoothDevice device,
|
||||||
|
int reason) {
|
||||||
|
|
||||||
|
Log.e("TEST", "onDeviceDisconnected reason=" + reason);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送 WiFi 配置到设备
|
||||||
|
*/
|
||||||
|
public void sendWifiConfig(String ssid, String password) {
|
||||||
|
if (waterDeviceManager == null || !waterDeviceManager.isBlConnected()) {
|
||||||
|
Log.e("TEST", "设备未连接,无法发送WiFi配置");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 根据设备协议组装数据
|
||||||
|
// 示例:假设协议是 [header(1) + ssid_len(1) + ssid(n) + pwd_len(1) + pwd(m)]
|
||||||
|
byte[] ssidBytes = ssid.getBytes();
|
||||||
|
byte[] pwdBytes = password.getBytes();
|
||||||
|
|
||||||
|
byte[] data = new byte[2 + ssidBytes.length + 2 + pwdBytes.length];
|
||||||
|
int index = 0;
|
||||||
|
data[index++] = 0x01; // header
|
||||||
|
data[index++] = (byte) ssidBytes.length;
|
||||||
|
System.arraycopy(ssidBytes, 0, data, index, ssidBytes.length);
|
||||||
|
index += ssidBytes.length;
|
||||||
|
data[index++] = (byte) pwdBytes.length;
|
||||||
|
System.arraycopy(pwdBytes, 0, data, index, pwdBytes.length);
|
||||||
|
|
||||||
|
waterDeviceManager.send(data);
|
||||||
|
Log.e("TEST", "发送WiFi配置: SSID=" + ssid + ", PWD=" + password);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理设备响应数据
|
||||||
|
*/
|
||||||
|
private void handleDeviceResponse(byte[] data) {
|
||||||
|
if (data == null || data.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 根据设备协议解析响应数据
|
||||||
|
// 示例:假设响应格式是 [type(1) + status(1) + ...]
|
||||||
|
Log.e("TEST", "处理设备响应: " + Arrays.toString(data));
|
||||||
|
|
||||||
|
if (data.length >= 2) {
|
||||||
|
byte type = data[0];
|
||||||
|
byte status = data[1];
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 0x01: // WiFi配置响应
|
||||||
|
if (status == 0x00) {
|
||||||
|
ToastUtil.showShort(mContext, "WiFi配置成功");
|
||||||
|
// TODO: 下一步操作
|
||||||
|
} else {
|
||||||
|
ToastUtil.showShort(mContext, "WiFi配置失败,错误码: " + status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.e("TEST", "未知响应类型: " + type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,9 +416,18 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
|
|||||||
bleAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
|
bleAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
|
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
|
||||||
ToastUtil.showShort(mContext, TextUtils.isEmpty(mDeviceList.get(position).getDeviceName()) ?
|
WaterDeviceEntity device = mDeviceList.get(position);
|
||||||
"未知设备" : mDeviceList.get(position).getDeviceName());
|
String deviceName = TextUtils.isEmpty(device.getDeviceName()) ?
|
||||||
//TODO 准备连接设备,方案未定
|
"未知设备" : device.getDeviceName();
|
||||||
|
String deviceAddress = device.getMacAddress();
|
||||||
|
|
||||||
|
ToastUtil.showShort(mContext, "正在连接: " + deviceName);
|
||||||
|
|
||||||
|
// 停止扫描
|
||||||
|
stopBleScan();
|
||||||
|
|
||||||
|
// 连接设备
|
||||||
|
connectToDevice(deviceAddress, deviceName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -433,9 +598,13 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
|
|||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
if (mScanCallback != null) {
|
// 停止扫描
|
||||||
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
|
stopBleScan();
|
||||||
scanner.stopScan(mScanCallback);
|
// 断开蓝牙连接
|
||||||
|
if (waterDeviceManager != null) {
|
||||||
|
waterDeviceManager.disconnect().enqueue();
|
||||||
|
waterDeviceManager.close();
|
||||||
|
waterDeviceManager = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.ckkj.water.AppConfig;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@@ -21,11 +23,20 @@ import no.nordicsemi.android.ble.BleManager;
|
|||||||
public class WaterDeviceManager extends BleManager {
|
public class WaterDeviceManager extends BleManager {
|
||||||
private BluetoothGattCharacteristic writeCharacteristic;
|
private BluetoothGattCharacteristic writeCharacteristic;
|
||||||
private BluetoothGattCharacteristic notifyCharacteristic;
|
private BluetoothGattCharacteristic notifyCharacteristic;
|
||||||
|
private OnDataReceivedListener dataListener;
|
||||||
|
|
||||||
|
public interface OnDataReceivedListener {
|
||||||
|
void onDataReceived(byte[] data);
|
||||||
|
}
|
||||||
|
|
||||||
public WaterDeviceManager(@NonNull Context context) {
|
public WaterDeviceManager(@NonNull Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOnDataReceivedListener(OnDataReceivedListener listener) {
|
||||||
|
this.dataListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected BleManagerGattCallback getGattCallback() {
|
protected BleManagerGattCallback getGattCallback() {
|
||||||
@@ -39,35 +50,88 @@ public class WaterDeviceManager extends BleManager {
|
|||||||
|
|
||||||
setNotificationCallback(notifyCharacteristic)
|
setNotificationCallback(notifyCharacteristic)
|
||||||
.with((device, data) -> {
|
.with((device, data) -> {
|
||||||
|
|
||||||
byte[] bytes = data.getValue();
|
byte[] bytes = data.getValue();
|
||||||
|
Log.e("TEST", "收到数据: " + Arrays.toString(bytes));
|
||||||
|
|
||||||
Log.e("TEST", "收到数据: "
|
if (dataListener != null) {
|
||||||
+ Arrays.toString(bytes));
|
dataListener.onDataReceived(bytes);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
enableNotifications(notifyCharacteristic)
|
enableNotifications(notifyCharacteristic)
|
||||||
.enqueue();
|
.enqueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public boolean isRequiredServiceSupported(@NonNull BluetoothGatt gatt) {
|
||||||
|
//
|
||||||
|
// BluetoothGattService service =
|
||||||
|
// gatt.getService(UUID.fromString(
|
||||||
|
// AppConfig.BLUETOOTH_DEVICE_UUID));
|
||||||
|
//
|
||||||
|
// if (service == null) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// writeCharacteristic =
|
||||||
|
// service.getCharacteristic(UUID.fromString(
|
||||||
|
// AppConfig.BLUETOOTH_DEVICE_UUID));
|
||||||
|
//
|
||||||
|
// notifyCharacteristic =
|
||||||
|
// service.getCharacteristic(UUID.fromString(
|
||||||
|
// AppConfig.BLUETOOTH_DEVICE_UUID));
|
||||||
|
//
|
||||||
|
// return writeCharacteristic != null
|
||||||
|
// && notifyCharacteristic != null;
|
||||||
|
// }
|
||||||
@Override
|
@Override
|
||||||
public boolean isRequiredServiceSupported(@NonNull BluetoothGatt gatt) {
|
public boolean isRequiredServiceSupported(
|
||||||
|
@NonNull BluetoothGatt gatt) {
|
||||||
|
|
||||||
BluetoothGattService service =
|
for (BluetoothGattService service :
|
||||||
gatt.getService(UUID.fromString(
|
gatt.getServices()) {
|
||||||
"0000ffff-0000-1000-8000-00805f9b34fb"));
|
|
||||||
|
|
||||||
if (service == null) {
|
Log.e("TEST",
|
||||||
return false;
|
"Service=" + service.getUuid());
|
||||||
|
|
||||||
|
for (BluetoothGattCharacteristic c :
|
||||||
|
service.getCharacteristics()) {
|
||||||
|
|
||||||
|
int properties = c.getProperties();
|
||||||
|
|
||||||
|
Log.e("TEST",
|
||||||
|
"Characteristic="
|
||||||
|
+ c.getUuid()
|
||||||
|
+ " props="
|
||||||
|
+ properties);
|
||||||
|
|
||||||
|
if ((properties &
|
||||||
|
BluetoothGattCharacteristic.PROPERTY_WRITE)
|
||||||
|
!= 0) {
|
||||||
|
|
||||||
|
writeCharacteristic = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((properties &
|
||||||
|
BluetoothGattCharacteristic.PROPERTY_NOTIFY)
|
||||||
|
!= 0) {
|
||||||
|
|
||||||
|
notifyCharacteristic = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeCharacteristic =
|
Log.e("TEST",
|
||||||
service.getCharacteristic(UUID.fromString(
|
"write="
|
||||||
"0000ff01-0000-1000-8000-00805f9b34fb"));
|
+ (writeCharacteristic != null
|
||||||
|
? writeCharacteristic.getUuid()
|
||||||
|
: "null"));
|
||||||
|
|
||||||
notifyCharacteristic =
|
Log.e("TEST",
|
||||||
service.getCharacteristic(UUID.fromString(
|
"notify="
|
||||||
"0000ff02-0000-1000-8000-00805f9b34fb"));
|
+ (notifyCharacteristic != null
|
||||||
|
? notifyCharacteristic.getUuid()
|
||||||
|
: "null"));
|
||||||
|
|
||||||
return writeCharacteristic != null
|
return writeCharacteristic != null
|
||||||
&& notifyCharacteristic != null;
|
&& notifyCharacteristic != null;
|
||||||
@@ -83,10 +147,23 @@ public class WaterDeviceManager extends BleManager {
|
|||||||
public void send(byte[] data) {
|
public void send(byte[] data) {
|
||||||
|
|
||||||
if (writeCharacteristic == null) {
|
if (writeCharacteristic == null) {
|
||||||
|
Log.e("TEST", "send: Write characteristic is null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeCharacteristic(writeCharacteristic, data)
|
writeCharacteristic(writeCharacteristic, data)
|
||||||
.enqueue();
|
.enqueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBlConnected() {
|
||||||
|
// 通过反射或状态检查来判断是否已连接
|
||||||
|
// BleManager 没有直接的 isConnected 方法,需要检查内部状态
|
||||||
|
try {
|
||||||
|
// 检查 writeCharacteristic 是否可用(连接后会初始化)
|
||||||
|
return writeCharacteristic != null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user