Slide 1

Slide 1 text

BLEΛ࢖ͬͨΞϓϦΛ ܧଓతʹ։ൃ͢ΔͨΊʹ @MoyuruAizawa

Slide 2

Slide 2 text

Moyuru Aizawa Software Engineer of Catlog, RABO. Previously at Azit, CyberAgent, and Eureka. Love Metal, Hardcore and EDM. Author of “ΈΜͳͷKotlin”. MoyuruAizawa Rank

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

‣ BLEʹΑΔσόΠεͱͷ௨৴ํ๏ ‣ GATT ‣ BluetoothGatt ‣ Coroutine, FlowΛ࢖ͬͨվળ ‣ Tips ͸ͳ͢͜ͱ

Slide 7

Slide 7 text

‣ BLEΛ࢖͏ΞϓϦ࡞ͬͯͱݴΘΕͨΒԿͱͳ͘͜ͷηογϣϯͷ͜ͱ Λࢥ͍ग़͢ ‣ ͦͯ͠BLEؔ࿈ͷAPIͷ࢖͍ํΛͳΜͱͳ͘ΩϟονΞοϓ͢Δ ‣ ͍ͭ͜͸CoroutineͱFlow࢖࣮ͬͯ૷ͨ͠Μͩ΄ʙΜ🤔ͬͯࢥ͏ ‣ ΅͘ͷ͔Μ͕͍͖͑ͨ͞ΐ͏ͷBLEΞϓϦΛ࣮૷͢Δ ΰʔϧ

Slide 8

Slide 8 text

BLE BLE (Bluetooth Low Energy) Bluetooth Low Energy (Bluetooth LE, BLE) ͱ͸ɺແઢPANٕज़Ͱ ͋Δ Bluetooth ͷҰ෦Ͱɺόʔδϣϯ 4.0 ͔Β௥Ճʹͳͬͨ௿ফඅ ిྗͷ௨৴Ϟʔυɻ Ҿ༻: https://ja.wikipedia.org/wiki/Bluetooth_Low_Energy

Slide 9

Slide 9 text

‣ BluetoothLeScanner ‣ पลͷBluetoothDeviceΛscan͢Δ ‣ BluetoothDevice ‣ deviceͷ৘ใ(name, address…)Λऔಘ͢Δ ‣ deviceͱconnectionΛுΔ ‣ BluetoothGatt, BluetoothGattService, BluetoothGattCharacteristic, BluetoothGattDescriptor ‣ GATTΛ࢖ͬͯdeviceͱ௨৴͢Δ BLEͰѻ͏ओͳAPI

Slide 10

Slide 10 text

‣ BluetoothLeScanner ‣ पลͷBluetoothDeviceΛscan͢Δ ‣ BluetoothDevice ‣ deviceͷ৘ใ(name, address…)Λऔಘ͢Δ ‣ deviceͱconnectionΛுΔ ‣ BluetoothGatt, BluetoothGattService, BluetoothGattCharacteristic, BluetoothGattDescriptor ‣ GATTΛ࢖ͬͯdeviceͱ௨৴͢Δ BLEͰѻ͏ओͳAPI

Slide 11

Slide 11 text

GATT? 🤔

Slide 12

Slide 12 text

GATT (Generic Attribute) GATT Service Characteristic Declaration, Value, Descriptor Characteristic Declaration, Value, Descriptor

Slide 13

Slide 13 text

ͳΔ΄ͲΘ͔ΒΜ

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

‣ ؾԹΛऔಘͰ͖Δ ‣ ࣪౓ΛऔಘͰ͖Δ ‣ ੺֎ઢͷൃ৴Λ໋ྩͰ͖Δ ‣ ௚લʹड৴ͨ͠੺֎ઢͷσʔλΛऔಘͰ͖Δ ՍۭͷεϚʔτϦϞίϯΛߟ͑ͯΈΔ e.g. ImaginaryRemo BLEΛ௨ͯ͠

Slide 16

Slide 16 text

GATT (Generic Attribute) GATT MeterService Temperature Characteristic 24.6℃ Humidity Characteristic 52% IRService Send Characteristic Received Characteristic e.g. ImaginaryRemo

Slide 17

Slide 17 text

ࢼ͠ʹImaginaryRemo͔Β ࣪౓Λऔಘͯ͠ΈΑ͏

