Fausto01
Fausto01

Reputation: 211

Qt 5.14.2 BLE not working as expected on Android

I made a simple app that write to a device a command, and the device reply with another command. On windows everything works fine, but on Android I'm not able to send the data. This is my code:

#include "communicator.hpp"
#include <QDataStream>
#include <QBluetoothLocalDevice>

#define SERVICE_UUID "{49535343-fe7d-4ae5-8fa9-9fafd205e455}"
#define SERVICE_CHARACTERISTIC_TX_UUID "{49535343-1e4d-4bd9-ba61-23c647249616}"
#define READ_DATA_REQUEST_CMD 0xA5
#define READ_DATA_RESPONSE_CMD 0xA6

Communicator::Communicator()
{
  QObject::connect(&m_receptionTimer, &QTimer::timeout, this, &Communicator::onReceptionTimerTimeout);
  m_receptionTimer.setInterval(2000);
}

void Communicator::connectToDevice(const QString& macAddress)
{
  m_controller.reset(QLowEnergyController::createCentral(QBluetoothAddress(macAddress), QBluetoothLocalDevice().address()));

  QObject::connect(m_controller.data(), &QLowEnergyController::connected, this, &Communicator::onDeviceConnected);
  QObject::connect(m_controller.data(), &QLowEnergyController::discoveryFinished, this, &Communicator::onDiscoveryFinished);
  QObject::connect(m_controller.data(), QOverload<QLowEnergyController::Error>::of(&QLowEnergyController::error), this, &Communicator::onDiscoveryError);
  QObject::connect(m_controller.data(), &QLowEnergyController::serviceDiscovered, this, &Communicator::onServiceDiscovered);

  m_controller->connectToDevice();
}

void Communicator::onDeviceConnected()
{
  qDebug() << "Device connected";

  m_serviceDiscovered = false;
  m_controller->discoverServices();
}

void Communicator::onDiscoveryFinished()
{
  if(!m_serviceDiscovered)
  {
    emit connectionFailed();
    return;
  }

  m_service.reset(m_controller->createServiceObject(QBluetoothUuid(QString(SERVICE_UUID)), this));
  if(m_service.isNull())
  {
    emit connectionFailed();
    return;
  }

  QObject::connect(m_service.data(), &QLowEnergyService::stateChanged, this, &Communicator::onServiceStateChanged);
  QObject::connect(m_service.data(), QOverload<QLowEnergyService::ServiceError>::of(&QLowEnergyService::error), this, &Communicator::onServiceError);
  QObject::connect(m_service.data(), &QLowEnergyService::characteristicChanged, this, &Communicator::onCharacteristicChanged);

  //************************
  //**********TEST**********
  //************************

  QObject::connect(m_service.data(), &QLowEnergyService::stateChanged, this, [](QLowEnergyService::ServiceState state)
  {
    qDebug() << "SERVICE STATE CHANGED -> STATE:" << state;
  });

  QObject::connect(m_service.data(), &QLowEnergyService::characteristicWritten, this, [](const QLowEnergyCharacteristic &info, const QByteArray &value)
  {
    qDebug() << "CHARACTERISTIC WRITTEN -> UUID:" << info.uuid().toString() << "VALUE:" << value;
  });

  QObject::connect(m_service.data(), &QLowEnergyService::characteristicChanged, this, [](const QLowEnergyCharacteristic &info, const QByteArray &value)
  {
    qDebug() << "CHARACTERISTIC CHANGED -> UUID:" << info.uuid().toString() << "VALUE:" << value;
  });

  QObject::connect(m_service.data(), &QLowEnergyService::characteristicRead, this, [](const QLowEnergyCharacteristic &info, const QByteArray &value)
  {
    qDebug() << "CHARACTERISTIC READ -> UUID:" << info.uuid().toString() << "VALUE:" << value;
  });

  QObject::connect(m_service.data(), &QLowEnergyService::descriptorRead, this, [](const QLowEnergyDescriptor &info, const QByteArray &value)
  {
    qDebug() << "DESCRIPTOR READ -> UUID:" << info.uuid().toString() << "VALUE:" << value;
  });

  QObject::connect(m_service.data(), &QLowEnergyService::descriptorWritten, this, [](const QLowEnergyDescriptor &info, const QByteArray &value)
  {
    qDebug() << "DESCRIPTOR WRITTEN -> UUID:" << info.uuid().toString() << "VALUE:" << value;
  });

  //************************
  //**********TEST**********
  //************************

  if(m_service->state() == QLowEnergyService::DiscoveryRequired)
    m_service->discoverDetails();
  else
  {
    searchCharacteristics();
    requestData();
  }
}

