"오로라 - XBEE 모듈을 이용한 OFF GRID 네트워크 구축"의 두 판 사이의 차이

MIE capstone
이동: 둘러보기, 검색
(Android UI)
(Bluetooth (Android))
 
(사용자 2명의 중간 판 2개는 보이지 않습니다)
229번째 줄: 229번째 줄:
 
:::현재 프로젝트에서는 안드로이드-라즈베리파이간 블루투스 연결 체크, set-parameter command result 체크, discovery result 체크, 받은 메시지 체크 등 5개의 otto bus 객체를 사용하고 있다.  
 
:::현재 프로젝트에서는 안드로이드-라즈베리파이간 블루투스 연결 체크, set-parameter command result 체크, discovery result 체크, 받은 메시지 체크 등 5개의 otto bus 객체를 사용하고 있다.  
 
:::하지만 이렇게 많은 싱글톤 객체를 사용하는 것이 문제를 해결하는 좋은 방법인 것은 아니다. 프로젝트 마감이 얼마 남지 않은 상황에서 목표에 도달하기 위해 궁여지책으로 사용하게 된 것인데, 추후에 플랫폼 한계를 극복하기 위해 Xamarin으로 어플리케이션을 다시 작성하면서 이 부분에 대한 설계를 다시 할 생각이다.
 
:::하지만 이렇게 많은 싱글톤 객체를 사용하는 것이 문제를 해결하는 좋은 방법인 것은 아니다. 프로젝트 마감이 얼마 남지 않은 상황에서 목표에 도달하기 위해 궁여지책으로 사용하게 된 것인데, 추후에 플랫폼 한계를 극복하기 위해 Xamarin으로 어플리케이션을 다시 작성하면서 이 부분에 대한 설계를 다시 할 생각이다.
 +
 +
 +
 +
::* 세부 인터페이스 설정 - 타임아웃 프로세스, '설정'기능 등.
 +
 +
 +
:::블루투스 장치 검색, 채널 탐색 등 시간이 소요되는 작업에 대해 progress 액션을 띄우며 사용자가 작업과 충돌될 수 있는 다른 동작을 하는 것을 방지하였다.
 +
 +
:::[[파일:1.png|200픽셀]] [[파일:탐색.jpg|200픽셀]]
 +
 +
 +
:::푸시알림 기능을 켜고 끌 수 있는 기능이 있어야 한다는 의견이 있어서 네번째 탭 '옵션'탭을 추가했다. 옵션탭에선 해당 어플리케이션에서 푸시알림, 진동, 소리알림을 켜고 끌 수 있으며 차단한 목록을 보여주는 창을 가지고 있다.
 +
:::또한 채널 내 다른 유저를 찾는 작업인 'Discover'의 시간을 사용자가 원하는대로 설정하는 기능, Database의 채팅 메시지를 어느정도 시간주기로 자동 삭제할 것인지를 설정할 수 있도록 구현할 예정이다.
 +
 +
:::[[파일:옵션2.png|200픽셀]] [[파일:블랙.png|200픽셀]]
  
 
=== Bluetooth (Android) ===
 
=== Bluetooth (Android) ===
246번째 줄: 261번째 줄:
  
 
:간단한 플로우 차트. 최초로 앱 구동시 모바일 기기에 블루투스 모듈이 지원되는지 여부를 체크한다. 블루투스 모듈이 지원되고 모듈이 켜져 있으면 바로 장치를 검색하고 연결할 수 있는 탭으로 이동한다. 페어링 여부를 확인하고 필요하다면 페어링 과정을 거치고 연결을 수행한다.
 
:간단한 플로우 차트. 최초로 앱 구동시 모바일 기기에 블루투스 모듈이 지원되는지 여부를 체크한다. 블루투스 모듈이 지원되고 모듈이 켜져 있으면 바로 장치를 검색하고 연결할 수 있는 탭으로 이동한다. 페어링 여부를 확인하고 필요하다면 페어링 과정을 거치고 연결을 수행한다.
 +
블루투스 client 의 socket type 은 RFCOMM 단의 SPP(Serial Port Profile) 프로파일 형태이다.
 +
 +
- SPP 의  UUID : 00001101-0000-1000-8000-00805F9B34FB
 +
 +
  
  
263번째 줄: 283번째 줄:
  
 
:Olora 디바이스에 적용되는 프로토콜에 맞는 데이터 패킷(full packet 일 경우, 1024 bytes)을 연결되어 있는 블루투스를 통해 전송하고 받는다. 최초 연결 시 ID(identification) 과 HP(Host Preamible) 을 보내 주고 Discovery 부분의 패킷헤더를 꾸며 각각 전송하고 Xbee 가 보내온 send result 패킷을 받고 상대를 확인하고 채팅가능한 상태로 판단한다.
 
:Olora 디바이스에 적용되는 프로토콜에 맞는 데이터 패킷(full packet 일 경우, 1024 bytes)을 연결되어 있는 블루투스를 통해 전송하고 받는다. 최초 연결 시 ID(identification) 과 HP(Host Preamible) 을 보내 주고 Discovery 부분의 패킷헤더를 꾸며 각각 전송하고 Xbee 가 보내온 send result 패킷을 받고 상대를 확인하고 채팅가능한 상태로 판단한다.
 +
안드로이드 상에서 String 타입의 BD ADRESS 와 XBEE MAC ADRESS 를 bytes 로 변환, command 등 모든 정보를 패킷헤더에 맞게 꾸밀 수 있는 method 를 작성하였다.
 +
패킷 몸체 에서 사용자의 데이터 영역의 마지막의 의미로 End Of Text 0x03 을 채워놓아 Xbee 가 데이터의 끝이라고 인지할 수 있게 하였다.
 +
 +
 +
 +
*블루투스 데이터 전송시 생긴 issue
 +
 +
:안드로이드 -> 라즈베리 파이 블루투스 통신으로 패킷을 전송할 시에 기존에 약속된 프로토콜 규약대로 데이터가 가지 않는 상황이 발생함을 깨달았다.
 +
 +
그 이유를 디버깅한 결과, 미리 정의된 1024 bytes 가 아닌 실제로는 1008 or 1020 bytes 가 전송되고 이에 따른 오류가 발생하였다.
 +
 +
더 고찰한 결과로는 bytes 가 소실되지는 않았고 1024 bytes 를 한번에 보내지 않고 끊어서 보내고 있다는 것을 확인하였다.
 +
 +
이는 1024 bytes 를 보내지 않는 경우일 때만 다음 번 전송되어오는 패킷 중 이전에 보내지지 않은 패킷만큼을 더해 온전한 1024 bytes 를 읽어들여 해결하였다.
  
 
=== Bluetooth Daemon (RPi) ===
 
=== Bluetooth Daemon (RPi) ===

2018년 6월 16일 (토) 12:00 기준 최신판

프로젝트

O'LoRa - POrtable LOng RAnge Off Grid Mesh-Network Router

팀 소개

  • 서울시립대학교 **공학과 권**
  • 서울시립대학교 **공학과 김**
  • 서울시립대학교 **공학과 이**
  • 서울시립대학교 **공학과 이**

프로젝트 개요

"Everywhere, Everyone, We are the one."

프로젝트 요약

OloraFlowchart2.png

  • User 는 위 구성도에 보이는 바와 같이 RF 모듈 + 라즈베리파이 (Terminal device) 하나와 모바일 폰 하나를 쌍으로 갖고 있다. 모바일 폰은 Terminal device 와 블루투스 프로토콜을 이용하여 통신을 하기위해 안드로이드 플랫폼 어플리케이션이 구동된다.
  • 어플리케이션에는 기본적으로 안드로이드 api 를 이용해 라즈베리파이와 블루투스 연결을 하는 기능, 블루투스로 문자를 보낼 수 있는 기능 등이 탑재된다. 현재 연결가능한 User 를 확인할 수 있어야한다. 그리고 메시지를 받으면 푸시 알림을 주는 기능이 있다.
  • Terminal device 의 라즈베리파이 보드는 역시 모바일 폰과 블루투스 통신을 하기위해 python 블루투스 api 를 이용해 통신 프로그램을 작성한다. 또한 Terminal device 간 XBee 통신을 하기 위해 제공되는 XBee python 라이브러리를 이용해 프로그램을 작성할 것이다. 이 프로그램에는 XBee 신호를 직접 송수신하는 로직이 포함된다.

