메뉴 건너뛰기

모바일앱


이 연재는 아래의 방법들을 설명합니다.

 

1. 라즈베리파이에서 소형 진동모터를 제어하는 방법
2. 라즈베리파이에서 BLE를 동작시키고 다른 디바이스에서 검색되도록 하는 방법
3. 안드로이드에서 라즈베리파이의 BLE(Advertising)신호를 검색하고 연결한 후 데이터를 전송하는 방법

 

현재 안드로이드 마켓에서 배포되고 있고 개발중인 "두근두근"이라는 앱에 블루투스 기능을 추가했던 내용을 기록하였습니다.

정확하게는, 앱과 라즈베리파이를 블루투스로 연결하였고 라즈베리파이에 붙어 있는 진동모터를 동작시키도록 개발한 내용을 정리하였습니다.

 

라즈베리파이에서 BLE를 동작키고 다른 디바이스에서 검색되도록 하는 방법

 

- 아래 경로에서 라즈베리파이의 BLE를 Advertising 모드로 설정하는 내용을 참고해서 진행합니다.

 https://scribles.net/running-ble-advertising-example-code-on-raspbian-stretch/

 

- 상기의 방법으로 환경을 모두 갖추었고 BLE의 동작이 잘 되는 것도 확인했다면 이제 나만의 UUID를 Advertising 하며 연결을 기다리는 GATT 서버 코드를 작성해 보겠습니다. 임의의 폴더를 하나 만들어서 아래 두개의 파일을 생성해 주세요.

1. 파일이름 : dkdk_service.py

import dbus
import dbus.mainloop.glib
from gpiozero import PWMOutputDevice
from time import sleep, time

try:
    from gi.repository import GObject
except ImportError:
    import gobject as GObject

from bluez_components import *

mainloop = None


def set_vib(motor, value):
    if value[0] == 0x00:
        motor.value = 0.8
    else:
        motor.value = 0

class cmdChrc(Characteristic):
    CMD_UUID = 'c6a89af5-0385-4d4a-8cb4-c856fcbf1321'

    def __init__(self, bus, index, service, motor):
        Characteristic.__init__(
            self, bus, index,
            self.CMD_UUID, 
            ['read', 'write'],
            service)
        self.value = [ 0x00 for i in xrange(1024) ] 
        self.motor = motor

    def ReadValue(self, options):
        print('RowCharacteristic Read: ' + repr(self.value))
        return self.value

    def WriteValue(self, value, options):
        print('RowCharacteristic Write: ' + repr(value))
        set_vib(self.motor, value)


class MotorService(Service):
    DKDK_SVC_UUID = 'c6a89af5-0385-4d4a-8cb4-c856fcbf1320'

    def __init__(self, bus, index, motor):
        Service.__init__(self, bus, index, self.DKDK_SVC_UUID, True)
        self.add_characteristic(cmdChrc(bus, 0, self, motor))


class MotorApplication(Application):
    def __init__(self, bus, motor):
        Application.__init__(self, bus)
        self.add_service(MotorService(bus, 0, motor))


class MotorAdvertisement(Advertisement):
    def __init__(self, bus, index):
        Advertisement.__init__(self, bus, index, 'peripheral')
        self.add_service_uuid(MotorService.DKDK_SVC_UUID)
        self.include_tx_power = True


def setup_motor():
    motor = PWMOutputDevice(14)
    return motor


def register_ad_cb():
    """
    Callback if registering advertisement was successful
    """
    print('Advertisement registered')


def register_ad_error_cb(error):
    """
    Callback if registering advertisement failed
    """
    print('Failed to register advertisement: ' + str(error))
    mainloop.quit()


def register_app_cb():
    """
    Callback if registering GATT application was successful
    """
    print('GATT application registered')


def register_app_error_cb(error):
    """
    Callback if registering GATT application failed.
    """
    print('Failed to register application: ' + str(error))
    mainloop.quit()


def main():
    global mainloop
    global motor

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    bus = dbus.SystemBus()

    # Get ServiceManager and AdvertisingManager
    service_manager = get_service_manager(bus)
    ad_manager = get_ad_manager(bus)

    # Create gatt services
    motor = setup_motor()
    app = MotorApplication(bus, motor)

    # Create advertisement
    dkdk_advertisement = MotorAdvertisement(bus, 0)

    mainloop = GObject.MainLoop()

    # Register gatt services
    service_manager.RegisterApplication(app.get_path(), {},
                                        reply_handler=register_app_cb,
                                        error_handler=register_app_error_cb)

    # Register advertisement
    ad_manager.RegisterAdvertisement(dkdk_advertisement.get_path(), {},
                                     reply_handler=register_ad_cb,
                                     error_handler=register_ad_error_cb)

    try:
        mainloop.run()
    except KeyboardInterrupt:
        print("Finished")


