به سرور GATT متصل شوید

اولین قدم در تعامل با دستگاه BLE، اتصال به آن است. به طور خاص، اتصال به سرور GATT در دستگاه. برای اتصال به سرور GATT در دستگاه BLE، از متد connectGatt() استفاده کنید. این روش سه پارامتر دارد: یک شی Context ، autoConnect (یک بولی که نشان می‌دهد آیا به‌محض در دسترس شدن دستگاه BLE به‌طور خودکار به آن متصل می‌شود)، و یک ارجاع به BluetoothGattCallback :

کاتلین

var bluetoothGatt: BluetoothGatt? = null
...

bluetoothGatt = device.connectGatt(this, false, gattCallback)

جاوا

bluetoothGatt = device.connectGatt(this, false, gattCallback);

این به سرور GATT میزبانی شده توسط دستگاه BLE متصل می شود و یک نمونه BluetoothGatt را برمی گرداند که می توانید از آن برای انجام عملیات کلاینت GATT استفاده کنید. تماس گیرنده (برنامه اندروید) مشتری GATT است. BluetoothGattCallback برای ارائه نتایج به مشتری، مانند وضعیت اتصال، و همچنین هرگونه عملیات مشتری GATT دیگر استفاده می شود.

یک سرویس محدود راه اندازی کنید

در مثال زیر، برنامه BLE یک فعالیت ( DeviceControlActivity ) برای اتصال به دستگاه‌های بلوتوث، نمایش داده‌های دستگاه، و نمایش خدمات و ویژگی‌های GATT که توسط دستگاه پشتیبانی می‌شود، ارائه می‌کند. بر اساس ورودی کاربر، این فعالیت با Service به نام BluetoothLeService ارتباط برقرار می کند که از طریق BLE API با دستگاه BLE تعامل دارد. ارتباط با استفاده از یک سرویس محدود انجام می‌شود که به فعالیت اجازه می‌دهد به BluetoothLeService متصل شود و عملکردهای تماس را برای اتصال به دستگاه‌ها برقرار کند. BluetoothLeService به پیاده سازی Binder نیاز دارد که دسترسی به سرویس را برای فعالیت فراهم می کند.

کاتلین

class BluetoothLeService : Service() {

    private val binder = LocalBinder()

    override fun onBind(intent: Intent): IBinder? {
        return binder
    }

    inner class LocalBinder : Binder() {
        fun getService() : BluetoothLeService {
            return this@BluetoothLeService
        }
    }
}

جاوا

class BluetoothLeService extends Service {

    private Binder binder = new LocalBinder();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    class LocalBinder extends Binder {
        public BluetoothLeService getService() {
            return BluetoothLeService.this;
        }
    }
}

این اکتیویتی می‌تواند سرویس را با استفاده از bindService() ، ارسال یک Intent برای شروع سرویس، اجرای ServiceConnection برای گوش دادن به رویدادهای اتصال و قطع، و یک پرچم برای مشخص کردن گزینه‌های اتصال اضافی، شروع کند.

کاتلین

class DeviceControlActivity : AppCompatActivity() {

    private var bluetoothService : BluetoothLeService? = null

    // Code to manage Service lifecycle.
    private val serviceConnection: ServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(
            componentName: ComponentName,
            service: IBinder
        ) {
            bluetoothService = (service as LocalBinder).getService()
            bluetoothService?.let { bluetooth ->
                // call functions on service to check connection and connect to devices
            }
        }

        override fun onServiceDisconnected(componentName: ComponentName) {
            bluetoothService = null
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.gatt_services_characteristics)

        val gattServiceIntent = Intent(this, BluetoothLeService::class.java)
        bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE)
    }
}

جاوا

class DeviceControlActivity extends AppCompatActivity {

    private BluetoothLeService bluetoothService;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bluetoothService = ((LocalBinder) service).getService();
            if (bluetoothService != null) {
                // call functions on service to check connection and connect to devices
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bluetoothService = null;
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.gatt_services_characteristics);

        Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
        bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
}

آداپتور بلوتوث را تنظیم کنید

هنگامی که سرویس به آن متصل شد، باید به BluetoothAdapter دسترسی پیدا کند. باید بررسی کنید که آداپتور در دستگاه موجود است. برای اطلاعات بیشتر در مورد BluetoothAdapter ، تنظیم بلوتوث را بخوانید. مثال زیر این کد راه‌اندازی را در یک تابع initialize() می‌پیچد که یک مقدار Boolean را نشان می‌دهد که موفقیت را نشان می‌دهد.