그리고 안정적인 네트워크를 유지하기 위한 로직인 aodv 라우팅 프로토콜을 이용하는 등의 로직이 포함된다.

  • Terminal device 는 우리 컨셉상 on/off 가 가능해야하고 on 하는 순간 이 프로그램들이 구동되게 설정할 것이다. 먼저 Terminal device 를 on 시키고 모바일 폰의 블루투스 앱으로 자신이 갖고 있는 Terminal device 와 연결한다. 그리고 모바일 폰으로 현재 연결되어있는 다른 User 를 확인하고 메시지를 보낸다. 대기하고 있는 User 의 단말기가 메시지를 받아 모바일 폰에 알림을 준다.


HW/SW구성도

  • H/W
    • 단말기
      • RF Module : Xbee Pro 900HP module (XBP9B-DMSTB002)
Digimesh 프로토콜을 따르는 RF 데이터를 송수신하는 저전력의 장거리 라디오모듈이다.
  • XBee Dongle : SparkFun XBee Explorer Dongle]
Xbee Module과 Raspberry Pi를 이어주는 보드로, XBee와는 Pin으로 연결되고, 컨트롤 치와는 USB타입으로 연결된다. Raspberry Pi와 Ardunio 계열에만 호환되는 XBee shield와는 달리 Universial하게 다른 장치와도 연결할 수 있다. 또한 Shield에 비해서 가격이 저렴하다.
  • RF Antenna : RPSMA RF antenna (ANT-916-CW-RAH)
900~928MHz 대역의 라디오전파를 위한 안테나이며 강한 고정을 위하여 RPSMA(나사선) 고정방식타입으로 선택하였다.
  • Raspberry Pi 3
Xbee 네트워크와 블루투스를 통해 사용자를 이어주는 역할을 담당하며, 원활한 DigiMesh network를 구성하기 위한 서비스 작업을 제공하는 기반 하드웨어이다.
  • 스마트폰
사용자와 Digimesh 네트워크를 이어주는 앱이 설치되는 단말기로써 사용자가 익숙한 스마트폰 환경을 통해 장치에 내장된 블루투스 칩을 이용하여 Terminal Device와 통신한다.


  • S/W
  • 단말기
  • Bluetooth Daemon
사용자와 단말기의 Bluetooth를 통해 패킷 교환을 담당하며 안정적으로 데이터를 전송한다.
  • Control Unit
기기의 전반적인 요소를 제어하며, 비정상 상황이 예측될 때 가능하면 재부팅할 일이 없도록 시스템을 강건하게 유지한다
  • XBee Process
XBee RF 모듈에게 직접 명령을 내리고, 다른 XBee로부터 오는 메세지를 받아주는 프로세스이다.


  • 스마트폰
  • Bluetooth Process
스마트폰과 Bluetooth 연결을 담당하는 process
안정적인 블루투스 연결을 유지한다. 모바일 기기와 통신 모듈 사이 통신을 하는 부분이다.
전체 프로세스에서 중간과정에 속하고 사용자의 메시지를 통신용 패킷 프로토콜에 따라 안정적으로 전송 & 수신 하는 역할을 한다.
  • Android UI
XBee 모듈의 파라미터 설정 및 통신 과정을 사용자에게 친숙한 메시저 형태의 인터페이스로 제공한다. 접근성을 위해 Channel Mask를 고정하고 어플리케이션 동작 흐름에 따라서만 통신할 수 있게 함으로서 configuration 자유도와 사용자 편리성을 trade 하였다.
사용자 입장에서, 사용자의 언어로 구성된 인터페이스로 인하여 훨씬 직관적으로 Xbee 모듈을 이용할 수 있게 하고, 안드로이드의 고유한 기능과 융합하여 메시지 관리, 블랙 유저 차단 등 더욱 강력한 기능을 구현할 수 있게한다.

프로젝트 배경

더 넓은 세계로 여행을 떠나는 여행가들에게 네트워크망이 닿지 않는 곳이라고 해서 결코 걸음을 멈출 수는 없을 것이다. 하지만 통신망으로부터 고립된 여행가에게 사고가 발생한다면? 외부와 연락할 방법이 없는 여행가는 우연히 구조대원이 주위를 지나치는 기적을 바랄 수밖에 없다.

이 단말기는 블루투스로 안드로이드로 폰과 연결되고 안드로이드 어플리케이션을 사용해 제어하며 단말기를 가진 다른 사용자와 텍스트로 정보교환을 할 수 있게 되어 각종 비상상황에서 주변의 구조원에게 자신의 상황을 알리고 도움을 청할 수 있다. 무전기처럼 사용법을 따로 숙지해야 하는 번거로움 없이 일반인에게도 친숙한 안드로이드 어플리케이션 인터페이스를 통해 처음 접한 누구라도 쉽게 off-grid에서 통신할 수 있는 장치를 개발하는 것이 목표이다.

프로젝트 목표

  • 네트워크견고성
중간사각지대를 연결해주는 2개 이상의 노드 중 하나가 없어져도 원래대로 통신이 가능한가?
  • 데이터전송신뢰성
메세지의 누락시 재전송 및 실패처리 메커니즘이 충분한가?
  • 송신거리
이상적으로 5km 밖에서도 통신이 가능한가(실험목표는 교내 500m 거리)?
  • 사용자편의성
기존 상용 SMS 어플리케이션과 비교했을 때 위화감을 최소화화였는가?
  • 전력소모량
높은 CPU 점유율로 인한 불필요한 전력낭비가 있는가?

프로젝트 기대효과

기존통신망 사각지대 보완

프로젝트 기간

2018.03.02 ~ 2018.06.14

개발 프로세스

전체 시스템 개요도

OloraFlowchart.png


Android UI

  • 개발환경 선택


개발 환경 Android studio Xamarin
기반 IDE IntelliJ Visual studio
지원 플래폼 안드로이드 안드로이드, ios, 웹등
개발 언어 JAVA C#
장 점 인텔리제이 기반 편리한 GUI 지원,

국내 다수 이용자로 커뮤니케이션 활성화

Android studio 개발 경험을 가진 팀원이 있음

C#과 Visual studio의 강력한 기능,

크로스 플랫폼, 네이티브 계층에 접근가능

단점 플랫폼이 안드로이드에 한정됨

각 플랫폼의 네이티브에 접근이

가능하더라도 실제로는 사용하기

어려운 기능이 있다는 평가가 있음.

국내에 커뮤니티가 많이 활성화되지 않음


자바와 C# 모두 생소한 언어이기 때문에 어떤 폼을 사용하더라도 배우는 과정을 거쳐야 했는데,
Android Studio가 플랫폼이 제한되더라도 커뮤니티가 활성화되어 있기 때문에 빠르게 결과물을 만들어 내기에 용이할 것으로 판단되었다.
Android Studio 개발 경험이 있는 팀원이 있다는 점도 안드로이드 스튜디오를 사용하게 된 이유 중 하나이다.


  • 개발과정


  • 레이아웃 및 컨셉 구상


레이아웃은 일반 유저들에게 친숙한 메신저 앱 처럼세개의 탭으로 구성했다.
또한 아웃도어의 자연 속 에서 많이 사용될 것으로 기대하는 어플리케이션인 만큼 사용자의 무드를 헤치지 않게 하기 위해 자연친화적이면서 차분한 느낌의 이미지를 배경화면으로 선택하였다.
15.jpg1.jpg



  • 시행착오. 사용자 친화적이지 않은 버튼 인터페이스


안드로이드는 단말기 아래쪽에 세개의 기능 키를 두고 있기 때문에 위와 같이 아래에 버튼을 두게 되면 버튼과 기능키가 혼동될 가능성이 높기 때문에 화면 아래쪽에 버튼을 두는 것을 권장하지 않는다.
그러므로 탭을 이동하는 버튼을 화면 상단에 위치시키고 또한 휴대폰 화면을 슬라이드하여 탭을 넘길 수 있도록프레그먼트 뷰를 이용하여 레이아웃을 구성했다.
21.jpg 20.jpg


  • OLora 어플리케이션만의 '채널' 개념 도입


