본문 바로가기

파이어몽키

[따라하기] 위험지역 경보 시스템 #1 - 비콘을 이용해 위험지역 진입 경보앱 만들기

업데이트 내역


2019-05 : 안드로이드 권한 모델 변경(런타임 시 권한 요청)이 적용되도록 업데이트

2019-10 : 안드로이드 권한 모델에 위치(ACCESS_FILE_LOCATION) 항목 추가



위 동영상(위험 지역 경보 시스템 데모) 중 위험 지역 진입 시 경보를 발생하는 앱을 따라하기를 통해 만들어 봅니다.

위험지역 진입 경보앱 만들기

❑ 앱 소개

앱 소개

이 앱은 위험지역에 진입 시 화면과 경고음으로 위험지역에 진입을 경고해 주는 (프로토 타입)앱입니다.


따라하기의 목적

따라하기를 통해 비콘을 이용한 위치기반 앱 개발을 시작합니다.


이 따라하기에서는 다음 내용을 다룹니다.

  • 비콘 컴포넌트를 이용해 비콘과의 거리를 활용합니다.
  • UI 컨트롤의 투명도에 애니메이션 효과를 적용해 화면경고 기능을 구현합니다.
  • 사운드 파일(*.mp3)을 앱과 배포해 경보음으로 사용합니다.

따라하기

0, 사전 준비사항

1, 빈 멀티-디바이스 애플리케이션 프로젝트를 생성하고 저장합니다.

2, 메인 UI를 만듭니다.

3, 위험 경보 UI를 추가합니다.

4, 위험경고 화면 효과, 경고음 기능을 추가합니다.

5, 비콘 컴포넌트 추가 후 이벤트를 만듭니다.

6, 비콘과 거리를 기반으로 위험지역 경보 이벤트를 발생합니다.

7, 블루투스 권한설정

8, 디바이스에 배포하고 테스트합니다.

0, 사전 준비사항

개발 도구 준비하기

델파이(또는 RAD Studio)를 실행합니다.(이 실습은 델파이 XE8 버전으로 작성되었습니다.)

만약, 델파이가 설치되지 않았다면 아래의 페이지에서 델파이를 다운로드 받아 설치 후 진행하시기 바랍니다.

따라하기에서 사용할 리소스 다운로드

DangerZoneAlertRes.zip


위 파일을 다운로드 후 적당한 위치에 압축해제하시기 바랍니다.

비콘 준비하기

비콘을 준비합니다.(시중에서 다양한 비콘을 판매하고 있습니다. 저는 Beacon B1을 이용해 진행했습니다.)
시중에서 판매되는 (표준을 지키는)대부분의 비콘과 연동됩니다.

비콘 정보 확인/설정하기

비콘의 UUID와 Major Id, Minor Id를 알아야 합니다. 제조사 사이트 또는 비콘 스캐너 앱을 통해 파악할 수 있습니다.
보통 같은 제품의 비콘은 동일한 UUID, Major Id, Minor Id로 설정되어있습니다. 비콘 별로 구별하기 위해 Major Id, Minor Id를 변경해야 하는데, 제조사의 안내에 따라 Major Id, Minor Id를 다르게 설정하기 바랍니다.(대부분 제조사에서 전용 앱을 제공합니다.)

1, 빈 멀티-디바이스 애플리케이션 프로젝트 생성

  1. File > New > Multi-Device Application - Delphi 메뉴를 선택 하고, Blank Application을 선택해 프로젝트를 생성합니다.
  2. File > Save all 메뉴를 선택 해 유닛이름을 "MainForm.pas"로 프로젝트 이름을 "DangerZoneAlert"으로 저장합니다.

2, 메인 UI를 만듭니다.

모바일 앱 스타일로 화면 구성하기 위해 폼디자이너 스타일을 Android로 변경합니다.


다음화면과 표를 이용해 위험지역 경보 앱 메인 UI를 개발합니다.
(이 따라하기는 비콘 활용 기능에 집중하기 위해 최소한의 UI로 구성했습니다.)


상위 오브젝트