کاتلین

private const val TAG = "BluetoothLeService"

class BluetoothLeService : Service() {

    private var bluetoothAdapter: BluetoothAdapter? = null

    fun initialize(): Boolean {
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        if (bluetoothAdapter == null) {
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.")
            return false
        }
        return true
    }

    ...
}

جاوا

class BluetoothLeService extends Service {

    public static final String TAG = "BluetoothLeService";

    private BluetoothAdapter bluetoothAdapter;

    public boolean initialize() {
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter == null) {
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
            return false;
        }
        return true;
    }

    ...
}

فعالیت این تابع را در اجرای ServiceConnection خود فراخوانی می کند. مدیریت یک مقدار بازگشتی نادرست از تابع initialize() به برنامه شما بستگی دارد. می‌توانید پیام خطایی به کاربر نشان دهید که نشان می‌دهد دستگاه فعلی از عملکرد بلوتوث پشتیبانی نمی‌کند یا ویژگی‌هایی را که برای کار کردن به بلوتوث نیاز دارند غیرفعال کنید. در مثال زیر، finish() روی اکتیویتی فراخوانی می شود تا کاربر را به صفحه قبلی بازگرداند.

کاتلین

class DeviceControlActivity : AppCompatActivity() {

    // Code to manage Service lifecycle.
    private val serviceConnection: ServiceConnection = object : ServiceConnection {
        override fun onServiceConnected(
            componentName: ComponentName,
            service: IBinder
        ) {
            bluetoothService = (service as LocalBinder).getService()
            bluetoothService?.let { bluetooth ->
                if (!bluetooth.initialize()) {
                    Log.e(TAG, "Unable to initialize Bluetooth")
                    finish()
                }
                // perform device connection
            }
        }

        override fun onServiceDisconnected(componentName: ComponentName) {
            bluetoothService = null
        }
    }

    ...
}

جاوا

class DeviceControlsActivity extends AppCompatActivity {

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bluetoothService = ((LocalBinder) service).getService();
            if (bluetoothService != null) {
                if (!bluetoothService.initialize()) {
                    Log.e(TAG, "Unable to initialize Bluetooth");
                    finish();
                }
                // perform device connection
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bluetoothService = null;
        }
    };

    ...
}

به یک دستگاه متصل شوید

هنگامی که نمونه BluetoothLeService مقداردهی اولیه شد، می تواند به دستگاه BLE متصل شود. فعالیت باید آدرس دستگاه را به سرویس ارسال کند تا بتواند اتصال را آغاز کند. این سرویس ابتدا getRemoteDevice() در BluetoothAdapter برای دسترسی به دستگاه فراخوانی می کند. اگر آداپتور نتواند دستگاهی را با آن آدرس پیدا کند، getRemoteDevice() یک IllegalArgumentException می اندازد.

کاتلین

fun connect(address: String): Boolean {
    bluetoothAdapter?.let { adapter ->
        try {
            val device = adapter.getRemoteDevice(address)
        } catch (exception: IllegalArgumentException) {
            Log.w(TAG, "Device not found with provided address.")
            return false
        }
    // connect to the GATT server on the device
    } ?: run {
        Log.w(TAG, "BluetoothAdapter not initialized")
        return false
    }
}

جاوا

public boolean connect(final String address) {
    if (bluetoothAdapter == null || address == null) {
        Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
        return false;
    }

    try {
        final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
    } catch (IllegalArgumentException exception) {
        Log.w(TAG, "Device not found with provided address.");
        return false;
    }
    // connect to the GATT server on the device
}

DeviceControlActivity این تابع connect() را پس از راه اندازی اولیه سرویس فراخوانی می کند. فعالیت باید در آدرس دستگاه BLE انجام شود. در مثال زیر، آدرس دستگاه به عنوان یک هدف اضافی به فعالیت ارسال می شود.

کاتلین

// Code to manage Service lifecycle.
private val serviceConnection: ServiceConnection = object : ServiceConnection {
    override fun onServiceConnected(
    componentName: ComponentName,
    service: IBinder
    ) {
        bluetoothService = (service as LocalBinder).getService()
        bluetoothService?.let { bluetooth ->
            if (!bluetooth.initialize()) {
                Log.e(TAG, "Unable to initialize Bluetooth")
                finish()
            }
            // perform device connection
            bluetooth.connect(deviceAddress)
        }
    }

    override fun onServiceDisconnected(componentName: ComponentName) {
        bluetoothService = null
    }
}

