|
|||||||
Время создания: 06.05.2018 13:58
Текстовые метки: qt, bluetooth
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Библиотека Qt - Bluetooth
Запись: xintrea/mytetra_syncro/master/base/1525604338rbsl73u914/text.html на raw.github.com
|
|||||||
|
|||||||
Аннотация. При разработке мобильных(и не только) приложений часто возникает необходимость организовать соединение между устройствами. Несмотря на популярность Qt в сфере мобильных разработок, в сети недостаточно информации даже на английском языке. Кроме двух-трёх демонстрационных примеров из официальной документации, сложно что-то найти. В статье рассматриваются различные способы работы с bluetooth средствами Qt. Рассказывается о возможных проблемах и способах их решения. Надеюсь, кому-то это будет полезно. Введение. В настоящее время я занят в проекте в сфере mobile healthcare. В нашем распоряжении есть портативный кардиомонитор (сенсоры + само устройство). Идея такова: пользователь из группы риска постоянно носит портативный кардиомонитор, а наше приложение на смартфоне в фоновом режиме мониторит состояние его здоровья. Дальнейших сценариев много, например, запись истории, или отправка сообщений доктору, или совершение экстренного вызова в случае сердечного приступа. Сейчас готов первый прототип для Symbian OS, имеющий неполный функционал, после завершения проекта планируется перенос и на другие популярные мобильные платформы. В рамках этой статьи я расскажу, как использовать Qt для организации bluetooth-взаимодействия с кардиомонитором. (Прилагается небольшой демо-проект, переделать его под другие цели довольно просто). Итак, есть кардиомонитор, во включенном состоянии непрерывно отправляющий пакеты с данными, есть C++/Qt приложение. Интерфейс реализуется на QML. Способ №1. Использование QML-элементов BluetoothDiscoveryModel и BluetoothSocket.
Почему бы не остановиться на этом? Я был бы рад, если бы было можно. Но, например, в моём случае, этот способ не сработал. Устройство если и находилось (странным образом, не всегда) и появлялось в списке, то с ним не удавалось установить соединение. Да и не всегда используется QML. Способ №2. Так как QML-элементы по непонятной причине работать с нашим кардиомонитором не хотели, было принято решение написать на C++/Qt свой bluetooth-плагин. Здесь я столкнулся с проблемой, которая упоминалась в аннотации - доступно очень мало информации. Но я разобрался, и спешу поделиться опытом. Сначала проверяем, доступен ли вообще bluetooth на устройстве с помощью QBluetoothLocalDevice. Если да, включаем его, делаем его видимым, и сканируем область на наличие устройств, мы находим различные сервисы, предоставляемые активными bluetooth-устройствами. /** * Check if Bluetooth is available on this device, * turn bluetooth on, read local device name, * make it visible to others, initialize discovery * agent to search for services. * Then start a service discovery. */ void BluetoothModule::startDiscovery() { localDevice = new QBluetoothLocalDevice(this); if (localDevice->isValid()) { qDebug() << "Bluetooth is available on this device"; localDevice->setHostMode(QBluetoothLocalDevice::HostDiscoverable); localDevice->powerOn(); localDevice->setHostMode(QBluetoothLocalDevice::HostDiscoverable); qDebug() << "Local device: " << localDevice->name() << " (" << localDevice->address().toString().trimmed() << ")"; // Create a discovery agent and connect to its signals discoveryAgent = new QBluetoothServiceDiscoveryAgent(this); connect(discoveryAgent, SIGNAL(finished()), this, SLOT(serviceDiscoverFinished())); discoveryAgent->start(); qDebug() << "Service discover started"; } else qDebug() << "Bluetooth is not available on this device"; } Когда обзор области завершится, сработает слот serviceDiscoverFinished(). Теперь мы можем получить список всех доступных сервисов. /** * Service discover finished. Get a list of services. * Read information about the found services and print it. */ void BluetoothModule::serviceDiscoverFinished() { qDebug() << "Service discover finished"; listOfServices = discoveryAgent->discoveredDevices(); if (!(listOfServices.isEmpty())) { qDebug() << "Found new services:"; for(int i = 0; i < listOfServices.size(); ++i) qDebug() << "Device: " << listOfServices.at(i).device().name().trimmed() << " (" << listOfServices.at(i).device().address().toString().trimmed() << ") \n" << "Service: " << listOfServices.at(i).serviceName() << ", " << listOfServices.at(i).serviceDescription() << ", " << listOfServices.at(i).serviceProvider(); } else qDebug() << "No services found"; } В списке оказались все сервисы, предоставляемые другими телефонами и ноутбуками, которые оказались неподалеку, но о кардиомониторе никакой информации найдено не было. То есть QBluetoothServiceDiscoveryAgent не находит ни одного сервиса на нашем устройстве. Что ж, это объясняет, почему QML BluetoothSocket (см. выше, способ №1) не подключался к кардиомонитору. Как следует из документации, он базируется как раз на QBluetoothSocket, которому для соединения необходимо указать QBluetoothService. Удивительно то, что QML QBluetoothDiscoveryModel, как я понял, базируется на QBluetoothServiceDiscoveryAgent, но иногда всё же находил кардиомонитор. Вот мы и пришли к тому, ради чего, по большому счету, я и писал эту статью. Из-за особенностей используемого девайса, приходится идти более долгим путем. Так как сервис на кардиомониторе находиться отказывался, пришлось искать способ как-то обойти явный его поиск. Немного модифицировав код, прибегнем к следующему решению: выполним поиск не сервисов, а устройств, их предоставляющийх, с помощью класса QBluetoothDeviceDiscoveryAgent. /** * Check if Bluetooth is available on this device, * turn bluetooth on, read local device name, * make it visible to others, initialize discovery * agent to search for devices. * Then start a device discovery. */ void BluetoothModule::startDiscovery() { localDevice = new QBluetoothLocalDevice(this); if (localDevice->isValid()) { qDebug() << "Bluetooth is available on this device"; localDevice->setHostMode(QBluetoothLocalDevice::HostDiscoverable); localDevice->powerOn(); localDevice->setHostMode(QBluetoothLocalDevice::HostDiscoverable); qDebug() << "Local device: " << localDevice->name() << " (" << localDevice->address().toString().trimmed() << ")"; // Create a discovery agent and connect to its signals discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this); connect(discoveryAgent, SIGNAL(finished()), this, SLOT(deviceDiscoverFinished())); discoveryAgent->start(); qDebug() << "Device discover started"; } else { qDebug() << "Bluetooth is not available on this device"; } } Напишем новый слот для сигнала об окончании обзора: /** * Device discover finished. Get a list of devices. * Read information about the found devices, * print to qDebug() a list of devices names and addresses and * send it to GUI (QML), where user can select a prefered device. */ void BluetoothModule::deviceDiscoverFinished() { qDebug() << "Device discover finished"; listOfDevices = discoveryAgent->discoveredDevices(); if (listOfDevices.isEmpty()) setError("No devices found"); qDebug() << "Found new devices:"; for (int i = 0; i < listOfDevices.size(); i++) { qDebug() << listOfDevices.at(i).name().trimmed() << " (" << listOfDevices.at(i).address().toString().trimmed() << ")"; setDevice(listOfDevices.at(i).name().trimmed() + " (" + listOfDevices.at(i).address().toString().trimmed() + ")"); } } И запустим приложение. Устройство найдено, это не может не радовать. Отправим этот список в QML интерфейс, заполнив ListView, подождем пользовательского выбора (как это реализуется, здесь описывать не буду, кому интересно, см. код проекта), и... самый интересный момент, описание которого (что уж говорить о примерах) я не нашел нигде. Теперь нужно соединить QBluetoothSocket с QBluetoothServiceInfo. Но сервис-то мы не искали. То есть искали, но не нашли. Оказывается, можно, зная физический адрес устройства, соединить сокет с сервисом, указав его тип и местоположение. /** * In GUI (QML) user select a device with index i. * Create a new socket, using Rfcomm protocol. * Socket connect to service on selected device, * with Uuid Serial Port. Connect a socket's signals with sockets. */ void BluetoothModule::deviceSelected(int i) { selectedDevice = listOfDevices.at(i); qDebug() << "User select a device: " << selectedDevice.name() << " (" << selectedDevice.address().toString().trimmed() << ")"; socket = new QBluetoothSocket(QBluetoothSocket::RfcommSocket, this); socket->connectToService(QBluetoothAddress(selectedDevice.address()), QBluetoothUuid(QBluetoothUuid::SerialPort)); connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(socketError(QBluetoothSocket::SocketError))); connect(socket, SIGNAL(connected()), this, SLOT(socketConnected())); connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); connect(socket, SIGNAL(readyRead()), this, SLOT(socketRead())); Остается только отправлять анализатору данные, принимаемые с кардиомонитора: /// Read data from device via socket. void BluetoothModule::socketRead() { QByteArray recievedData = socket->readAll(); emit dataRecieved(recievedData); } Работающий bluetooth-модуль готов. Прилагаю проект, который позволяет осуществлять поиск устройств, просматривать список найденных, и выбирать, к какому из них следует подключиться. Код снабжен достаточным количеством комментариев, думаю, не вызовет затруднений разобраться и использовать его в других проектах. Например, соединившись, можно не только читать из сокета, но и отправлять пакеты: socket->write(data,data[1]); В статье я постарался показать особенности работы с bluetooth средствами Qt, предупредить о некоторых подводных камнях, поджидающих программистов, и предложить способы решения проблем, с которыми я лично столкнулся в процессе разработки. P. S. Все рассмотренные классы работы с Bluetooth - часть Qt Mobility / Connectivity API, для их использования в .pro файле необходимо раскомментировать/дописать следующие строки: CONFIG += mobility MOBILITY = connectivity Напоминаю, что для работы на Symbian смартфонах приложению необходимы дополнительные полномочия. symbian:TARGET.CAPABILITY += NetworkServices LocalServices Location ReadUserData UserEnvironment WriteUserData ReadDeviceData WriteDeviceData Чтобы запустить приложение на смартфоне, sis пакет необходимо подписать: www.symbiansigned.com. P. P. S. Про особенности сборки и запуска под MeeGo не могу пока ничего сказать - не было возможности проверить. Надеюсь, появится устройство, буду с удовольствием разрабатывать и под эту платформу ;) |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|