if __name__ == '__main__':
    main()


2. 파일이름: bluez_components.py

파일경로: https://github.com/WIStudent/Bluetooth-Low-Energy-LED-Matrix/blob/master/src/bluez_components.py

 

- dkdk_service.py 파일은 타 디바이스가 주위의 BLE 디바이스를 스캔하면 라즈베리파이가 나타나도록 하는 코드입니다. 뿐만아니라 타 디바이스가 BLE로 연결을 시도하면 연결을 맺고 명령을 받는 기능도 가지고 있습니다. 이에 수정이 필요한 부분의 코드를 살펴 보겠습니다.


1. 타 디바이스가 스캔을 하면 검색되어지는 UUID를 정하는 곳 입니다 : 가령, 안드로이드 앱에서 주위의 BLE 디바이스를 검색했는데 여러 BLE 디바이스가 나오면 이 UUID로 연결 대상을 찾을 수 있습니다. (참고로, mac address 로 찾을 수도 있습니다)


:
class MotorService(Service):
    # 이 UUID의 내용을 새로 생성한 UUID로 바꿔 주세요
    DKDK_SVC_UUID = 'c6a89af5-0385-4d4a-8cb4-c856fcbf1320'

    def __init__(self, bus, index, motor):
        Service.__init__(self, bus, index, self.DKDK_SVC_UUID, True)
        self.add_characteristic(cmdChrc(bus, 0, self, motor))
:


2. 타 디바이스가 라즈베리파이와 연결을 완료하고 명령을 전송할때 사용할 통신 특성(characteristic)입니다.

즉, 라즈베리파이와 연결한 디바이스는 이 특성을 선택하여 통신을 시도합니다. 이때 특성을 찾아 선택하는 방법도 UUID 입니다. 이 UUID도 아래의 코드에서 수정해 줍니다.