오브젝트

속성

값(또는 설명)

 Form1

 ToolBar1

 

 

 ToolBar1

 Label1

 Align

 Client

 Text

 위험지역 경보 앱

 TextSettings.HorzAlign

 Center

 Switch1

 Align

 Right

 Form1

 Image1

 MultiResBitmap

 "factory.png 선택"

 StatusBar1

 Height

 32

 StatusBar1

 Label2

 Align

 Client

 Text

 위험지역과의 거리: 

3, 위험 경고 UI를 추가합니다.

위험 지역 진입 시 화면으로 경고를 주기위한 UI를 추가합니다. 붉은색 배경에 안내문구를 표시하고, 화면을 깜빡이게 합니다.

화면을 깜빡이는 기능은 다음 단계에서 진행합니다.


다음화면과 표를 이용해 위험 경보 UI를 추가합니다.


상위 오브젝트 

 오브젝트

 속성

 값(또는 설명)

 Form1

 Rectangle1

 Align

 Client

 Fill.Color

 Red

 Opacity

 0.8

 Rectangle1

 Layout1

 Align

 Center

 Width/Height

 228/186(적당히)

 Layout1

 Label3

 Align Text
 Text

 위 그림 참고(위험지역에 ...)

 lblDistance Align Bottom
 Name lblDistance

 TextSettings.Font.Size

 24
 TextSettings.HorzAlign Center

4, 위험경고 화면 효과, 경고음 기능을 추가합니다.

위험지역 진입을 알리기위해 경고음을 발생하고, 경고화면을 표시 후 화면을 깝빡여주는 기능을 구현합니다.


경고음을 실행하기위해 TMediaPlayer 컴포넌트를 경고화면을 깜빡이기 위해 TFloatAnimation 컴포넌트를 추가합니다.


Rectangle1의 FloatAnimation은 Opacity 속성 추가 메뉴(Create New TFloatAnimation)를 선택해 생성합니다.


상위 오브젝트

오브젝트

속성

값(또는 설명)

 Form1

 MediaPlayer1

 

 

 Rectangle1

 FloatAnimation1

 Autoreverse

 True

 Duration

 1

 Loop

 True

 PropertyName Opacity

 StartValue/StopValue

 0.8/0.2

경고음 사운드 파일 배포등록하기

구현에 앞서 경고음으로 사용할 사운드파일을 배포관리자에 등록힙니다.

  1. 메인메뉴에서 Project > Deployment 메뉴를 선택해 Deployment 윈도우를 표시합니다.
  2. Add files 버튼을 누르고 "alert.mp3"파일을 선택해 추가합니다.
  3. 배포 플랫폼 선택 콤보박스를 Android platform과 iOS Device platform으로 선택하고, 아래를 참고해 Remote Path를 수정합니다.

    • Android platform - assets\internal\
    • iOS Device platform - StartUp\Documents\

경고 기능 구현하기

private 영역에 아래 코드를 참고해 4개의 메소드를 선언합니다.

  private
    { Private declarations }
    procedure StartWarning; // 위험지역 진입 경고 시작
    procedure StopWarning;  // 위험지역 진입 경고 중지

    procedure StartSiren;   // 사이렌 시작
    procedure StopSiren;    // 사이렌 중지
  public

Ctrl + Shift + C 단축키(Complete Class at Cursor)를 이용해 메소드의 구현부를 자동완성합니다.

아래 코드를 참고해 구현부를 완성합니다.

procedure TForm1.StartSiren;
begin
  MediaPlayer1.FileName := TPath.Combine(TPath.GetDocumentsPath, 'alert.mp3');
  MediaPlayer1.Play;
end;

procedure TForm1.StopSiren;
begin
  MediaPlayer1.Stop;
end;

procedure TForm1.StartWarning;
begin
  Rectangle1.Visible := True;
  FloatAnimation1.Enabled := True;

  StartSiren;
end;

procedure TForm1.StopWarning;
begin
  Rectangle1.Visible := False;
  FloatAnimation1.Enabled := False;

  StopSiren;
end;

