webOS Article/2. Luna Service 활용하기

Bluetooth 사용하기 2 - 채팅앱 만들기

cstleb 2021. 10. 3. 11:40

지난 포스팅에선 Luna-Service를 이용하여 라즈베피라이 4의 블루투스 기능을 설정하고 사용하는 법에 대해 소개해 드렸습니다. 이번 시간에는 스마트 폰 블루투스 앱과 블루투스 기능을 이용하여 메세지를 주고받는 application을 만들어 보겠습니다.

 

이 포스팅은 webOS OSE 개발자 사이트 : Bluetooth GuideLS2 API Bluetooth2를 참고하여 작성되었습니다.

 

 

before you begin


1. web app 생성하기

웹 앱을 개발하기 위해서 먼저 아래의 명령어를 입력해 웹 어플리케이션 템플릿을 생성합니다.

ares-generate -t <template> <App Directory>

이에 대해 자세한 내용은 이전 포스팅인 웹 앱(web application) 개발하기를 참고하시기 바랍니다.

 

2. permission 추가하기

shell에서 아래의 명령어를 입력해 블루투스 연결에 필요한 permission을 찾아봅니다.

# ls-monitor -i com.webos.service.bluetooth2

위의 두 퍼미션을 1번에서 생성된 앱 템플릿의 appinfo.json의 requiredPermissions에 아래와 같이 추가합니다.

"requiredPermissions": [
    "time.query",
    "activity.operation",
    "bluetooth.operation",
    "bluetooth.query"
  ]

 

채팅창 UI 구성하기


index.html 파일에 다음과 같이 코드를 작성합니다.

<!DOCTYPE html>
<html>
    <head>
        <title>Chatting App</title>
        <link rel="stylesheet" href="./css/index.css">
        <script src="./js/index.js"></script>
    </head>
    <body>
        <div id="main">
            <div id="chat-input">
                <button onclick="bluetoothConnection()">Bluetooth 연결</button>
              <input type="text" id="test" placeholder="메시지를 입력해주세요.">
              <button onclick="send()">전송</button>
            </div>
            <div id="chat">
              <!-- 채팅 메시지 영역 -->
            </div>
          </div>
    </body>
</html>

먼저 Bluetooth 연결 버튼을 만들고, 버튼을 누르면 js폴더의 index.js파일의 bluetoothConnection()함수가 실행되도록 합니다. 

메시지 입력란을 구성하고 전송 버튼을 만듭니다. 전송 버튼을 누르면 같은 js 파일의 send()함수가 실행됩니다.

그 아래에 채팅 메시지가 나타날 영역을 구성합니다.

구성된 채팅창 UI 화면은 아래 그림과 같이 나타납니다.

구성된 채팅 화면

 

css폴더의 index.css 파일 코드는 아래 더보기를 통해 확인할 수 있습니다.

더보기

 

#main {
  width: 100%;
  height: 480px;
}

#chat-input {
  background-color: #E2E2E2;
  padding: 10px;
  margin-bottom: 20px;
}

#test {
  width: 200px;
}

#chat {
  background-color: #E2E2E2;
  height: 100%;
  overflow-y: auto;
  padding-top: 20px;
  padding-left: 20px;
}

 

채팅 서비스 개발하기


1. 블루투스 연결하기

아래의 코드는 이전 포스팅: Bluetooth 사용하기의 1~5번에 해당되는 내용입니다.

function setAdapterState(){
  var url = 'luna://com.webos.service.bluetooth2/adapter/setState';
  var params = {
    "powered":true,
    "name":"cosmos",
    "discoverable":true
  };
  bridge.onservicecallback = (res)=>{console.log('setAdapterState', res);};
  bridge.call(url, JSON.stringify(params));
  console.log("after-bridge.call");
}

function setTrustedDevice(){
  var url = 'luna://com.webos.service.bluetooth2/device/setState';
  var params = {
    "address":"<모바일 기기의 MAC ADDRESS>", 
    "trusted":true
  };
  bridge.onservicecallback = (res)=>{console.log('setTrustedDevice', res);};
  bridge.call(url, JSON.stringify(params));
  console.log("after-bridge.call");
}