일반적인 채팅 어플리케이션과 다르게 OLora는 XBee의 local parameter인 CM, ID, HP를 설정해주어야 하고 같은 파라미터를 설정한 Xbee끼리만 통신을 할 수 있다.
이는 일반 사용자가 이해하기 어려운 부분이기 때문에 어플리케이션에서는 CM, ID, HP를 하나로 합한 파라미터니 '채널' 개념을 도입하기로였다.
사용자는 '채널'이라는 이름의 파라미터 하나를 입력해서 연결하면 내부적으로 '채널'을 CM. ID, HP로 파싱하여 XBee로 전달하게된다.
'채널'과 '채팅방' 개념을 어떻게 조합할 것인지에 대한 아이디어는 여러가지가 있었다.
처음에는 채팅방 목록에서 채팅방을 생성할때 몇 채널에 대한 채팅방인지 입력받아서 채널-채팅방 쌍의 채팅방이 리스트로 나타나게 하도록 구상하였는데 (아래의 좌측사진)
팀원들과 토의한 결과 '브로드 캐스트' 방식으로 대화를 많이 하게되면 채널에 부하가 심해져서 되도록 '유니캐스트' 방식으로 채팅이 이루어져야 하고 그러려면 한 채널에 대해서 유니캐스트 채팅룸과 브로드캐스트 채팅룸을 구별하기 쉽게 디자인 해야한다고 결론내렸다.
채널을 채팅룸 그룹을 묶는 상위개념으로 인식할 수 있도록 현재 보고 있는 채널을 채팅룸 탭의 위에 제목처럼 띄우고 해당 채널의 퍼블릭룸 하나와 여러개의 유니캐스트 채팅방을 나타나도록 하는 모델이 적합할것이라고 생각하게 됐다.(아래 우측 사진)
채팅목록 탭의 윗부분에 현재 설정한 채널이 나타나며, 채널을 설정하면 현재 채널에 대한 퍼블릭 대화방(브로드캐스트로만 대화할 수 있는 방)이 기본적으로 나타난다.
17.jpg18.jpg



  • OLora Database


서버가 없는 모델이기 때문에 필요한 정보를 어플리케이션 내에 저장해 둘 필요가 있다.
안드로이드에는 SQLite를 지원하여 어플리케이션에서 하나의 데이터베이스 파일을 가질 수 있고, 생성한 채널, 채팅방, 설정사항 등 어플리케이션 실행과 무관하게 저장이 필요한 정보는 데이터베이스에 보관해야하기 때문에 다음과 같은 테이블 구조를 가지는 DB를 구성하여 관리하고있다.
야호.jpg


  • 어플리케이션 디자인 결정


인터페이스의 큰 틀이 정해지고, 서비스 플로우가 어느정도 윤곽을 갖춘 이후에 어플리케이션 디자인과 디테일 요소를 결정할 수 있었다.
기존의 차분하면서 여행지에 있는 듯한 무드를 이어갈 수 있는 컨셉은 유지하면서 저작권문제가 없는 이미지를 찾아서 적용하였고, 깔끔한 인터페이스로 사용자에게 편안한 사용감을 제공하기 위하여 깔끔한 흰색 단색 아이콘 위주로 디자인하였다. 아이콘 역시 저작권문제를 피하기 위해 직접 Photoshop으로 제작하였다.
차후 진행 계획 중 이미지를 전송할 수 있도록 할 예정인데 그렇게되면 간단한 프로필 사진정도는 나타낼 수 있게 될 것이므로 사용자 프로필사진을 나타낼 수 있는 부분은 연예인 사진으로 대체해두었다
이미지.jpg



  • 백그라운드 서비스 문제 발생 - 해결책. otto bus 라이브러리


UI가 완성되어갈 때 쯤 프로젝트 진행에 문제가 발생했는데 블루투스 연결(연결 요청, 확인, 끊김 등), 메시지 수신, 명령어 결과 수신 등 백그라운드에서 비동기적으로 발생하는 '통신' 이벤트들에 어떻게 어플리케이션을 반응시킬 수 있는지가 그것이었다.
안드로이드는 화면을 구성하는 '액티비티'라는 객체와 내부적으로 동작하는 '서비스'라는 객체가 있는데, 액티비티의 경우 안드로이드 화면에 그려지는 순간 create되며 화면에서 사라질 때 stop되었다가 destroy되는 '생명 주기'를 가진 메소드이다.
현재 어플리케이션이 어떤 액티비티 객체를 실행시키고 있고 상태는 어떠한지를 '액티비티 컨텍스트'라고 하는데 모든 액티비티들은 처음 화면에 그려질 때 onCreate 메소드 내부에 있는 명령들이 수행되어 화면에 그려지게 되고 그 이후에 다른 동작을 수행하려면 동기적으로 이벤트를 발생시켜주던가, 아니면 비동기적으로 발생하는 이벤트에 대한 '리스너'를 정의해주어서 리스너 안에서 처리를 해주어야한다.
그런데 서비스와 액티비티 간에는 통신할 수 있는 '리스너'가 없고 통신을 하려면 서비스 - 액티비티 간의 소켓 통신을 위해 바인드가 이루어져야 하는데 (이를 바인드 서비스라고 한다) 바인드 서비스의 경우 서비스 - 액티비티 간의 데이터 전달이 액티비티 컨텍스트 내에서 동기적으로 밖에 이루어지지 않는다는 것이다.
Sync.jpg
채팅창 액티비티로 예를들자면 메시지를 쓴 후 '보내기'버튼을 클릭하여 보낼 내용을 블루투스와 통신하는 서비스로 전달하는 '동기적 데이터 전달'은 괜찮지만, 블루투스와 통신하는 서비스가 블루투스로부터 메시지를 수신했을 때 그 데이터를 액티비티가 전달받아서 화면에 띄우려면 액티비티 내에서 '수신'버튼을 클릭하는 등 동기적으로 수신할 수 밖에 없어서 문제가 된다.(아니면 주기적으로 서비스로부터 메시지를 읽는 동작을 수행하는 것을 생각해 볼 수 있는데 매우 비효율적이다.)
이를 해결해 줄 문제로 찾은것이 otto bus 라이브러리 이다.
otto bus 라이브러리는 비동기적인 이벤트가 발생하는 곳에서 otto 객체에 값을 담아 'post' 해주면 이벤트를 받는곳에서는 싱글톤 패턴으로 생성되는 otto bus 객체가 생성되어 사용자가 내부에 정의시킨 함수를 통해 전달된 데이터를 읽을 수 있도록 하는 '이벤트 버스' 라이브러리이다.
Otto.jpg
현재 프로젝트에서는 안드로이드-라즈베리파이간 블루투스 연결 체크, set-parameter command result 체크, discovery result 체크, 받은 메시지 체크 등 5개의 otto bus 객체를 사용하고 있다.
하지만 이렇게 많은 싱글톤 객체를 사용하는 것이 문제를 해결하는 좋은 방법인 것은 아니다. 프로젝트 마감이 얼마 남지 않은 상황에서 목표에 도달하기 위해 궁여지책으로 사용하게 된 것인데, 추후에 플랫폼 한계를 극복하기 위해 Xamarin으로 어플리케이션을 다시 작성하면서 이 부분에 대한 설계를 다시 할 생각이다.


  • 세부 인터페이스 설정 - 타임아웃 프로세스, '설정'기능 등.


블루투스 장치 검색, 채널 탐색 등 시간이 소요되는 작업에 대해 progress 액션을 띄우며 사용자가 작업과 충돌될 수 있는 다른 동작을 하는 것을 방지하였다.
1.png 탐색.jpg


푸시알림 기능을 켜고 끌 수 있는 기능이 있어야 한다는 의견이 있어서 네번째 탭 '옵션'탭을 추가했다. 옵션탭에선 해당 어플리케이션에서 푸시알림, 진동, 소리알림을 켜고 끌 수 있으며 차단한 목록을 보여주는 창을 가지고 있다.
또한 채널 내 다른 유저를 찾는 작업인 'Discover'의 시간을 사용자가 원하는대로 설정하는 기능, Database의 채팅 메시지를 어느정도 시간주기로 자동 삭제할 것인지를 설정할 수 있도록 구현할 예정이다.
옵션2.png 블랙.png

Bluetooth (Android)

  • 시스템 개요
안드로이드 시스템과 라즈베리파이 시스템간의 통신을 위한 방법으로 Bluetooth 통신 채택하였다. 통신 모듈이 포함된 대기하고 있는 라즈베리파이에 접속하기 위한 Client TYPE 프로그램을 작성하였다. 어플리케이션에 안드로이드 시스템에서 상시 돌아가는 백그라운드 대몬 프로그램에 해당한다. 모바일 기기의 블루투스 지원여부, 다른 장치와의 페어링 여부 확인 등 부가적인 기능 지원한다. 블루투스를 지원하지 않는 안드로이드 기기일 경우는 사용자에게 알림을 주고 종료한다. 블루투스를 지원하는 안드로이드 기기인데 블루투스 모듈이 ON 되어있지 않으면 사용자에게 요청을 하여 ON 을 한다. 과거 페어링 되어있는 장치들은 연결하기 전에 표시해주어 따로 장치 검색과정을 건너뛰게 하였다. 페어링이 되어있지 않으면 장치검색 프로세스를 작동시킬 수 있다.

2.png1.png3.png



  • 기본 프로그램 플로우
간단한 플로우 차트. 최초로 앱 구동시 모바일 기기에 블루투스 모듈이 지원되는지 여부를 체크한다. 블루투스 모듈이 지원되고 모듈이 켜져 있으면 바로 장치를 검색하고 연결할 수 있는 탭으로 이동한다. 페어링 여부를 확인하고 필요하다면 페어링 과정을 거치고 연결을 수행한다.