جاوا

private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        bluetoothService = ((LocalBinder) service).getService();
        if (bluetoothService != null) {
            if (!bluetoothService.initialize()) {
                Log.e(TAG, "Unable to initialize Bluetooth");
                finish();
            }
            // perform device connection
            bluetoothService.connect(deviceAddress);
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        bluetoothService = null;
    }
};

اعلام پاسخ به تماس GATT

هنگامی که فعالیت به سرویس می گوید که به کدام دستگاه متصل شود و سرویس به دستگاه متصل می شود، سرویس باید به سرور GATT در دستگاه BLE متصل شود. این اتصال به BluetoothGattCallback برای دریافت اعلان‌های مربوط به وضعیت اتصال، کشف سرویس، خواندن مشخصه‌ها و اعلان‌های مشخصه نیاز دارد.

این موضوع بر روی اعلان‌های وضعیت اتصال متمرکز است. برای نحوه انجام کشف سرویس، خواندن مشخصه ها و درخواست اعلان های مشخصه، به انتقال داده های BLE مراجعه کنید.

تابع onConnectionStateChange() زمانی فعال می شود که اتصال به سرور GATT دستگاه تغییر کند. در مثال زیر، callback در کلاس Service تعریف شده است، بنابراین می توان آن را با BluetoothDevice پس از اتصال سرویس به آن استفاده کرد.

کاتلین

private val bluetoothGattCallback = object : BluetoothGattCallback() {
    override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            // successfully connected to the GATT Server
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            // disconnected from the GATT Server
        }
    }
}

جاوا

private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            // successfully connected to the GATT Server
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            // disconnected from the GATT Server
        }
    }
};

به سرویس گات متصل شوید

هنگامی که BluetoothGattCallback اعلام شد، سرویس می تواند از شی BluetoothDevice از connect() برای اتصال به سرویس GATT در دستگاه استفاده کند.

تابع connectGatt() استفاده می شود. این به یک شی Context ، یک پرچم بولین autoConnect ، و BluetoothGattCallback نیاز دارد. در این مثال، برنامه به طور مستقیم به دستگاه BLE متصل می شود، بنابراین false برای autoConnect ارسال می شود.

یک ویژگی BluetoothGatt نیز اضافه شده است. این به سرویس اجازه می دهد تا زمانی که دیگر به آن نیازی نیست اتصال را ببندد .

کاتلین

class BluetoothLeService : Service() {

...

    private var bluetoothGatt: BluetoothGatt? = null

    ...

    fun connect(address: String): Boolean {
        bluetoothAdapter?.let { adapter ->
            try {
                val device = adapter.getRemoteDevice(address)
                // connect to the GATT server on the device
                bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)
                return true
            } catch (exception: IllegalArgumentException) {
                Log.w(TAG, "Device not found with provided address.  Unable to connect.")
                return false
            }
        } ?: run {
            Log.w(TAG, "BluetoothAdapter not initialized")
            return false
        }
    }
}

جاوا

class BluetoothLeService extends Service {

...

    private BluetoothGatt bluetoothGatt;

    ...

    public boolean connect(final String address) {
        if (bluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }
        try {
            final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
            // connect to the GATT server on the device
            bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);
            return true;
        } catch (IllegalArgumentException exception) {
            Log.w(TAG, "Device not found with provided address.  Unable to connect.");
            return false;
        }
    }
}

به روز رسانی پخش

هنگامی که سرور به سرور گات متصل یا قطع می شود، باید فعالیت وضعیت جدید را مطلع کند. چندین راه برای انجام این کار وجود دارد. مثال زیر از پخش برای ارسال اطلاعات از سرویس به فعالیت استفاده می کند.

این سرویس عملکردی را برای پخش وضعیت جدید اعلام می کند. این تابع یک رشته عملیاتی را می گیرد که قبل از پخش به سیستم به یک شی Intent ارسال می شود.

کاتلین

private fun broadcastUpdate(action: String) {
    val intent = Intent(action)
    sendBroadcast(intent)
}

جاوا

private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

هنگامی که عملکرد پخش در جای خود قرار گرفت، در BluetoothGattCallback برای ارسال اطلاعات در مورد وضعیت اتصال با سرور GATT استفاده می شود. ثابت ها و وضعیت اتصال فعلی سرویس در سرویسی که اقدامات Intent را نشان می دهد، اعلام می شود.

کاتلین

class BluetoothLeService : Service() {

    private var connectionState = STATE_DISCONNECTED

