Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: SDFileSystem mbed-os-example-ble-GattServer max32630fthr
main.cpp
- Committer:
- aureliocarella
- Date:
- 2020-07-16
- Revision:
- 21:51e162c130a9
- Parent:
- 0:c671a4833315
File content as of revision 21:51e162c130a9:
/* 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"
#include "max32630fthr.h"
#include "RpcServer.h"
#include "StringInOut.h"
#include "Peripherals.h"
#include "MAX30001.h"
#include "DataLoggingService.h"
#include "PushButton.h"
#include "Streaming.h"
#include "SDFileSystem.h"
#include "version.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:
uint8_t second;
uint8_t minute;
uint8_t hour;
ClockService() :
_ecg("485f4145-52b9-4644-af1f-7a6b9322490f", 0),
_rr_bpm("0a924ca7-87cd-4699-a3bd-abdcd9cf126a", 0),
_max30001_chr("8dd6a1b7-bc75-4741-8a26-264af75807de", 0),
_ecg_service(
/* uuid */ "51311102-030e-485f-b122-f8f381aa84ed",
/* characteristics */ _ecg_characteristics,
/* numCharacteristics */ sizeof(_ecg_characteristics) /
sizeof(_ecg_characteristics[0])
),
_server(NULL),
_event_queue(NULL)
{
// update internal pointers (value, descriptors and characteristics array)
_ecg_characteristics[0] = &_ecg;
_ecg_characteristics[1] = &_rr_bpm;
_ecg_characteristics[2] = &_max30001_chr;
// setup authorization handlers
_ecg.setWriteAuthorizationCallback(this, &Self::authorize_client_write);
_rr_bpm.setWriteAuthorizationCallback(this, &Self::authorize_client_write);
_max30001_chr.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(_ecg_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", _ecg_service.getHandle());
printf("\thour characteristic value handle %u\r\n", _ecg.getValueHandle());
printf("\tminute characteristic value handle %u\r\n", _rr_bpm.getValueHandle());
printf("\tsecond characteristic value handle %u\r\n", _max30001_chr.getValueHandle());
_event_queue->call_every(2000 /* 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 == _ecg.getValueHandle()) {
printf(" (hour characteristic)\r\n");
hour = e->data[0];
} else if (e->handle == _rr_bpm.getValueHandle()) {
printf(" (minute characteristic)\r\n");
minute = e->data[0];
} else if (e->handle == _max30001_chr.getValueHandle()) {
printf(" (second characteristic)\r\n");
second = e->data[0];
} 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 == _ecg.getValueHandle()) {
printf(" (hour characteristic)\r\n");
} else if (e->handle == _rr_bpm.getValueHandle()) {
printf(" (minute characteristic)\r\n");
} else if (e->handle == _max30001_chr.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 == _ecg.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)
{
ble_error_t err = _max30001_chr.get(*_server, second);
if (err) {
printf("read of the second value returned error %u\r\n", err);
return;
}
second = (second + 1) % 60;
uint32_t* mamt;
//MAX30001::MAX30001_REG_map_t reg = (MAX30001_REG_map_t)0x01;
uint8_t addr = 0x01; //STATUS
printf("reg=%d ", addr);
int res = Peripherals::max30001()->max30001_reg_read(addr, mamt);
printf("MAMT=%d, %x\r\n", res, *mamt);
/* printf("RPC");
char reply1[128];
// process the RPC string
RPC_call("/System/ReadVer", reply1);
//Send reply to debug port
printf(reply1);*/
printf("%d-%d-%d\r\n",hour, minute, second);
err = _max30001_chr.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)
{
ble_error_t err = _rr_bpm.get(*_server, minute);
if (err) {
printf("read of the minute value returned error %u\r\n", err);
return;
}
minute = (minute + 1) % 60;
err = _rr_bpm.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)
{
ble_error_t err = _ecg.get(*_server, hour);
if (err) {
printf("read of the hour value returned error %u\r\n", err);
return;
}
hour = (hour + 1) % 24;
err = _ecg.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> _ecg;
ReadWriteNotifyIndicateCharacteristic<uint8_t> _rr_bpm;
ReadWriteNotifyIndicateCharacteristic<uint8_t> _max30001_chr;
// list of the characteristics of the clock service
GattCharacteristic* _ecg_characteristics[3];
// demo service
GattService _ecg_service;
GattServer* _server;
events::EventQueue *_event_queue;
};
//Init PMIC on FTHR board and set logic thresholds to 3.3V
MAX32630FTHR pegasus(MAX32630FTHR::VIO_3V3);
SDFileSystem sd(P0_5, P0_6, P0_4, P0_7, "sd"); // mosi, miso, sclk, cs
//SD card insertion detection pin
DigitalIn SDDetect(P2_2, PullUp);
/// DigitalOut for CS
DigitalOut cs(P5_6);
/// SPI Master 2 with SPI0_SS for use with MAX30001
SPI spi(SPI2_MOSI, SPI2_MISO, SPI2_SCK); // used by MAX30001
/// SPI Master 1
QuadSpiInterface quadSpiInterface(SPI1_MOSI, SPI1_MISO, SPI1_SCK,
SPI1_SS); // used by S25FS512
/// External Flash
S25FS512 s25fs512(&quadSpiInterface);
/// ECG device
MAX30001 max30001(&spi, &cs);
InterruptIn max30001_InterruptB(P5_5);
InterruptIn max30001_Interrupt2B(P5_4);
/// HSP platform LED
HspLed hspLed(LED_RED);
/// Packet TimeStamp Timer, set for 1uS
Timer timestampTimer;
/// HSP Platform push button
PushButton pushButton(SW1);
// local input state of the RPC
int inputState;
// RPC request buffer
char request[128];
// RPC reply buffer
char reply[128];
int main() {
// display start banner
printf("Maxim Integrated mbed hSensor %d.%d.%d %02d/%02d/%02d\n",
VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH,
VERSION_MONTH, VERSION_DAY, VERSION_SHORT_YEAR);
// turn on red led
printf("Init HSPLED...\n");
fflush(stdout);
hspLed.on();
// set NVIC priorities for GPIO to prevent priority inversion
printf("Init NVIC Priorities...\n");
fflush(stdout);
NVIC_SetPriority(GPIO_P0_IRQn, 5);
NVIC_SetPriority(GPIO_P1_IRQn, 5);
NVIC_SetPriority(GPIO_P2_IRQn, 5);
NVIC_SetPriority(GPIO_P3_IRQn, 5);
NVIC_SetPriority(GPIO_P4_IRQn, 5);
NVIC_SetPriority(GPIO_P5_IRQn, 5);
NVIC_SetPriority(GPIO_P6_IRQn, 5);
// used by the MAX30001
NVIC_SetPriority(SPIM2_IRQn, 0);
// Be able to statically reference these devices anywhere in the application
Peripherals::setS25FS512(&s25fs512);
Peripherals::setTimestampTimer(×tampTimer);
Peripherals::setHspLed(&hspLed);
Peripherals::setPushButton(&pushButton);
Peripherals::setMAX30001(&max30001);
Peripherals::setSdFS(&sd);
Peripherals::setSDDetect(&SDDetect);
// init the S25FS256 external flash device
printf("Init S25FS512...\n");
fflush(stdout);
s25fs512.init();
// start blinking led1
printf("Init HSPLED Blink...\n");
fflush(stdout);
//
// MAX30001
//
printf("Init MAX30001 callbacks, interrupts...\n");
fflush(stdout);
max30001_InterruptB.disable_irq();
max30001_Interrupt2B.disable_irq();
max30001_InterruptB.mode(PullUp);
max30001_InterruptB.fall(&MAX30001Mid_IntB_Handler);
max30001_Interrupt2B.mode(PullUp);
max30001_Interrupt2B.fall(&MAX30001Mid_Int2B_Handler);
max30001_InterruptB.enable_irq();
max30001_Interrupt2B.enable_irq();
MAX30001_AllowInterrupts(1);
max30001.max30001_sw_rst(); // Do a software reset of the MAX30001
max30001.max30001_INT_assignment(MAX30001::MAX30001_INT_B, MAX30001::MAX30001_NO_INT, MAX30001::MAX30001_NO_INT, // en_enint_loc, en_eovf_loc, en_fstint_loc,
MAX30001::MAX30001_INT_2B, MAX30001::MAX30001_INT_2B, MAX30001::MAX30001_NO_INT, // en_dcloffint_loc, en_bint_loc, en_bovf_loc,
MAX30001::MAX30001_INT_2B, MAX30001::MAX30001_INT_2B, MAX30001::MAX30001_NO_INT, // en_bover_loc, en_bundr_loc, en_bcgmon_loc,
MAX30001::MAX30001_INT_B, MAX30001::MAX30001_NO_INT, MAX30001::MAX30001_NO_INT, // en_pint_loc, en_povf_loc, en_pedge_loc,
MAX30001::MAX30001_INT_2B, MAX30001::MAX30001_INT_B, MAX30001::MAX30001_NO_INT, // en_lonint_loc, en_rrint_loc, en_samp_loc,
MAX30001::MAX30001_INT_ODNR, MAX30001::MAX30001_INT_ODNR); // intb_Type, int2b_Type)
max30001.onDataAvailable(&StreamPacketUint32);
// initialize the RPC server
printf("Init RPC Server...\n");
fflush(stdout);
RPC_init();
// initialize the logging service
printf("Init LoggingService...\n");
fflush(stdout);
LoggingService_Init();
// initialize the SD disk
sd.disk_initialize();
// start main loop
printf("Start main loop...\n");
fflush(stdout);
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;
}