블루투스 client 의 socket type 은 RFCOMM 단의 SPP(Serial Port Profile) 프로파일 형태이다.

- SPP 의  UUID : 00001101-0000-1000-8000-00805F9B34FB 



플로우차트.png



  • 통신부 내부 디자인
라즈베리파이와의 연결 중, 연결 끊김, 연결 성공 등 모든 상태를 안정적으로 Handling 하도록 내부 시스템을 설계하였다. 연결 상태의 변화를 감지하여 최소한의 정보를 사용자에게 제공하도록 하였다. 연결과 통신 프로세스가 안정적으로, 기기내의 다른 프로세스와 간섭을 피하기 위해 synchronized 한 thread 를 구동한다.

플로우차트2.png


  • Xbee 통신 모듈에 명령
Olora 디바이스에 적용되는 프로토콜에 맞는 데이터 패킷(full packet 일 경우, 1024 bytes)을 연결되어 있는 블루투스를 통해 전송하고 받는다. 최초 연결 시 ID(identification) 과 HP(Host Preamible) 을 보내 주고 Discovery 부분의 패킷헤더를 꾸며 각각 전송하고 Xbee 가 보내온 send result 패킷을 받고 상대를 확인하고 채팅가능한 상태로 판단한다.

안드로이드 상에서 String 타입의 BD ADRESS 와 XBEE MAC ADRESS 를 bytes 로 변환, command 등 모든 정보를 패킷헤더에 맞게 꾸밀 수 있는 method 를 작성하였다. 패킷 몸체 에서 사용자의 데이터 영역의 마지막의 의미로 End Of Text 0x03 을 채워놓아 Xbee 가 데이터의 끝이라고 인지할 수 있게 하였다.


  • 블루투스 데이터 전송시 생긴 issue
안드로이드 -> 라즈베리 파이 블루투스 통신으로 패킷을 전송할 시에 기존에 약속된 프로토콜 규약대로 데이터가 가지 않는 상황이 발생함을 깨달았다.

그 이유를 디버깅한 결과, 미리 정의된 1024 bytes 가 아닌 실제로는 1008 or 1020 bytes 가 전송되고 이에 따른 오류가 발생하였다.

더 고찰한 결과로는 bytes 가 소실되지는 않았고 1024 bytes 를 한번에 보내지 않고 끊어서 보내고 있다는 것을 확인하였다.

이는 1024 bytes 를 보내지 않는 경우일 때만 다음 번 전송되어오는 패킷 중 이전에 보내지지 않은 패킷만큼을 더해 온전한 1024 bytes 를 읽어들여 해결하였다.

Bluetooth Daemon (RPi)

  • 개요
  • 목적
블루투스 클라이언트로부터, 즉 사용자로부터 오로라 패킷을 받아들이며, RF모듈인 XBee에게 데이터를 송신하고, 통신모듈로부터 들어온 데이터를 사용자에게 안전하게 데이터를 전송한다.
  • 요약
개발 환경 Linux, Debian Stretch on PC (AMD64)
Linux, Ubuntu Mate 16.04.3 on Raspberry Pi3 Model B (ARMv7, Thumb)
개발 언어 GNU C
의 존 성 Pthread, BlueZ, OpenSSL
플랫폼 지원 C99이상의 C언어, Linux OS, 의존성을 만족하는 Devices
통신 프로토콜 RFCOMM
다중 접속 불가능 (1명의 클라이언트만 세션 생성 가능)
  • 전체 시스템 디자인
  • 오로라 전체 통신 구조

Olorapacketflow.png

패킷 해더에는 SRC/DST의 하드웨어 ADDRESS가 기입되며, SRC/DST에 의해 오로라 시스템은 패킷의 향방을 결정한다. HostA는 HostB로 보내기 위해서 Olora 프로토콜을 사용하는 디바이스 장치를 거쳐야 하며, SRC/DST 변환 과정은 위 그림과 같다. 우선 HostA에서 SRC에 자기자신의 블루투스 주소와, HostB의 XBee 어드레스를 DST에 기입한다. 패킷이 나가는 방향에서 Bluetooth Daemon과 Control Unit은 SRC/DST에 대해 어떤 조작도 하지 않으며, 오로라 장치의 XBee 프로세스에서 SRC를 디바이스의 XBee 주소로 변경한다. XBee의 RF채널을 통해 데이터가 전송되면, XBee에서 DST 주소를 확인하고 일치하면 Control Unit에 패킷을 전송한다. Bluetooth Daemon에서 HostB가 살아있는지 확인하고 살아있으면, DST를 HostB의 블루투스 주소로 변환하여 HostB에게 RFCOMM 프로토콜을 이용해 안전하게 패킷을 보낸다. 사용자는 이 메시지가 패킷의 SRC에서 HostA로부터 온 메세지 임을 알 수 있다.


  • Packet Stack 구조

Olora packet protocol stack


FIELD WHERE TO USE Size (Bytes) DESCRIPTION
SRC All 8 패킷 발원지 주소
DST All 8 패킷 도착지 주소로 수신자의 XBee 어드레스이며, 수신자 기기에서 수신자의 블루투스 어드레스로 전환
CM XBee 8 XBee 통신에 사용되는 Channel의 Mask Bits
HP XBee 1 XBee 통신에 사용되는 Preamble ID Bits (0x00~0x07)
ID XBee 2 XBee 통신에 사용되는 Network Identifier Bits (0x0000~0x7FFF)
OPT All 1 패킷의 플래그 정보 저장
SEQ BD, XBee 2 데이터가 큰 경우 패킷을 분할해야하며, 순서를 보장하기 위해 패킷 순서를 저장한다. SEQ가 0이면 마지막 패킷이다.
CMD BD, XBee 2 장치에서 수행할 명령
TS App, XBee 4 패킷이 사용자로부터 출발한 시간의 Time Stamp
DATA All 988 데이터 영역
※ CM,HP,ID가 모두 일치해야 XBee끼리 통신 가능하다.
  • Flags in option field
7 6 5 4 3 2 1 0
Urgent Error CMD Defined Area Internal Packet Order
  • Urgent Flag
Packet 중에서 우선적으로 처리해야하는 패킷이다.
  • Error Flag
어떤 CMD가 실패하거나 CMD에 대해 응답이 필요한 경우 Error Flags를 1로 셋하고, Data에 에러 사유를 명시한다.
  • CMD Defined Area
CMD에 의존하는 Flags으로 한 CMD에 최대 8개의 옵션을 줄 수 있다.
  • Internal Packet Order
XBee에서는 한번에 256Bytes를 전송하므로 1024Bytes의 패킷을 분할할 수 밖에 없으며, 패킷을 분할하고 재결합할 때 필요한 순서(0~4)번을 기록한다.


  • Bluetooth Daemon Desgin
  • Control Process
블루투스 데몬의 주 구성요소로써 Control Process는 3개의 Threads로 이루어져 있으며, Control Process는 데몬이 종료될 때까지 유지된다.
  • Input Gate
Session으로부터 들어온 패킷을 명령어을 분석하여 경로를 결정한다. 세션 내부에서 만든 패킷일 경우 Control Unit및 Control Process와 상호작용하며 나머지는 Gate Unit으로 내보낸다.
  • Output Gate
Gate Unit으로부터 들어온 패킷을 분석하고, 블루투스 내부에서 만든 패킷일 경우 일정한 명령을 수행한다. 만일 사용자 세션이 종료된 상태라면 Gate Unit으로 들어오는 데이터들은 Packet Container Unit으로 보낸다.
  • Spinner
Control Process의 Threads를 모니터링하며 상태를 주기적으로 업데이트하며, Spinner가 종료되면 블루투스 데몬이 종료하며, 디바이스를 다시 스캔 가능하게 설정한다.


  • Seession Process
Session Process는 4개의 Threads로 이루어져 있으며, 3개의 Threads는 클라이언트가 서버에 접속함에 따라 세션을 구성할 때 생성되고, 연결이 종료되거나 끊어지면 생성된 Threads는 동시에 종료하여 다음 클라이언트를 맞이할 준비를 한다.
  • Input Service Session
