20200716 read Status Register each second
Dependencies: SDFileSystem mbed-os-example-ble-GattServer max32630fthr
Diff: main.cpp
- Revision:
- 0:c671a4833315
- Child:
- 21:51e162c130a9
diff -r 000000000000 -r c671a4833315 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Wed Jun 20 11:03:22 2018 +0100 @@ -0,0 +1,411 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include "platform/Callback.h" +#include "events/EventQueue.h" +#include "platform/NonCopyable.h" + +#include "ble/BLE.h" +#include "ble/Gap.h" +#include "ble/GattClient.h" +#include "ble/GapAdvertisingParams.h" +#include "ble/GapAdvertisingData.h" +#include "ble/GattServer.h" +#include "BLEProcess.h" + +using mbed::callback; + +/** + * A Clock service that demonstrate the GattServer features. + * + * The clock service host three characteristics that model the current hour, + * minute and second of the clock. The value of the second characteristic is + * incremented automatically by the system. + * + * A client can subscribe to updates of the clock characteristics and get + * notified when one of the value is changed. Clients can also change value of + * the second, minute and hour characteristric. + */ +class ClockService { + typedef ClockService Self; + +public: + ClockService() : + _hour_char("485f4145-52b9-4644-af1f-7a6b9322490f", 0), + _minute_char("0a924ca7-87cd-4699-a3bd-abdcd9cf126a", 0), + _second_char("8dd6a1b7-bc75-4741-8a26-264af75807de", 0), + _clock_service( + /* uuid */ "51311102-030e-485f-b122-f8f381aa84ed", + /* characteristics */ _clock_characteristics, + /* numCharacteristics */ sizeof(_clock_characteristics) / + sizeof(_clock_characteristics[0]) + ), + _server(NULL), + _event_queue(NULL) + { + // update internal pointers (value, descriptors and characteristics array) + _clock_characteristics[0] = &_hour_char; + _clock_characteristics[1] = &_minute_char; + _clock_characteristics[2] = &_second_char; + + // setup authorization handlers + _hour_char.setWriteAuthorizationCallback(this, &Self::authorize_client_write); + _minute_char.setWriteAuthorizationCallback(this, &Self::authorize_client_write); + _second_char.setWriteAuthorizationCallback(this, &Self::authorize_client_write); + } + + + + void start(BLE &ble_interface, events::EventQueue &event_queue) + { + if (_event_queue) { + return; + } + + _server = &ble_interface.gattServer(); + _event_queue = &event_queue; + + // register the service + printf("Adding demo service\r\n"); + ble_error_t err = _server->addService(_clock_service); + + if (err) { + printf("Error %u during demo service registration.\r\n", err); + return; + } + + // read write handler + _server->onDataSent(as_cb(&Self::when_data_sent)); + _server->onDataWritten(as_cb(&Self::when_data_written)); + _server->onDataRead(as_cb(&Self::when_data_read)); + + // updates subscribtion handlers + _server->onUpdatesEnabled(as_cb(&Self::when_update_enabled)); + _server->onUpdatesDisabled(as_cb(&Self::when_update_disabled)); + _server->onConfirmationReceived(as_cb(&Self::when_confirmation_received)); + + // print the handles + printf("clock service registered\r\n"); + printf("service handle: %u\r\n", _clock_service.getHandle()); + printf("\thour characteristic value handle %u\r\n", _hour_char.getValueHandle()); + printf("\tminute characteristic value handle %u\r\n", _minute_char.getValueHandle()); + printf("\tsecond characteristic value handle %u\r\n", _second_char.getValueHandle()); + + _event_queue->call_every(1000 /* ms */, callback(this, &Self::increment_second)); + } + +private: + + /** + * Handler called when a notification or an indication has been sent. + */ + void when_data_sent(unsigned count) + { + printf("sent %u updates\r\n", count); + } + + /** + * Handler called after an attribute has been written. + */ + void when_data_written(const GattWriteCallbackParams *e) + { + printf("data written:\r\n"); + printf("\tconnection handle: %u\r\n", e->connHandle); + printf("\tattribute handle: %u", e->handle); + if (e->handle == _hour_char.getValueHandle()) { + printf(" (hour characteristic)\r\n"); + } else if (e->handle == _minute_char.getValueHandle()) { + printf(" (minute characteristic)\r\n"); + } else if (e->handle == _second_char.getValueHandle()) { + printf(" (second characteristic)\r\n"); + } else { + printf("\r\n"); + } + printf("\twrite operation: %u\r\n", e->writeOp); + printf("\toffset: %u\r\n", e->offset); + printf("\tlength: %u\r\n", e->len); + printf("\t data: "); + + for (size_t i = 0; i < e->len; ++i) { + printf("%02X", e->data[i]); + } + + printf("\r\n"); + } + + /** + * Handler called after an attribute has been read. + */ + void when_data_read(const GattReadCallbackParams *e) + { + printf("data read:\r\n"); + printf("\tconnection handle: %u\r\n", e->connHandle); + printf("\tattribute handle: %u", e->handle); + if (e->handle == _hour_char.getValueHandle()) { + printf(" (hour characteristic)\r\n"); + } else if (e->handle == _minute_char.getValueHandle()) { + printf(" (minute characteristic)\r\n"); + } else if (e->handle == _second_char.getValueHandle()) { + printf(" (second characteristic)\r\n"); + } else { + printf("\r\n"); + } + } + + /** + * Handler called after a client has subscribed to notification or indication. + * + * @param handle Handle of the characteristic value affected by the change. + */ + void when_update_enabled(GattAttribute::Handle_t handle) + { + printf("update enabled on handle %d\r\n", handle); + } + + /** + * Handler called after a client has cancelled his subscription from + * notification or indication. + * + * @param handle Handle of the characteristic value affected by the change. + */ + void when_update_disabled(GattAttribute::Handle_t handle) + { + printf("update disabled on handle %d\r\n", handle); + } + + /** + * Handler called when an indication confirmation has been received. + * + * @param handle Handle of the characteristic value that has emitted the + * indication. + */ + void when_confirmation_received(GattAttribute::Handle_t handle) + { + printf("confirmation received on handle %d\r\n", handle); + } + + /** + * Handler called when a write request is received. + * + * This handler verify that the value submitted by the client is valid before + * authorizing the operation. + */ + void authorize_client_write(GattWriteAuthCallbackParams *e) + { + printf("characteristic %u write authorization\r\n", e->handle); + + if (e->offset != 0) { + printf("Error invalid offset\r\n"); + e->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; + return; + } + + if (e->len != 1) { + printf("Error invalid len\r\n"); + e->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; + return; + } + + if ((e->data[0] >= 60) || + ((e->data[0] >= 24) && (e->handle == _hour_char.getValueHandle()))) { + printf("Error invalid data\r\n"); + e->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; + return; + } + + e->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; + } + + /** + * Increment the second counter. + */ + void increment_second(void) + { + uint8_t second = 0; + ble_error_t err = _second_char.get(*_server, second); + if (err) { + printf("read of the second value returned error %u\r\n", err); + return; + } + + second = (second + 1) % 60; + + err = _second_char.set(*_server, second); + if (err) { + printf("write of the second value returned error %u\r\n", err); + return; + } + + if (second == 0) { + increment_minute(); + } + } + + /** + * Increment the minute counter. + */ + void increment_minute(void) + { + uint8_t minute = 0; + ble_error_t err = _minute_char.get(*_server, minute); + if (err) { + printf("read of the minute value returned error %u\r\n", err); + return; + } + + minute = (minute + 1) % 60; + + err = _minute_char.set(*_server, minute); + if (err) { + printf("write of the minute value returned error %u\r\n", err); + return; + } + + if (minute == 0) { + increment_hour(); + } + } + + /** + * Increment the hour counter. + */ + void increment_hour(void) + { + uint8_t hour = 0; + ble_error_t err = _hour_char.get(*_server, hour); + if (err) { + printf("read of the hour value returned error %u\r\n", err); + return; + } + + hour = (hour + 1) % 24; + + err = _hour_char.set(*_server, hour); + if (err) { + printf("write of the hour value returned error %u\r\n", err); + return; + } + } + +private: + /** + * Helper that construct an event handler from a member function of this + * instance. + */ + template<typename Arg> + FunctionPointerWithContext<Arg> as_cb(void (Self::*member)(Arg)) + { + return makeFunctionPointer(this, member); + } + + /** + * Read, Write, Notify, Indicate Characteristic declaration helper. + * + * @tparam T type of data held by the characteristic. + */ + template<typename T> + class ReadWriteNotifyIndicateCharacteristic : public GattCharacteristic { + public: + /** + * Construct a characteristic that can be read or written and emit + * notification or indication. + * + * @param[in] uuid The UUID of the characteristic. + * @param[in] initial_value Initial value contained by the characteristic. + */ + ReadWriteNotifyIndicateCharacteristic(const UUID & uuid, const T& initial_value) : + GattCharacteristic( + /* UUID */ uuid, + /* Initial value */ &_value, + /* Value size */ sizeof(_value), + /* Value capacity */ sizeof(_value), + /* Properties */ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY | + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE, + /* Descriptors */ NULL, + /* Num descriptors */ 0, + /* variable len */ false + ), + _value(initial_value) { + } + + /** + * Get the value of this characteristic. + * + * @param[in] server GattServer instance that contain the characteristic + * value. + * @param[in] dst Variable that will receive the characteristic value. + * + * @return BLE_ERROR_NONE in case of success or an appropriate error code. + */ + ble_error_t get(GattServer &server, T& dst) const + { + uint16_t value_length = sizeof(dst); + return server.read(getValueHandle(), &dst, &value_length); + } + + /** + * Assign a new value to this characteristic. + * + * @param[in] server GattServer instance that will receive the new value. + * @param[in] value The new value to set. + * @param[in] local_only Flag that determine if the change should be kept + * locally or forwarded to subscribed clients. + */ + ble_error_t set( + GattServer &server, const uint8_t &value, bool local_only = false + ) const { + return server.write(getValueHandle(), &value, sizeof(value), local_only); + } + + private: + uint8_t _value; + }; + + ReadWriteNotifyIndicateCharacteristic<uint8_t> _hour_char; + ReadWriteNotifyIndicateCharacteristic<uint8_t> _minute_char; + ReadWriteNotifyIndicateCharacteristic<uint8_t> _second_char; + + // list of the characteristics of the clock service + GattCharacteristic* _clock_characteristics[3]; + + // demo service + GattService _clock_service; + + GattServer* _server; + events::EventQueue *_event_queue; +}; + +int main() { + BLE &ble_interface = BLE::Instance(); + events::EventQueue event_queue; + ClockService demo_service; + BLEProcess ble_process(event_queue, ble_interface); + + ble_process.on_init(callback(&demo_service, &ClockService::start)); + + // bind the event queue to the ble interface, initialize the interface + // and start advertising + ble_process.start(); + + // Process the event queue. + event_queue.dispatch_forever(); + + return 0; +}