동일한 method를 공유하는 파라미터들을 한 함수에 작성합니다.

그에 따라 블루투스 켜기, device 이름 설정하기, Bluetooth가 검색이 가능하도록 설정하기를 한 함수 setAdapterState에 작성하고, 각각에 해당하는 파라미터들을 params 변수에 한꺼번에 넣어줍니다.

 

2번에 해당하는 trusted device로 설정하기의 경우 독립적인 메소드를 사용하기 때문에 따로 작성합니다.

address에는 모바일 기기의 MAC address를 입력합니다.

 

2. SPP channel 만들기

이제 7번~10번에 해당되는 내용을 함수로 작성할 차례입니다.

let channelId;

function createChannel(){
  var url = 'luna://com.webos.service.bluetooth2/spp/createChannel';
  var params = {
    "name":"service1", "uuid":"00001101-0000-1000-8000-00805f9b34fb", "subscribe":true
  };
  bridge.onservicecallback = channelHandler;
  bridge.call(url, JSON.stringify(params));
}

function channelHandler(res) {
    console.log("createChannel", res);
    let response = JSON.parse(res);
    if(response.channelId) {
        channelId = response.channelId;
        var url = 'luna://com.webos.service.bluetooth2/spp/readData';
        var params = {
            "channelId": channelId,
            "subscribe": true
        };
        bridge.onservicecallback = messageHandler;
        bridge.call(url, JSON.stringify(params));
    }
}

SPP 채널을 만들기 위해 createChannel 함수를 생성해 필요한 파라미터를 넣어주고, channelID를 받아 수신된 메시지를 읽어오기 위해 channelHandler 함수를 생성했습니다. 이 때 channelID는 createChannel을 실행했을 때 return값으로 받아오는 값이기 때문에 다음 channelHandler 함수에 넣기 위해 전역변수로 선언합니다. 

메시지를 읽어오면 해당 메시지를 화면에 띄우기 위해 messageHandler 함수를 onservicecallback으로 지정합니다.

 

3. 메시지 주고받기

function messageHandler(msg) {
    console.log("messageHandler", msg);
    let message = JSON.parse(msg);
    let text = atob(message.data);
    chat.innerHTML += `</br>Bluetooth: ${text}`;
    console.log("Recieved data: ", text);
}

/* 메시지 전송 함수 */
function send() {
    // 입력되어있는 데이터 가져오기
    var message = document.getElementById('test').value;
    
    // 가져왔으니 데이터 빈칸으로 변경
    document.getElementById('test').value = '';
  
    // 내가 전송할 메시지 클라이언트에게 표시
    document.getElementById('chat').innerHTML += `<br/>${name}: ${message}`;
  
    // 서버로 message 이벤트 전달 + 데이터와 함께
    if(!channelId) {
        console.log("Channel ID has not defined.");
    }
    var url = 'luna://com.webos.service.bluetooth2/spp/writeData';
    var params = {
        "channelId": channelId,
        "data": btoa(message)
        //"data": "aGVsbG8NCg0K"
    };
    bridge.onservicecallback = (res)=>{console.log(res);};
    bridge.call(url, JSON.stringify(params));
}

메시지를 전송하기 위해 send()함수를 구성하고, 수신된 메시지를 화면에 표시하기 위해 messageHandler()함수를 구성합니다.

send 함수에서 spp/writedata 메소드를 사용하기 위해 해당 url과 파라미터들을 작성해줍니다. 

이 내용은 이전 포스팅의 13. 메시지 보내기에 해당됩니다.

 

이 때 데이터의 메시지 값이 base64 format으로 인코딩되어 전송되어야 하기 때문에 btoa() 함수를 사용합니다. 반대로 전송받은 메시지를 화면에 출력할 때는 atob()함수를 사용해 그 값을 다시 디코딩합니다.