사용자 클라이언트로부터 데이터를 받아 패킷 해더를 분석하며, 세션에서 수행할 수 있는 작업을 수행한 뒤 Control Process의 Input Gate로 전송한다. 사용자 패킷이 Input Service Session에서 수행가능한 내부 기능은 아래 표와 같다. 만일 사용자가 명령에 대한 권한이 없는 경우 해당 패킷은 내부로 넘어가지 않고 드롭된다. 만일, 결과가 즉시 리턴되는 패킷이면 Message Service Session으로 결과 패킷을 전송한다.
Command Name Command Description
SERVICE RESET 블루투스 장치를 리셋한다. 장치를 리셋하므로 사용자 연결은 끊어진다.
SERVICE SCAN ENABLE 블루투스 장치를 스캔 가능한 상태로 만든다.
SERVICE SCAN DISABLE 블루투스 장치를 스캔 불가능한 상태로 만든다. 이미 연결된 세션일 경우 보안을 위해 SCAN을 DISABLE하는 것이 좋으며, 배터리를 절약할 수 있다.
SERVICE SHUTDOWN 오로라 Device를 안전하게 종료한다. 하드웨어 종료에 대한 권한은 나중에 추가할 것이다.
  • Output Service Session
Control Process의 Output Gate으로부터 RF모듈로부터 들어온 데이터를 받는다. 현재 세션 유저로 향하지 않으면 패킷은 드롭된다.
  • Message Service Session
Input Service Session과 Output Service Session으로부터 받은 데이터를 사용자에게 송신한다. 통신 장애가 발생하거나, 세션 유저의 연결이 갑작스럽게 끊어지게 되면, Message Queue에 있는 패킷은 Packet Container Unit으로 보낸다.
  • Spinner
Service Session를 모니터링하며 상태를 주기적으로 업데이트 한다. 각각 서비스에서 미쳐 끝내지 못한 작업을 마무리하고 세션을 안전하게 종료하고 자원을 반납한다. Spinner가 종료되면 블루투스 데몬은 다음 클라이언트를 위해 서비스를 준비를 한다.

Control Unit (RPi)

  • 개요
  • 목적
기기의 전반적인 요소를 제어하며, 비정상 상황이 예측될 때 가능하면 재부팅할 일이 없도록 시스템을 강건하게 유지한다.
  • 요약
개발 환경 Linux, Debian Stretch on PC (AMD64)
Linux, Ubuntu Mate 16.04.3 on Raspberry Pi3 Model B (ARMv7, Thumb)
개발 언어 Python3, Bash Shell Script
의 존 성 SQLite
플랫폼 지원 Python을 지원하는 Linux OS의 모든 Devices


  • Control Unit Desgin
  • Gate Unit
Olora 디바이스의 데이터 흐름을 제어한다. 디바이스 내부에서 통신으로 사용되는 파이프의 입출력 방향은 [BD <->XBee 서비스], [SVHU->BD,XBee 서비스], [PKCU->BD]으로 이루어져 있으며 파이프는 입출력 양단이 정해져 있고 오류를 방지하기 위해서, 파이프에 입출력을 할 때는 원자적(Atomic)이거나 잠금(Lock)을 걸어야한다. 여러 코드가 분산되어 있기 때문에 여러 입구를 두어 글로벌 락을 거는 것보다 입출구를 하나로 두고 그 앞에서 방향을 제어하는 것이 맞다고 판단했으며, 여러 입구를 두게 되면 데이터를 수신한 프로세스는 원하는 결과가 왔는지 체크를 해야하고 그렇지 않으면 반납을 하거나 맞는 프로세스에게 돌려주어야 하므로 다중 입출구로 구현하게 되면 큰 오버해드가 예상된다. 또한 프로세스가 비정상 종료되면 파이프의 양단에 비정상 데이터가 즉각적으로 오게되므로 중앙에서 흐름 제어를 하게되면 신속하고 종합적으로 대응할 수 있다.
  • Packet Container Unit
블루투스 데몬이 사용자에게 전달하지 못하고 반송된 패킷을 임시로 저장하며, 세션이 다시 시작되는 경우 데이터 베이스에 있는 패킷을 다시 클라이언트에게 재전송한다. 만일 현재 세션 유저가 재전송하려는 패킷 목적 주소가 다르면 패킷을 데이터베이스에 Push하여 Privacy를 유지한다. 내부 데이터베이스는 SQLite를 통해 다루어진다.
  • Service Homeostasis Unit
블루투스 데몬과 XBee 서비스 프로세스를 관리한다. 관리하는 프로세스가 비정상적으로 종료되는 경우에 프로세스를 재시작하며, 내부 오류로 인해 의미 없는 무한 루프에 걸린 프로세스를 감지하여 종료시키고 재실행한다. Service Homeostasis Unit은 다른 프로세스로부터 제어 패킷을 입력을 받을 때는 파이프가 아닌 TCP/IP로 통신하며 출력할 때는 Gate Unit을 통해 파이프로 각 프로세스로 전파한다. Service Homeostasis Unit의 데이터 전송도는 [BD<->XBee 서비스]나 [PKCU->BD]에 비해 현저하게 낮기 때문에 속도를 위해 공유 메모리나 PIPE로 구성할 필요가 없으며, 에러 보고나 내부 요청이 우선적이기 때문에 루프백을 사용하여 안정적인 TCP/IP 통신을 활용하였다.
  • Start Shell Script
Olora 시스템을 실행하기 위한 서비스 쉘 스크립트이다. Olora 시스템을 사용하려는 개발자는 이 쉘 스크립트를 통해 블루투스 Ping, Olora 시스템 빌드, 프로세스 제어를 수행할 수 있다.


XBee (RPi)

  • Xbee 연결문제
전용 프로세서가 탑재되어 있는 Programmable XBee Module의 경우 serial 명령어가 바로 radio module로 가지 않고 항상 전용프로세서를 먼저 거치고 radio module로 가게 된다. 따라서 programmable module을 사용하면서 따로 프로세서를 쓰는 일이 없다면 bypass모드로 설정을 하는 코드를 작성해서 넣어줘야 한다. 당연히 non-programmable XBee는 이와 같은 문제가 없이 baud rate과 port만 맞는다면 잘 연결이 된다.
[XBee connection troubleshooting]


  • XBee-Pro 900HP가 실용적으로 전송가능한 데이터는?
최대속도가 200Kbit/s고 평균적으로 수십Kbit/s 의 전송속도를 갖으며, 송신거리가 아주 길어지거나(14km이상) 장애물이 많은 경우 전송속도가 10Kbit/s 로 떨어질 수 있다. 나아가 잦은 packet 전송은 중앙서버가 없는 mesh network에게 큰 부하가 될 수 있기 때문에 큰 용량을 갖는 사진 데이터는 지양한다. 만약 전송이 가능하다면 최대한 압축을 시킨 형태로 보내야할 것이다.
[XBee Spec]


  • 현재 주파수 대역이 좋지 않을때 어떻게 해결하는가?
주파수호핑 방법 중 하나인 FHSS로 XBee는 자체적으로 Channel Mask에 의해서 선택된 900~928Mhz 사이의 주파수구간을 빠르게 호핑하면서 돌아다니므로 확률적으로 몇개의 좋지 않은 채널이 전체 시스템을 붕괴시킬 일을 거의 없다. 즉, 사용자측에서는 단순히 Channel Mask와 Channel Mask에 있는 1bit들의 최소한의 갯수를 정하는 MF 파라미터만 신경쓰면 된다. 하지만, 위에서 설명한 바와 같이 CM는 사용자가 직접 설정하기 굉장히 애로한 부분이기 때문에 default값으로 남겨둔다.
[FHSS란?], [XBee Channel Mask]


  • 중간노드가 사라질 경우?
우리가 사용하는 Digimesh Protocol은 AODV 방식으로 중간노드가 사라질 경우 가능한 대체 루트를 자동적으로 찾는다. 만약 가능한 루트가 발견되지 않을 시에는 synchronous unicast의 경우 'route not found'로 send fail message를 리턴한다.
[AODV란?]


  • XBee 보안문제
EE parameter 변수를 1로 수정하여 AES 암호화를 할 수 있으며 이떄 암호화키는 KY변수를 가지고 수정하면 된다. 하지만 XBee칩회사인 digi에서 제공하는 XCTU 프로그램이 아닌 사용자 자체 프로그램으로 KY값을 수정하는 것을 digi사에서 지양하라고 설명서에 나와있고, 우리의 경우 모든 파라미터를 XCTU가 아닌 자체 프로그램으로 수정하는 것이기 때문에 EE나 KY parameter의 수정으로 보안을 획득하지는 못하였다. 나아가서 암호화 시 전송속도가 매우 느려지기(200KB/s --> 수십KB/s) 때문에 이번에는 사용하지 않는 것으로 하였다.
[XBee 암호화]


  • data영역의 데이터 경계 구분 및 텍스트 인코딩 방식