사운드파일 경로 설정을 위해 사용한 TPath가 선언된 System.IOUtils 유닛을 유즈절에 추가합니다.

5, 비콘 컴포넌트 추가 후 이벤트를 만듭니다.

비콘 컴포넌트(TBeacon)는 안드로이드, iOS, OSX를 지원합니다. 타겟 플랫폼을 안드로이드로 변경 후 폼위에 비콘 컴포넌트를 추가합니다.


모니터링 대상 비콘 등록

비콘을 모니터링하기 위해 비콘 정보를 등록합니다.

  1. Beacon1 컴포넌트의 MonitorizedRegions 속성을 선택 후 확장 버튼을 클릭합니다.
  2. 편집창에서 Add new 버튼을 눌러 항목을 추가합니다.
  3. 대상 비콘의 UUID, Major, Minor 값을 입력합니다.

(만약, Major, Minor 값을 -1로 설정하면 UUID만 이용해 대상을 탐색합니다.)


비콘 인스턴스 할당하기

비콘 변수를 선언하고, BeaconEnter, BeaconExit 이벤트에서 비콘 인스턴스를 할당해줍니다.


private 영역에 변수를 선언합니다.

  private
    { Private declarations }
    FBeacon: IBeacon;

 비콘 컴포넌트의 OnBeaconEnter, OnBeaconExit 이벤트 핸들러를 만들고 비콘 인스턴스를 할당합니다.

(이벤트 핸들러는 이벤트의 우측 영역을 더블클릭하면 생성됩니다.)


procedure TForm1.Beacon1BeaconEnter(const Sender: TObject;
  const ABeacon: IBeacon; const CurrentBeaconList: TBeaconList);
begin
  FBeacon := ABeacon;
end;

procedure TForm1.Beacon1BeaconExit(const Sender: TObject;
  const ABeacon: IBeacon; const CurrentBeaconList: TBeaconList);
begin
  FBeacon := nil;
end;

비콘 컴포넌트 동작을 활성화하기

안드로이드 권한 모델 변경으로 10.3 이상의 버전은 런타임 시 권한 요청하도록 변경이 필요합니다.

10.2 이전 버전
스위치(Switch1)를 키면 비콘이 동작하도록 Switch1의 OnSwitch 이벤트 핸들러 생성 후 아래 코드를 입력합니다.
procedure TForm1.Switch1Switch(Sender: TObject);
begin
  Beacon1.Enabled := Switch1.IsChecked;
end;

10.3 이상 버전(런타임 시 권한 요청)
구현부 uses 절에 필요한 유닛을 추가합니다.

implementation

uses
  System.Permissions,
{$IFDEF ANDROID}
  Androidapi.JNI.Os,
  Androidapi.JNI.JavaTypes,
  Androidapi.Helpers,
{$ENDIF}
  FMX.DialogService;

{$R *.fmx}

스위치(Switch1)를 키면 권한을 요청하고, 비콘이 동작하도록 Switch1의 OnSwitch 이벤트 핸들러 생성 후 아래 코드를 입력합니다.(2019-10-23: 위치(ACESS_FINE_LOCATION) 권한이 없어 비콘이 실행되지 않아 추가)

procedure TForm1.Switch1Switch(Sender: TObject);
begin
{$IFDEF ANDROID}
  if Switch1.IsChecked then
  begin
    PermissionsService.RequestPermissions(
      [
        JStringToString(TJManifest_permission.JavaClass.BLUETOOTH),
        JStringToString(TJManifest_permission.JavaClass.BLUETOOTH_ADMIN), 
        JStringToString(TJManifest_permission.JavaClass.ACCESS_FINE_LOCATION)
     ],
      procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>)
        begin
          if (Length(AGrantResults) = 3)
          and (AGrantResults[0] = TPermissionStatus.Granted)
          and (AGrantResults[1] = TPermissionStatus.Granted)
          and (AGrantResults[2] = TPermissionStatus.Granted) then
            { activate or deactivate the location sensor }
            Beacon1.Enabled := True
          else
          begin
            Switch1.IsChecked := False;
            TDialogService.ShowMessage('블루투스 권한이 없습니다.');
          end;
        end);
  end
  else
    Beacon1.Enabled := False;
{$ELSE}
  Beacon1.Enabled := Switch1.IsChecked;
{$ENDIF}
end;