* btoa(), atob() 함수는 한글을 지원하지 않습니다.

 

4. 어플리케이션 실행 화면

개발한 블루투스 어플리케이션을 webOS로 패키징하여 설치합니다. 어플리케이션 패키징 및 설치 과정은 이전 포스팅인 webOS에서 동작하는 웹앱(web application) 개발하기를 참고해주시기 바랍니다.

 

휴대폰과 웹 어플리케이션에서 각각 메시지를 보내면 다음과 같은 결과가 출력됩니다.

휴대폰 채팅 화면
웹 어플리케이션 채팅 화면

 

 

서비스의 전체 코드(js 파일)는 아래의 더보기를 참고해주시기 바랍니다.

더보기
let name = 'tester1';

var bridge = new WebOSServiceBridge();

function bluetoothConnection(){
  setAdapterState();
  setTrustedDevice();
  createChannel();
}

function setAdapterState(){
  var url = 'luna://com.webos.service.bluetooth2/adapter/setState';
  var params = {
    "powered":true,
    "name":"cosmos",
    "discoverable":true
  };
  bridge.onservicecallback = (res)=>{console.log('setAdapterState', res);};
  bridge.call(url, JSON.stringify(params));
  console.log("after-bridge.call");
}

function setTrustedDevice(){
  var url = 'luna://com.webos.service.bluetooth2/device/setState';
  var params = {
    "address":"<모바일 기기의 MAC ADDRESS>", 
    "trusted":true
  };
  bridge.onservicecallback = (res)=>{console.log('setTrustedDevice', res);};
  bridge.call(url, JSON.stringify(params));
  console.log("after-bridge.call");
}

let channelId;

function createChannel(){
  var url = 'luna://com.webos.service.bluetooth2/spp/createChannel';
  var params = {
    "name":"service1", "uuid":"00001101-0000-1000-8000-00805f9b34fb", "subscribe":true
  };
  bridge.onservicecallback = channelHandler;
  bridge.call(url, JSON.stringify(params));
}

function channelHandler(res) {
    console.log("createChannel", res);
    let response = JSON.parse(res);
    if(response.channelId) {
        channelId = response.channelId;
        var url = 'luna://com.webos.service.bluetooth2/spp/readData';
        var params = {
            "channelId": channelId,
            "subscribe": true
        };
        bridge.onservicecallback = messageHandler;
        bridge.call(url, JSON.stringify(params));
    }
}

function messageHandler(msg) {
    console.log("messageHandler", msg);
    let message = JSON.parse(msg);
    let text = atob(message.data);
    chat.innerHTML += `</br>Bluetooth: ${text}`;
    console.log("Recieved data: ", text);
}

/* 메시지 전송 함수 */
function send() {
    // 입력되어있는 데이터 가져오기
    var message = document.getElementById('test').value;
    
    // 가져왔으니 데이터 빈칸으로 변경
    document.getElementById('test').value = '';
  
    // 내가 전송할 메시지 클라이언트에게 표시
    document.getElementById('chat').innerHTML += `<br/>${name}: ${message}`;
  
    // 서버로 message 이벤트 전달 + 데이터와 함께
    if(!channelId) {
        console.log("Channel ID has not defined.");
    }
    var url = 'luna://com.webos.service.bluetooth2/spp/writeData';
    var params = {
        "channelId": channelId,
        "data": btoa(message)
        //"data": "aGVsbG8NCg0K"
    };
    bridge.onservicecallback = (res)=>{console.log(res);};
    bridge.call(url, JSON.stringify(params));
}

'webOS Article > 2. Luna Service 활용하기' 카테고리의 다른 글

Bluetooth 사용하기 1  (0) 2021.09.14
TTS 활용하여 음성 출력하기  (0) 2021.08.24
media 불러오기  (0) 2021.08.14
Database 사용하기 (DB8)  (0) 2021.08.14
Luna Service API 호출하기(by Enact App)  (0) 2021.07.24