UTF8 Structure.png
0x03.png
< 위:UTF8구조, 아래:End Of Text Character >
local parameter를 set하는 명령어 중 비가변길이를 갖는 데이터는 상관이 없지만, 가변길이를 갖는 Node Identifier(호스트별명)은 데이터의 끝을 EOT(End of Text문자 아스키로 0x03)으로 끝을 판별한다. 마찬가지로 send message 하는 경우에도 message가 끝나는 곳에 0x03을 붙여주면 Null인 공간을 제외하고 실제 message부분만을 취할 수 있다. 데이터 영역의 text 인코딩은 utf-8방못 식을 사용하게 되며 이때 한글은 한 음절당 3바이트, 일반적인 ASCII문자는 1바이트로 표시가 된다. XBeepacket을 여러번 전송해야 하는 장문메세지의 경우 중간전송을 실패하는 경우, 만약 잘려나가는 바이트에서 0x03이 존재하면 0x03을 잘못 EOT으로 인식할 수도 있을 것 같지만, 애초에 어느 한 인코딩 방식을 사용하기로 약속한 이상(여기서는 utf8) 중복되도록 encoding되지 않도록 utf8이 설계되어있다. 즉, 일단 UTF8로 인코딩/디코딩을 약속한 이상 EOT(0x03)문자가 3바이트인 한글에서 발견되는 일이 절대 없는 것이다. 만약 데이터 전송의 무결성을 더 추구하고 싶다면 header에 데이터의 총 바이트 수를 적어둬서 0x03에만 의존하는 것을 보완할 수 있다.
내부적으로 XBee의 parameter를 설정할때는 순수한 binary로 가기 때문에 중간에 0x03을 쓰는 경우가 발생한다. 이때 NI를 제외한 모든 parameter는 고정길이를 갖기 때문에 0x03을 기준으로 잘라서 읽는 것이 아니라 미리 알고 있는 해당 parameter의 길이만큼 읽어서 0x03을 잘못 읽는 것을 방지한다.
[UTF-8 구조], [UTF-8 설계원칙]


  • XBee packet 또한 비가변적으로 쓸 것인가?
XBeePacketFrame.png
Xbee packet의 데이터영역은 가변적으로서 0~256바이트까지 쓸 수 있다. 맨 처음에는 Fullpacket에 맞춰서 EOT까지 보내는 것이 아니라, 무조건 Fullpacket의 data영역크기(988바이트)를 다 보낼 수 있는 최소 크기인 5 Xbee packet으로 고정적으로 보내려고 하였다. 물론 이 경우에도 전송자체에는 전혀 지장이 없었지만 'HelloWorld'라는 10Byte 메세지 하나를 보내는데만 무려 (988-1(EOF))-10 = 977바이트의 Null 데이터가 공중에 날려져 Mesh Network에 큰 부하를 주는 것이었다. 원래 Fullpacket로 다시 재결합하기 위해서 각 Xbee packet에 원래 부모 full packet의 header를 공통으로 찍어주는 것을 제외한다고 해도 이는 큰 낭비였다. 따라서, 고정길이를 갖는 fullpacket은 일단 앞서 말한듯이 동일하게 사용하되 fullpacket의 데이터영역에 찍혀 있는 EOT문자의 위치를 XBee process 단에서 확인하여 딱 필요한 만큼만을 data영역에 넣어서 가변적으로 Xbee packet에 넣어 전송하고, 이후 받는 쪽에서는 header 속의 metadata를 해석하여 원래 fullpacket을 다시 재구성한다.


  • 장문메세지 송신/수신
XBee packet은 길이가 비가변적이므로 사용자가 장문메세지를 보내게 될 경우 XBee packet을 2개 이상을 사용하여 보내야 하는 경우가 생긴다. 만약, 네트워크 내에 단 2명의 사람만이 서로에게 송/수신 할 경우에는 문제가 되지 않지만 메쉬 네트워크의 모양이 복잡해지고 여러명의 사용자가 동시에 이야기를 할 경우에는 packet의 순서가 보장되지 않는다. 따라서 하나의 장문메세지에 대응되는 n개의 XBee packet을 보내게 될 경우 n개의 Xbee packet에는 같은 timestamp를 data영역에 찍어서 상대방에게 보낸다. 수신자는 특정사용자가 보낸 동일timestamp의 Xbee packet을 모아서 수신자쪽 폰으로 보낸다. 메세지가 중간에 누락되는 경우가 생길 수 있으므로 원래 발신자측에서 보낸 XBeepacket갯수보다 적게 수신될 수 있다. 따라서, 정해놓은 시간보다 entry가 오래동안 살아있으면 해당 entry에 대응되는 모든 XBee packet이 다 오지 않아도 flush 시켜서 예외처리를 한다. 이 모든 것을 Message-Handler(Re-organizer) 프로세스가 처리한다.


  • 메세지 전송 실패시
HeaderOption.png
비가변적 길이의 packet을 사용하는 지금, 사용자가 보내고자 하는 메세지의 끝은 EOT로 처리된다. 5개의 XbeePacket으로 나눠보내게 될때 EOT가 들어가게 되는 i번째 위치일때 앞에 보내진 1~i번째 XBeepacket의 send result가 '성공'일 경우, (i+1)~5번째 send result는 fail이 되어도 사실상 보내져야할 것은 다 보내진 것이기 때문에 의미상으로는 Send Fail이 아니다. 즉, 다시말하자면 Send Fail의 기준은 i번째 xbeepacket에 EOT가 positioning하게 될때 그 1~i번째 packet중 하나라도 send fail이면 사용자에게 send fail이 된 message부분을 data영역에 써서 알려준다. 또한 Command Result는 일반적으로 header의 28번째 바이트의 상위 7번째 비트로 보내주게 되는데, 한번의 send명령에 XBee단에서는 5번의 Xbeepacket이 보내지게 되므로 1비트로 각각의 result를 대응시킬 수 없다. 따라서, 만약에 하나라도 send fail이 날 경우에는 다른 명령어 result header처럼 "header[27]|=0b01000000" 를 취해 에러를 알려주고, 나머지 하위 5비트로 one-hot coding으로 각각의 XBee Packet의 send result를 적어둔다. 하지만, 도중에 다른 의견이 있어서 오히려 수신자측이 받은 메세지형태와 똑같이 Send Command Result data영역을 만들어서 보여줘야 한다는 의견이 있어서 전송실패된 부분을 돌려주는게 아니라 전송된 부분 그대로를 돌려주는 것으로 수정하였다.


  • 낯선 network에 새로운 node를 연결하는 방법?
무조건 네트워크고유값인 CM,HP,ID 모르는 이상 서로 모르는 노드끼리 통신할 수 있는 방법이 없다. 하지만 약간 우회적인 해결책이 있다. 특정 채널에서는 누구나 서로 통신할 수 있도록 미리 '그' 채널값을 약속해두고, 누군가와는 통신을 하고 싶다면 해당 채널로 들어가서 broadcasting 하는 것이다. 다만 이 방법의 한계점은 다른 사람들이 평소에 default값으로 '그'채널의 네트워크고유값으로 설정되어 있어야만 들을 수 있다는 것이다. 만약 스마트폰에서 '그'채널과 현재 사용하는 채널을 주기적으로 바꿔가면서 '그'채널에서 들려오는 소리를 들을 수 있도록 설정을 한다면 위와 같은 우회적인 solution이 유효할 수 있다. 하지만, 채널을 주기적으로 바꾸는 것은 overhead가 크기 때문에 왠만해서는 사용자들이 설정하지 않을 것이다.


  • Xbeeprocess의 XbeeNetwork 인스턴스와 phone의 device database 동기화
Xbeeprocess에는 오직 하나의 XbeeDevice 인스턴스만이 존재하지만, XbeeDevice로 만드는 XbeeNetwork 인스턴스는 당시 XbeeDevice가 가지고 있는 네트워크고유값(HP,ID)에 대응되는 것으로 여러개를 만들수는 있으나 runtime에 새로 생성할 수 없어서 사실상 채널(네트워크고유값)을 바꾸고 나면 사용자측은 비록 phone database에 그 사람이 있으나 discover network 명령어를 내려 XbeeNetwork 인스턴스를 overwrite한뒤에 비로소 unicast(private chat)를 할 수 있다. 참고로 수신자가 채널 내의 모든 불특정 다수인 broadcast는 검색 없이 바로 사용이 가능하다.


  • 명령어