:
class cmdChrc(Characteristic):
    # 아래의 내용을 UUID를 새로 생성하여 바꿔주거나 생성했던 UUID를 일부 수정한 후 바꿔 줍니다.
    CMD_UUID = 'c6a89af5-0385-4d4a-8cb4-c856fcbf1321'

    def __init__(self, bus, index, service, motor):
        Characteristic.__init__(
:

3. 자, 이제 코드를 실행해 보겠습니다.

$ sudo python dkdk_service.py

4. 코드 실행후 아래와 같은 내용이 표시되면 정상입니다.

pi@raspberrypi:~/work/dkdk $ sudo python dkdk_service.py 
GetManagedObjects
GetAll
returning props
GATT application registered
Advertisement registered


스마트폰에서 블루투스 장치를 검색해 보면 라즈베리파이와 동일한 mac address를 가진 장치가 표시되고 있을 겁니다.


* raspberry pi의 mac 어드레스 확인 방법 (아래 XX.. 부분을 확인하세요)

$ hciconfig
hci0:	Type: Primary  Bus: UART
	BD Address: XX:XX:XX:XX:xx:XX  ACL MTU: 1021:8  SCO MTU: 64:1
	UP RUNNING 
	RX bytes:801 acl:0 sco:0 events:54 errors:0
	TX bytes:1928 acl:0 sco:0 commands:54 errors:0


- (데이터 또는)명령을 받으면 수행할 핸들러 수정하기

1. 아래의 코드는 타 디바이스로 부터 데이터를 받으면 호출되는 부분입니다. 'value'에 수신한 데이터가 담깁니다.(바이트 배열)

def WriteValue(self, value, options):
        # 타 디바이스가 바이트 배열 데이터를 보내면 value에 해당하는 값이 담깁니다.
        print('RowCharacteristic Write: ' + repr(value))
        # 이 예제에서는 모터를 끄고 켜는 함수를 호출합니다
        set_vib(self.motor, value)


2. 아래는 'value' 배열값의 첫번째가 '0x00'이면 모터를 켜고 그 외의 값이면 모터를 끄는 코드입니다.

def set_vib(motor, value):
    if value[0] == 0x00:
        motor.value = 0.8
    else:
        motor.value = 0



- 다음 연재에서는 안드로이드에서 블루투스로 라즈베리파이를 스캔하고 연결한 후 명령을 보내는 방법과 코드를 설명해 드리겠습니다.


* 아래 경로들의 내용을 참고하였습니다.

https://tobiastrumm.de/2016/10/04/turning-a-raspberry-pi-3-into-a-bluetooth-low-energy-peripheral/

https://github.com/WIStudent/Bluetooth-Low-Energy-LED-Matrix

 

 


* 참고로 열심히 배포중이고 개발중인 두근두근앱은 "https://top.dkdk.io"에서 확인하실 수 있습니다^_^









번호 제목 글쓴이 날짜 조회 수
공지 [TIP] 안드로이드 앱 빌드시 "Error:Execution failed for task ':app:compileDebugJavaWithJavac'" 오류가 발생할 경우 파이팅건맨 2017.09.13 6999
공지 [TIP] 죽지 않는 안드로이드 서비스 만들기 (Unstoppable service) [6] 파이팅건맨 2015.06.26 6604
공지 [TIP] 안드로이드 앱이 처음 설치될때 Referrer 정보 받아 오기 파이팅건맨 2016.08.30 3634
44 tizen .net wearable widget 질문해도 될까요? [1] 갤럭시규 2019.08.19 7
43 [TIP] iOS - UIWebView에 로컬 html 파일 로드하기 (Swift 4) 파이팅건맨 2019.06.05 48
42 [TIP] Tizen Push가 갑자기 내려오지 않을때 파이팅건맨 2019.05.20 40
41 [TIP] 두근두근앱이 라즈베리파이를 두근거리도록 개발한 기록 #3 파이팅건맨 2019.05.15 131
» [TIP] 두근두근앱이 라즈베리파이를 두근거리도록 개발한 기록 #2 파이팅건맨 2019.05.11 151
39 [TIP] Android 코드에서 블루투스 연결이 잘 안될때 파이팅건맨 2019.05.11 209
38 [TIP] 두근두근앱이 라즈베리파이를 두근거리도록 개발한 기록 #1 파이팅건맨 2019.05.07 220
37 [TIP] 타이젠 스튜디오에서 웨어러블 디바이스로 디버깅을 위한 바이너리 전송이 안 될때 파이팅건맨 2019.03.26 158
36 [TIP] http://tizen.org/system/tizenid 으로 타이젠 고유 id를 확보할 때 유의할 점 파이팅건맨 2019.03.14 80
35 [TIP] "cordova run android" 명령을 실행했는데 "A problem occurred evaluating project ':CordovaLib'"오류가 뜰때 파이팅건맨 2019.01.22 288
34 [TIP] Mac에서 Cordova run android 를 실행했는데 "Command failed with exit code EACCES" 오류가 뜰때 파이팅건맨 2019.01.22 151
33 [TIP] 안드로이드에서 심박수 측정하는 코드 [6] 파이팅건맨 2019.01.02 550
32 [TIP] Google Cloud API 사용시 안드로이드의 Assets 폴더에 있는 Crendential 파일 사용하기 파이팅건맨 2018.05.29 225
31 [TIP] 안드로이드 스튜디오에서 Error:android-apt plugin is incompatible with the Android Gradle plugin. Please use 'annotationProcessor' configuration instead 오류날때 파이팅건맨 2018.02.02 1951
30 [TIP] 안드로이드 앱 빌드시 "Error:Execution failed for task ':app:compileDebugJavaWithJavac'" 오류가 발생할 경우 파이팅건맨 2017.09.13 6999
29 [TIP] Error:java.lang.OutOfMemoryError: GC overhead limit exceeded 파이팅건맨 2016.11.03 881
28 [TIP] 안드로이드 앱이 처음 설치될때 Referrer 정보 받아 오기 파이팅건맨 2016.08.30 3634
27 [TIP] 안드로이드에서 구글 스프레드시트에 데이터 쓰기 파이팅건맨 2016.05.04 2475
26 [TIP] 안드로이드에서 키보드가 나타날 때 레이아웃이 위로 움직인다면 파이팅건맨 2016.03.22 838
25 [TIP] 안드로이드 - 설치된 앱 목록 얻기 파이팅건맨 2016.03.22 3116
위로