/* This file is part of the ArduinoBLE library. Copyright (c) 2018 Arduino SA. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "utility/ATT.h" #include "utility/BLEUuid.h" #include "utility/HCI.h" #include "remote/BLERemoteDevice.h" #include "BLEDevice.h" BLEDevice::BLEDevice() : _advertisementTypeMask(0), _eirDataLength(0), _rssi(127) { memset(_address, 0x00, sizeof(_address)); } BLEDevice::BLEDevice(uint8_t addressType, uint8_t address[6]) : _addressType(addressType), _advertisementTypeMask(0), _eirDataLength(0), _rssi(127) { memcpy(_address, address, sizeof(_address)); } BLEDevice::~BLEDevice() { } void BLEDevice::poll() { HCI.poll(); } void BLEDevice::poll(unsigned long timeout) { HCI.poll(timeout); } bool BLEDevice::connected() const { HCI.poll(); if (!(*this)) { return false; } return ATT.connected(_addressType, _address); } bool BLEDevice::disconnect() { return ATT.disconnect(_addressType, _address); } String BLEDevice::address() const { char result[18]; sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x", _address[5], _address[4], _address[3], _address[2], _address[1], _address[0]); return result; } bool BLEDevice::hasLocalName() const { return (localName().length() > 0); } bool BLEDevice::hasAdvertisedServiceUuid() const { return hasAdvertisedServiceUuid(0); } bool BLEDevice::hasAdvertisedServiceUuid(int index) const { return (advertisedServiceUuid(index).length() > 0); } int BLEDevice::advertisedServiceUuidCount() const { int advertisedServiceCount = 0; for (unsigned char i = 0; i < _eirDataLength;) { int eirLength = _eirData[i++]; int eirType = _eirData[i++]; if (eirType == 0x02 || eirType == 0x03 || eirType == 0x06 || eirType == 0x07) { int uuidLength; if (eirType == 0x02 || eirType == 0x03) { uuidLength = 2; } else /*if (eirType == 0x06 || eirType == 0x07)*/ { uuidLength = 16; } for (int j = 0; j < (eirLength - 1); j += uuidLength) { advertisedServiceCount++; } } i += (eirLength - 1); } return advertisedServiceCount; } String BLEDevice::localName() const { String localName = ""; for (int i = 0; i < _eirDataLength;) { int eirLength = _eirData[i++]; int eirType = _eirData[i++]; if (eirType == 0x08 || eirType == 0x09) { localName.reserve(eirLength - 1); for (int j = 0; j < (eirLength - 1); j++) { localName += (char)_eirData[i + j]; } break; } i += (eirLength - 1); } return localName; } String BLEDevice::advertisedServiceUuid() const { return advertisedServiceUuid(0); } String BLEDevice::advertisedServiceUuid(int index) const { String serviceUuid; int uuidIndex = 0; for (unsigned char i = 0; i < _eirDataLength;) { int eirLength = _eirData[i++]; int eirType = _eirData[i++]; if (eirType == 0x02 || eirType == 0x03 || eirType == 0x06 || eirType == 0x07) { int uuidLength; if (eirType == 0x02 || eirType == 0x03) { uuidLength = 2; } else /*if (eirType == 0x06 || eirType == 0x07)*/ { uuidLength = 16; } for (int j = 0; j < (eirLength - 1); j += uuidLength) { if (uuidIndex == index) { serviceUuid = BLEUuid::uuidToString(&_eirData[i + j * uuidLength], uuidLength); } uuidIndex++; } } i += (eirLength - 1); } return serviceUuid; } bool BLEDevice::hasAdvertisementData() const { return (_eirDataLength > 0); } int BLEDevice::advertisementDataLength() const { return _eirDataLength; } int BLEDevice::advertisementData(uint8_t value[], int length) const { if (length > _eirDataLength) length = _eirDataLength; if (length) { memcpy(value, _eirData, length); } return length; } bool BLEDevice::hasManufacturerData() const { return (manufacturerDataLength() > 0); } int BLEDevice::manufacturerDataLength() const { int length = 0; for (int i = 0; i < _eirDataLength;) { int eirLength = _eirData[i++]; int eirType = _eirData[i++]; if (eirType == 0xFF) { length = (eirLength - 1); break; } i += (eirLength - 1); } return length; } int BLEDevice::manufacturerData(uint8_t value[], int length) const { for (int i = 0; i < _eirDataLength;) { int eirLength = _eirData[i++]; int eirType = _eirData[i++]; if (eirType == 0xFF) { if (length > (eirLength - 1)) length = (eirLength - 1); memcpy(value, &_eirData[i], length); break; } i += (eirLength - 1); } return length; } int BLEDevice::rssi() { uint16_t handle = ATT.connectionHandle(_addressType, _address); if (handle != 0xffff) { return HCI.readRssi(handle); } return _rssi; } bool BLEDevice::connect() { return ATT.connect(_addressType, _address); } bool BLEDevice::discoverAttributes() { return ATT.discoverAttributes(_addressType, _address, NULL); } bool BLEDevice::discoverService(const char* serviceUuid) { return ATT.discoverAttributes(_addressType, _address, serviceUuid); } BLEDevice::operator bool() const { uint8_t zeros[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}; return (memcmp(_address, zeros, sizeof(zeros)) != 0); } bool BLEDevice::operator==(const BLEDevice& rhs) const { return ((_addressType == rhs._addressType) && memcmp(_address, rhs._address, sizeof(_address)) == 0); } bool BLEDevice::operator!=(const BLEDevice& rhs) const { return ((_addressType != rhs._addressType) || memcmp(_address, rhs._address, sizeof(_address)) != 0); } String BLEDevice::deviceName() { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { BLEService genericAccessService = service("1800"); if (genericAccessService) { BLECharacteristic deviceNameCharacteristic = genericAccessService.characteristic("2a00"); if (deviceNameCharacteristic) { deviceNameCharacteristic.read(); String result; int valueLength = deviceNameCharacteristic.valueLength(); const char* value = (const char*)deviceNameCharacteristic.value(); result.reserve(valueLength); for (int i = 0; i < valueLength; i++) { result += value[i]; } return result; } } } return ""; } int BLEDevice::appearance() { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { BLEService genericAccessService = service("1801"); if (genericAccessService) { BLECharacteristic appearanceCharacteristic = genericAccessService.characteristic("2a01"); if (appearanceCharacteristic) { appearanceCharacteristic.read(); uint16_t result = 0; memcpy (&result, appearanceCharacteristic.value(), min((int)sizeof(result), appearanceCharacteristic.valueLength())); return result; } } } return 0; } int BLEDevice::serviceCount() const { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { return device->serviceCount(); } return 0; } bool BLEDevice::hasService(const char* uuid) const { return hasService(uuid, 0); } bool BLEDevice::hasService(const char* uuid, int index) const { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { int count = 0; int numServices = device->serviceCount(); for (int i = 0; i < numServices; i++) { BLERemoteService* s = device->service(i); if (strcasecmp(uuid, s->uuid()) == 0) { if (count == index) { return true; } count++; } } } return false; } BLEService BLEDevice::service(int index) const { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { if (index < (int)device->serviceCount()) { return BLEService(device->service(index)); } } return BLEService(); } BLEService BLEDevice::service(const char * uuid) const { return service(uuid, 0); } BLEService BLEDevice::service(const char * uuid, int index) const { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { int count = 0; int numServices = device->serviceCount(); for (int i = 0; i < numServices; i++) { BLERemoteService* s = device->service(i); if (strcasecmp(uuid, s->uuid()) == 0) { if (count == index) { return BLEService(s); } count++; } } } return BLEService(); } int BLEDevice::characteristicCount() const { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { int result = 0; int numServices = device->serviceCount(); for (int i = 0; i < numServices; i++) { result += device->service(i)->characteristicCount(); } return result; } return 0; } bool BLEDevice::hasCharacteristic(const char* uuid) const { return hasCharacteristic(uuid, 0); } bool BLEDevice::hasCharacteristic(const char* uuid, int index) const { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { int count = 0; int numServices = device->serviceCount(); for (int i = 0; i < numServices; i++) { BLERemoteService* s = device->service(i); int numCharacteristics = s->characteristicCount(); for (int j = 0; j < numCharacteristics; j++) { BLERemoteCharacteristic* c = s->characteristic(j); if (strcasecmp(c->uuid(), uuid) == 0) { if (count == index) { return true; } } count++; } } } return false; } BLECharacteristic BLEDevice::characteristic(int index) const { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { int count = 0; int numServices = device->serviceCount(); for (int i = 0; i < numServices; i++) { BLERemoteService* s = device->service(i); int numCharacteristics = s->characteristicCount(); for (int j = 0; j < numCharacteristics; j++) { if (count == index) { BLERemoteCharacteristic* c = s->characteristic(j); return BLECharacteristic(c); } count++; } } } return BLECharacteristic(); } BLECharacteristic BLEDevice::characteristic(const char * uuid) const { return characteristic(uuid, 0); } BLECharacteristic BLEDevice::characteristic(const char * uuid, int index) const { BLERemoteDevice* device = ATT.device(_addressType, _address); if (device) { int count = 0; int numServices = device->serviceCount(); for (int i = 0; i < numServices; i++) { BLERemoteService* s = device->service(i); int numCharacteristics = s->characteristicCount(); for (int j = 0; j < numCharacteristics; j++) { BLERemoteCharacteristic* c = s->characteristic(j); if (strcasecmp(c->uuid(), uuid) == 0) { if (count == index) { return BLECharacteristic(c); } count++; } } } } return BLECharacteristic(); } bool BLEDevice::hasAddress(uint8_t addressType, uint8_t address[6]) { return (_addressType == addressType) && (memcmp(_address, address, sizeof(_address)) == 0); } void BLEDevice::setAdvertisementData(uint8_t type, uint8_t eirDataLength, uint8_t eirData[], int8_t rssi) { _advertisementTypeMask = (1 << type); _eirDataLength = eirDataLength; memcpy(_eirData, eirData, eirDataLength); _rssi = rssi; } void BLEDevice::setScanResponseData(uint8_t eirDataLength, uint8_t eirData[], int8_t rssi) { _advertisementTypeMask |= (1 << 0x04); memcpy(&_eirData[_eirDataLength], eirData, eirDataLength); _eirDataLength += eirDataLength; _rssi = rssi; } bool BLEDevice::discovered() { // expect, 0x03 or 0x04 flag to be set return (_advertisementTypeMask & 0x18) != 0; }