CMD흐름도.png
일반적인 사용자가 XBee를 configure하고 메세지를 send/receive하는데 필요한 명령어 목록은 간단해야 한다. 예를 들어 서로 통신하고 싶은 Xbee기기들은 CM,HP,ID 이 3가지 변수가 모두 같아야 한 네트워크 안에 있는 것으로 인식되어 송수신이 가능하다. 하지만HP(0x00~0x07),ID(0x0000~0x7fff)의 설정은 비교적 간단한 반면 CM 파라미터는 64개의 bit 중 최소 MF개의 1bit가 선택되도록 받아야한다. 사용자는 십진수에 익숙한 반면 해당 조건을 만족시키는 십진수 조건을 만들기는 굉장히 어려우며, 원래 그대로의 이진수 조건을 사용자에게 요구하여 64개의 bit를 모두 입력하게 만드는 것은 더욱 무리다. CM 파라미터 같은 경우 default 값으로 두어도 성능에 큰 영향이 없기 때문에 사용자가 만지지 않는 편이 훨씬 낫다. XBee모듈 출력강도도 조절이 가능하지만 애초에 XBee자체가 워낙 저전력이고, 체감상 배터리를 XBee보다 라즈베리파이가 훨씬 많이 잡아먹어 사용자가 PL레벨 조정을 통한 에너지 절약 효과는 거의 미미하다고 판단되므로 Xbee 출력 또한 default로 설정한다.
  • broadcast로 온 명령어와 unicast로 온 message는 어떻게 구분하는가?
header에 있는 CMD의 번호로 단순하게 구분해주면 된다.


  • 명령어의 synchronous한 실행
XbeeSynchro.png
완전하게 sequential하게 명령어를 실행하기 위해서는 사용자측에서는 XBee가 하나의 명령어를 수행하고 명령어결과를 리턴할때까지 기다리고 확인하고 나서야 다음 명령어를 수행해야 한다. 즉, 이렇게 명령어를 순차적으로 수행할 경우 약간 느려질수는 있으나 논리적으로는 안전한 편이 된다. Discover network와 send 명령어를 제외한 나머지 명령어는 빠르게 실행되므로 속도의 감소는 감수할만한 trade-off다. send나 discover network의 느린 속도는 중앙서버가 없는 mesh network 본연의 특징이므로 어쩔 수가 없는 부분이다. 만약 동시다발적으로 수행해도 논리가 깨지지 않는 명령어 관계를 찾는다면 concurrent하게 처리해도 될 것이다.


  • Packet 구조에 변화가 필요할시 어떻게 하는가?
어느 정도 만들다 보면 맨 처음에 정했던 packet의 각 part의 사용방법을 달리 하고 싶을 때가 있을 것이다. 일례로 CM 같은 경우 결국에 default값으로 사용하게 됨에 따라서 헤더에 여유공간이 생기게 되었다. 다시 회의를 해서 아예 header를 바꾸는 방법도 있지만, meta data를 더 넣을 여유가 마침 필요한 참이라 다행히 header 구조를 바꾸지 않고 CM부분의 역할만 바꿔서 그대로 사용했다.


  • 이론적으로 Dead Lock 위험이 있는가?
Command Exeucutor는 Command Returner에게 CMD_result를 pipe로 보내놓고 wait상태에 들어가는데 만약 도중에 pipe가 못쓰게 되어 returner가 못 받을시 executor는 영영 못깨어날 수 있고, 받았는데 갑자기 unknown 에러로 죽게 되어 wait상태의 executor를 깨우지 못할 경우에만 command executor가 deadlock된다. 하지만, executor와 returner사이의 pipe는 둘만 쓰기 때문에 pipe에 파이썬 multiprocessing이 pickle하지 못하는 형태의 데이터를 넣지만 않는다면 pipe_error가 날 일이 없기 때문에 원래 예상대로 byte형태의 데이터인지 확인만 잘 하면 이 부분에서 deadlock은 일어나지 않는다. 두번째로, Command Returner와 Message Returner가 외부의 control unit쪽으로 연결되는 out_pipe를 공유해서 쓰고 있다. 따라서 반드시 배타적으로 이용해야 되기 때문에 lock variable이나 event variable을 써서 한쪽만이 write 할 수 있도록 해야 한다. 또한 Command Returner가 Message Returner보다 pipe 사용의 우선순위가 있도록 설계한다.


  • 개발 중 deadlock 생겼을 때 어떻게 하였는가?
원래는 Command Executor가 Command Result를 Command Returner에게 pipe로 보내준뒤, Command Executor는 event variable을 clear하고 wait하다가 Command Result를 받은 Command Returner가 event variable을 다시 set해줌으로써 깨어나길 기대했었다. 하지만, 멀티프로세싱은 이와 같은 순서를 함부로 가정하면 안되는게 Command Result를 Command Returner 받은 뒤 Command Executor가 clear,wait 하기 전에 event variable을 set해버리면 Command Executor는 깨어나지 못한다. 따라서, 이것은 Pipe를 통해서 직접적으로 Command Executor가 Command Returner로부터 어떠한 신호를 받을때까지 wait하도록 explicit하게 만든다.


  • Out_pipe가 하나인데 어느 것의 우선순위가 더 높은가(Command Result vs Message)?
Message에 찍히는 timestamp는 보낼때의 시각으로 찍고 채팅방에도 그렇게 나오도록 약속을 하였으므로, 굳이 바로 바로 받을 필요도 없거니와 중간에 packet이 날아오는데 delay가 있을수가 있다. 즉, 애초에 거의 atomic 하게 메세지가 전달되지 못하는 상황이다. 또한 사용자는 순차적인 명령어 실행으로 인해 약간 명령 수행이 느려질 수 있는 상황인데 엎친데 겹친격으로 메세지가 막 들어온다고 해서 명령어 리턴이 delay되면 이는 최악일것이다. 따라서, Command Result Return의 우선순위가 Message Return보다 높도록 event variable을 사용하였다. 나아가 out_pipe를 공유하여 쓰기 때문에 일단 쓸때는 무조건 주위에 lock variable을 쳐준다.


  • Inter Process Communication
XBeeprocess 내부에서는 python의 multiprocessing library에서 제공하는 Process,Queue,Pipe,Event 메소드를 사용하여 IPC를 구현하였다. 자료의 producer/consumer 관계에 있는 모든 프로세스 쌍들은 cpu 자원 절약을 위하여 spinlock을 사용하지 않고 input을 확인한다. Event Variable을 사용하여 동기화를 구현한다. Queue는 copy가 2번이나 일어나게 되므로 pipe보다 3배 정도 느리므로 빠른 속도가 요구될 때는 pipe를 사용한다. 나아가 외부(control unit)와의 통신은 os.pipe 로 통신한다. 두 개 이상의 process CMD returner와 MSG returner가 out_pipe를 사용하게 되므로 이때 lock variable을 사용할 수도 있지만, CMD result의 결과 return이 더 우선순위가 있다고 보므로 우선순위가 낮은 쪽은 event variable의 wait기능을 사용하여 우선순위가 높은 CMD returner가 pipe를 쓰고 끝날때까지는 out_pipe를 쓰지 못하게 만들면 된다
[Python IPC]


  • 프로세스
    • Command Listener : Control Unit으로부터 들어오는 명령어를 fetch하여 command queue에 넣어주며, 에러 발생시 Xbeeprocess 전체를 reset 시켜주는 역할을 한다.
    • Command Executor : Command를 직접적으로 수행하는 부분으로서 header의 command 영역을 해석하여 대응되는 명령을 실행한다. 명령 실행에 대한 결과(success/fail)을 다시 사용자 방향으로 돌려주는데 그 result는 pipe를 통해 command returner에게 보내주고 최대한 synchro를 맞추기 위해서 Command Returner 가 받을 때까지 잠시 wait하여 다음 command를 실행하는 것을 방지한다.
    • Command Returner : Command Result를 사용자에게 보내주는 역할을 하며 Xbeeprocess 전체 재부팅시 control unit의 svin process에게 Xbeeprocess 방향 파이프를 다시 열것을 요청하는 역할을 한다.
    • Message Handler  : Remote XBee로부터 받은 메세지를 Source Address, Timestamp, Internal Sequence 별로 재조합하여 원래의 full packet을 만들고 message returner에게 flush해주는 프로세스다. 전송 중 누락되는 메세지가 있어서 원래 full(부모) packet 크기 만큼 못 채워진 메세지 entry는 가장 최근에 도착한 XBee(자식) packet 의 시간을 기준으로 3초를 초과하면 flush를 한다.
    • Message Returner : flush된 full packetized 메세지를 폰으로 전송해주는 역할을 하며, 외부로 향하는 파이프는 command returner와 공유를 하기 때문에 lock 변수를 써서 둘 중 한 프로세스만이 쓰도록 하며, 또한 Command Returner의 파이프 쓰기 우선순위가 높도록 event variable로 설정해준다.
  • 프로세스 갯수
