Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Let's make Android Bike

Let's make Android Bike

Video: https://academy.realm.io/jp/posts/droidkaigi17-android-bike/

[Slides in Japanese] Talked at DroidKaigi 2017, intended to share my findings on making a cycling computer app that using a Bluetooth Low Energy bike speed/cadence sensor and a pressure sensor to get altitude difference.

Avatar for Yuki Fujisaki

Yuki Fujisaki

March 09, 2017
Tweet

More Decks by Yuki Fujisaki

Other Decks in Programming

Transcript

  1. 2. SensorEventListener Λ
 ࣮૷͢Δ @Override public void onSensorChanged(SensorEvent event) {

    if (event.sensor == pressure) { // event.values[0] ʹ float Ͱؾѹ஋͕ೖ͍ͬͯΔ Log.v(TAG, "Pressure: " + event.values[0]); } }
  2. ஌ݟJavadocʹ ߴ͞ͷࠩΛٻΊΔͱ͖ʹ͸ɺͦΕͧΕͷ஍఺Ͱࢉ ग़ͨ͠ߴ͞ಉ࢜ΛҾ͘΂͠ɻւ໘ؾѹ͕෼͔Βͳ ͚Ε͹ɺPRESSURE_STANDARD_ATMOSPHEREఆ ਺Λ࢖͏ͱҰൠతͳؾѹͳΒ͍͍ͩͨྑ͍݁Ռ͕ ಘΒΕΔɻ To calculate altitude differences,

    you must calculate the difference between the altitudes at both points. If you don't know the altitude as sea level, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE} instead, which will give good results considering the range of pressure typically involved.
  3. GATT.Service.Characteristic • GATT • has_many Services
 (ࣗసंαʔϏεɺόοςϦʔαʔϏεͱ͔) • has_many Characteristics


    (ճస਺΍ɺൃੜ࣌ࠁͳͲ࣮ࡍͷ஋) • Service, Characteristic͸ͦΕͧΕܾ·ͬͨUUID ׂ͕Γ౰ͯΒΕ͓ͯΓUUIDͰݕࡧͰ͖Δ
  4. …ͷதͷCharacteristic CSC Measurement Characteristic • ϗΠʔϧͱΫϥϯΫͦΕͧΕͷ • ճస਺ • ࠷ޙʹΠϕϯτ͕ൃੜͨ࣌ؒ͠

    • ී௨͸1ඵִؒͰૹ৴͞ΕΔ • ΫϥϯΫͷ஋ͰέΠσϯε͕෼͔Δ • ϗΠʔϧճస਺ʹλΠϠܘΛֻ͚ͯ΍Ε͹଎౓͕෼͔Δ
  5. nRF Toolbox for BLE • Nordic Semiconductor • ௒௿ফඅిྗͷۙڑ཭ແઢʹڧ͍௨৴Ϟ δϡʔϧͷձࣾ

    • ֤छϓϩϑΝΠϧͷσϞΞϓϦ • Φʔϓϯιʔε (TV΋Wear΋ରԠ) • New BSDϥΠηϯε
  6. 2. onRequestPermissionsResult Λதܧͯ͠४උ׬ྃ @Override public void onRequestPermissionsResult( int requestCode, @NonNull

    String[] permissions, @NonNull int[] grantResults) { MainActivityPermissionsDispatcher .onRequestPermissionsResult(this, requestCode, grantResults); };
  7. ૣ଎σόΠεΛ୳ͯ͠σʔλΛ औಘ͍ͨ͠ʂʂ ΍Δ͜ͱ 1. पғͷσόΠεΛεΩϟϯ͢Δ 2. ݟ͔ͭͬͨσόΠε಺ͷGATTαʔόʹ઀ଓ͢Δ 3. GATTαʔό͔ΒCSC ServiceΛऔಘ͢Δ

    4. CSC Service͕࣋ͬͯΔCSC Measurement CharacteristicΛऔಘ 5. ߋ৽͕͋ͬͨΒ௨஌(Notification)ΛૹͬͯͶͱཁٻ͢Δ
  8. 1. ·ͣ͸εΩϟφʔΛ΋Β͏ // Bluetooth Service Λ΋Βͬͯ BluetoothManager bluetoothManager = (BluetoothManager)

    context.getSystemService(Context.BLUETOOTH_SERVICE); // Bluetooth ΞμϓλΛ΋Βͬͯ BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); // Ξμϓλ͕࣋ͬͯΔ BLE ͷεΩϟφΛ΋Βͬͯ͘Δ bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
  9. ͰɺεΩϟϯ͢Δ // εΩϟϯઃఆ ScanSettings settings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_BALANCED) //

    ଞLOW_LATENCYͱ͔ .build(); // CSC Service ͚ͩݟ͚ͭͯཉ͍͠ͷͰϑΟϧλΛઃఆ͢Δ List<ScanFilter> filters = new ArrayList<>(); filters.add(new ScanFilter.Builder() .setServiceUuid(new ParcelUuid(SERVICE_UUID)) .build()); // εΩϟϯΑΖ͘͠! bluetoothLeScanner.startScan(filters, settings, callback);
  10. 2. σόΠε಺ͷ GATT αʔό ʹ઀ଓ͢Δ @Override public void onScanResult(int callbackType,

    ScanResult result) { super.onScanResult(callbackType, result); // CSC Service Λ࣋ͭσόΠε! BluetoothDevice device = result.getDevice(); // GATT αʔόʔʹܨ͍Ͱ͘Ε! BluetoothGatt gatt = device.connectGatt(context, false, new GattCallback()); } σόΠε͕ݟ͔ͭΔͱݺͼग़͞ΕΔίʔϧόοΫ಺ͰDPOOFDU(BUU͢Δɻ
  11. 4. ݟ͔ͭͬͨService͔Β͓໨౰ͯ ͷCharacteristicΛ΋Β͏ @Override public void onServicesDiscovered(BluetoothGatt gatt, int status)

    { BluetoothGattService service = gatt.getService(SERVICE_UUID); if (service != null) { characteristic = service.getCharacteristic(CHARACTERISTIC_UUID); Ұཡ͕औಘͰ͖ͨΒɺ$4$4FSWJDFΛऔಘͯ͠ɺ
 $4$.FBTVSFNFOU$IBSBDUFSJTUJDΛऔಘ
  12. 5. ௨஌ͯ͠ͶͬͯઃఆΛ͢Δ // 1. Bluetooth Stackʹड͚औͬͨΒίʔϧόοΫͯ͠Ͷ! ͬͯ఻͑Δ gatt.setCharacteristicNotification(characteristic, true); //

    2. Device ʹ΋ߋ৽௨஌ͯ͘͠ΕΑͳ! ͬͯ఻͑Δ // (descriptorʹॻ͖ࠐΉͱૹ৴͞ΕΔ) final BluetoothGattDescriptor descriptor = characteristic.getDescriptor( CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID); if (descriptor != null) { descriptor .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(descriptor); }
  13. σʔλऔಘ։࢝ʂ @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { //

    σʔλΛௐཧ͢Δ } PO$IBSBDUFSJTUJD$IBOHFE͕ݺͼग़͞ΕΔΑ͏ʹͳΔʂ
 ૹ৴ִؒ͸େମඵʹճ
  14. CSC Measurement͕ૹΔσʔλ • ઌ಄͔Β • 1byte - flags (1: ϗΠʔϧ͋Δ͔,

    2: ΫϥϯΫ͋Δ͔) • ϗΠʔϧ͋ΔͳΒ • uint32 ྦྷੵճసճ਺ • uint16 ࠷ޙͷΠϕϯτ͕͋ͬͨ࣌ؒ • ΫϥϯΫ͋ΔͳΒ • uint16 ྦྷੵճసճ਺ • uint16 ࠷ޙͷΠϕϯτ͕͋ͬͨ࣌ؒ
  15. int offset = 0; byte flags = characteristic.getValue()[offset]; offset +=

    1; boolean wheelRevPresent = (flags & 1) > 0; boolean crankRevPreset = (flags & 2) > 0; if (wheelRevPresent) { int wheelRevolutions = characteristic.getIntValue( BluetoothGattCharacteristic.FORMAT_UINT32, offset); offset += 4; int wheelEventTime = characteristic.getIntValue( BluetoothGattCharacteristic.FORMAT_UINT16, offset); offset += 2; } if (crankRevPreset) { int crankRevolutions = characteristic.getIntValue( BluetoothGattCharacteristic.FORMAT_UINT16, offset); offset += 2; int crankEventTime = characteristic.getIntValue( BluetoothGattCharacteristic.FORMAT_UINT16, offset); offset += 2; }
  16. ը໘Λ࡞͍ͬͯͧ͘ • େલఏ: എܠ͸ࠇ • AMOLEDͩͱൃޫͯ͠Δ෦෼͔͠ిྗফඅ͠ͳ͍ • ϨΠΞ΢τ͸ ContraintLayout (Support

    Library) • ཁૉؒͰؔ܎ੑΛηοτ͍ͯ͘͠ • ৘ใΛิ׬͢ΔΞχϝʔγϣϯ • ਺ࣈΛग़͚ͩ͢Ͱͳ͘ɺಈ͖Ͱঢ়گΛ఻͑Δ • ઐ༻ͷViewΛ࡞Δ
  17. ConstraintLayout • ྫ: ୯Ґදࣔ • ࠨ୺Λ΄͔ͷߦͱἧ͑ͭͭ • ॎͷҐஔ͸ಉ͡ߦͷ਺஋ͷϕʔεϥΠϯʹἧ͑Δ • …ͳΜͯࢦఆ͕؆୯ʹͰ͖ͯΑ͍

    • খ਺఺ҎԼͷද͚ࣔͩผͷ TextView ʹͯ͠ϑΥϯταΠζΛ খ͘͢͞Δ…ͳΜͯ΍ͬͯ΋มʹζϨͨΓ͠ͳ͍ • GUIͰҰ௨ΓઃఆͰ͖Δ͚Ͳɺͪΐͬͱෆ۩߹͋ͬͨΓ͢Δ ͷͰඍௐ੔͸XMLͰ΍Δ͔ͳʔͬͯײ͡
  18. ஋͕͘Δͨͼʹɺݱࡏ஋
 →৽͍͠஋Ͱ setFloatValues ͢Δ private void setAnimatorValue(ValueAnimator animator, float newValue)

    { float currentValue = (Float) animator.getAnimatedValue(); animator.setFloatValues(currentValue, newValue); animator.start(); }
  19. ॎઢҰຊͷ଎౓ϝʔλʔ (HorizontalBarGraphView) • Viewͷ෯ʹରͯ͠ݱࡏ஋ʹ౰ͨΔׂ߹ͷҐஔʹ ॎઢΛҾ͚͹Α͍ʂ • Ҏ্ͩʂ @Override protected void

    onDraw(Canvas canvas) { super.onDraw(canvas); int x = (int) ( (Math.min(current, max) - min) / (max - min) * canvas.getWidth() ); canvas.drawLine(x, 0, x, getHeight(), paint); }
  20. ͘Δ͘ΔճΔ΍ͭ (CircularRevolutionsView) • ࣮ࡍͷճసΛ࠶ݱ͢Δͱ͓΋͠Ζͦ͏ͩͳʁ • ValueAnimator Ͱ 0.0f → 1.0f

    ΛӬٱϧʔ ϓɺ 1 ճసʹ͔͚Δ࣌ؒ (duration) Λߋ৽͠ ͍ͯ͘ • ͋ͱ͸ԁΛඳ͘ɺεϜʔζʹճͯ͠΋͍͍͚ Ͳ45°ͣͭʹ͢ΔͱͦΕͬΆ͍͔ͳʁ
  21. int centerX = width / 2 + x; int centerY

    = height / 2 + y; // ਅΜதͷখؙ͍͞ canvas.drawCircle(centerX, centerY, width / 16, paint); if (rpm != 0.0f) { // 1पΛ8ஈ֊ʹؙΊΔ int step = (int) ((Float) animator.getAnimatedValue() * 8); float degree = step / (float) split; // ͦͷҐஔʹؙΛඳ͘ canvas.drawCircle( centerX - (float) Math.cos(2 * Math.PI * degree) * (width / 2) * 0.75f, centerY - (float) Math.sin(2 * Math.PI * degree) * (height / 2) * 0.75f, width / 8, paint); }
  22. !?

  23. ࢥΘ͵ॴʹ • API͕͋ͬͨΓ͢Δ • ಛʹެࣜͰެ։͞Εͯͳ͍͚Ͳ֤छݴޠͰϥΠϒϥϦ͕ ࣮૷͞ΕͯΔ • APIͳͯ͘΋੺֎ઢϦϞίϯ͋Δ΋ͷ͸IRKitͱ͔Ͱૢ࡞Ͱ ͖Δ •

    HTTPϦΫΤετ1ຊͰ͍ΖΜͳ΋ͷΛίϯτϩʔϧ • ࣗ෼ͷཉ͍͠Ϟϊͭ͘ΔͨΊʹ৭ʑܨ͍ͰΔ͚ͩͰݟ͑ͯ ͘Δੈք͕͋ΔΑʂ
  24. ·ͱΊ • Android BikeͬͯͷΛ΍ͬͯΈͨ • BLEͱ୺຤ͷηϯαΛ࢖ͬͨαΠίϯ࡞Δͷͨͷ͠ʔʂ • ࠓޙ΋ػೳ଍͍͖͍ͯͨ͠ • SDK࡞֦ͬͯுՄೳʹɺServiceԽɺ૸ͬͨه࿥Λอଘ

    (Google Fitͱ͔Stravaͱ͔)ɺࣗಈͰىಈͯ͠ه࿥։࢝ (Awareness API)ɺ௿ফඅిྗԽ • ΦʔϓϯιʔεͰ͢ʂʂʂϓϧϦΫΑΖ͘͠ʂʂʂʂʂ • https://github.com/tnj/android-bike-proto