From df8858c1cf3a0d7bedd8e20f60e7b0b13e5d103a Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 30 Aug 2022 17:58:14 +0900 Subject: [PATCH] Add generic access, device information. Update some methods --- src/main.cpp | 146 +++++++++++++++++++++++++++++-------- src/wxbeacon2.cpp | 13 +++- src/wxbeacon2.hpp | 95 ++++++++++++++++-------- src/wxbeacon2_ble.cpp | 164 ++++++++++++++++++++++++++++++++++-------- src/wxbeacon2_ble.hpp | 51 ++++++++++--- 5 files changed, 375 insertions(+), 94 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 8fcefe9..2dd31ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -68,7 +68,7 @@ const char* ntpURL[] = "time.cloudflare.com", }; -// For configrate time +// For configurate time #ifdef M5S_WXBEACON2_GMT_OFFSET_HOUR long gmtOffsetSec = 3600 * M5S_WXBEACON2_GMT_OFFSET_HOUR; #else @@ -114,11 +114,8 @@ struct ScopedWB2Client */ bool changeBeaconMode() { - if((uint64_t)wb2address == 0) - { - WB2_LOGE("Address not yet obtained"); - return false; - } + if(!wb2Client) { WB2_LOGE("Client null"); return false; } + if((uint64_t)wb2address == 0) { WB2_LOGE("Address not yet obtained"); return false; } ScopedWB2Client wb2(*wb2Client); @@ -151,11 +148,8 @@ bool changeBeaconMode() */ bool changeDefaultSetting() { - if((uint64_t)wb2address == 0) - { - WB2_LOGE("Address not yet obtained"); - return false; - } + if(!wb2Client) { WB2_LOGE("Client null"); return false; } + if((uint64_t)wb2address == 0) { WB2_LOGE("Address not yet obtained"); return false; } ScopedWB2Client wb2(*wb2Client); @@ -184,11 +178,8 @@ bool changeDefaultSetting() /* turnon LED 1 second */ bool turnOnLED() { - if((uint64_t)wb2address == 0) - { - WB2_LOGE("Address not yet obtained"); - return false; - } + if(!wb2Client) { WB2_LOGE("Client null"); return false; } + if((uint64_t)wb2address == 0) { WB2_LOGE("Address not yet obtained"); return false; } ScopedWB2Client wb2(*wb2Client); @@ -212,8 +203,8 @@ time_t lastUpdate = -1; void wb2_advertise_task(void*) { NimBLEScan* scan = BLEDevice::getScan(); - WxBeacon2AdvertiseCallbacks* cb = new WxBeacon2AdvertiseCallbacks(); - scan->setAdvertisedDeviceCallbacks(cb); + WxBeacon2AdvertiseCallbacks cb; + scan->setAdvertisedDeviceCallbacks(&cb); scan->setInterval(1000); scan->setWindow(900); scan->setActiveScan(true); @@ -222,22 +213,23 @@ void wb2_advertise_task(void*) { ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); - cb->clear(); + cb.clear(); WB2_LOGI("Start scanning"); scan->start(60, false); while(scan->isScanning()) { delay(100); } - WB2_LOGI("End of scanning. detected :%d", cb->detected()); + WB2_LOGI("End of scanning. detected :%d", cb.detected()); - if(cb->detected()) + if(cb.detected()) { - wb2address = cb->address(); - advertiseData = cb->data(); + wb2address = cb.address(); + advertiseData = cb.data(); lastUpdate = time(nullptr); if(TURNON_LED) { turnOnLED(); } } - updatedAdvertise = cb->detected(); + updatedAdvertise = cb.detected(); advertiseBusy = false; + scan->clearResults(); } } @@ -314,6 +306,102 @@ void playAdvertiseData(const WxBeacon2::AdvertiseData& data) constexpr char DEFAULT_TICKER_TEXT[] = "WEATHEROID Type A Airi"; constexpr char NOTICE_TICKER_TEXT[] = "Press and hold C to put the WxBeacon2 into the broadcast mode. "; + +#if 0 +bool test_connect() +{ + if(!wb2Client) { WB2_LOGE("Client null"); return false; } + if((uint64_t)wb2address == 0) { WB2_LOGE("Address not yet obtained"); return false; } + + ScopedWB2Client wb2(*wb2Client); + + WB2_LOGI("connect to : %s", wb2address.toString().c_str()); + if(!wb2.client().isConnected() && !wb2.client().connect(wb2address)) + { + WB2_LOGE("Failed to connect"); + return false; + } + + std::string str; + auto b = wb2.client().getDeviceName(str); + printf("%d DeviceName:%s\n", b, str.c_str()); + + WxBeacon2::GenericAccesssService::Appearance app; + b = wb2.client().getAppearance(app); + printf("%d Appearnce:%x\n", b, app._category); + + WxBeacon2::GenericAccesssService::PeripheralPreferredConnectionParameters params; + b = wb2.client().getPeripheralPreferredConnectionParameters(params); + printf("%d params:%x/%x/%x/%x\n", b, + params._intervalMin, params._intervalMax, params._slaveLatency, params._timeout); + + b = wb2.client().getModelNumber(str); + printf("%d ModelNumber:[%s]\n", b, str.c_str()); + b = wb2.client().getSerialNumber(str); + printf("%d SerialNumber:[%s]\n", b, str.c_str()); + b = wb2.client().getFirmwareRevision(str); + printf("%d FirmRev:[%s]\n", b, str.c_str()); + b = wb2.client().getHardwareRevision(str); + printf("%d HardRev:[%s]\n", b, str.c_str()); + b = wb2.client().getManufacturerName(str); + printf("%d ManufacturerName:[%s]\n", b, str.c_str()); + + return true; +} + +bool test_getLatestPageData() +{ + if(advertiseData.format() == WxBeacon2::ADVFormat::E) { WB2_LOGE("Not recording mode"); return false; } + + if(!wb2Client) { WB2_LOGE("Client null"); return false; } + if((uint64_t)wb2address == 0) { WB2_LOGE("Address not yet obtained"); return false; } + + ScopedWB2Client wb2(*wb2Client); + + WB2_LOGI("connect to : %s", wb2address.toString().c_str()); + if(!wb2.client().isConnected() && !wb2.client().connect(wb2address)) + { + WB2_LOGE("Failed to connect"); + return false; + } + + WxBeacon2::LatestPage lpage; + if(!wb2.client().getLatestPage(lpage)) { WB2_LOGE("Failed to get latest page"); return false; } + printf("Latest: %d,%d\n", lpage._page, lpage._row); + + int retryCount = 3; + WxBeacon2::ResponseFlag flag = { 2 }; + do + { + if(!wb2.client().requestPage(lpage._page, lpage._row)) { continue; } // request + for(;;) // get response flag + { + wb2.client().getResponseFlag(flag); + if((int)flag._updateFlag != 0) { break; } + delay(10); + } + }while((int)flag._updateFlag == 2 && retryCount--); + if((int)flag._updateFlag != 1) { WB2_LOGE("Failed to request or get response"); return false; } + + int cnt = lpage._row + 1; + while(cnt--) + { + WxBeacon2::ResponseData data; + if(!wb2.client().getResponseData(data)) { WB2_LOGE("Failed to get response data"); return false; } + time_t t = flag._time32 + lpage._interval * data._data._row; + auto lt = std::localtime(&t); + printf("DATA: row:%02d time:%4d/%02d/%02d %02d:%02d:%02d\n", + data._data._row, + lt->tm_year + 1900, + lt->tm_mon + 1, + lt->tm_mday, + lt->tm_hour, + lt->tm_min, + lt->tm_sec); + } + return true; +} +#endif // } @@ -336,7 +424,7 @@ void setup() scfg.task_pinned_core = 0; M5.Speaker.config(scfg); M5.Speaker.begin(); - M5.Speaker.setVolume(bd == m5::board_t::board_M5StackCore2 ? 64 : 128); + M5.Speaker.setVolume(bd == m5::board_t::board_M5Stack ? 128 : 64); delay(500); if (M5.Display.width() < M5.Display.height()) @@ -368,15 +456,15 @@ void setup() abort(); } - // Configrate local time. + // Configurate local time. M5.Display.setCursor(0,0); - M5.Display.printf("Configrate time"); + M5.Display.printf("Configurate time"); configTime(gmtOffsetSec, daylightOffsetSec, ntpURL[0]); std::tm t; if(getLocalTime(&t)) { - WB2_LOGI("Configrate time : %4d/%02d/%02d (%d) %02d:%02d:%02d", + WB2_LOGI("Configurate time : %4d/%02d/%02d (%d) %02d:%02d:%02d", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, @@ -489,6 +577,8 @@ void loop() { if(!longPressB) { + // test_connect(); + // test_getLatestPageData(); #if 0 // TEST auto lv = ticker->level() + 1; diff --git a/src/wxbeacon2.cpp b/src/wxbeacon2.cpp index 0d2d74d..3bbc30f 100644 --- a/src/wxbeacon2.cpp +++ b/src/wxbeacon2.cpp @@ -83,7 +83,7 @@ bool WxBeacon2::MeasurementInterval::from(const std::string& v) } // -WxBeacon2::Data::Data(std::time_t pageTime, uint16_t interval, const WxBeacon2::ResponseData& rd) +WxBeacon2::Data::Data(const std::time_t pageTime, const uint16_t interval, const WxBeacon2::ResponseData& rd) : _time(pageTime + rd._data._row * interval) , _temperature(rd._data._temperature) , _relativeHumidity(rd._data._relativeHumidity) @@ -126,6 +126,17 @@ bool WxBeacon2::ADVSetting::from(const std::string& v) return string2array(_array, v); } +// +bool WxBeacon2::GenericAccesssService::Appearance::from(const std::string& v) +{ + return string2array(_array, v); +} + +bool WxBeacon2::GenericAccesssService::PeripheralPreferredConnectionParameters::from(const std::string& v) +{ + return string2array(_array, v); +} + // bool WxBeacon2::AdvertiseData::from(const WxBeacon2::BeaconMode bmode, const uint8_t* mdata, const size_t length) { diff --git a/src/wxbeacon2.hpp b/src/wxbeacon2.hpp index 42db098..5ec8bb9 100644 --- a/src/wxbeacon2.hpp +++ b/src/wxbeacon2.hpp @@ -17,7 +17,8 @@ class WxBeacon2 public: /// @name Constants /// @{ - constexpr static uint16_t COMPANY_ID = 0x02d5; // Company ID of OMRON + constexpr static uint16_t COMPANY_ID = 0x02d5; // Company ID of OMRON Corporation + constexpr static uint16_t IBEACON_COMPANY_ID = 0x004c; // Company ID of Apple, Inc. constexpr static uint16_t PAGE_MAX = 2048; // [0-2047] constexpr static uint8_t ROW_MAX = 13; // [0-12] constexpr static uint16_t SEQUENCE_MAX = 255; // [0-255] @@ -86,8 +87,7 @@ class WxBeacon2 (ROW_MAX - srow) + (erow + 1) + (ROW_MAX * ((epage + PAGE_MAX - spage - 1) % PAGE_MAX) ) : (erow >= srow) ? erow - srow + 1 : 0 /* Invalid srow,erow*/; } - - // Sensor data type + /*! @brief Temperature @note Unit: 0.01 degC @@ -239,7 +239,7 @@ class WxBeacon2 uint16_t raw() const { return _v; } private: uint16_t _v; - } __attribute__((__packed__)); + } __attribute__((__packed__)); // Sensor Service (Service UUID: 0x3000) /*! @@ -311,7 +311,7 @@ class WxBeacon2 }; /*! - @brief Request page 0x3003 + @brief Request page @note Property: READ, WRITE */ union RequestPage @@ -348,7 +348,6 @@ class WxBeacon2 Retrieving, // 0x00: Retrieving Completed, // 0x01: Completed FailedToRetrieveData, // 0x02: Failed to retrieve data - Error, // Other errors }; std::array _array; @@ -358,11 +357,6 @@ class WxBeacon2 uint32_t _time32; // created time of page. UNIX TIME Unit: 1 sec } __attribute__((__packed__)); - ResponseFlag() : _array{} { _updateFlag = UpdateFlag::Error; } - explicit ResponseFlag(const std::string& v) : _array{} { from(v); } - - UpdateFlag updateFlag() const { return _updateFlag; } - /// @name Convert /// @{ bool from(const std::string& v); @@ -418,7 +412,7 @@ class WxBeacon2 Heatstroke _heatstroke; BatteryVoltage16 _batteryVoltage; - Data(std::time_t pageTime, uint16_t interval, const ResponseData& rd); + Data(const std::time_t pageTime, const uint16_t interval, const ResponseData& rd); Data(const time_t t, const int16_t tmp, const int16_t hum, const int16_t light, const int16_t uv, const int16_t presure, const int16_t noise, const int16_t dis, const int16_t heat, const uint16_t battery16) : _time(t) , _temperature(tmp) @@ -540,7 +534,7 @@ class WxBeacon2 uint8_t _cpu; uint8_t _battery; uint8_t _rfu; - }; + } __attribute__((__packed__)); ErrorStatus() : _array{} {} @@ -593,10 +587,69 @@ class WxBeacon2 const static ADVSetting DEFAULT_SETTING; } __attribute__((__packed__)); + + //! @brief Generic Access Service (Service UUID : 0x1800) + struct GenericAccesssService + { + constexpr static uint16_t UUID = 0x1800; + //! @brief Device name + struct DeviceName + { + constexpr static uint16_t UUID = 0x2A00; + }; + //! @brief Appearance + union Appearance + { + constexpr static uint16_t UUID = 0x2A01; + std::array _array; + uint16_t _category; + + bool from(const std::string& v); + }; + //! @brief Peripheral preferred connection parameters + union PeripheralPreferredConnectionParameters + { + constexpr static uint16_t UUID = 0x2A04; + std::array _array; + struct + { + uint16_t _intervalMin; // Minimum connection interval (Unit 1.25 ms) + uint16_t _intervalMax; // Maximum connection interval (Unit 1.25 ms) + uint16_t _slaveLatency; // Slave Latency + uint16_t _timeout; // Connection supervision timeout multiplier + } __attribute__((__packed__)); + + bool from(const std::string& v); + }; + }; + + //! @brief Device Information Service (Service UUID : 0x180A) + struct DeviceInformationService + { + constexpr static uint16_t UUID = 0x180A; + //! @brief Model number + struct ModelNumber { constexpr static uint16_t UUID = 0x2A24; }; + //! @brief Serial number + struct SerialNumber { constexpr static uint16_t UUID = 0x2A25; }; + //! @brief Firmware revision + struct FirmwareRevision { constexpr static uint16_t UUID = 0x2A26; }; + //! @brief Hardware revision + struct HardwareRevision { constexpr static uint16_t UUID = 0x2A27; }; + //! @brief Manufacturer name + struct ManufacturerName { constexpr static uint16_t UUID = 0x2A29; }; + }; + /*! @brief Advertise data */ class AdvertiseData { public: + // ADV 2 length + constexpr static size_t LENGTH_A = 29 - 5 + 1; + constexpr static size_t LENGTH_B = 30 - 2 + 1; + constexpr static size_t LENGTH_C = 25 - 9 + 1; + constexpr static size_t LENGTH_D = 26 - 5 + 1; + constexpr static size_t LENGTH_E = 26 - 5 + 1; + /*! @brief (A) Beacon */ struct A { @@ -744,21 +797,5 @@ class WxBeacon2 E _e; }; }; - - // Device Information Service (Service UUID: 0x180A) - /*! @brief Device Information */ - struct DeviceInformation - { - std::string modelNumber; - std::string serialNumber; - std::string firmwareRevision; - std::string hardwareRevision; - std::string manufacturerName; - - bool valid() const - { - return !modelNumber.empty() && !serialNumber.empty() && !firmwareRevision.empty() && !hardwareRevision.empty() && !manufacturerName.empty(); - }; - }; }; #endif diff --git a/src/wxbeacon2_ble.cpp b/src/wxbeacon2_ble.cpp index 7c1ba75..0b0bbe9 100644 --- a/src/wxbeacon2_ble.cpp +++ b/src/wxbeacon2_ble.cpp @@ -31,7 +31,7 @@ template struct has_from struct has_from bool getClientValue(NimBLEClient* client, T& value, uint16_t gatt16) +template bool getCustomCharacteristicValue(NimBLEClient* client, T& value, const uint16_t gatt16) { static_assert(has_from::value, "T must be has bool from(const std::string&)"); assert(client); @@ -59,7 +59,38 @@ template bool getClientValue(NimBLEClient* client, T& value, uint16_ printf("\n"); } #endif + return value.from(v); +} +/*! + @brief get assigned characteristics value + @tparam T value of type + @param client Client + @param value store target + @param gat16 16bit uuid of custom UUID + @retval true Succeed + @retval false Failed + */ +template bool getCharacteristicValue(NimBLEClient* client, T& value, const uint16_t sUUID, const uint16_t cUUID) +{ + static_assert(has_from::value, "T must be has bool from(const std::string&)"); + assert(client); + + auto sv = client->getService(NimBLEUUID(sUUID)); + if(!sv) { WB2_LOGE("Service not exists. %x", sUUID); return false; } + auto v = sv->getValue(NimBLEUUID(cUUID));; + + WB2_LOGI("GATT:%x/%x len:%zu / %zu:", sUUID, cUUID, v.length(), v.size()); +#if defined(WB2_LOG_LEVEL) && WB2_LOG_LEVEL >= ESP_LOG_INFO + if(v.length() > 0) + { + for(size_t i = 0; i < v.length(); ++i) + { + printf("%02x", v[i]); + } + printf("\n"); + } +#endif return value.from(v); } // @@ -75,17 +106,18 @@ void WxBeacon2AdvertiseCallbacks::clear() void WxBeacon2AdvertiseCallbacks::onResult(NimBLEAdvertisedDevice* device) { auto dname = device->getName(); + auto mdata = device->getManufacturerData(); bool detected = false; - WB2_LOGV("devicename:%s", dname.c_str()); - if(validName(dname)) + WB2_LOGV("devicename:%s companyID:%x", dname.c_str(), mdata.empty() ? 0 : *(const uint16_t*)mdata.data()); + if(validName(dname) && !mdata.empty() && *(const uint16_t*)mdata.data() == WxBeacon2::COMPANY_ID) { WB2_LOGI("Device <%s> ----------------", device->toString().c_str()); WB2_LOGI(" Service UUID : %s", device->haveServiceUUID() ? device->getServiceUUID().toString().c_str() : "None"); WB2_LOGI(" Address : %s", device->getAddress().toString().c_str()); WB2_LOGI(" Name : %s", device->getName().c_str()); - - auto fmt = format(dname); - _data.from(fmt, reinterpret_cast(device->getManufacturerData().data()), device->getManufacturerData().length()); + + auto fmt = format(dname, mdata.length()); + _data.from(fmt, reinterpret_cast(mdata.data()), mdata.length()); // EP if(fmt == WxBeacon2::ADVFormat::E) @@ -238,17 +270,17 @@ WxBeacon2Client::~WxBeacon2Client() // Sensor Service (Service UUID: 0x3000) bool WxBeacon2Client::getLatestData(WxBeacon2::LatestData& ldata) { - return getClientValue(_client, ldata, WxBeacon2::LatestData::UUID); + return getCustomCharacteristicValue(_client, ldata, WxBeacon2::LatestData::UUID); } bool WxBeacon2Client::getLatestPage(WxBeacon2::LatestPage& lpage) { - return getClientValue(_client, lpage, WxBeacon2::LatestPage::UUID); + return getCustomCharacteristicValue(_client, lpage, WxBeacon2::LatestPage::UUID); } bool WxBeacon2Client::getRequestPage(WxBeacon2::RequestPage& req) { - return getClientValue(_client, req, WxBeacon2::RequestPage::UUID); + return getCustomCharacteristicValue(_client, req, WxBeacon2::RequestPage::UUID); } bool WxBeacon2Client::requestPage(uint16_t page, uint8_t row) @@ -265,7 +297,12 @@ bool WxBeacon2Client::requestPage(uint16_t page, uint8_t row) bool WxBeacon2Client::getResponseFlag(WxBeacon2::ResponseFlag& flag) { - return getClientValue(_client, flag, WxBeacon2::ResponseFlag::UUID); + return getCustomCharacteristicValue(_client, flag, WxBeacon2::ResponseFlag::UUID); +} + +bool WxBeacon2Client::getResponseData(WxBeacon2::ResponseData& data) +{ + return getCustomCharacteristicValue(_client, data, WxBeacon2::ResponseData::UUID); } bool WxBeacon2Client::getResponseData(DataVector& vec, const uint16_t pageFrom, const uint8_t rowFrom) @@ -330,20 +367,20 @@ size_t WxBeacon2Client::_getResponseData(DataVector& vec, const uint16_t page, c while(rcnt--) { // 1) Write request page - WB2_LOGV("Request %u,%u", page, lastRow); // row 常に 0? + WB2_LOGV("Request %u,%u", page, lastRow); if(!requestPage(page, lastRow)) { WB2_LOGE("Failed to request"); continue; } // 2) Read response flag - WxBeacon2::ResponseFlag flag; // updateFlag() is ERROR + WxBeacon2::ResponseFlag flag = { (uint8_t)WxBeacon2::ResponseFlag::UpdateFlag::FailedToRetrieveData }; do { delay(50); // Avoid "Controller not ready to receive packets" in ble_hci_trans_hs_acl_tx() if(!getResponseFlag(flag)) { WB2_LOGE("Failed to get respose flag"); break; } - }while(flag.updateFlag() == WxBeacon2::ResponseFlag::UpdateFlag::Retrieving); + }while(flag._updateFlag == WxBeacon2::ResponseFlag::UpdateFlag::Retrieving); - WB2_LOGD("updateFlag %x", (uint8_t)flag.updateFlag()); + WB2_LOGD("updateFlag %x", flag._updateFlag); - if(flag.updateFlag() != WxBeacon2::ResponseFlag::UpdateFlag::Completed) { WB2_LOGD("flag is not completed"); continue; } + if(flag._updateFlag != WxBeacon2::ResponseFlag::UpdateFlag::Completed) { WB2_LOGD("flag is not completed"); continue; } WB2_LOGI("read data. created time: %u", flag._time32); @@ -358,14 +395,14 @@ size_t WxBeacon2Client::_getResponseData(DataVector& vec, const uint16_t page, c if(v.empty()) { WB2_LOGE("Failed to get response data"); } WxBeacon2::ResponseData rd; - if(!rd.from(v) || rd._data._row >= WxBeacon2::ROW_MAX) { break; } + if(!rd.from(v) || rd._data._row >= WxBeacon2::ROW_MAX) { break; } // Invalid data if(rd._data._row >= firstRow && rd._data._row <= lastRow) { WB2_LOGV("store row:%u", rd._data._row); tmp.push_back(rd); } - if(rd._data._row <= firstRow || rd._data._row == 0) { break; } + if(rd._data._row <= firstRow || rd._data._row == 0) { break; } // No more rows on the page } // Sort by row asend and push back Data. @@ -411,17 +448,17 @@ bool WxBeacon2Client::getPageCreatedTime(std::vector& vec, const uint1 WB2_LOGD("req : %u, %u", req._page, req._row); #endif delay(50); // Avoid "Controller not ready to receive packets" in ble_hci_trans_hs_acl_tx() - - WxBeacon2::ResponseFlag flag; + + WxBeacon2::ResponseFlag flag = { (uint8_t)WxBeacon2::ResponseFlag::UpdateFlag::FailedToRetrieveData }; do { - flag = WxBeacon2::ResponseFlag(); if(!getResponseFlag(flag)) { WB2_LOGD("Failed to getResponseFlag"); break; } - }while(flag.updateFlag() == WxBeacon2::ResponseFlag::UpdateFlag::Retrieving); + delay(10); + }while(flag._updateFlag == WxBeacon2::ResponseFlag::UpdateFlag::Retrieving); WB2_LOGD("%02x:%08x", flag._updateFlag, flag._time32); - if(flag.updateFlag() != WxBeacon2::ResponseFlag::UpdateFlag::Completed) { continue; } + if(flag._updateFlag != WxBeacon2::ResponseFlag::UpdateFlag::Completed) { continue; } WB2_LOGI("emplace_back %u", page); vec.emplace_back(page, static_cast(flag._time32)); @@ -433,11 +470,10 @@ bool WxBeacon2Client::getPageCreatedTime(std::vector& vec, const uint1 return vec.size() == to - from + 1; } - // Setting Service (Service UUID: 0x3010) bool WxBeacon2Client::getMeasurementInterval(WxBeacon2::MeasurementInterval& mi) { - return getClientValue(_client, mi, WxBeacon2::MeasurementInterval::UUID); + return getCustomCharacteristicValue(_client, mi, WxBeacon2::MeasurementInterval::UUID); } bool WxBeacon2Client::setMeasurementInterval(const uint16_t sec) @@ -457,7 +493,7 @@ bool WxBeacon2Client::setMeasurementInterval(const uint16_t sec) // Control Service (Service UUID: 0x3030) bool WxBeacon2Client::getTimeInformation(WxBeacon2::TimeInformation& info) { - return getClientValue(_client, info, WxBeacon2::TimeInformation::UUID); + return getCustomCharacteristicValue(_client, info, WxBeacon2::TimeInformation::UUID); } bool WxBeacon2Client::setTimeInformation(const time_t unixTime) @@ -499,7 +535,7 @@ bool WxBeacon2Client::setLED(const uint8_t duration) bool WxBeacon2Client::getErrorStatus(WxBeacon2::ErrorStatus& status) { - return getClientValue(_client, status, WxBeacon2::ErrorStatus::UUID); + return getCustomCharacteristicValue(_client, status, WxBeacon2::ErrorStatus::UUID); } bool WxBeacon2Client::setErrorStatus(const WxBeacon2::ErrorStatus& status) @@ -514,7 +550,7 @@ bool WxBeacon2Client::setErrorStatus(const WxBeacon2::ErrorStatus& status) // Parameter Service (Service UUID: 0x3040) bool WxBeacon2Client::getADVSetting(WxBeacon2::ADVSetting& setting) { - return getClientValue(_client, setting, WxBeacon2::ADVSetting::UUID); + return getCustomCharacteristicValue(_client, setting, WxBeacon2::ADVSetting::UUID); } bool WxBeacon2Client::setADVSetting(const WxBeacon2::ADVSetting& setting) @@ -525,3 +561,75 @@ bool WxBeacon2Client::setADVSetting(const WxBeacon2::ADVSetting& setting) if(!sv) { WB2_LOGE("Service not exists"); return false; } return sv->setValue(customCharacteristicsUUID(WxBeacon2::ADVSetting::UUID), static_cast(setting)); } + +// Generic Access Service +bool WxBeacon2Client::getDeviceName(std::string& dname) +{ + assert(_client); + dname.clear(); + auto sv = _client->getService(NimBLEUUID(WxBeacon2::GenericAccesssService::UUID)); + if(!sv) { WB2_LOGE("Service not exists"); return false; } + dname = sv->getValue(NimBLEUUID(WxBeacon2::GenericAccesssService::DeviceName::UUID)); + return !dname.empty(); +} + +bool WxBeacon2Client::getAppearance(WxBeacon2::GenericAccesssService::Appearance& app) +{ + return getCharacteristicValue(_client, app, WxBeacon2::GenericAccesssService::UUID, WxBeacon2::GenericAccesssService::Appearance::UUID); +} + +bool WxBeacon2Client::getPeripheralPreferredConnectionParameters(WxBeacon2::GenericAccesssService::PeripheralPreferredConnectionParameters& params) +{ + return getCharacteristicValue(_client, params, WxBeacon2::GenericAccesssService::UUID, WxBeacon2::GenericAccesssService::PeripheralPreferredConnectionParameters::UUID); +} + +// Device Information Service +bool WxBeacon2Client::getModelNumber(std::string& mnum) +{ + assert(_client); + mnum.clear(); + auto sv = _client->getService(NimBLEUUID(WxBeacon2::DeviceInformationService::UUID)); + if(!sv) { WB2_LOGE("Service not exists"); return false; } + mnum = sv->getValue(NimBLEUUID(WxBeacon2::DeviceInformationService::ModelNumber::UUID)); + return !mnum.empty(); +} + +bool WxBeacon2Client::getSerialNumber(std::string& snum) +{ + assert(_client); + snum.clear(); + auto sv = _client->getService(NimBLEUUID(WxBeacon2::DeviceInformationService::UUID)); + if(!sv) { WB2_LOGE("Service not exists"); return false; } + snum = sv->getValue(NimBLEUUID(WxBeacon2::DeviceInformationService::SerialNumber::UUID)); + return !snum.empty(); +} + +bool WxBeacon2Client::getFirmwareRevision(std::string& frev) +{ + assert(_client); + frev.clear(); + auto sv = _client->getService(NimBLEUUID(WxBeacon2::DeviceInformationService::UUID)); + if(!sv) { WB2_LOGE("Service not exists"); return false; } + frev = sv->getValue(NimBLEUUID(WxBeacon2::DeviceInformationService::FirmwareRevision::UUID)); + return !frev.empty(); +} + +bool WxBeacon2Client::getHardwareRevision(std::string& hrev) +{ + assert(_client); + hrev.clear(); + auto sv = _client->getService(NimBLEUUID(WxBeacon2::DeviceInformationService::UUID)); + if(!sv) { WB2_LOGE("Service not exists"); return false; } + hrev = sv->getValue(NimBLEUUID(WxBeacon2::DeviceInformationService::HardwareRevision::UUID)); + return !hrev.empty(); +} + +bool WxBeacon2Client::getManufacturerName(std::string& mname) +{ + assert(_client); + mname.clear(); + auto sv = _client->getService(NimBLEUUID(WxBeacon2::DeviceInformationService::UUID)); + if(!sv) { WB2_LOGE("Service not exists"); return false; } + mname = sv->getValue(NimBLEUUID(WxBeacon2::DeviceInformationService::ManufacturerName::UUID)); + return !mname.empty(); +} diff --git a/src/wxbeacon2_ble.hpp b/src/wxbeacon2_ble.hpp index a9446e8..c889ba9 100644 --- a/src/wxbeacon2_ble.hpp +++ b/src/wxbeacon2_ble.hpp @@ -22,21 +22,29 @@ class WxBeacon2AdvertiseCallbacks: public NimBLEAdvertisedDeviceCallbacks void onResult(NimBLEAdvertisedDevice* device) override; void clear(); - bool detected() { return static_cast(_address) != 0ULL; } + bool detected() const { return static_cast(_address) != 0ULL; } const NimBLEAddress& address() const { return _address; } const WxBeacon2::AdvertiseData& data() const { return _data; } - static WxBeacon2::ADVFormat format(std::string& name) + static WxBeacon2::ADVFormat format(const std::string& name, const size_t mlen) { - if(name == "IM") return WxBeacon2::ADVFormat::D; - if(name == "EP") return WxBeacon2::ADVFormat::E; - if(name == "Env") return WxBeacon2::ADVFormat::C; + if(name == "IM" && mlen == WxBeacon2::AdvertiseData::LENGTH_D) return WxBeacon2::ADVFormat::D; + if(name == "EP" && mlen == WxBeacon2::AdvertiseData::LENGTH_E) return WxBeacon2::ADVFormat::E; + if(name == "Env") + { + switch(mlen) + { + case WxBeacon2::AdvertiseData::LENGTH_A: return WxBeacon2::ADVFormat::A; + case WxBeacon2::AdvertiseData::LENGTH_B: return WxBeacon2::ADVFormat::B; + case WxBeacon2::AdvertiseData::LENGTH_C: return WxBeacon2::ADVFormat::C; + } + } return WxBeacon2::ADVFormat::Unknown; } private: - bool validName(std::string& name) const { return name == "IM" || name == "EP" || name == "Env"; } + bool validName(const std::string& name) const { return name == "IM" || name == "EP" || name == "Env"; } private: NimBLEAddress _address; // detected device addrress @@ -66,6 +74,7 @@ class WxBeacon2Client WxBeacon2Client* _wc; }; + //! @brief Unixtime of page struct PageTime { uint16_t _page; @@ -91,7 +100,7 @@ class WxBeacon2Client } /// @} - /// @name Get value + /// @name Get value from custom service /// @{ /*! @brief get latest data */ bool getLatestData(WxBeacon2::LatestData& ldata); @@ -101,7 +110,9 @@ class WxBeacon2Client bool getRequestPage(WxBeacon2::RequestPage& req); /*! @brief get response flag */ bool getResponseFlag(WxBeacon2::ResponseFlag& flag); - // get recording data. + /*! @brief get single response data */ + bool getResponseData(WxBeacon2::ResponseData& data); + // @brief get recording data form pageFrom,rowFrom to latest. bool getResponseData(DataVector& v, const uint16_t pageFrom = 0, const uint8_t rowFrom = 0); /*! @brief get measurement interval */ bool getMeasurementInterval(WxBeacon2::MeasurementInterval& mi); @@ -114,6 +125,30 @@ class WxBeacon2Client /*! @brief get advertise setting */ bool getADVSetting(WxBeacon2::ADVSetting& setting); /// @} + + /// @name Get value from Generic access service + /// @{ + /*! @brief get device name */ + bool getDeviceName(std::string& dname); + /*! @brief get appearance */ + bool getAppearance(WxBeacon2::GenericAccesssService::Appearance& app); + /*! @brief get appearance */ + bool getPeripheralPreferredConnectionParameters(WxBeacon2::GenericAccesssService::PeripheralPreferredConnectionParameters& params); + /// @} + + /// @name Get value from Device information service + /// @{ + /*! @brief get model number string */ + bool getModelNumber(std::string& mnum); + /*! @brief get serial number string */ + bool getSerialNumber(std::string& snum); + /*! @brief get firmware revision string */ + bool getFirmwareRevision(std::string& frev); + /*! @brief get hardware revision string */ + bool getHardwareRevision(std::string& hrev); + /*! @brief get manufacturer name string */ + bool getManufacturerName(std::string& mname); + /// @} /// @name Set value /// @{