XBeeCPU.png
< XBee CPU Usage FullPacketSend & Receive/ 2sec >
Command Listener, Command Executor, Command Returner, Message Handler 그리고 Message Returner로 총 5개로 구성된다. 현재는 seqeuntial하게 command를 처리하기 때문에 사실상 command listener와, commmand executor 그리고 command returner를 하나로 묶어도 된다. Message handler도 message returner와 묶을 수도 있지만 message handler는 계속 old message entry를 검사해서 flush를 해야 되기 때문에 message returner와 합치게 되면 age check 기능을 동시에 수행하지 못하게 된다. 따라서, 최대한 줄이면 5개에서 4개로 줄일 수 있기는 한데 Message Handler를 제외한 모든 process들은 input이 없으면 자동으로 sleep해주기 때문에 4개로 줄여도 5개일때나 실제 돌아가고 있는 유효 프로세스 갯수는 동일하다. 향후 concurrent한 명령어 수행까지 고려를 한다면 현재 프로세스 5개로 하는 것이 더 올바른 구조이다. 실제로 2초주기로 두 쌍의 XBee가 988byte full data를 주고 받게 하였을 때 프로세스 5개일때나 4개일때나 terminal에서 top명령으로 cpu 점유율을 확인하였을 때 둘다 2~3% 였다. 일반적으로 XBee에게는 명령을 내리지 않을 때는 항상 message handler의 aging 알고리즘을 제외한 나머지 프로세스는 block된 상태로 있으므로 cpu 사용률이 0~1% 였다. 즉, 병목지점은 따로 없다.


  • ATmode 에러가 자주 나는데 어떻게 대처하는가?
XBeeReset.png
< XBee Reset >
모든 프로그램은 현재 API모드에서 돌아가므로 ATmode 에러가 나면 아예 Xbee API를 이용할 수 없으므로 치명적이다. 이는 Xbee serial을 다시 close했다가 open하는 것으로 해결되지 않고, 아예 해당 process프로세스를 재시작하여 해결하였다. 이때 control unit쪽의 gate(연결관리체)와 연결이 끊어지게 되면 gate가 해당 입구를 닫게 되는데, Xbeeprocess에서는 재시작을 완료하면 반드시 tcp socket을 통해 svin server process에게 gate를 열어달라고 요청을 한다.



  • 블루투스연결이 끊어졌을 경우 어떻게 대처하는가?
블루투스연결이 끊어진 경우 사실상 사용자는 XBee를 사용하지 않는 것과 같은 효과를 내야 한다. 하지만 XBee process는 켜져 있을 경우 항상 발신자쪽의 메세지를 수신(handshaking)하게 되고 이는 발신자측에서 수신자측이 받았다고 받아들이게 된다. 따라서, 폰과의 블루투스 연결이 끊어졌을 경우 XBee Process 또한 중단시켜서 이를 방지한다. 블루투스가 끊어질 경우 Pi control unit process에서 블루투스가 끊어짐을 감지하고 Xbee process에게 중단 요청을 보내면 Xbee process의 handler(Command listener)가 본인을 제외한 다른 Xbee process를 중단시킨다(SIGSTOP). 이후 블루투스가 다시 연결되면 SIGCONT로 중단되었던 지점부터 다시 모든 Xbeeprocess가 재개된다.

프로젝트 결과

평가

  • 견고성
    • 중간노드삭제 테스트 -> 사각지대를 보완해주는 2대 이상의 XBee 중 하나를 제거해도 양끝 노드 간 통신이 가능한가?
    • 이동중 통신 테스트 -> 긴 송신거리(500m)를 두고 서로 걸어다니는 환경에서도 통신이 가능한가?
    • 자체 강제종료 테스트 -> 예기치 않은 에러로 인한 종료에 robust한가?
  • 전송신뢰성
    • 장문메세지 처리 테스트 -> fullpacket으로 연속 100회 전송
    • 전송실패 처리 테스트 -> [긴 송신거리(500m) + 장애물 있는 환경]에서 고의로 전송실패 유도
    • 송신거리 테스트 -> 음악관~미래관(500m)
  • 에너지효율
    • (H/W) -> 라즈베리파이zero를 추가적으로 구입할 경제력이 되지 못하여 더 이상의 H/W적인 에너지효율 개선은 불가했다.
    • (S/W) -> 소스코드 개선을 통한 cpu자원 절약
  • 사용자편의
    • 사용자인터페이스가 충분히 직관적인가?
    • 사용자에게 불필요하게 많은 명령어를 요구하는가?

보완점

  • 에너지효율성
    • H/W : 라즈베리파이3 -> 라즈베리파이zero
    • S/W : Bluetooth packet stack을 재정의하여 가변적으로 packet을 보낼 수 있도록 한다. 즉, 패킷의 데이터 영역 내에 낭비되는 부분이 최대한 없도록 개선한다.
  • 다대다통신 : 채팅방 동기화 문제를 해결하는 알고리즘을 만들어 단체 private 채팅을 가능하도록 개선한다.
  • GPS : 야외에서 사용하는 만큼 본인의 위치를 타인에게 알려주고 싶을 때 스마트폰에 있는 GPS 정보를 가져와 data에 넣어 보내주도록 개선한다.
  • 대용량데이터(사진)전송 : 현재 XBee의 최대전송속도는 200KB/s이므로 원본사진을 그대로 보내기 어려우므로 압축과정을 거쳐 보낼 수 있도록 개선한다.
  • 보안
    • 사용자<->오로라 디바이스의 암호화 지원
    • 블루투스 장치 연결 암호 활성화

느낀점

  • < HK > - Bluetooth & Control Unit
코드를 구성하기 전에 차분히 설계 도안을 생각하는 것이 중요하며, 작업이 빨리 진행되는 것은 좋으나 서두르면 오히려 독이 될 수 있다. 프로젝트의 전체적인 흐름을 구성하고, 맡은 부분을 작성하기 앞서 논리적인 흐름을 구하는 것이 도움이 된다. 물론 진행 중에 바뀔 수는 있고 몇 번은 뒤엎은 적도 있었지만 큰 틀은 벗어나지 않았다. Gprof, Valgrind 등의 프로파일링 툴을 통해 프로그램의 메모리 누수나 병목지점을 확인하고 수정하였다. 어느 한 부분만 동작이 잘된다고 해서 전체 프로젝트가 종료된 것은 아니므로 끝까지 팀원과 함께해야 한다. 오로라의 메인 표어는 여기서 나온 것이다.
  • < MS > - Android Communication
- 개인 프로젝트가 아닌 팀 프로젝트를 통해 협업과 프로젝트 관리하는 좋은 경험을 했다고 생각한다. 동시에 동료들간 의사소통의 중요성에 대해 생각해 보았다.
- 해당 개발 플랫폼에서 사용되는 프로그래밍 언어에 대한 지식이 부족했는데 빠르게 인터넷 자료를 활용하여 해결하는 방법을 사용하여 시간과 노력을 줄였다.
- 개인간 역할이 명확히 나뉘어져 있어서 프로젝트 전체에 대한 이해가 다소 늦게 이뤄졌던것에 대해 반성을 한다. 팀 구성원 모두가 전체 프로젝트에 대해 이해도가 높으면 그만큼 협업도 수월하고 의사소통이 원활히 되어 결과적으로 과업을 수행하는 데 큰 도움이 되겠다고 생각을 하였다.
  • < MG > - Android UI
  • < MJ > - XBee
-첫번째, 첫단추를 잘 꿰어야 된다는 말이 있듯이, 뼈대를 심사숙고하여 정하는 것이 중요하다. 나아가 향후 변동사항의 가능성을 간과하지 말고 Plan A만이 있는 방법은 지양한다. 실제로, 그 순간에는 최선이라고 보고 '이것뿐이다!'라고 생각한 것이 나중에 보면 차선일 수도 있는 것이므로 겸손한 자세가 필요하다.
-두번째, 혼자서는 못하는 일들은 여럿이 하니 가능하게 되었고, 무엇보다 내가 부족했던 점을 팀원들로부터 많이 배울 수 있는 기회가 많아서 고마웠다.
-세번째, Self-test가 잘 된다고 합쳤을 때 마냥 잘 되는 것이 아니므로 test를 하는 시간을 충분히 감안해야 한다. 생각보다 합쳤을 때 나오는 문제가 많았다.
-네번째, 단순 코드 돌리는 용도가 아닌 이상 최종사용자의 경험을 고려해서 방향을 매번 재확인하는 것이 중요하다.
-다섯번째, Debugging의 비중이 최소 절반 이상을 차지하므로 Self Testing module을 한번 잘 만들어놓는 편이 좋다.
-여섯번째, 여러개의 테스트 버젼을 만들다 보면 버젼관리가 중요하다.

참조 문헌 및 참고 자료

XBee Python API

OTTO

BlueZ

Android Bluetooth Sample Code