6, 비콘과 거리를 기반으로 위험지역 경보 이벤트를 발생합니다.

이제 모든 준비는 완료되었고, 비콘과의 거리를 계산해 위험지역 진입 경보 이벤트를 발생하면 됩니다.

위험지역 진입에 대한 경보는 아래와 같은 기준으로 시작하고 중단합니다.

위험지역 진입 경보 기준

  1. 비콘과의 거리가 1.0미터 이하인 경우 위험지역으로 간주한다.
  2. 경보가 아닌 상태에서 위험지역에 3초이상 연속으로 머무른 경우 경보를 시작한다.
  3. 경보가 발생중인 상태에서 위험지역을 3초이상 연속으로 벗어난 경우 경보를 중단한다.

비콘과 거리를 주기적으로 확인하기 위해 타이머 컴포넌트(Timer1)를 폼에 추가합니다.


아래 변수를 private 영역에 선언합니다.

    FIsWarning: Boolean;          // 경고 중인가?
    FDangerAreaStaySecs,          // 위험지역에 머무른 시간(초)
    FDangerAreaOutSecs: Integer;  // 위험지역에서 벗어난 시간(초)


Timer1의 OnTimer 이벤트 핸드러를 생성하고, 아래 코드를 추가합니다.

procedure TForm1.Timer1Timer(Sender: TObject);
var
  InDangerArea: Boolean;  // 위험지역에 있는가?(비콘과의 거리가 1m 이내인가?)
begin
  if Assigned(FBeacon) then
  begin
    InDangerArea := FBeacon.Distance <= 1;

    // 경고 중 아님
      // 위험지역에 3초간 머무른 경우 경고 시작
    if not FIsWarning then
    begin
      if InDangerArea then
        Inc(FDangerAreaStaySecs)
      else
        FDangerAreaStaySecs := 0
      ;

      if FDangerAreaStaySecs >= 3 then
      begin
        FIsWarning := True;
        StartWarning;
        FDangerAreaStaySecs := 0
      end;
    end
    // 경고 중
      // 위험지역을 3초 이상 벗어난 경우 경고 중단
    else if FIsWarning then
    begin
      if not InDangerArea then
        Inc(FDangerAreaOutSecs)
      else
        FDangerAreaOutSecs := 0;

      if FDangerAreaOutSecs >= 3 then
      begin
        FIsWarning := False;
        StopWarning;
        FDangerAreaOutSecs := 0;
       end;
    end;

    // 위험지역과의 거리를 표시
    lblDistance.Text := FBeacon.Distance.ToString;
    Label3.Text := '위험지역과의 거리: ' + FBeacon.Distance.ToString + ' m';
  end;
end;

7, 블루투스 권한설정

(이 과정은 안드로이드 플랫폼을 사용하지 않는 경우 생략할 수 있습니다.)

비콘은 블루투스 LE(Low Energy) 기반으로 통신합니다.  블루투스 사용권한을 설정합니다.

Project > Options 메뉴를 선택 후 Uses Permissions 화면으로 이동합니다.


Bluetooth와 Bluetooth admin 두개의 권한을 사용하도록 설정합니다.

8, 디바이스에 배포하고 테스트합니다.

처음 디바이스에 배포하는 경우 먼저 모바일 개발환경 설정하고 진행하기 바랍니다.


개발을 완료했습니다. 이제 준비된 개발환경으로 플랫폼을 선택 후 Run > Run Without Debugging 메뉴를 이용해 실행하고 테스트 합니다.



테스트 방법 : 

  • 앱을 실행 후 비콘과 1미터 이내로 접근 후 3초이상 머무르면 경고가 발생
  • 경고 발생 중 1미터 초과 거리로 이동 후 3초이상 머무르면 경고 중단


참고자료