Notice
Recent Posts
Recent Comments
Link
투케이2K
927. (Android/Java) [간단 소스] 블루투스 GATT 서버 Open 활성 및 클라이언트 접속 상태 확인 소스 코드 - Bluetooth GATT Server 본문
Android
927. (Android/Java) [간단 소스] 블루투스 GATT 서버 Open 활성 및 클라이언트 접속 상태 확인 소스 코드 - Bluetooth GATT Server
투케이2K 2025. 1. 2. 19:06[개발 환경 설정]
개발 툴 : AndroidStudio
개발 언어 : Java / Kotlin
[소스 코드]
// --------------------------------------------------------------------------------------
[개발 및 테스트 환경]
// --------------------------------------------------------------------------------------
- 언어 : Java / Kotlin
- 개발 툴 : AndroidStudio
- 기술 구분 : Bluetooth / GATT / Server
// --------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------
[사전) 필요 권한 설정 관련]
// --------------------------------------------------------------------------------------
/**
* // ---------------------------------------------------------------
* 1. 필요 퍼미션 : 필요 퍼미션 권한 : 위치 및 GPS 권한 , 블루투스 권한 - SCAN , ADVERTISE , CONNECT
*
* // TODO [공통]
* <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
* <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
*
* <uses-permission android:name="android.permission.BLUETOOTH"/>
* <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
* <uses-feature android:name="android.hardware.bluetooth_le" />
*
* // TODO [안드로이드 12 이상 : S 버전]
* <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
* <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
* <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
* // ---------------------------------------------------------------
* 2. 참고 :
*
* - 서버 및 클라이언트 GATT 서비스 설정 시 UUID 값이 일치해야합니다
* - 클라이언트가 Read 요청 시 서버는 응답 확인 현재 날짜 및 시간 값을 반환합니다
* - 클라이언트가 Write 요청 시 setValue 로 지정 된 값 그대로 반환합니다
* - 클라이언트는 원격 서버와 연결 완료 후 실시간 Notify 알림을 받을 수 있게 설정합니다
* // ---------------------------------------------------------------
* */
// --------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------
[소스 코드]
// --------------------------------------------------------------------------------------
// --------------------------------------------------------------
// TODO [전역 변수 선언]
// --------------------------------------------------------------
private static String ACTIVITY_NAME = "C_Bluetooth_Gatt_Server_Module";
private static Context mMainCtx; // [컨텍스트]
// TODO [UUIDs for the GATT service and characteristic] : 서버 및 클라이언트 UUID 값이 일치해야합니다
private static final UUID SERVICE_UUID = UUID.fromString("0000180A-0000-1000-8000-00805F9B34FB");
private static final UUID CHARACTERISTIC_UUID = UUID.fromString("00002A29-0000-1000-8000-00805F9B34FB");
private static BluetoothAdapter bluetoothAdapter; // [블루투스 어댑터]
static BluetoothGattServer mServer = null; // [Gatt 서버]
static ArrayList clientMacList = new ArrayList(); // [연결 된 클라이언트 리스트 목록]
// --------------------------------------------------------------
// TODO [클래스 생성 Context 지정 및 싱글톤 패턴 정의]
// --------------------------------------------------------------
public void setContext(Context ctx) {
mMainCtx = ctx;
}
public static C_Bluetooth_Gatt_Server_Module getInstance() {
return C_Bluetooth_Gatt_Server_Module.LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final C_Bluetooth_Gatt_Server_Module INSTANCE = new C_Bluetooth_Gatt_Server_Module();
}
// --------------------------------------------------------------
// TODO [블루투스 어댑터 지정 및 GATT 서버 활성 소스 코드]
// --------------------------------------------------------------
// [블루투스 지원 가능 기기 및 활성 상태 확인]
BluetoothManager bluetoothManager = (BluetoothManager) mMainCtx.getSystemService(BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
if (bluetoothAdapter == null) {
String connectErrorMessage = "[Error] :: 블루투스 기능 지원 여부 확인 필요 (Bluetooth is not supported on this device)";
return;
} else {
if (bluetoothAdapter.isEnabled() == false) {
String connectErrorMessage = "[Error] :: 블루투스 기능 활성 상태 확인 필요 (Bluetooth isEnabled False)";
return;
} else {
mServer = bluetoothManager.openGattServer(mMainCtx, new BluetoothGattServerCallback() {
// --------------------------------------------------
// TODO [원격 장치가 연결되거나 연결 해제되었을 때를 나타내는 콜백입니다]
// --------------------------------------------------
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
if (mMainCtx != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // TODO [안드로이드 12 이상]
if (ActivityCompat.checkSelfPermission(mMainCtx, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
}
S_Log._W_(ACTIVITY_NAME + " :: onConnectionStateChange :: 원격 장치 연결 및 해제 상태 체크 콜백 동작", new String[]{
"Device Name :: " + String.valueOf(device.getName()),
"Device Mac :: " + String.valueOf(device.getAddress()),
"Device Status :: " + String.valueOf(newState)
});
if (newState == BluetoothAdapter.STATE_CONNECTED) {
S_Log.w("BLE_GATT", "Device connected :: " + device.getAddress());
// [클라이언트 리스트 배열에 추가]
if (clientMacList != null && clientMacList.contains(device.getAddress()) == false){
clientMacList.add(String.valueOf(device.getAddress()));
S_Log.w("BLE_GATT", "Add List :: " + clientMacList);
}
} else if (newState == BluetoothAdapter.STATE_DISCONNECTED) {
S_Log.e("BLE_GATT", "Device disconnected :: " + device.getAddress());
// [포함 되어 있는 경우]
if (clientMacList != null && clientMacList.contains(device.getAddress()) == true){
int idx = clientMacList.indexOf(device.getAddress());
if (idx >= 0){
clientMacList.remove(idx); // [클라이언트 삭제 수행]
S_Log.e("BLE_GATT", "Remove List :: " + clientMacList);
}
}
}
}
// --------------------------------------------------
// TODO [원격 클라이언트가 로컬 특성을 읽으려고 요청했습니다.]
// --------------------------------------------------
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
S_Log.w("BLE_Read", "MAC : " + String.valueOf(device.getAddress()) + " / " + "OFFSET : " + String.valueOf(offset));
if (mMainCtx != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // TODO [안드로이드 12 이상]
if (ActivityCompat.checkSelfPermission(mMainCtx, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
}
// TODO [로그 출력 수행]
S_Log._W_(ACTIVITY_NAME + " :: onCharacteristicReadRequest :: 클라이언트 >> 서버 [읽기] 요청", new String[]{
"Device Name :: " + String.valueOf(device.getName()),
"Device Mac :: " + String.valueOf(device.getAddress()),
"Device UUID :: " + String.valueOf(characteristic.getUuid())
});
// TODO [읽기 요청에 대한 응답 반환]
if (CHARACTERISTIC_UUID.equals(characteristic.getUuid())){ // [CHARACTERISTIC_UUID 가 일치하는 경우]
String msg = "Success Read Response - " + C_Util.getFormNowDate("yyy-MM-dd HH:mm:ss SSS E요일");
byte resData [] = msg.getBytes(StandardCharsets.UTF_8);
S_Log._W_(ACTIVITY_NAME + " :: onCharacteristicReadRequest :: 서버 >> 클라이언트 [읽기] 응답", new String[]{
"Device Name :: " + String.valueOf(device.getName()),
"Device Mac :: " + String.valueOf(device.getAddress()),
"Device UUID :: " + String.valueOf(characteristic.getUuid()),
"Server >> Client :: " + String.valueOf(msg)
});
// TODO [응답 반환] : [BluetoothGatt.GATT_SUCCESS]
mServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, resData);
}
else {
S_Log.e("BLE_Read", "UUID Not Equals (CHARACTERISTIC_UUID = "+CHARACTERISTIC_UUID+" / characteristic.getUuid = "+String.valueOf(characteristic.getUuid()) + ")");
}
}
// --------------------------------------------------
// TODO [원격 클라이언트가 로컬 특성에 대한 쓰기를 요청했습니다.]
// --------------------------------------------------
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
S_Log.w("BLE_Write", "MAC : " + String.valueOf(device.getAddress()) + " / " + "OFFSET : " + String.valueOf(offset));
if (mMainCtx != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { // TODO [안드로이드 12 이상]
if (ActivityCompat.checkSelfPermission(mMainCtx, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
}
// TODO [클라이언트가 setValue 로 보낸 데이터 확인]
String receivedValue = "";
try {
receivedValue = new String(value, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
// TODO [로그 출력 수행]
S_Log._W_(ACTIVITY_NAME + " :: onCharacteristicWriteRequest :: 클라이언트 >> 서버 [쓰기] 요청", new String[]{
"Device Name :: " + String.valueOf(device.getName()),
"Device Mac :: " + String.valueOf(device.getAddress()),
"Device UUID :: " + String.valueOf(characteristic.getUuid()),
"Client >> Server :: " + String.valueOf(receivedValue)
});
// TODO [쓰기 요청에 대한 응답 반환]
if (CHARACTERISTIC_UUID.equals(characteristic.getUuid())){ // [CHARACTERISTIC_UUID 가 일치하는 경우]
byte resData [] = null;
String msg = "";
if (C_Util.stringNotNull(receivedValue)){ // [디바이스가 보낸 값이 널이 아닌 경우]
msg = receivedValue; // [요청 값 그대로 반환]
resData = msg.getBytes(StandardCharsets.UTF_8);
}
else {
msg = "Default"; // [기본 값]
resData = msg.getBytes(StandardCharsets.UTF_8);
}
S_Log._W_(ACTIVITY_NAME + " :: onCharacteristicReadRequest :: 서버 >> 클라이언트 [쓰기] 응답", new String[]{
"Device Name :: " + String.valueOf(device.getName()),
"Device Mac :: " + String.valueOf(device.getAddress()),
"Device UUID :: " + String.valueOf(characteristic.getUuid()),
"Server >> Client :: " + String.valueOf(msg)
});
// TODO [응답 반환] : [BluetoothGatt.GATT_SUCCESS]
mServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, resData);
}
else {
S_Log.e("BLE_Write", "UUID Not Equals (CHARACTERISTIC_UUID = "+CHARACTERISTIC_UUID+" / characteristic.getUuid = "+String.valueOf(characteristic.getUuid()) + ")");
}
}
});
// --------------------------------------------------
// TODO [GATT service 추가] : 읽기, 쓰기 허용 설정
// --------------------------------------------------
BluetoothGattService service = new BluetoothGattService(SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(
CHARACTERISTIC_UUID,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE
);
service.addCharacteristic(characteristic);
mServer.addService(service);
}
}
// --------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------
[참고 사이트]
// --------------------------------------------------------------------------------------
https://blog.naver.com/kkh0977/223704324947
https://blog.naver.com/kkh0977/223704480147
https://blog.naver.com/kkh0977/223704485666
// --------------------------------------------------------------------------------------
반응형
'Android' 카테고리의 다른 글
Comments