void Communicator::onDiscoveryError(QLowEnergyController::Error error)
{
  qDebug() << "Discovery error" << error;
  emit connectionFailed();
}

void Communicator::onServiceDiscovered(const QBluetoothUuid& service)
{
  if(service.toString() == SERVICE_UUID)
    m_serviceDiscovered = true;
}

void Communicator::onServiceStateChanged(QLowEnergyService::ServiceState state)
{
  switch(state)
  {
    case QLowEnergyService::InvalidService:
    case QLowEnergyService::DiscoveryRequired:
    case QLowEnergyService::DiscoveringServices:
    case QLowEnergyService::LocalService:
      break;

    case QLowEnergyService::ServiceDiscovered:
      searchCharacteristics();
      requestData();
      break;
  }
}

void Communicator::onServiceError(QLowEnergyService::ServiceError error)
{
  qDebug() << "Service error" << error;
  emit connectionFailed();
}

void Communicator::onCharacteristicChanged(const QLowEnergyCharacteristic& info, const QByteArray& value)
{
  if(info.uuid() != m_writeCharacteristic.uuid())
    return;

  m_receptionBuffer.append(value);
  parseResponse(m_receptionBuffer);
}

void Communicator::onReceptionTimerTimeout()
{
  qDebug() << "Reception timeout";
  requestData();
  emit errorOccurred();
}

void Communicator::searchCharacteristics()
{
  const auto& characteristics = m_service->characteristics();
  for(const QLowEnergyCharacteristic& characteristic : characteristics)
  {
    if(!characteristic.isValid())
      continue;

    if(characteristic.uuid().toString() != SERVICE_CHARACTERISTIC_TX_UUID)
      continue;

    if(characteristic.properties() & QLowEnergyCharacteristic::WriteNoResponse || characteristic.properties() & QLowEnergyCharacteristic::Write)
    {
      m_writeCharacteristic = characteristic;
      if(characteristic.properties() & QLowEnergyCharacteristic::WriteNoResponse)
        m_writeMode = QLowEnergyService::WriteWithoutResponse;
      else
        m_writeMode = QLowEnergyService::WriteWithResponse;

      QLowEnergyDescriptor notificationDescriptor = characteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
      if(notificationDescriptor.isValid())
        m_service->writeDescriptor(notificationDescriptor, QByteArray::fromHex("0100"));
    }
  }
}

bool Communicator::write(const QByteArray& data)
{
  if(m_service.isNull())
    return false;

  if(!m_writeCharacteristic.isValid())
    return false;

  m_service->writeCharacteristic(m_writeCharacteristic, data, m_writeMode);
  return true;
}

void Communicator::requestData()
{
  m_receptionBuffer.clear();
  m_receptionTimer.start();
  write(QByteArray(1, READ_DATA_REQUEST_CMD));
}

void Communicator::parseResponse(const QByteArray& data)
{
  const quint8 headerLength = 3;
  if(data.length() < headerLength)
    return;

  quint8 cmd;
  quint16 length;

  QDataStream stream(data);
  stream >> cmd;
  stream >> length;

  if(cmd != READ_DATA_RESPONSE_CMD || length == 0)
    return;

  // non ho ancora ricevuto tutti i dati
  if(data.length() - headerLength != length)
    return;

  m_receptionTimer.stop();
  emit dataChanged(data.mid(3));
  requestData();
}

This is the log of my application on Android:
06-07-22 16:48:55.353 |W| ACCESS_COARSE|FINE_LOCATION permission available
06-07-22 16:48:57.986 |D| Device connected
06-07-22 16:48:58.018 |D| SERVICE STATE CHANGED -> STATE: QLowEnergyService::DiscoveringServices
06-07-22 16:49:03.284 |D| Discovery error QLowEnergyController::ConnectionError
06-07-22 16:49:03.287 |D| SERVICE STATE CHANGED -> STATE: QLowEnergyService::InvalidService
06-07-22 16:49:03.287 |D| Device disconnected

Am I doing something wrong?

Upvotes: 0

Views: 192

Answers (0)

Related Questions