Используем Bluetooth модуль HC-06 в приложениях Android.

Поводом к написанию статьи послужило то, что за прошедший год понадобилось подключать Bluetooth (BT) модуль HC-06 к проекту Android три раза. Стало понятно, что необходимо данный материал законспектировать (хотя бы для себя), возможно кому-то еще пригодится!

Коротко о том, что такое HC-06, для тех кто этого не знает. HC-06 - это преобразователь (конвертер) сигналов последовательного интерфейса RS232 (UART) в сигналы, которые передаются с помощью интерфейса Bluetooth и наоборот. Этот модуль чрезвычайно популярен в настоящее время у разработчиков, т.к. врядли на сегодняшний день Вы найдете что-нибудь дешевле (стоимость на Aliexpress вместе с пересылкой 200-250 рублей). Для чего все это нужно? К примеру, у Вас есть устройство на микроконтроллере, которое должно передавать данные (управляться) без проводов в пределах комнаты.

Статья адресована Android-разработчикам, которые хотели бы применить этот модуль в своем проекте для передачи данных. Я создаю приложения в Android Studio (AS), поэтому описание будет "заточено" под эту IDE.

Также стоит сказать, что за основу BT-приложения было выбрано приложение-пример из Android sample application - BluetoothChat. Это приложение можно установить в AS через меню File->New->Import Sample. Приложение многопоточное и достаточно сложное для понимания, но, как говорится "волков бояться - в лес не ходить". Для того чтобы на первых порах "поиграть" с BT через терминальную программу, можно подключить модуль HC-06 со стороны UART к адаптеру на микросхеме FTDI с уровнями сигналов 3,3V, который подключается уже в USB-порт компьютера. Как подключать смотри на рис.1


Рис.1 Подключение модуля HC-06 к адаптеру на микросхеме FTDI

Теперь опишем что необходимо сделать в приложении для того чтобы обменяться данными через модуль HC-06. Куски кода можно копировать прямо из BluetoothChat. Если нужно что-то изменить я постараюсь описать как можно подробнее.

Во-первых, для работы с Bluetooth приложение должно иметь в файле AndroidManifest.xml следующие разрешения:

<manifest ...>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
</manifest>

Второе, создаем класс в проекте, что-то типа BluetoothService (в BluetoothChat этот класс называется BluetoothChatService), добавляем импорт и код из BluetoothChat, можно поменять константу TAG, если она вдруг не нравится.

Для модуля HC-06 необходимо установить следующие константы:

private static final UUID MY_UUID_SECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final UUID MY_UUID_INSECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

Замечание: в этом классе я, например, не использовал класс AcceptThread, т.к. он нужен для обеспечения подключения других устройств в нашему, что по логике работы моих приложений оказалось лишним. Закомментировал его объявление, объявление всех его экземпляров и вызовы методов этого класса. Далее, "играясь" с BluetoothChat обнаружил "тонкое" место в методе connected этого класса. привожу часть кода

...
        // Start the thread to manage the connection and perform transmissions
        mConnectedThread = new ConnectedThread(socket, socketType);
        setState(STATE_CONNECTED);
        mConnectedThread.start();
        Log.d(TAG, "mConnectedThread.start() "+mConnectedThread);

        // Send the name of the connected device back to the UI Activity
        Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
        Bundle bundle = new Bundle();
        bundle.putString(Constants.DEVICE_NAME, device.getName());
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        //setState(STATE_CONNECTED);

т.е. пришлось перенести установку состояния "ПОДСОЕДИНЕНО" ( setState(STATE_CONNECTED); ) перед стартом потока mConnectedThread.start();. Плавающий баг приложения заключался в том, что подключение к модулю происходило, что называется "через раз".

Третье, создаем интерфейс Constants (файл Constants.java), в котором определены некоторые константы, используемые классом BluetoothChatService.

Четвертое, внедряем работу с BT в Activity (или Fragment) приложения.
Объявляем глобальные переменные в Activity (или Fragment)

    // Intent request codes
    private static final int REQUEST_CONNECT_DEVICE_SECURE = 1;
    private static final int REQUEST_CONNECT_DEVICE_INSECURE = 2;
    private static final int REQUEST_ENABLE_BT = 3;

    /**
     * Name of the connected device
     */

    private String mConnectedDeviceName = null;

    /**
     * Array adapter for the conversation thread
     */

    private ArrayAdapter<String> mConversationArrayAdapter;

    /**
     * String buffer for outgoing messages
     */

    private StringBuffer mOutStringBuffer;

    /**
     * Local Bluetooth adapter
     */

    private BluetoothAdapter mBluetoothAdapter = null;

    /**
     * Member object for the chat services
     */

    private BluetoothChatService mChatService = null;

В метод onCreate() активности или фрагмента добавляем:

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

if (mBluetoothAdapter == null) {
    FragmentActivity activity = getActivity();
    Toast.makeText(activity, getString(R.string.bt_not_enabled_leaving), Toast.LENGTH_LONG).show();
    activity.finish();
}

Добавляем метод onStart() с кодом:

@Override
    protected void onStart() {
        super.onStart();
        // If BT is not on, request that it be enabled.
        // setupChat() will then be called during onActivityResult
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
            // Otherwise, setup the chat session
        } else if (mChatService == null) {
            setupChat();
        }
    }

а также onDestroy():

@Override
    public void onDestroy() {
        super.onDestroy();

        if (mChatService != null) {
            mChatService.stop();
        }
    }

В активность или фрагмент добавляем setupChat(), sendMessage(), connectDevice(), класс Handler, метод onActivityResult().

Пятое - добавляем нужные для сообщений и названий строки в values/strings.xml

Шестое, добавляем активность DeviceListActivity и xml-файл разметки для нее и в манифест приложения нужно добавить схему и описание активности:

<activity
   android:name=".DeviceListActivity"
   android:configChanges="orientation|keyboardHidden"
   android:label="@string/select_device"
   android:theme="@android:style/Theme.Holo.Dialog" />

Файл device_name.xml, который используется в DeviceListActivity для описания строк с именами устройств.

Вот в общем-то и все! Должно работать.