Slide 18

Slide 18 text

‣ ImaginaryRemoͱͷίωΫγϣϯΛுΔ ‣ onConnectionStateChangeͰCONNECTED͕དྷΔͷΛ଴ͭ ‣ ίωΫγϣϯ͕ுΕͨΒdiscoverServicesͰServicesΛ୳͢ ‣ onServicesDiscoveredͰServices͕ݟ͔ͭΔͷΛ଴ͭ ‣ BluetoothGattΛ௨ͯ͡໨తͷServiceΛऔಘ͠ɺ BluetoothGattService͔Β໨తCharacteristicΛऔಘ͠ɺૢ࡞͢Δ େ·͔ͳྲྀΕ

Slide 19

Slide 19 text

imaginaryRemoDevice.connectGatt( context = context, autoConnect = false, callback = object : BluetoothGattCallback() { … } ) BluetoothDevice#connectGatt

Slide 20

Slide 20 text

imaginaryRemoDevice.connectGatt( context = context, autoConnect = false, callback = object : BluetoothGattCallback() { … } ) BluetoothDevice#connectGatt

Slide 21

Slide 21 text

override fun onConnectionStateChange( gatt: BluetoothGatt, status: Int, newState: Int ) { if (newState == BluetoothGatt.STATE_CONNECTED) { val isDiscoveryStarted = gatt.discoverServices() if (!isDiscoveryStarted) { // operationͷࣦഊΛϢʔβʔʹ௨஌ͨ͠ΓͳͲɻ } } BluetoothGattCallback#onConnectionStateChange

Slide 22

Slide 22 text

override fun onConnectionStateChange( gatt: BluetoothGatt, status: Int, newState: Int ) { if (newState == BluetoothGatt.STATE_CONNECTED) { val isDiscoveryStarted = gatt.discoverServices() if (!isDiscoveryStarted) { // operationͷࣦഊΛϢʔβʔʹ௨஌ͨ͠ΓͳͲɻ } } BluetoothGattCallback#onConnectionStateChange

Slide 23

Slide 23 text

override fun onConnectionStateChange( gatt: BluetoothGatt, status: Int, newState: Int ) { if (newState == BluetoothGatt.STATE_CONNECTED) { val isDiscoveryStarted = gatt.discoverServices() if (!isDiscoveryStarted) { // operationͷࣦഊΛϢʔβʔʹ௨஌ͨ͠ΓͳͲɻ } } BluetoothGattCallback#onConnectionStateChange

Slide 24

Slide 24 text

override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { val characteristic = gatt.getService(METER_SERVICE_UUID) ?.getCharacteristic(HUMIDITY_CHARACTERISTIC_UUID) requireNotNull(characteristic) val isSuccess = gatt.readCharacteristic(characteristic) if (!isSuccess) { // operationͷࣦഊΛϢʔβʔʹ௨஌ͨ͠ΓͳͲɻ Ҏ߱εϥΠυ಺Ͱ͸লུ } } BluetoothGattCallback#onServiceDiscovered

Slide 25

Slide 25 text

override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { val characteristic = gatt.getService(METER_SERVICE_UUID) ?.getCharacteristic(HUMIDITY_CHARACTERISTIC_UUID) requireNotNull(characteristic) val isSuccess = gatt.readCharacteristic(characteristic) if (!isSuccess) { // operationͷࣦഊΛϢʔβʔʹ௨஌ͨ͠ΓͳͲɻ Ҏ߱εϥΠυ಺Ͱ͸লུ } } BluetoothGattCallback#onServiceDiscovered

Slide 26

Slide 26 text

override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { val characteristic = gatt.getService(METER_SERVICE_UUID) ?.getCharacteristic(HUMIDITY_CHARACTERISTIC_UUID) requireNotNull(characteristic) val isSuccess = gatt.readCharacteristic(characteristic) if (!isSuccess) { // operationͷࣦഊΛϢʔβʔʹ௨஌ͨ͠ΓͳͲɻ Ҏ߱εϥΠυ಺Ͱ͸লུ } } BluetoothGattCallback#onServiceDiscovered

Slide 27

Slide 27 text

override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { val characteristic = gatt.getService(METER_SERVICE_UUID) ?.getCharacteristic(HUMIDITY_CHARACTERISTIC_UUID) requireNotNull(characteristic) val isSuccess = gatt.readCharacteristic(characteristic) if (!isSuccess) { // operationͷࣦഊΛϢʔβʔʹ௨஌ͨ͠ΓͳͲɻ Ҏ߱εϥΠυ಺Ͱ͸লུ } } BluetoothGattCallback#onServiceDiscovered

Slide 28

Slide 28 text

override fun onCharacteristicRead( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int ) { if ( characteristic.uuid == HUMIDITY_CHARACTERISTIC_UUID ) { characteristic.value // ࣪౓ GET!! } } BluetoothGattCallback#onCharacteristicRead

Slide 29

Slide 29 text

override fun onCharacteristicRead( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int ) { if ( characteristic.uuid == HUMIDITY_CHARACTERISTIC_UUID ) { characteristic.value // ࣪౓ GET!! } } BluetoothGattCallback#onCharacteristicRead

Slide 30

Slide 30 text

‣ onConnectionStateChangeͰCONNECTEDΛ଴ͭ ‣ discoverServicesΛ࣮ߦ͢Δ ‣ onServicesDiscoveredΛ଴ͭ ‣ onServicesDiscoveredҎ߱ ೚ҙͷService, CharacteristicʹΞΫηεՄೳʹͳΔ ‣ READ/WRITEΛߦͬͨΒͦͷ׬ྃ΋଴ͨͳ͚Ε͹ͳΒͳ͍ ͓͖͍֮͑ͯͨ͜ͱ

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

ImaginaryRemoʹ͍ͭͯ ΋͏গ͠ෳࡶͳέʔεΛߟ͑ͯΈΔ

Slide 33

Slide 33 text

‣ αʔόʔΛܦ༝ͯ͠ɺ֎ग़ઌ͔ΒՈిΛૢ࡞Ͱ͖Δ ‣ Wi-Fiʹ઀ଓ͢Δඞཁ͕͋Δɻ ‣ σόΠε͕ΞΫηεՄೳͳWi-FiͷSSIDҰཡΛεϚʔτϑΥϯʹฦ͢ ‣ Wi-FiͷSSIDͱPASSWORDΛσόΠεʹૹ৴͢Δ ྫ͑͹ɺσόΠεͷॳظઃఆϑϩʔ

Slide 34

Slide 34 text

Service͸͓ͦΒ͘͜ͷΑ͏ͳઃܭʹͳΔ ConfigurationService SSID Characteristic Password Characteristic FoundSSID Characteristic RequestSearchSSID Characteristic

Slide 35

Slide 35 text

override fun onConnectionStateChange( gatt: BluetoothGatt, status: Int, newState: Int ) { if (newState == BluetoothGatt.STATE_CONNECTED) gatt.discoverServices() } ͱΓ͋͑ͣconnectedΛ଴ͭ

Slide 36

Slide 36 text

override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { val characteristics = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(FOUND_SSID_CHARACTERISTIC) requireNotNull(characteristics) gatt.setCharacteristicNotification(characteristics, true) val descriptor = characteristics.getDescriptor(CCCD) descriptor .value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE gatt.writeDescriptor(descriptor) } servicesDiscoveredΛ଴ͭ

Slide 37

Slide 37 text

override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { val characteristics = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(FOUND_SSID_CHARACTERISTIC) requireNotNull(characteristics) gatt.setCharacteristicNotification(characteristics, true) val descriptor = characteristics.getDescriptor(CCCD) descriptor .value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE gatt.writeDescriptor(descriptor) } σόΠε͕ݟ͚ͭͨSSIDΛ௨஌͢ΔΑ͏ઃఆ͢Δ

Slide 38

Slide 38 text

override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { val characteristics = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(FOUND_SSID_CHARACTERISTIC) requireNotNull(characteristics) gatt.setCharacteristicNotification(characteristics, true) val descriptor = characteristics.getDescriptor(CCCD) descriptor .value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE gatt.writeDescriptor(descriptor) } σόΠε͕ݟ͚ͭͨSSIDΛ௨஌͢ΔΑ͏ઃఆ͢Δ

Slide 39

Slide 39 text

override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { val characteristics = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(FOUND_SSID_CHARACTERISTIC) requireNotNull(characteristics) gatt.setCharacteristicNotification(characteristics, true) val descriptor = characteristics.getDescriptor(CCCD) descriptor .value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE gatt.writeDescriptor(descriptor) } σόΠε͕ݟ͚ͭͨSSIDΛ௨஌͢ΔΑ͏ઃఆ͢Δ

Slide 40

Slide 40 text

override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { val characteristics = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(FOUND_SSID_CHARACTERISTIC) requireNotNull(characteristics) gatt.setCharacteristicNotification(characteristics, true) val descriptor = characteristics.getDescriptor(CCCD) descriptor .value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE gatt.writeDescriptor(descriptor) } σόΠε͕ݟ͚ͭͨSSIDΛ௨஌͢ΔΑ͏ઃఆ͢Δ BluetoothSIGͰ༧Ί ఆٛ͞Ε͍ͯΔUUID

Slide 41

Slide 41 text

override fun onDescriptorWrite( gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int ) { if (descriptor.uuid == CCCD) { val characteristic = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(REQUEST_SEARCH_SSID_CHARACTERISTIC) requireNotNull(characteristic) characteristic.value = flag gatt.writeCharacteristic(characteristic) } } ௨஌ͷઃఆ׬ྃΛ଴ͭ

Slide 42

Slide 42 text

override fun onDescriptorWrite( gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int ) { if (descriptor.uuid == CCCD) { val characteristic = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(REQUEST_SEARCH_SSID_CHARACTERISTIC) requireNotNull(characteristic) characteristic.value = flag gatt.writeCharacteristic(characteristic) } } SSIDͷݕࡧΛ໋ྩ͢Δ

Slide 43

Slide 43 text

override fun onDescriptorWrite( gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int ) { if (descriptor.uuid == CCCD) { val characteristic = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(REQUEST_SEARCH_SSID_CHARACTERISTIC) requireNotNull(characteristic) characteristic.value = flag gatt.writeCharacteristic(characteristic) } } SSIDͷݕࡧΛ໋ྩ͢Δ

Slide 44

Slide 44 text

override fun onCharacteristicChanged( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic ) { if (characteristic.uuid == FOUND_SSID_CHARACTERISTIC) { appendSsidToList(characteristic.value) } } ݟ͔ͭͬͨSSIDͷ௨஌Λ଴ͬͯஞ࣍UIʹ൓ө͢Δ

Slide 45

Slide 45 text

override fun onCharacteristicChanged( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic ) { if (characteristic.uuid == FOUND_SSID_CHARACTERISTIC) { appendSsidToList(characteristic.value) } } ݟ͔ͭͬͨSSIDͷ௨஌Λ଴ͬͯஞ࣍UIʹ൓ө͢Δ

Slide 46

Slide 46 text

onClick { val characteristic = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(SSID_CHARACTERISTIC) requireNotNull(characteristic) characteristic.value = selectedSsid gatt.writeCharacteristic(characteristic) } બ୒͞ΕͨSSIDΛॻ͖ࠐΉ

Slide 47

Slide 47 text

onClick { val characteristic = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(SSID_CHARACTERISTIC) requireNotNull(characteristic) characteristic.value = selectedSsid gatt.writeCharacteristic(characteristic) } બ୒͞ΕͨSSIDΛॻ͖ࠐΉ

Slide 48

Slide 48 text

override fun onCharacteristicWrite( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int ) { when (characteristic.uuid) { SSID_CHARACTERISTIC -> { val characteristic = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(PASSWORD_CHARACTERISTIC) requireNotNull(characteristic) characteristic.value = password gatt.writeCharacteristic(characteristic) } PASSWORD_CHARACTERISTIC -> { … } } } SSIDͷॻ͖ࠐΈ׬ྃΛ଴ͭ

Slide 49

Slide 49 text

override fun onCharacteristicWrite( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int ) { when (characteristic.uuid) { SSID_CHARACTERISTIC -> { val characteristic = gatt.getService(CONFIGURATION_SERVICE) ?.getCharacteristic(PASSWORD_CHARACTERISTIC) requireNotNull(characteristic) characteristic.value = password gatt.writeCharacteristic(characteristic) } PASSWORD_CHARACTERISTIC -> { … } } } PASSWORDΛॻ͖ࠐΉ

Slide 50

Slide 50 text

override fun onCharacteristicWrite( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int ) { when (characteristic.uuid) { SSID_CHARACTERISTIC -> { … } PASSWORD_CHARACTERISTIC -> { // PASSWORDॻ͖ࠐΈ׬ྃ // Զͨͪͷઓ͍͸·ͩ͜Ε͔Βͩ!!! // ૄ௨֬ೝͳΓͳΜͳΓͷ໋ྩ΁ଓ͘… } } } PASSWORDॻ͖ࠐΈ׬ྃΛ଴ͭ

Slide 51

Slide 51 text

override fun onCharacteristicWrite( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int ) { when (characteristic.uuid) { SSID_CHARACTERISTIC -> { … } PASSWORD_CHARACTERISTIC -> { // PASSWORDॻ͖ࠐΈ׬ྃ // Զͨͪͷઓ͍͸·ͩ͜Ε͔Βͩ!!! // ૄ௨֬ೝͳΓͳΜͳΓͷ໋ྩ΁ଓ͘… } } } PASSWORDॻ͖ࠐΈ׬ྃΛ଴ͭ

Slide 52

Slide 52 text

ແཧʔ!!

Slide 53

Slide 53 text

Catlog΋࠷ॳ͸͜Μͳײ͡ͷίʔυͩͬͨ ͳͥ͜ΜͳίʔϧόοΫ஍ࠈʹ…

Slide 54

Slide 54 text

Catlog΋࠷ॳ͸͜Μͳײ͡ͷίʔυͩͬͨ onCharacteristicWriteͱ͔onDescriptorWriteͱ ͔ͷதͰ࣍ͷ໋ྩ΍ͬͯΔ͚ͲͳΜͰ…?

Slide 55

Slide 55 text

Catlog΋࠷ॳ͸͜Μͳײ͡ͷίʔυͩͬͨ Ұؾʹ໋ྩ͢ΓΌ͑͑΍Μ

Slide 56

Slide 56 text

Catlog΋࠷ॳ͸͜Μͳײ͡ͷίʔυͩͬͨ ͳΔ΄ͲͶɺͳΜ໋͔ྩͨ͠Βɺͦͷ݁Ռ͕ฦͬ ͯ͘Δ·Ͱ͸࣍ͷ໋ྩ͸Ͱ͖ͳ͍ͷ͔ɻ

Slide 57

Slide 57 text

Catlog΋࠷ॳ͸͜Μͳײ͡ͷίʔυͩͬͨ ͔ͩΒ͜Μͳ͜ͱʹ…

Slide 58

Slide 58 text

!

Slide 59

Slide 59 text

CoroutineͱFlowͰ͍͍ ײ͡ʹͰ͖ΔͷͰ͸?

Slide 60

Slide 60 text

Catlog΋࠷ॳ͸͜Μͳײ͡ͷίʔυͩͬͨ CoroutineͰॻ͚ΔΑ͏ʹ͢Ε͹ɺ໋ྩͩͨ͠Β໋ ྩͷ׬ྃΛawait͢Δͬͯ͜ͱ͕Ͱ͖ͦ͏ͳ༧ײ

Slide 61

Slide 61 text

Catlog΋࠷ॳ͸͜Μͳײ͡ͷίʔυͩͬͨ “Ұؾʹ໋ྩ͢ΓΌ͑͑΍Μ” Λίʔυͷݟͨ໨্͸࣮ݱͰ͖ͦ͏ͩ

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

BLE x Coroutine x Flow

Slide 64

Slide 64 text

‣ BLEؔ࿈ͷAPIΛCoroutine΍FlowͰwrap͢Δ ‣ BluetoothDeviceͱͷ௨৴Λ୲͏BluetoothClientΫϥεΛ࣮૷͢Δ ‣ ֤eventΛawait͢Δؔ਺Λఏڙ͢Δ ུ֓

Slide 65

Slide 65 text

‣ sealed interfaceΛ࢖ͬͯBluetoothGattCallbackͷ֤ؔ਺ʹରԠ͢ Δdata classΛఆٛ͢Δ ‣ લड़ͷdata classΛFlowͰแΜͰฦ֦͢ுؔ਺Λ࣮૷͢Δ ‣ RxBindingͱ͔CorbindͰ΍ΒΕͯΔख๏ BluetoothDevice#connectGatt

Slide 66

Slide 66 text

sealed interface BluetoothGattEvent { val gatt: BluetoothGatt data class ConnectionStateChange( override val gatt: BluetoothGatt, val status: Int, val newState: Int ) : BluetoothGattEvent data class ServicesDiscovered( override val gatt: BluetoothGatt, val status: Int ) : BluetoothGattEvent // ུ } BluetoothDevice#connectGatt

Slide 67

Slide 67 text

fun BluetoothDevice.connectGatt(context: Context, autoConnect: Boolean) = channelFlow { val gatt = connectGatt(context, autoConnect, object : BluetoothGattCallback(){ override fun onConnectionStateChange( gatt: BluetoothGatt, status: Int, newState: Int ) { trySend(BluetoothGattEvent.ConnectionStateChange(gatt, status, newState)) } override fun onServicesDiscovered( gatt: BluetoothGatt, status: Int ) { trySend(BluetoothGattEvent.ServicesDiscovered(gatt, status)) } // ུ }) } BluetoothDevice#connectGatt

Slide 68

Slide 68 text

fun BluetoothDevice.connectGatt(context: Context, autoConnect: Boolean) = channelFlow { // ུ awaitClose { gatt.disconnect() gatt.close() } } BluetoothDevice#connectGatt

Slide 69

Slide 69 text

class BluetoothClient( private val context: Context, private val device: BluetoothDevice ) { private val gattEvent = MutableStateFlow(null) private lateinit var gatt: BluetoothGatt // ུ } BluetoothClient

Slide 70

Slide 70 text

class BluetoothClient( private val context: Context, private val device: BluetoothDevice ) { private val gattEvent = MutableStateFlow(null) private lateinit var gatt: BluetoothGatt // ུ } BluetoothClient

Slide 71

Slide 71 text

class BluetoothClient( private val context: Context, private val device: BluetoothDevice ) { private val gattEvent = MutableStateFlow(null) private lateinit var gatt: BluetoothGatt // ུ } BluetoothClient

Slide 72

Slide 72 text

fun connect(autoConnect: Boolean, coroutineScope: CoroutineScope) { device.connectGatt(context, autoConnect) .onEach { gatt = it.gatt gattEvent.value = it } .launchIn(coroutineScope) } BluetoothClient#connect

Slide 73

Slide 73 text

fun connect(autoConnect: Boolean, coroutineScope: CoroutineScope) { device.connectGatt(context, autoConnect) .onEach { gatt = it.gatt gattEvent.value = it } .launchIn(coroutineScope) } BluetoothClient#connect

Slide 74

Slide 74 text

suspend fun awaitConnected() { if (connectionState == BluetoothProfile.STATE_CONNECTED) return gattEvent.first { it is BluetoothGattEvent.ConnectionStateChange && it.newState == BluetoothProfile.STATE_CONNECTED } } CONNECTEDΛawaitͰ͖Δ

Slide 75

Slide 75 text

suspend fun awaitConnected() { if (connectionState == BluetoothProfile.STATE_CONNECTED) return gattEvent.first { it is BluetoothGattEvent.ConnectionStateChange && it.newState == BluetoothProfile.STATE_CONNECTED } } CONNECTEDΛawaitͰ͖Δ ͜ͷsuspend functionΛݺΜͩCoroutine͸ ͜ͷ৚͕݅ຬͨ͞ΕΔ·Ͱதஅ͞ΕΔ

Slide 76

Slide 76 text

suspend fun awaitServicesDiscovered() { gattEvent .first { it is BluetoothGattEvent.ServicesDiscovered } } suspend fun awaitCharacteristicWritten(characteristicUuid: UUID) { gattEvent .first { it is BluetoothGattEvent.CharacteristicWrite && it.characteristic.uuid == characteristicUuid } } ֤event·Ͱawait͢Δ͜ͱ΋Ͱ͖Δ

Slide 77

Slide 77 text

bleClient.run { connect(false, coroutineScope) awaitConnected() discoverServices() awaitServicesDiscovered() setNotificationEnabled( CONFIGURATION_SERVICE, FOUND_SSID_CHARACTERISTIC, true ) awaitNotificationEnabled() observeNotification(FOUND_SSID_CHARACTERISTIC) .onEach { appendSsidToList(it.characteristic.value) } .launchIn(coroutineScope) writeCharacteristic( CONFIGURATION_SERVICE, REQUEST_SEARCH_SSID_CHARACTERISTIC, flag ) … σόΠεͱͷ௨৴खॱͱίʔυͷྲྀΕ͕Ұக͢Δ

Slide 78

Slide 78 text

bleClient.run { connect(false, coroutineScope) awaitConnected() discoverServices() awaitServicesDiscovered() setNotificationEnabled( CONFIGURATION_SERVICE, FOUND_SSID_CHARACTERISTIC, true ) awaitNotificationEnabled() observeNotification(FOUND_SSID_CHARACTERISTIC) .onEach { appendSsidToList(it.characteristic.value) } .launchIn(coroutineScope) writeCharacteristic( CONFIGURATION_SERVICE, REQUEST_SEARCH_SSID_CHARACTERISTIC, flag ) } σόΠεͱͷ௨৴खॱͱίʔυͷྲྀΕ͕Ұக͢Δ

Slide 79

Slide 79 text

bleClient.run { connect(false, coroutineScope) awaitConnected() discoverServices() awaitServicesDiscovered() setNotificationEnabled( CONFIGURATION_SERVICE, FOUND_SSID_CHARACTERISTIC, true ) awaitNotificationEnabled() observeNotification(FOUND_SSID_CHARACTERISTIC) .onEach { appendSsidToList(it.characteristic.value) } .launchIn(coroutineScope) writeCharacteristic( CONFIGURATION_SERVICE, REQUEST_SEARCH_SSID_CHARACTERISTIC, flag ) } σόΠεͱͷ௨৴खॱͱίʔυͷྲྀΕ͕Ұக͢Δ

Slide 80

Slide 80 text

bleClient.run { connect(false, coroutineScope) awaitConnected() discoverServices() awaitServicesDiscovered() setNotificationEnabled( CONFIGURATION_SERVICE, FOUND_SSID_CHARACTERISTIC, true ) awaitNotificationEnabled() observeNotification(FOUND_SSID_CHARACTERISTIC) .onEach { appendSsidToList(it.characteristic.value) } .launchIn(coroutineScope) writeCharacteristic( CONFIGURATION_SERVICE, REQUEST_SEARCH_SSID_CHARACTERISTIC, flag ) } σόΠεͱͷ௨৴खॱͱίʔυͷྲྀΕ͕Ұக͢Δ

Slide 81

Slide 81 text

bleClient.run { connect(false, coroutineScope) awaitConnected() discoverServices() awaitServicesDiscovered() setNotificationEnabled( CONFIGURATION_SERVICE, FOUND_SSID_CHARACTERISTIC, true ) awaitNotificationEnabled() observeNotification(FOUND_SSID_CHARACTERISTIC) .onEach { appendSsidToList(it.characteristic.value) } .launchIn(coroutineScope) writeCharacteristic( CONFIGURATION_SERVICE, REQUEST_SEARCH_SSID_CHARACTERISTIC, flag ) } σόΠεͱͷ௨৴खॱͱίʔυͷྲྀΕ͕Ұக͢Δ

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

ͦΜͳʹ؁͘ͳ͍BLE

Slide 84

Slide 84 text

։ൃऀʹݫ͍͠BLE͞Μ BLE͏Μͱ΋͢Μͱ΋͍Θͳ͘ͳͬͨΜ͚ͩͲ…

Slide 85

Slide 85 text

‣ BluetoothͷAPI͕௜໧ΩϝΔࣄ͕͋Δ ‣ ୺຤ͷઃఆͰBluetoothΛҰ୴OFFʹͯ͠࠶౓ONʹ… ‣ ͦΕͰ΋࣏Βͳ͔ͬͨΒ୺຤Λ࠶ىಈͰ… ‣ ͢Μ·ͤΜ… ׬શʹ௜໧͢ΔBLE͞Μ

Slide 86

Slide 86 text

‣ ϢʔβʔͷखݩͰͲΜͳෆ۩߹͕ى͖͔ͨ ௐࠪͰ͖ΔΑ͏ʹͨ͠΄͏͕ྑͦ͞͏ɻ ‣ Crashlytics ‣ Ϋϥογϡൃੜ࣌ʹͦͷखલͰى͖͍ͯͨϩά͸ݟΕΔ ‣ ΫϥογϡΛ൐͏ෆ۩߹͸͋·Γͳ͍ͷͰɺ༗༻Ͱ͸ͳ͍ ‣ Bugfender ‣ Logcat΁ͷग़ྗ͢΂ͯɺ΋͘͠͸બఆͨ͠ϩάΛऩूͰ͖Δ ‣ Bugfenderศརͦ͏(ݕ౼த) ෆ໌ͳෆ۩߹ʹରԠ͢ΔͨΊʹ

Slide 87

Slide 87 text

No content

Slide 88

Slide 88 text

BLEΛ࢖ͬͨΞϓϦ ͱ͍ͬͯ΋BLEͷϢʔεέʔε͸༷ʑ

Slide 89

Slide 89 text

Catlog Series BLE BLE HTTP ઃఆ ϩά ϩ ά BLE HTTP ઃఆ ϩά

Slide 90

Slide 90 text

Catlog Series BLE BLE HTTP ઃఆ ϩά ϩ ά BLE HTTP ઃఆ ϩά

Slide 91

Slide 91 text

SwitchBot & SwitchBot Hub (͸ͨͿΜ͜Μͳײ͡) BLE ઃఆ ࡞ಈ໋ྩ HTTP SwitchBotͷ࡞ಈ໋ྩ SwitchBotͷ࡞ಈ໋ྩ BLE ࡞ಈ໋ྩ HTTP?

Slide 92

Slide 92 text

SwitchBot & SwitchBot Hub (͸ͨͿΜ͜Μͳײ͡) BLE ઃఆ ࡞ಈ໋ྩ HTTP SwitchBotͷ࡞ಈ໋ྩ SwitchBotͷ࡞ಈ໋ྩ BLE ࡞ಈ໋ྩ HTTP?

Slide 93

Slide 93 text

‣ Catlog͸σόΠεͷઃఆͷΈBLEΛ࢖͍ͬͯΔ ‣ ઃఆը໘͕ੜ͖͍ͯΔؒͷΈCatlogͱίωΫγϣϯΛு͍ͬͯΕ͹ ͍͍ ‣ ઃఆը໘಺ͰconnectionΛone-shotͰ࢖͍ࣺͯΒΕΔ ‣ SwitchBot͸ઃఆ͓Αͼ࡞ಈ໋ྩʹBLEΛ࢖͍ͬͯΔ ‣ ΞϓϦ͕ىಈத͸ৗ࣌SwitchBotͱίωΫγϣϯΛுΓɺ͍ͭͰ΋ SwitchBotʹରͯ͠BLEΛ௨໋ͯ͠ྩͰ͖ΔΑ͏ʹ͍ͯ͠Δ(ͱ༧૝) BLEͷϢʔεέʔε͸ΞϓϦʹΑ༷ͬͯʑ

Slide 94

Slide 94 text

‣ Catlog͸FragmentͷlifecycleͷதͰBLEΛ࢖͑Ε͹͍͍ ‣ SwitchBot͸ɺ1Activityߏ੒ͳΒActivityͷlifecycleͷதɺͦ͏Ͱͳ ͍ͳΒServiceͷதͰBLEΛ࢖͏ඞཁ͕͋Γͦ͏ BLEͷϢʔεέʔε͸ΞϓϦʹΑ༷ͬͯʑ

Slide 95

Slide 95 text

‣ Catlog͸FragmentͷlifecycleͷதͰBLEΛ࢖͑Ε͹͍͍ ‣ SwitchBot͸ɺ1Activityߏ੒ͳΒActivityͷlifecycleͷதɺͦ͏Ͱͳ ͍ͳΒServiceͷதͰBLEΛ࢖͏ඞཁ͕͋Γͦ͏ ‣ ΞϓϦʹΑͬͯBLE·ΘΓͷઃܭ͸มΘΓͦ͏ BLEͷϢʔεέʔε͸ΞϓϦʹΑ༷ͬͯʑ

Slide 96

Slide 96 text

Ϣʔεέʔεʹ߹Θͤͯ ࠷ߴͷBLEΞϓϦΛ࡞Ζ͏

Slide 97

Slide 97 text

Thank you