uni-app을 사용하여 저전력 블루투스(BLE) 방식의 열전사 프린터와 연결하고 데이터를 송신하는 과정은 일련의 단계로 구성된다. 아래는 실제 하드웨어와 통신하기 위한 핵심 흐름과 코드 구현 예시이다.
주요 처리 순서:
- 블루투스 어댑터 초기화 (
openBluetoothAdapter) – 실패 시 상태 변경 이벤트 리스너 등록 - 주변 BLE 장치 검색 시작 (
startBluetoothDevicesDiscovery) - 새 장치 발견 시 콜백 처리 (
onBluetoothDeviceFound) – 특정 이름 또는 서비스로 필터링
<4>장치와 연결 생성 (
createBLEConnection) – 동시에 검색 중단
<5>연결된 장치의 서비스 목록 조회 (getBLEDeviceServices)
<6>특정 서비스 내 특성값(Characteristic) 가져오기 (getBLEDeviceCharacteristics)
<7>데이터 수신 감지 리스너 설정 (onBLECharacteristicValueChange)
<8>프린터로 바이너리 명령 전송 (writeBLECharacteristicValue)
블루투스 초기화 및 자동 검색
initBluetooth() {
uni.openBluetoothAdapter({
success: () => {
console.log('블루투스 어댑터 초기화 성공');
this.startDeviceSearch();
},
fail: (err) => {
console.error('블루투스 초기화 실패:', err);
if (err.errCode === 10001) {
uni.showToast({ title: '블루투스를 활성화하세요', icon: 'none' });
}
// 상태 변화 감지를 통해 나중에 재시도 가능
uni.onBluetoothAdapterStateChange((state) => {
if (state.available) this.initBluetooth();
});
}
});
}
주변 기기 탐색 시작
startDeviceSearch() {
uni.startBluetoothDevicesDiscovery({
success: () => {
console.log('기기 탐색 시작');
this.listenForDevices();
},
fail: (err) => {
console.error('탐색 시작 실패:', err);
}
});
}
기기 발견 이벤트 리스닝
listenForDevices() {
uni.onBluetoothDeviceFound((result) => {
result.devices.forEach(device => {
if (device.name && device.name.includes('Printer')) { // 장치명 기반 필터링
const deviceId = device.deviceId;
uni.setStorageSync('PRINTER_DEVICE_ID', deviceId);
console.log(`매칭된 프린터: ${device.name} (${deviceId})`);
this.stopDeviceSearch(); // 성능 최적화를 위해 검색 중단
this.connectToDevice(deviceId); // 연결 시도
}
});
});
}
검색 중단 및 연결 설정
stopDeviceSearch() {
uni.stopBluetoothDevicesDiscovery({
success: () => console.log('기기 검색 중단 완료'),
fail: (err) => console.warn('검색 중단 실패:', err)
});
}
connectToDevice(deviceId) {
uni.createBLEConnection({
deviceId,
success: () => {
console.log('BLE 연결 성공');
this.fetchServiceList(deviceId);
},
fail: (err) => {
console.error('연결 실패:', err);
}
});
}
서비스 및 특성값 조회
연결 후에는 디바이스가 제공하는 서비스 UUID 목록을 가져와야 하며, 이 작업은 비동기적이므로 지연 처리가 필요하다.
fetchServiceList(deviceId) {
setTimeout(() => { // 안정적인 응답을 위해 딜레이 추가
uni.getBLEDeviceServices({
deviceId,
success: (res) => {
const services = res.services.filter(s => s.uuid.includes('FFE0'));
if (services.length > 0) {
const serviceId = services[0].uuid;
uni.setStorageSync('SERVICE_UUID', serviceId);
this.fetchCharacteristics(deviceId, serviceId);
}
},
fail: (err) => {
console.error('서비스 조회 실패:', err);
}
});
}, 1000);
}
fetchCharacteristics(deviceId, serviceId) {
uni.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success: (res) => {
const writableChar = res.characteristics.find(c => c.properties.write);
if (writableChar) {
uni.setStorageSync('CHARACTERISTIC_UUID', writableChar.uuid);
uni.showToast({ title: '프린터 준비 완료', icon: 'success' });
}
},
fail: (err) => {
console.error('특성값 조회 실패:', err);
}
});
}
프린터로 데이터 전송
실제 인쇄 명령은 16진수 형식의 ArrayBuffer로 전송되어야 한다. 아래는 간단한 출력 예시:
sendPrintCommand() {
const dataArrays = [
new Uint8Array([0x1B, 0x40]), // 프린터 초기화
new Uint8Array([0x50, 0x72, 0x69, 0x6E, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74]), // "Print Test"
new Uint8Array([0x0A, 0x0A, 0x0A]) // 줄 바꿈 + 용지 절단
];
const deviceId = uni.getStorageSync('PRINTER_DEVICE_ID');
const serviceId = uni.getStorageSync('SERVICE_UUID');
const charId = uni.getStorageSync('CHARACTERISTIC_UUID');
dataArrays.forEach((data, index) => {
const buffer = new ArrayBuffer(data.length);
const view = new Uint8Array(buffer);
view.set(data);
uni.writeBLECharacteristicValue({
deviceId,
serviceId,
characteristicId: charId,
value: buffer,
success: () => {
console.log(`${index + 1}번째 명령 전송 성공`);
},
fail: (err) => {
console.error('데이터 전송 실패:', err);
this.handleErrorCode(err.errCode);
}
});
});
}
에러 코드 처리
handleErrorCode(code) {
const errorMessages = {
10000: '블루투스 어댑터 미초기화',
10001: '블루투스 어댑터 비활성화',
10002: '장치를 찾을 수 없음',
10003: '연결 실패',
10004: '서비스 없음',
10005: '특성값 없음',
10006: '연결 끊김'
};
const message = errorMessages[code] || '알 수 없는 오류';
uni.showToast({ title: message, icon: 'none' });
if ([10000, 10001].includes(code)) {
this.initBluetooth(); // 재시도 트리거
}
}
페이지 로드 시 최초로 initBluetooth()를 호출하면 전체 흐름이 시작되며, 사용자가 인쇄 요청을 할 때마다 sendPrintCommand()를 실행하면 된다. 필요한 스토리지 키들은 사전에 정의하고, 실제 장치 사양에 맞게 UUID와 바이너리 포맷을 조정해야 한다.