![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
A cute tiny piece of code implementing an IoT NAND device, demonstrating how to setup and advertise a cute GATT (NAND) service. The code has been tested on a Nordic nRF51822-DK.
Dependencies: BLE_API mbed nRF51822
Fork of BLE_HeartRate_IDB0XA1 by
Revision 26:dce30a5341bb, committed 2018-05-19
- Comitter:
- hux
- Date:
- Sat May 19 14:10:17 2018 +0000
- Parent:
- 25:339931243be4
- Commit message:
- Published
Changed in this revision
diff -r 339931243be4 -r dce30a5341bb bricks/advertise.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/advertise.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,114 @@ +// advertise.h - begin peripheral role and start advertising +// +// Synopsis: +// +// advertise(O&o); +// advertise(O&o, const char *mode, int ms = 100); +// +// payload(O&o,const char *mode) // set advertising payload +// +// Arguments: +// +// o: Blob object (to avoid name clashes) +// mode: String containing a sequence of mode characters defining both +// advertising type (upper case characters) and advertising flags +// (lower case characters), some of which have meaning, all others +// are ignored and can be used for spacing (e.g. ':', ' ', '-', ...) +// ms: Advertising interval in mili seconds (default 100 ms) +// +// Advertising Type +// +// C: ADV_CONNECTABLE_UNDIRECTED +// D: ADV_CONNECTABLE_DIRECTED +// S: ADV_SCANNABLE_UNDIRECTED +// N: ADV_NON_CONNECTABLE_UNDIRECTED +// +// Advertising Flags: +// +// l: LE Limited Discoverable Mode (value: 0x01) +// g: LE General Discoverable Mode (value: 0x02) +// n: BR/EDR Not Supported (value: 0x04) +// c: Controller: simultaneous LE & BR/EDR (value: 0x08) +// h: Host: Simultaneous LE & BR/EDR (value: 0x10) +// +// Examples 1: Start advertising in peripheral role with type 'connectable, uni- +// directed, and flags 'BR/EDR Not Supported', 'LE General Discoverable Mode' +// +// advertise(o,"C:ng",100); // start advertising @ 100 msec interval +// advertise(o,"Cng",100); // same as above +// advertise(o,"gCn",100); // same as above +// +// Examples 2: Start advertising in peripheral role with type 'connectable, +// directed, and flags 'LE Limited Discoverable Mode' +// +// advertise("D:l",100) // Connectable Directed, limited discoverable +// +// Examples 3: A typical peripheral advertising session starts with setup of +// device name, advertising name and advertising data. +// +// device(o,"Smart Button"); // setup device name +// name(o,"Smart Button #1"); // setup advertising name +// data(o,"My Home"); // setup advertising data +// advertise(o,"C:ng",100); // start advertising @ 100 msec interval +// +// Example 4: restart advertising based on pre-defined advertising payload +// +// +// See also: SERVICE +// +#ifndef _ADVERTISE_H_ +#define _ADVERTISE_H_ + +#include "bricks/blob.h" +#include "bricks/trace.h" + +#define _GADAT GapAdvertisingData +#define _GAPAR GapAdvertisingParams + + + inline void payload(O&o,const char *p) // Set advertising type and flags + { + _GAPAR::AdvertisingType_t typ = _GAPAR::ADV_CONNECTABLE_UNDIRECTED; + uint8_t mask = 0x00; + + for (;*p; p++) + { + switch (*p) + { + case 'l': mask = mask | _GADAT::LE_LIMITED_DISCOVERABLE; break; + case 'g': mask = mask | _GADAT::LE_GENERAL_DISCOVERABLE; break; + case 'n': mask = mask | _GADAT::BREDR_NOT_SUPPORTED; break; + case 'c': mask = mask | _GADAT::SIMULTANEOUS_LE_BREDR_C; break; + case 'h': mask = mask | _GADAT::SIMULTANEOUS_LE_BREDR_H; break; + + case 'C': typ = _GAPAR::ADV_CONNECTABLE_UNDIRECTED; break; + case 'D': typ = _GAPAR::ADV_CONNECTABLE_DIRECTED; break; + case 'S': typ = _GAPAR::ADV_SCANNABLE_UNDIRECTED; break; + case 'N': typ = _GAPAR::ADV_NON_CONNECTABLE_UNDIRECTED; break; + } + } + + o.gap().setAdvertisingType(typ); + o.gap().accumulateAdvertisingPayload(mask); + } + + + inline void advertise(O&o) // start advertising + { + o.gap().startAdvertising(); // start advertising + trace(o,2,"<advertising>\n"); + } + + inline void advertise(O&o, int ms) // start advertising (msec: periode) + { + o.gap().setAdvertisingInterval(ms); + advertise(o); // start advertising + } + + inline void advertise(Blob &o, const char *mode, int ms = 100) + { + payload(o,mode); // set advertising payload type & mask + advertise(o,ms); // start advertising + } + +#endif // _ADVERTISE_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/blob.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/blob.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,59 @@ +// blob.h - 'BLOBs' are BLuetooth OBjects +#ifndef _BLOB_H_ +#define _BLOB_H_ + +#include "ble/BLE.h" +#include "ble/Gap.h" + +#define _ICCC BLE::InitializationCompleteCallbackContext // pure short hand +#define _GDCP Gap::DisconnectionCallbackParams_t // pure short hand +#define _GCCP Gap::ConnectionCallbackParams_t // pure short hand +#define _GWCP GattWriteCallbackParams // pure short hand + + class Blob : public BLE + { + public: + const _ICCC *pComplete; // params to _ICCC context + const _GCCP *pConnect; // params to _GCCP context + const _GDCP *pDisconnect; // params to _GDPC context + const _GWCP *pWritten; // params to _GWCP context + + public: // construction + Blob() : BLE() // standard constructor + { + pComplete = 0; pDisconnect = 0; + } + }; + + +// Setup Advertising Name (syntactic sugar) + + inline void name(Blob &o, const char *str) + { + o.gap().accumulateAdvertisingPayload( + GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)str, strlen(str)+1); + } + + +// Setup Device Name (syntactic sugar) + + inline void device(Blob &o, const char *text) + { + o.gap().setDeviceName((const uint8_t *)text); + } + + +// Setup Advertising Data (syntactic sugar) + + inline void data(Blob &o, const uint8_t *buf, size_t buflen) + { + o.gap().accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, buf, buflen); + } + + inline void data(Blob &o, const char *text) + { + size_t len = strlen(text); + data(o,(const uint8_t*)text,len); + } + +#endif // _BLOB_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/bricks.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/bricks.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,32 @@ +// bricks.h - single header for inclusion of BLE stuff +// and most common brick headers + +#ifndef _BRICKS_H_ +#define _BRICKS_H_ + +#include <mbed.h> +#include "ble/BLE.h" + + // include o.h before any other includes of bricks headers + +#include "bricks/o.h" + + // include other bricks headers in any order (we use alphabetical order) + +#include "bricks/advertise.h" +#include "bricks/blinker.h" +#include "bricks/callback.h" +#include "bricks/characteristic.h" +#include "bricks/collection.h" +#include "bricks/enroll.h" +#include "bricks/get.h" +#include "bricks/init.h" +#include "bricks/print.h" +#include "bricks/service.h" +#include "bricks/set.h" +#include "bricks/sleep.h" +#include "bricks/trace.h" +#include "bricks/types.h" +#include "bricks/updated.h" + +#endif // _BRICKS_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/callback.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/callback.cpp Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,83 @@ +// callback.cpp - deal with callbacks + +#include "bricks/blob.h" +#include "bricks/callback.h" +#include "bricks/trace.h" + +//============================================================================== +// Setup Data Written Callback +//============================================================================== + + static void (*cbWritten)(O&o) = 0; // where we have to follow up + + // default onDataWritten callback + + static void dfWritten(const GattWriteCallbackParams *params) + { + O o; // setup a blob + o.pWritten = params; // store to provide access + + trace(o,2,"<data written>\n"); + + if (cbWritten) + (*cbWritten)(o); // user callback follow-up + } + + + void onWritten(O&o, void (*fptr)(O&o)) // setup data written callback + { + cbWritten = fptr; // remember function to follow-up + o.gattServer().onDataWritten(dfWritten);// actual calback setup in GATT + } + +//============================================================================== +// Setup Connection Callback +//============================================================================== + + static void (*cbConnect)(Blob&) = 0;// where we have to follow up + + // default connection callback + + static void dfConnect(const Gap::ConnectionCallbackParams_t *params) + { + O o; // setup a blob object + o.pConnect = params; // store to provide access + + trace(o,2,"<connect>\n"); + + if (cbConnect) + (*cbConnect)(o); // user callback follow-up + } + + void onConnect(O&o,void(*fptr)(O&)) // setup connection callback + { + cbConnect = fptr; // remember function to follow-up + o.gap().onConnection(dfConnect); // actual callback setup in GAP + } + +//============================================================================== +// Setup Disconnection Callback +//============================================================================== + + static void (*cbDisconnect)(O&o) = 0; // where we have to follow up + + // default disconnection callback + + static void dfDisconnect(const Gap::DisconnectionCallbackParams_t *params) + { + Blob o; // setup a blob + o.pDisconnect = params; // store to provide access + + trace(o,2,"<disconnected>\n"); + + if (cbDisconnect) + (*cbDisconnect)(o); // user callback follow-up + } + + void onDisconnect(O&o,void(*fptr)(O&o)) // setup disconnection callback + { + cbDisconnect = fptr; // remember function to follow-up + + o.gap().onDisconnection(dfDisconnect);// actualcallback setup in GAP + } +
diff -r 339931243be4 -r dce30a5341bb bricks/callback.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/callback.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,13 @@ +// callback.h - deal with callbacks +// +// +#ifndef _CALLBACK_H_ +#define _CALLBACK_H_ + +#include "bricks/o.h" + + void onConnect (O&o, void(*fptr)(O&o)); // setup connection callback + void onDisconnect(O&o, void(*fptr)(O&o)); // setup disconnect callback + void onWritten (O&o, void (*fptr)(O&)); // setup data written callback + +#endif // _CALLBACK_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/characteristic.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/characteristic.cpp Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,7 @@ +// characteristic.cpp - an easy to use overloaded characteristic class + +#include "bricks/characteristic.h" + + IniBuffer characteristicInidata; // to initialize a characteristic + +// eof \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/characteristic.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/characteristic.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,247 @@ +// characteristic.h - an easy to use overloaded characteristic class +// +// Synopsis: +// +// Characteristic<type> myCharacteristic(col,uuid,mode); +// Characteristic<type> myCharacteristic(col,uuid,mode,name); +// Characteristic<type> myCharacteristic(col,uuid,mode,name,inidata); +// Characteristic<type> myCharacteristic(col,uuid,mode,inidata); +// +// Declaration of a characteristic, specifying value type, UUID and access mode, +// optionally providing a name (user descriptor) and initial data. +// +// col: Collection, to which the constructed characteristic is automati- +// cally added. After construction and adding all characteristics to +// a collection this collection is passed to a service constructor, +// which gets thus knowledge about all characteristics which have to +// be involved in the service +// +// uuid: A unique universal ID which identifies the characteristic +// +// mode: A string comprising zero or more mode characters which +// define possible properties of the characteristic. +// +// "" no properties +// "b" broadcast +// "r" read access +// "s" send & pray (write without response) +// "w" write access (with feedback) +// "n" notification (no client confirmation) +// "i" indication (with client confirmation) +// "a" authentication signed write +// "x" extended access +// +// name: An optional name provided in terms of a user descriptor. If this +// argument is omitted the user descriptor is not created. +// +// inidata: A pointer to a variable containing initializing data for the +// characteristic. If the inidata argument is not provided, an +// implicit initialization is performed which clears the characte- +// ristic (all bytes set to 0). As a concluding remark it needs to +// be noted that the implicite initialozation works only for +// sizeof(Type) <= SIZE_INIBUFFER (defined in "bricks/types.h") +// +// +// Example 1: A protocol might be declared as a class as follows +// +// Collection col; // collection used during setup +// +// Characteristic<ObjectId> id(col,0x2AC3,"rw","ID"); +// Characteristic<ObjectName> name(col,0x2ABE,"rw","Name"); +// Characteristic<Digital> presence(col,0x2A56,"r","Presence"); +// +// Service presenceDetection(col,0xA001); // instantiate service +// +// onSetup(Blob &blue) +// { +// blue.service(presenceDetection); // add service +// } +// +// Example 2: service definition by means of a service definition class +// +// class PresenceDetector +// { +// public: +// Collection col; // collection used during setup +// +// Characteristic<ObjectId> id; // ID of presence detector +// Characteristic<ObjectName> name; // name of presence detector +// Characteristic<Digital> presence; // digital presence value +// Characteristic<DateTime> timestamp; // last detection change's time +// Characteristic<ObjectName> layout; // name of model railway layout +// +// Service presenceDetection; // the service +// +// public: +// PresenceDetector(Blob &blue, cost UUID uuid) : +// list; // init service list +// id(list,0x2AC3,"rw","ID"), // instantiate characteristic +// name(list,0x2ABE,"rw","Name"), // instantiate characteristic +// presence(list,0x2A56,"r","Presence"),// instantiate characteristic +// timestamp(list,0x2A08,"r","Timestamp"),// instantiate characteristic +// layout(list,0x2ABE,"rw","Layout"), // instantiate characteristic +// presenceDetection(list,uuid) // instantiate service +// { +// blue.service(presenceDetection); // add service +// } +// }; +// +#ifndef _CHARACTERISTIC_H_ +#define _CHARACTERISTIC_H_ + +#include "ble/BLE.h" +#include "ble/Gap.h" +#include "bricks/types.h" +#include "bricks/collection.h" +#include "bricks/descriptor.h" + + // the following #define is helpful if characteristics are defined on base + // of the BLE API (the tedious way) + +#define UserDescriptor(name,attribute,descriptor,text) \ + static char *name = text; \ + static GattAttribute attribute(0x2901,(uint8_t*)name,strlen(name),strlen(name)); \ + static GattAttribute *descriptor[] = {&attribute}; + + // the declaration of Characteristics needs the availability of an + // initializing buffer in the default case + + extern IniBuffer characteristicInidata; // to initialize a characteristic + + // class definition of a characteristic initializer + + class CharacteristicInitializer // provides pointers to initialized data + { + private: + IniBuffer inibuf; // initialized data + + public: + ObjectId *pObjectId() { return (ObjectId*) &inibuf; } + ObjectName *pObjectName() { return (ObjectName*) &inibuf; } + Bool *pBool() { return (Bool*) &inibuf; } + Digital *pDigital() { return (Digital*) &inibuf; } + DateTime *pDateTime() { return (DateTime*) &inibuf; } + Buffer *pBuffer() { return (Buffer*) &inibuf; } + + public: + CharacteristicInitializer() // constructor + { + memset(inibuf,0,sizeof(IniBuffer)); + + // besides of the local inibuf also initialize the global + // init buffer characteristicInidata; + + memset(characteristicInidata,0,sizeof(IniBuffer)); + } + }; + + // definition of class Characteristic + + class Service; + + template <typename Type> + class Characteristic : public GattCharacteristic + { + private: + void init() // initialize inibuffer + { + static int initialized = 0; + + if (!initialized) // clear init data buffer + { initialized = 1; + memset(characteristicInidata,0,sizeof(IniBuffer)); + } + } + + private: + UserDescriptor descriptor; // characteristic's user descriptor + + uint8_t getmask(const char *pmask) + { + uint8_t mask = 0; + + for(;*pmask;pmask++) + { switch (*pmask) + { + case 'b': // broad cast + mask = mask | BLE_GATT_CHAR_PROPERTIES_BROADCAST; + break; + case 'r': // read + mask = mask | BLE_GATT_CHAR_PROPERTIES_READ; + break; + case 's': // send (& pray) + mask = mask | BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE; + break; + case 'w': // write + mask = mask | BLE_GATT_CHAR_PROPERTIES_WRITE; + break; + case 'n': // notify + mask = mask | BLE_GATT_CHAR_PROPERTIES_NOTIFY; + break; + case 'i': // indicate + mask = mask | BLE_GATT_CHAR_PROPERTIES_INDICATE; + break; + case 'a': // authenticated signed write + mask = mask | BLE_GATT_CHAR_PROPERTIES_AUTHENTICATED_SIGNED_WRITES; + break; + case 'x': // extended properties + mask = mask | BLE_GATT_CHAR_PROPERTIES_EXTENDED_PROPERTIES; + break; + } + } + return mask; + } +/* + public: + Characteristic<Type> (Service &service, UUID uuid, const char *mode, + Type *valuePtr = (Type*)&characteristicInidata) : + GattCharacteristic(uuid, + reinterpret_cast<uint8_t*> (valuePtr), + sizeof(Type), + sizeof(Type), + getmask(mode), + NULL, // descriptor list + 0, // number of descriptors + false), + descriptor("") // construct descriptor + { + init(); // assure initializing + + // there is a potential danger that we use implicite initializing + // with sizeof(Type) > sizeof(inibuffer). This must be prevented! + + int feasible = (sizeof(Type) <= sizeof(IniBuffer)); + if (feasible || valuePtr != (Type*)&characteristicInidata) + { + service.add(this); // add to service + } // otherwise just ignore! + } +*/ + public: + Characteristic<Type> (Service &service, UUID uuid, const char *mode, + const char *name, Type *valuePtr = (Type*)&characteristicInidata) : + GattCharacteristic(uuid, + reinterpret_cast<uint8_t*> (valuePtr), + sizeof(Type), + sizeof(Type), + getmask(mode), + descriptor.plist, // descriptor list + 1, // number of descriptors + false), + descriptor(name) // construct descriptor + { + init(); // assure initializing + + // there is a potential danger that we use implicite initializing + // with sizeof(Type) > sizeof(inibuffer). This must be prevented! + + int feasible = (sizeof(Type) <= sizeof(IniBuffer)); + if (feasible || valuePtr != (Type*)&characteristicInidata) + { + service.add(this); // add to service + } // otherwise just ignore! + } + }; + + +#endif // _CHARACTERISTIC_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/collection.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/collection.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,93 @@ +// collection.h - used to setup a collection of characteristics in order to +// setup a service. +// +// Description: +// Collections are used to setup a service comprising a collection of +// characteristics. The setup sequence starts with declaring a collection +// (this constructs an empty collection) while passing the collection in- +// stance repeatedly to a constructor of a characteristic, which auto-adds +// the new constructed characteristics to the collection. Finally, when the +// collection instance is passed to a service constructor, the service con- +// structor is able to access all characteristics which have been collected +// previously. +// +// Example 1: A protocol might be declared as a class as follows +// +// Collection col; // collection used during setup +// +// Characteristic<ObjectId> id(col,0x2AC3,"rw","ID"); +// Characteristic<ObjectName> name(col,0x2ABE,"rw","Name"); +// Characteristic<Digital> presence(col,0x2A56,"r","Presence"); +// +// Service presenceDetection(col,0xA001); // instantiate service +// +// onSetup(Blob &blue) +// { +// blue.service(presenceDetection); // add service +// } +// +// Example 2: service definition by means of a service definition class +// +// class PresenceDetector +// { +// public: +// Collection col; // collection used during setup +// +// Characteristic<ObjectId> id; // ID of presence detector +// Characteristic<ObjectName> name; // name of presence detector +// Characteristic<Digital> presence; // digital presence value +// Characteristic<DateTime> timestamp; // last detection change's time +// Characteristic<ObjectName> layout; // name of model railway layout +// +// Service presenceDetection; // the service +// +// public: +// PresenceDetector(Blob &blue, cost UUID uuid) : +// list; // init service list +// id(list,0x2AC3,"rw","ID"), // instantiate characteristic +// name(list,0x2ABE,"rw","Name"), // instantiate characteristic +// presence(list,0x2A56,"r","Presence"),// instantiate characteristic +// timestamp(list,0x2A08,"r","Timestamp"),// instantiate characteristic +// layout(list,0x2ABE,"rw","Layout"), // instantiate characteristic +// presenceDetection(list,uuid) // instantiate service +// { +// blue.service(presenceDetection); // add service +// } +// }; +// +#ifndef _COLLECTION_H_ +#define _COLLECTION_H_ + +#include "ble/BLE.h" +#include "ble/Gap.h" + + class Collection + { + private: + typedef GattCharacteristic *GattCharPtr; + + public: + GattCharacteristic **plist; + int count; + + Collection() // construct empty collection + { + plist = 0; count = 0; // init properties + } + + void add(GattCharPtr p) + { + GattCharPtr *pnew = new GattCharPtr[count+1]; + pnew[count] = p; // add new characteristic + + if (count > 0) + { for (int i=0; i < count; i++) + pnew[i] = plist[i]; + delete plist; + } + plist = pnew; // update list pointer + count++; + } + }; + +#endif // _COLLECTION_H_
diff -r 339931243be4 -r dce30a5341bb bricks/descriptor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/descriptor.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,50 @@ +// descriptor.h - a description list, containing a single descriptor +// +// Synopsis: +// +// Descriptor user(0x2901,"User"); +// Descriptor client(0x2902,"Client"); +// +// UserDescriptor status("Status"); // user descriptor 0x2901 +// ClientDescriptor client("Client"); // client descriptor 0x2902 +// +#ifndef _DESCRIPTOR_H_ +#define _DESCRIPTOR_H_ + +#include "ble/BLE.h" +#include "ble/Gap.h" + + class Descriptor : public GattAttribute + { + private: + typedef GattAttribute *GattAttributePtr; + + public: + GattAttributePtr plist[1]; + + Descriptor(UUID uuid, const char *name) : + GattAttribute(uuid,(uint8_t*)name,strlen(name),strlen(name)) + { + plist[0] = this; + } + }; + + class UserDescriptor : public Descriptor + { + public: + UserDescriptor(const char *name) : Descriptor(0x2901,name) + { + // empty + } + }; + + class ClientDescriptor : public Descriptor + { + public: + ClientDescriptor(const char *name) : Descriptor(0x2902,name) + { + // empty + } + }; + +#endif // _DESCRIPTOR_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/enroll.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/enroll.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,83 @@ +// enroll.h - enroll a service +// +// Synopsis: +// +// void enroll(Blob &o, GattService &gservice); +// void enroll(Blob &o, uint16_t bcid = 0xFFFF); +// +// void enroll(Blob &o, Service &service, uint16_t bcid = 0xFFFF); +// +// Arguments: +// +// o: Blob object (to avoid name clashes) +// gservice: The GattService to be enrolled +// service: The Service to be enrolled +// bcid: Broadcast ID (optional); if not provided the default value +// 0xFFFF will be used +// +// Description +// +// There are three ways to call enrollment. +// +// In the first case the service has been setup via a GattService class. +// The service is enrolled by registering the service @ GAP. On advertising +// the provided broadcast ID is used (otherwise default ID 0xFFFF). +// +// The second case is based on a Service class object which has been setup +// by having added a set of characteristics to the internal collection. Upon +// enrollment a GattService instance will be created internally and enroll- +// ment of this GattService will be performed according to the first case. +// +// The third way is to enroll only the service id. This calling syntax is +// used, if a Gatt servive is pre-enrolled without enrolling the service ID. +// +// Example 1: enrollment of GattService +// +// enroll(o,gservice); +// enroll(o,0xFFFF); +// +// See also: SERVICE +// +#ifndef _ENROLL_H_ +#define _ENROLL_H_ + +#include "bricks/blob.h" +#include "bricks/service.h" + + + inline void enroll(Blob &o, uint16_t bcid = 0xFFFF) + { + static uint16_t list[1]; + + // Custom UUID, FFFF is reserved for development + // Used for UUID's broadcast in advertising packet + + list[0] = bcid; // set broadcast ID + +// o.pble->gap().accumulateAdvertisingPayload( + o.gap().accumulateAdvertisingPayload( + GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, + (uint8_t *)list, sizeof(list)); + } + + + inline void enroll(Blob &o, GattService &gservice, uint16_t bcid = 0xFFFF) + { +// o.pble->addService(gservice); // add service to GATT attributes + o.addService(gservice); // add service to GATT attributes + enroll(o,bcid); // enroll GattService (without BCID) + } + + + inline void enroll(Blob &o, Service &service, uint16_t bcid = 0) + { + Collection &collection = service.collection; + GattService gservice(service.uuid, collection.plist, collection.count); + + if (bcid == 0) + bcid = service.uuid; + + enroll(o,gservice,bcid); // enroll GattService (with BCID) + } + +#endif // _ENROLL_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/get.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/get.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,52 @@ +// get.h - get data from a characteristics +// +// Synopsis: +// +// Get data from a 'characteristic' into a 'data' variable +// +// get(characteristic,data) +// +// The 'get' function (overloaded function family) is implemented for all +// types defined in "bricks/types.h". +// +// See also: CHARACTERISTIC, SET +// +#ifndef _GET_H_ +#define _GET_H_ + +#include "ble/BLE.h" +#include "ble/Gap.h" +#include "ble/GattClient.h" +#include "bricks/o.h" +#include "bricks/types.h" +#include "bricks/characteristic.h" + + inline void get(O&o, Characteristic<Bool> &chr, Bool &data) + { + uint16_t size = sizeof(Bool)/sizeof(uint8_t); + o.gattServer().read(chr.getValueHandle(), (uint8_t*)&data,&size); + } + + inline void get(O&o, Characteristic<ObjectId> &chr, ObjectId &data) + { + uint16_t size = sizeof(ObjectId)/sizeof(uint8_t); + o.gattServer().read(chr.getValueHandle(), (uint8_t*)&data,&size); + } + + inline void get(O&o, Characteristic<Buffer> &chr, Buffer &data) + { + uint16_t size = sizeof(Buffer)/sizeof(uint8_t); + o.gattServer().read(chr.getValueHandle(), (uint8_t*)&data,&size); + } + +// we provide also some GET methods for GattCharacteristics. However the use +// of these methods are more dangerous, because a GattCharacteristics can be +// of any type and the compiler cannot help us to check !!! + + inline void get(O&o,GattCharacteristic &chr, Bool &data) + { + uint16_t size = sizeof(Bool)/sizeof(uint8_t); + o.gattServer().read(chr.getValueHandle(), (uint8_t*)&data, &size); + } + +#endif // _GET_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/init.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/init.cpp Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,71 @@ +// init.cpp - initialize BLE system + +#include "bricks/init.h" +#include "bricks/blinker.h" +#include "bricks/trace.h" + + static void (*cbSetup)(O&o) = 0; // setup callback + static void (*cbError)(O&o) = 0; // error callback + + static void cbDefaultError(O&o) // default error handler + { + Blinker blink; + blink.error(); // 'error' blink sequence + } + + static void cbDefaultComplete(O&o) // default error handler + { + // empty + } + + static void initComplete(BLE::InitializationCompleteCallbackContext *params) + { + Blob o; // setup a blob + o.pComplete = params; // store to provide access + + // params->error = (ble_error_t)((int)BLE_ERROR_NONE+1); // uncomment for debug + + trace(o,2,"<initialized>\n"); + + if (params->error != BLE_ERROR_NONE) + { + trace(o,0,"error\n"); + if (cbError) + (*cbError)(o); // handle error in follow-up + + return; + } + + // Ensure that it is the default instance of BLE + + if(o.getInstanceID() != BLE::DEFAULT_INSTANCE) + { + return; + } + + (*cbSetup)(o); // followup with setup + } + + + void init(O&o, void (*scb)(O&o),void (*ecb)(O&o)) // init BLE system + { + cbSetup = scb; // store setup callback + cbError = ecb; // store error callback + o.init(initComplete); + } + + + void init(O&o, void (*scb)(O&o)) // initialize BLE system + { + init(o,scb,cbDefaultError); // continue with default error callback + } + + void init(O&o) // initialize BLE system + { + init(o,cbDefaultComplete,cbDefaultError); + while (!o.hasInitialized()) + { + trace(o,1,"waiting for initialized BLE!\n"); + wait_ms(200); + } + }
diff -r 339931243be4 -r dce30a5341bb bricks/init.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/init.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,90 @@ +// init.h - initialize BLE system +// +// Synopsis: +// +// void init(Blob &o, void (*scb)(Blob&),void (*ecb)(Blob&)) // init BLE +// void init(Blob &o, void (*scb)(Blob&)) // init BLE +// +// Arguments: +// +// o: Blob object (to avoid name clashes) +// scb: setup callback +// ecb: error callback +// +// Description: +// +// Initialize BLE system, providing a setup callback and optionally an error +// callback. The actual initializing happens in the setup callback. If an +// error occurs during initializin, the error callback will be consulted. If +// no error callback is provided an implicit error callback will be called. +// +// Example 1: simple BLE system setup +// +// void cbSetup(Blob &o) +// { +// device(o,"IoT node"); // setup device name +// name(o,"Node #1"); // setup advertising name +// advertise("C:ng"); // start advertising +// } +// +// main(void) +// { +// Blob o; // our Blob object +// init(o,cbSetup); // init BLE base layer, always do first +// while (true) // Infinite loop waiting for BLE events +// sleep(o); // low energy waiting +// } +// +// Example 2: Provide also error handler +// +// void cbError(Blob &o) +// { +// trace(0,"init error!"); +// } +// +// void cbSetup(Blob &o) +// { +// device(o,"IoT node"); // setup device name +// name(o,"Node #1"); // setup advertising name +// advertise("C:ng"); // start advertising +// } +// +// main(void) +// { +// Blob o; // our Blob object +// init(o,cbSetup,cbError); // init BLE base layer, always do first +// while (true) // Infinite loop waiting for BLE events +// sleep(o); // low energy waiting +// } +// +// Example 3: Without Callbacks (busy waiting until initialized) +// +// main(void) +// { +// Blob o; // our Blob object +// init(o); // init BLE base layer, always do first +// +// device(o,"IoT node"); // setup device name +// name(o,"Node #1"); // setup advertising name +// advertise("C:ng"); // start advertising +// +// while (true) // Infinite loop waiting for BLE events +// sleep(o); // low energy waiting +// } +// +// See also: BLOB, INIT +// +#ifndef _INIT_H_ +#define _INIT_H_ + +#include "bricks/o.h" + +//============================================================================== +// Initializing +//============================================================================== + + void init(O&o, void (*scb)(O&o),void (*ecb)(O&o)); // init BLE system + void init(O&o, void (*scb)(O&o)); // init BLE system + void init(O&o); // init BLE system + +#endif // _INIT_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/o.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/o.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,9 @@ +// o.h - dealing with THE object 'O' +#ifndef _O_H_ +#define _O_H_ + +#define O Blob + +#include "bricks/blob.h" + +#endif // _O_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/print.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/print.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,49 @@ +// print.h - print value of a characteristic +// +// Synopsis: +// +// See also: CHARACTERISTIC, GET, SET +// +#ifndef _PRINT_H_ +#define _PRINT_H_ + +#include <stdio.h> +#include "ble/Gap.h" +#include "bricks/o.h" +#include "bricks/types.h" +#include "bricks/characteristic.h" + +//============================================================================== +// Some Callbacks +//============================================================================== + + inline void print(O&o, Characteristic<Buffer> &chr, const char *name) + { + //Serial out(USBTX, USBRX); // serial port to PC terminal + + Buffer data; uint8_t *p = data; + get(o,chr,data); + + printf("%s: %02x-%02x-%02x-%02x-%02x-",name,(int)(p[0]),(int)(p[1]),(int)(p[2]),(int)(p[3]),(int)(p[4])); + printf("%02x-%02x-%02x-%02x-%02x\r\n",(int)(p[5]),(int)(p[6]),(int)(p[7]),(int)(p[8]),(int)(p[9])); + } + + inline void print(O&o, Characteristic<Bool> &chr, const char *name) + { + //Serial out(USBTX, USBRX); // serial port to PC terminal + Bool data; + + get(o,chr,data); + printf("%s: %02x\r\n",name,data); + } + +// we provide also some PRINT methods for non Characteristics. If the value of +// a characteristic needs to be printed the value must be fetched before with +// the GET function. + + inline void print(O&o, Bool data, const char *name) + { + printf("%s: %02x\r\n",name,(int)data); + } + +#endif // _PRINT_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/service.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/service.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,85 @@ +// service.h - declaring services and definitions +// +// Synopsis +// +// Example 1: A protocol might be declared as a class as follows +// +// Collection col; // collection used during setup +// +// Characteristic<ObjectId> id(col,0x2AC3,"rw","ID"); +// Characteristic<ObjectName> name(col,0x2ABE,"rw","Name"); +// Characteristic<Digital> presence(col,0x2A56,"r","Presence"); +// +// Service presenceDetection(col,0xA001); // instantiate service +// +// onSetup(Blob &blue) +// { +// blue.service(presenceDetection); // use service +// } +// +// Example 2: service definition by means of a service definition class +// +// class PresenceDetector +// { +// public: +// Collection col; // collection used during setup +// +// Characteristic<ObjectId> id; // ID of presence detector +// Characteristic<ObjectName> name; // name of presence detector +// Characteristic<Digital> presence; // digital presence value +// Characteristic<DateTime> timestamp; // last detection change's time +// Characteristic<ObjectName> layout; // name of model railway layout +// +// Service presenceDetection; // the service +// +// public: +// PresenceDetector(Blob &blue, cost UUID uuid) : +// list; // init service list +// id(list,0x2AC3,"rw","ID"), // instantiate characteristic +// name(list,0x2ABE,"rw","Name"), // instantiate characteristic +// presence(list,0x2A56,"r","Presence"),// instantiate characteristic +// timestamp(list,0x2A08,"r","Timestamp"),// instantiate characteristic +// layout(list,0x2ABE,"rw","Layout"), // instantiate characteristic +// presenceDetection(list,uuid) // instantiate service +// { +// Blob blue; +// blue.service(presenceDetection); // use service +// } +// }; +// +#ifndef _SERVICE_H_ +#define _SERVICE_H_ + +#include "ble/BLE.h" +#include "ble/Gap.h" +#include "bricks/blob.h" +#include "bricks/collection.h" + + // the following #define is helpful if a service is defined on base of the + // BLE API (the tedios way) + +#define GattListLength(list) (sizeof(list)/sizeof(GattCharacteristic*)) + + // Service classs definition + + class Service + { + public: // public properties + Collection collection; // collection of characteristics + uint16_t uuid; // UUID of service + const char *name; // name of service + + public: // public methods + Service(uint16_t _uuid, const char* _name = NULL) : collection() + { + uuid = _uuid; + name = _name; + } + + void add(GattCharacteristic *pChr) // add a characteristic to collection + { + collection.add(pChr); + } + }; + +#endif // _SERVICE_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/set.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/set.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,46 @@ +// set.h - set data from a characteristics +// +// Synopsis: +// +// Set data from a 'characteristic' based on 'data' variable +// +// set(characteristic,data) +// +// The 'set' function (overloaded function family) is implemented for all +// types defined in "bricks/types.h". +// +// See also: CHARACTERISTIC, GET +// +#ifndef _SET_H_ +#define _SET_H_ + +#include "ble/BLE.h" +#include "ble/Gap.h" +#include "ble/GattServer.h" +#include "bricks/o.h" +#include "bricks/types.h" +#include "bricks/characteristic.h" + + inline void set(O&o, Characteristic<Bool> &chr, const Bool &data) + { + uint16_t size = sizeof(Bool)/sizeof(uint8_t); + o.gattServer().write(chr.getValueHandle(), (uint8_t*)&data,size); + } + + inline void set(O&o, Characteristic<ObjectId> &chr, const ObjectId &data) + { + uint16_t size = sizeof(ObjectId)/sizeof(uint8_t); + o.gattServer().write(chr.getValueHandle(), (uint8_t*)&data,size); + } + +// we provide also some SET methods for GattCharacteristics. However the use +// of these methods are more dangerous, because a GattCharacteristics can be +// of any type and the compiler cannot help us to check !!! + + inline void set(O&o,GattCharacteristic &chr,Bool data) + { + uint16_t size = sizeof(Bool)/sizeof(uint8_t); + o.gattServer().write(chr.getValueHandle(), (uint8_t*)&data,size); + } + +#endif // _SET_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/sleep.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/sleep.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,50 @@ +// sleep.h - sleep for some time +// +// Synopsis: +// +// void sleep(Blob &o) // low energy sleep for some mili seconds +// void sleep(Blob &o, int ms) // low enery sleep, handle BLE events +// +// Arguments: +// +// o: Blob object (to avoid name clashes) +// msec: sleeping interval in miliseconds +// +// Description: +// +// +// Example 1: main function body to setup BLE functionality +// +// main(void) +// { +// Blob o; // our Blob object +// init(o,onSetup,onError); // init BLE base layer, always do first +// +// while (true) // Infinite loop waiting for BLE events +// sleep(o); // low power waiting for BLE events +// } +// +// Example 2: Turn LED on for 2 seconds miliseconds +// +// led = 1; // turn LED on +// sleep(o,2000); // wait 2000 miliseconds (in LE mode) +// led = 0; // turn LED off +// +// See also: BLOB, INIT +// +#ifndef _SLEEP_H_ +#define _SLEEP_H_ + +#include "bricks/blob.h" + + inline void sleep(Blob &o) // low power waiting for BLE events + { + o.waitForEvent(); // low power waiting for BLE events + } + + inline void sleep(Blob &o, int ms) // low power waiting for BLE events + { + wait_ms(ms); // low power waiting for some miliseconds + } + +#endif // _SLEEP_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/target.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/target.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,25 @@ +// bricks/target.h - define target specifics + +#ifndef _BRICKS_TARGET_H_ +#define _BRICKS_TARGET_H_ + +//#define MTCONNECT04 // MtM nRF51822 module MtConnect04 + +// platform specific defines - comment out platforms which are not used +// We work only with two kinds of platform: +// +// a) a NUCLEO platform from STMicroelectronics +// b) a nRF51 platform from Nordic Semiconductor +// +// If we have not a nRF51 target then we coclude it is a NUCLEO target. This is +// not really clean, but under above assumptions it works so far! + +#ifdef TARGET_NRF51822 +# define LED_INVERTED // inverted LED for nRF51 targets +#endif + +#ifdef MTCONNECT04 +# define BLINK_LED p16 +#endif + +#endif // _BRICKS_TARGET_H_
diff -r 339931243be4 -r dce30a5341bb bricks/trace.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/trace.cpp Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,54 @@ +// trace.cpp - trace a message depending on verbose level +// + +#include <stdio.h> +#include "bricks/trace.h" + + static Serial pc(USBTX, USBRX); // serial port to PC terminal + +//============================================================================== +// Managing the Verbose Lewvel +//============================================================================== + + static int threshold = 0; // verbose threshold, no verbose messages + + void verbose(O&o, int level) // setup verbose level + { + threshold = level; // update verbose threshold + } + +//============================================================================== +// Printing Trace Messages +//============================================================================== + + void trace(O&o, int level, const char *msg) // trace a message + { + if (level <= threshold) // update verbose threshold + { char buf[2] = " "; // must be terminated + + for (; *msg; msg++) + { buf[0] = *msg; + printf(buf); + if (*msg == '\n') + { buf[0] = '\r'; // auto adding carriage return + printf(buf); + } + } + } + } + + void trace(O&o, int level, const char*format, int value) + { + char buf[80]; + if (strlen(format) > 70) + trace(o,level," *** buffer overflow *** "); + else + { sprintf(buf,format,value); + trace(o,level,buf); + } + } + + void traceln(O&o, int level, const char*msg) // trace with CR & LF + { + trace(o,level,msg); trace(o,level,"\r\n"); + }
diff -r 339931243be4 -r dce30a5341bb bricks/trace.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/trace.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,15 @@ +// trace.h - tracing +#ifndef _TRACE_H_ +#define _TRACE_H_ + +#include <mbed.h> +#include "bricks/o.h" + + void trace(O&o, int level, const char *msg); // trace a message + void trace(O&o, int level, const char*, int); // trace an int value + + void traceln(O&o, int level, const char*msg); // trace with CR & LF + + void verbose(O&o, int level = 9999); // setup verbose level + +#endif // _TRACE_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/types.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,24 @@ +// types.h - declaring some common types +// +// The type identifiers are all begining with capital letter in order to leave +// the lower capital identifiers free for 'overloaded' variable or characteristic +// identifiers. +// + +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#define SIZE_OBJECTNAME 40 +#define SIZE_INIBUFFER SIZE_OBJECTNAME + + typedef uint8_t ObjectId[6]; + typedef char ObjectName[SIZE_OBJECTNAME]; // use UTF8S coded characters + typedef uint8_t Bool; + typedef uint8_t Digital; + typedef uint8_t DateTime[10]; + typedef uint8_t Buffer[10]; + + typedef uint8_t IniBuffer[SIZE_INIBUFFER]; + + +#endif // _TYPES_H_ \ No newline at end of file
diff -r 339931243be4 -r dce30a5341bb bricks/updated.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bricks/updated.h Sat May 19 14:10:17 2018 +0000 @@ -0,0 +1,25 @@ +// updated.h - check whether a characteristic has been updated +// note: can only used within 'onWritten' callback +// +// Synopsis: +// +// See also: CHARACTERISTIC, GET, SET, ONWRITTEN +// +#ifndef _UPDATED_H_ +#define _UPDATED_H_ + +#include "ble/Gap.h" +#include "bricks/blob.h" + + inline int updated(Blob &o,GattCharacteristic &chr) // has char. been updated? + { + const GattWriteCallbackParams *p = o.pWritten; + if (!p) + return 0; // no update! + else if(p->handle == chr.getValueHandle()) + return 1; + else + return 0; + } + +#endif // _SET_H_ \ No newline at end of file