🟨 전송
데이터를 전송하려면 serviceUUID와 characteristicUUID가 필요하다.
Service, Characteristic
이 둘은 블루투스 기기가 가지고 있는 기능 같은 것이다
그냥 특성(characteristic)을 그룹으로 모아놓은 것이 Service이다
- Service는 특정 기능이나 데이터를 제공하는 논리적 그룹
- Characteristic은 특정 기능이나 데이터
🔸 BluetoothActivity.java
블루투스를 관리하는 파일에 이렇게 블루투스 전송 함수를 만들어준다.
보내고자 하는 값을 byte 형식으로 변경해서 블루투스 기기(Peripheral)로 전송하게 된다.
// 블루투스 전송 함수
public void sendCommand(String command) {
try {
if (bluetoothGatt != null) {
UUID serviceUUID = UUID.fromString("블루투스 기기의 ServiceUUID");
UUID cmdCharacteristicUUID = UUID.fromString("블루투스 기기의 CharacteristicUUID");
BluetoothGattCharacteristic cmdCharacteristic = bluetoothGatt.getService(serviceUUID)
.getCharacteristic(cmdCharacteristicUUID);
if (cmdCharacteristic != null) {
byte[] commandBytes = command.getBytes();
cmdCharacteristic.setValue(commandBytes);
bluetoothGatt.writeCharacteristic(cmdCharacteristic);
}
}
} catch (Exception e) {
Log.e(TAG, "Data transmission error", e);
finish();
}
}
만약 본인의 serviceUUID와 CharacteristicUUID가 무엇인지 모른다면 1편에서 만들었던 BluetoothGattCallback을 이용하여 쉽게 알아낼 수 있다.
GattCallback 내부의 onServicesDiscovered 메서드를 아래처럼 작성하면 블루투스 기기(Peripheral)와 연결된 후, 해당 peripheral의 service 목록과 특성 목록을 가져올 수 있다.
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "Connected to GATT server.");
// 연결 성공
gatt.discoverServices(); // 서비스 발견
ConnectFragment fragment = new ConnectFragment();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
bluetoothGatt.close(); // 연결 종료 시 GATT 객체 닫기
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "Services discovered.");
// 서비스 목록 가져오기
List<BluetoothGattService> services = gatt.getServices();
for (BluetoothGattService service : services) {
// 서비스 UUID 로그 출력
Log.i(TAG, "Service UUID: " + service.getUuid().toString());
// 특성 목록 가져오기
List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
for (BluetoothGattCharacteristic characteristic : characteristics) {
// 특성 UUID 로그 출력
Log.i(TAG, "Characteristic UUID: " + characteristic.getUuid().toString());
}
}
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
};
이렇게 하면 여러 개의 serviceUUID와 Characteristic이 나올텐데 본인이 필요한 UUID를 사용하면 된다. 잘 모르겠다면 목록을 gpt에게 전달하면서 조금 징징거리면 알려준다ㅎㅎㅎ,,,
🔸 ConnectFragment.java
이제 블루투스 전송을 하고자 하는 파일에서 아래처럼 원하는 값을 전송해주면 완료!
// 블루투스 전송 함수
binding.btnSend.setOnClickListener(v -> {
bluetoothActivity.sendCommand("보내고 싶은 값");
Toast.makeText(getActivity(), "send data", Toast.LENGTH_SHORT);
Log.d(TAG, "send data");
});
🟠 전체 코드
🔸 BluetoothActivity.java
package com.example.gatttest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.Button;
import android.widget.Toast;
import com.example.gatttest.adapter.DeviceAdapter;
import com.example.gatttest.databinding.ActivityBluetoothBinding;
import java.util.ArrayList;
import java.util.UUID;
@SuppressWarnings("ALL")
public class BluetoothActivity extends AppCompatActivity {
private static final String TAG = "BluetoothActivity";
private ActivityBluetoothBinding binding;
private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bluetoothLeScanner;
private ArrayList<BluetoothDevice> discoveredDevicesList = new ArrayList<>();
private DeviceAdapter deviceAdapter;
private BluetoothGatt bluetoothGatt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityBluetoothBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// RecyclerView 설정
RecyclerView recyclerView = binding.recyclerView;
deviceAdapter = new DeviceAdapter(discoveredDevicesList, this::onDeviceClick);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(deviceAdapter);
initBluetooth();
}
private void initBluetooth() {
bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Toast.makeText(this, "Bluetooth가 비활성화되어 있습니다.", Toast.LENGTH_SHORT).show();
return;
}
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
binding.btnScan.setOnClickListener(v -> {
discoveredDevicesList.clear(); // 리스트 초기화
deviceAdapter.notifyDataSetChanged(); // 어댑터에 변경 사항 알림
scanDevice();
});
}
private void scanDevice() {
long SCAN_PERIOD = 10000; // 스캔 시간
bluetoothLeScanner.startScan(scanCallback);
Log.d(TAG, "scan start");
new Handler().postDelayed(() -> {
bluetoothLeScanner.stopScan(scanCallback);
Toast.makeText(this, "스캔 종료", Toast.LENGTH_SHORT).show();
}, SCAN_PERIOD);
}
private final ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
BluetoothDevice device = result.getDevice();
if (device != null && !discoveredDevicesList.contains(device)) {
if (device.getName() != null) {
discoveredDevicesList.add(device);
deviceAdapter.notifyItemInserted(discoveredDevicesList.size() - 1); // 새 장치 추가 시 알림
Log.d(TAG, "Discovered device: " + device.getName());
}
}
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e(TAG, "Scan failed with error code: " + errorCode);
}
};
private void onDeviceClick(BluetoothDevice device) {
connectToDevice(device);
}
private void connectToDevice(BluetoothDevice device) {
bluetoothGatt = device.connectGatt(this, false, gattCallback);
Log.d(TAG, "Connecting to device: " + device.getName());
}
public void sendCommand(String command) {
try {
if (bluetoothGatt != null) {
UUID serviceUUID = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e");
UUID cmdCharacteristicUUID = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e");
BluetoothGattCharacteristic cmdCharacteristic = bluetoothGatt.getService(serviceUUID)
.getCharacteristic(cmdCharacteristicUUID);
if (cmdCharacteristic != null) {
byte[] commandBytes = command.getBytes();
cmdCharacteristic.setValue(commandBytes);
bluetoothGatt.writeCharacteristic(cmdCharacteristic);
}
}
} catch (Exception e) {
Log.e(TAG, "Data transmission error", e);
finish();
}
}
private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i(TAG, "Connected to GATT server.");
// 연결 성공
gatt.discoverServices(); // 서비스 발견
ConnectFragment fragment = new ConnectFragment();
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
bluetoothGatt.close(); // 연결 종료 시 GATT 객체 닫기
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "Services discovered.");
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
};
}
🔸 ConnectFragment.java
package com.example.gatttest;
import android.bluetooth.BluetoothGatt;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.example.gatttest.databinding.FragmentConnectBinding;
public class ConnectFragment extends Fragment {
private final static String TAG = "ConnectFragment";
private FragmentConnectBinding binding;
private BluetoothActivity bluetoothActivity;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentConnectBinding.inflate(inflater, container, false);
initBluetooth();
return binding.getRoot();
}
private void initBluetooth() {
bluetoothActivity = ((BluetoothActivity) getActivity());
binding.btnSend.setOnClickListener(v -> {
bluetoothActivity.sendCommand("hello world");
Toast.makeText(getActivity(), "send data", Toast.LENGTH_SHORT);
Log.d(TAG, "send data");
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
'안드로이드' 카테고리의 다른 글
[JAVA] BluetoothGatt로 BLE 사용하기(1) - 스캔 및 연결 (0) | 2025.01.14 |
---|---|
[안드로이드] Unknown Kotlin JVM target: 21 오류 해결법 (0) | 2025.01.04 |
2023 Droid knights 운영진 후기 (2) | 2023.12.31 |
[Android studio] unexpected end of stream 에러 해결 (0) | 2023.12.18 |
Android 4대 컴포넌트(앱 구성 요소)와 Intent (0) | 2022.09.28 |