    private val bluetoothGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() {
        override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                // successfully connected to the GATT Server
                connectionState = STATE_CONNECTED
                broadcastUpdate(ACTION_GATT_CONNECTED)
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                // disconnected from the GATT Server
                connectionState = STATE_DISCONNECTED
                broadcastUpdate(ACTION_GATT_DISCONNECTED)
            }
        }
    }

    ...

    companion object {
        const val ACTION_GATT_CONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED"
        const val ACTION_GATT_DISCONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"

        private const val STATE_DISCONNECTED = 0
        private const val STATE_CONNECTED = 2

    }
}

جاوا

class BluetoothLeService extends Service {

    public final static String ACTION_GATT_CONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";

    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTED = 2;

    private int connectionState;
    ...

    private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                // successfully connected to the GATT Server
                connectionState = STATE_CONNECTED;
                broadcastUpdate(ACTION_GATT_CONNECTED);
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                // disconnected from the GATT Server
                connectionState = STATE_DISCONNECTED;
                broadcastUpdate(ACTION_GATT_DISCONNECTED);
            }
        }
    };

    
}

به به روز رسانی در فعالیت گوش دهید

هنگامی که سرویس به‌روزرسانی‌های اتصال را پخش می‌کند، فعالیت باید یک BroadcastReceiver را پیاده‌سازی کند. هنگام تنظیم فعالیت، این گیرنده را ثبت کنید و زمانی که فعالیت از صفحه خارج شد، آن را لغو ثبت کنید. با گوش دادن به رویدادها از سرویس، فعالیت می تواند رابط کاربری را بر اساس وضعیت اتصال فعلی با دستگاه BLE به روز کند.

کاتلین

class DeviceControlActivity : AppCompatActivity() {

...

    private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            when (intent.action) {
                BluetoothLeService.ACTION_GATT_CONNECTED -> {
                    connected = true
                    updateConnectionState(R.string.connected)
                }
                BluetoothLeService.ACTION_GATT_DISCONNECTED -> {
                    connected = false
                    updateConnectionState(R.string.disconnected)
                }
            }
        }
    }

    override fun onResume() {
        super.onResume()
        registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter())
        if (bluetoothService != null) {
            val result = bluetoothService!!.connect(deviceAddress)
            Log.d(DeviceControlsActivity.TAG, "Connect request result=$result")
        }
    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(gattUpdateReceiver)
    }

    private fun makeGattUpdateIntentFilter(): IntentFilter? {
        return IntentFilter().apply {
            addAction(BluetoothLeService.ACTION_GATT_CONNECTED)
            addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED)
        }
    }
}

جاوا

class DeviceControlsActivity extends AppCompatActivity {

...

    private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                connected = true;
                updateConnectionState(R.string.connected);
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                connected = false;
                updateConnectionState(R.string.disconnected);
            }
        }
    };

    @Override
    protected void onResume() {
        super.onResume();

        registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter());
        if (bluetoothService != null) {
            final boolean result = bluetoothService.connect(deviceAddress);
            Log.d(TAG, "Connect request result=" + result);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(gattUpdateReceiver);
    }

    private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
        return intentFilter;
    }
}

در انتقال داده‌های BLE ، از BroadcastReceiver نیز برای برقراری ارتباط با کشف سرویس و همچنین داده‌های مشخصه از دستگاه استفاده می‌شود.

اتصال GATT را ببندید

یکی از گام‌های مهم هنگام برخورد با اتصالات بلوتوث، بستن اتصال پس از پایان کار با آن است. برای انجام این کار، تابع close() در شی BluetoothGatt فراخوانی کنید. در مثال زیر، این سرویس به BluetoothGatt اشاره دارد. هنگامی که فعالیت از سرویس جدا می شود، برای جلوگیری از تخلیه باتری دستگاه، اتصال بسته می شود.

کاتلین

class BluetoothLeService : Service() {

...

    override fun onUnbind(intent: Intent?): Boolean {
        close()
        return super.onUnbind(intent)
    }

    private fun close() {
        bluetoothGatt?.let { gatt ->
            gatt.close()
            bluetoothGatt = null
        }
    }
}

جاوا

class BluetoothLeService extends Service {

...

      @Override
      public boolean onUnbind(Intent intent) {
          close();
          return super.onUnbind(intent);
      }

      private void close() {
          if (bluetoothGatt == null) {
              Return;
          }
          bluetoothGatt.close();
          bluetoothGatt = null;
      }
}