microbit: Final updates to BLE profile

Minor changes to BLE profile services (cleanup prior to merge with master branch):

  - Added temperature period characteristic to match design pattern of other sensors.
  - Updated endinaness of MicroBitLEDService Matrix to be more intuitive.
  - Cleaned up Microbit.cpp by removing unused reference to BLE services.
  - Exposed serial number and naming information thr BLE DeviceInformation and API.
  - Updated BLE advertisement period to be alightly more repsonsive (200ms vs 1000ms).
  - BUGFIX: micro:bit name is now always generated, regardless of BLE services.
  - BUGFIX: Scroll Period data length.
This commit is contained in:
Joe Finney 2015-10-18 14:46:42 +01:00
parent 2011886e64
commit db3eccf6ff
9 changed files with 169 additions and 106 deletions

View file

@ -50,11 +50,15 @@
#define MICROBIT_FLAG_DISPLAY_RUNNING 0x00000004
#define MICROBIT_FLAG_COMPASS_RUNNING 0x00000008
// MicroBit naming constants
#define MICROBIT_NAME_LENGTH 5
#define MICROBIT_NAME_CODE_LETTERS 5
// Random number generator
#define NRF51822_RNG_ADDRESS 0x4000D000
// mBed pin assignments of core components.
// mbed pin assignments of core components.
#define MICROBIT_PIN_SDA P0_30
#define MICROBIT_PIN_SCL P0_0
@ -114,13 +118,6 @@ class MicroBit
// Bluetooth related member variables.
BLEDevice *ble;
MicroBitDFUService *ble_firmware_update_service;
MicroBitEventService *ble_event_service;
MicroBitLEDService *ble_led_service;
MicroBitAccelerometerService *ble_accelerometer_service;
MicroBitMagnetometerService *ble_magnetometer_service;
MicroBitButtonService *ble_button_service;
MicroBitIOPinService *ble_io_pin_service;
MicroBitTemperatureService *ble_temperature_service;
/**
* Constructor.
@ -155,6 +152,25 @@ class MicroBit
*/
void init();
/**
* Derives the friendly name for this device, autogenerated from our hardware Device ID.
*/
void deriveName();
/**
* Return the friendly name for this device.
*
* @return A string representing the friendly name of this device.
*/
ManagedString getName();
/**
* Return the serial number of this device.
*
* @return A string representing the serial number of this device.
*/
ManagedString getSerial();
/**
* Will reset the micro:bit when called.
*
@ -257,7 +273,6 @@ extern MicroBit uBit;
// Used to reset state and restart advertising ourselves.
//
void bleDisconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason);
extern char MICROBIT_BLE_DEVICE_NAME[];
// Entry point for application programs. Called after the super-main function
// has initialized the device and runtime environment.

View file

@ -47,14 +47,6 @@ class MicroBitDFUService
*/
MicroBitDFUService(BLEDevice &BLE);
/**
* Returns the friendly name for this device, autogenerated from our Device ID.
*
* @param name Pointer to a string where the data will be written.
* @return The number of bytes written.
*/
int getName(char *name);
/**
* Begin the pairing process. Typically called when device is powered up with buttons held down.
* Scroll a description on the display, then displays the device ID code as a histogram on the matrix display.

View file

@ -41,9 +41,9 @@ class MicroBitLEDService
BLEDevice &ble;
// memory for our 8 bit control characteristics.
uint32_t matrixCharacteristicBuffer;
uint8_t matrixCharacteristicBuffer[5];
uint16_t scrollingSpeedCharacteristicBuffer;
uint8_t textCharacteristicBuffer[MICROBIT_BLE_MAXIMUM_SCROLLTEXT];
uint8_t scrollingSpeedCharacteristicBuffer;
// Handles to access each characteristic when they are held by Soft Device.
GattAttribute::Handle_t matrixCharacteristicHandle;

View file

@ -6,6 +6,7 @@
// UUIDs for our service and characteristics
extern const uint8_t MicroBitTemperatureServiceUUID[];
extern const uint8_t MicroBitTemperatureServiceDataUUID[];
extern const uint8_t MicroBitTemperatureServicePeriodUUID[];
/**
@ -23,6 +24,11 @@ class MicroBitTemperatureService
*/
MicroBitTemperatureService(BLEDevice &_ble);
/**
* Callback. Invoked when any of our attributes are written via BLE.
*/
void onDataWritten(const GattWriteCallbackParams *params);
/**
* Temperature update callback
*/
@ -35,9 +41,11 @@ class MicroBitTemperatureService
// memory for our 8 bit temperature characteristic.
int8_t temperatureDataCharacteristicBuffer;
uint16_t temperaturePeriodCharacteristicBuffer;
// Handles to access each characteristic when they are held by Soft Device.
GattAttribute::Handle_t temperatureDataCharacteristicHandle;
GattAttribute::Handle_t temperaturePeriodCharacteristicHandle;
};

View file

@ -3,12 +3,11 @@
char MICROBIT_BLE_DEVICE_NAME[] = "BBC micro:bit [xxxxx]";
#if CONFIG_ENABLED(MICROBIT_BLE_ENABLED) && CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE)
const char MICROBIT_BLE_MANUFACTURER[] = "The Cast of W1A";
const char MICROBIT_BLE_MODEL[] = "micro:bit";
const char MICROBIT_BLE_SERIAL[] = "SN1";
const char MICROBIT_BLE_HARDWARE_VERSION[] = "0.2";
const char MICROBIT_BLE_FIRMWARE_VERSION[] = "1.1";
const char MICROBIT_BLE_SOFTWARE_VERSION[] = "1.0";
const char* MICROBIT_BLE_MANUFACTURER = "The Cast of W1A";
const char* MICROBIT_BLE_MODEL = "BBC micro:bit";
const char* MICROBIT_BLE_HARDWARE_VERSION = "1.0";
const char* MICROBIT_BLE_FIRMWARE_VERSION = "1.1";
const char* MICROBIT_BLE_SOFTWARE_VERSION = NULL;
#endif
/**
@ -94,59 +93,65 @@ void MicroBit::init()
// Seed our random number generator
seedRandom();
// Generate the name for our device.
this->deriveName();
#if CONFIG_ENABLED(MICROBIT_BLE_ENABLED)
// Start the BLE stack.
ble = new BLEDevice();
ble->init();
ble->onDisconnection(bleDisconnectionCallback);
// Bring up any configured auxiliary services.
#if CONFIG_ENABLED(MICROBIT_BLE_DFU_SERVICE)
ble_firmware_update_service = new MicroBitDFUService(*ble);
// Compute our auto-generated MicroBit device name.
ble_firmware_update_service->getName(MICROBIT_BLE_DEVICE_NAME+15);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE)
DeviceInformationService ble_device_information_service (*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, MICROBIT_BLE_SERIAL, MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION);
DeviceInformationService ble_device_information_service (*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, getSerial().toCharArray(), MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_EVENT_SERVICE)
ble_event_service = new MicroBitEventService(*ble);
new MicroBitEventService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_LED_SERVICE)
ble_led_service = new MicroBitLEDService(*ble);
new MicroBitLEDService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_ACCELEROMETER_SERVICE)
ble_accelerometer_service = new MicroBitAccelerometerService(*ble);
new MicroBitAccelerometerService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_MAGNETOMETER_SERVICE)
ble_magnetometer_service = new MicroBitMagnetometerService(*ble);
new MicroBitMagnetometerService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_BUTTON_SERVICE)
ble_button_service = new MicroBitButtonService(*ble);
new MicroBitButtonService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_IO_PIN_SERVICE)
ble_io_pin_service = new MicroBitIOPinService(*ble);
new MicroBitIOPinService(*ble);
#endif
#if CONFIG_ENABLED(MICROBIT_BLE_TEMPERATURE_SERVICE)
ble_temperature_service = new MicroBitTemperatureService(*ble);
new MicroBitTemperatureService(*ble);
#endif
// Configure for high speed mode where possible.
Gap::ConnectionParams_t fast;
ble->getPreferredConnectionParams(&fast);
fast.minConnectionInterval = 8; // 10 ms
fast.maxConnectionInterval = 16; // 20 ms
fast.slaveLatency = 0;
ble->setPreferredConnectionParams(&fast);
// Setup advertising.
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)MICROBIT_BLE_DEVICE_NAME, sizeof(MICROBIT_BLE_DEVICE_NAME));
ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
ble->setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(1000));
ble->setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(200));
ble->startAdvertising();
#endif
@ -154,6 +159,69 @@ void MicroBit::init()
systemTicker.attach(this, &MicroBit::systemTick, MICROBIT_DISPLAY_REFRESH_PERIOD);
}
/**
* Derives the friendly name for this device, autogenerated from our hardware Device ID.
*/
void MicroBit::deriveName()
{
const uint8_t codebook[MICROBIT_NAME_LENGTH][MICROBIT_NAME_CODE_LETTERS] =
{
{'z', 'v', 'g', 'p', 't'},
{'u', 'o', 'i', 'e', 'a'},
{'z', 'v', 'g', 'p', 't'},
{'u', 'o', 'i', 'e', 'a'},
{'z', 'v', 'g', 'p', 't'}
};
char *name = MICROBIT_BLE_DEVICE_NAME+15;
// We count right to left, so fast forward the pointer.
name += MICROBIT_NAME_LENGTH;
uint32_t n = NRF_FICR->DEVICEID[1];
int ld = 1;
int d = MICROBIT_NAME_CODE_LETTERS;
int h;
for (int i=0; i<MICROBIT_NAME_LENGTH;i++)
{
h = (n % d) / ld;
n -= h;
d *= MICROBIT_NAME_CODE_LETTERS;
ld *= MICROBIT_NAME_CODE_LETTERS;
*--name = codebook[i][h];
}
}
/**
* Return the friendly name for this device.
*
* @return A string representing the friendly name of this device.
*/
ManagedString MicroBit::getName()
{
return ManagedString(MICROBIT_BLE_DEVICE_NAME+15, MICROBIT_NAME_LENGTH);
}
/**
* Return the serial number of this device.
*
* @return A string representing the serial number of this device.
*/
ManagedString MicroBit::getSerial()
{
// We take to 16 bit numbers here, as we want the full range of ID bits, but don't want negative numbers...
int n1 = NRF_FICR->DEVICEID[1] & 0xffff;
int n2 = (NRF_FICR->DEVICEID[1] >> 16) & 0xffff;
// Simply concat the two numbers.
ManagedString s1 = ManagedString(n1);
ManagedString s2 = ManagedString(n2);
return s1+s2;
}
/**
* Will reset the micro:bit when called.
*

View file

@ -7,6 +7,8 @@ Serial pc(USBTX, USBRX);
MicroBit uBit;
InterruptIn resetButton(MICROBIT_PIN_BUTTON_RESET);
extern char* MICROBIT_BLE_DEVICE_NAME;
void
reset()
{
@ -64,10 +66,7 @@ int main()
}
if (!uBit.ble_firmware_update_service)
{
uBit.ble_firmware_update_service = new MicroBitDFUService(*uBit.ble);
uBit.ble_firmware_update_service->getName(MICROBIT_BLE_DEVICE_NAME+14);
}
// Ensure we're advertising.
uBit.ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);

View file

@ -49,45 +49,6 @@ MicroBitDFUService::MicroBitDFUService(BLEDevice &_ble) :
ble.gattServer().onDataWritten(this, &MicroBitDFUService::onDataWritten);
}
/**
* Returns the friendly name for this device, autogenerated from our Device ID.
*
* @param name Pointer to a string where the data will be written.
* @return The number of bytes written.
*/
int MicroBitDFUService::getName(char *name)
{
const uint8_t codebook[5][5] =
{
{'z', 'v', 'g', 'p', 't'},
{'u', 'o', 'i', 'e', 'a'},
{'z', 'v', 'g', 'p', 't'},
{'u', 'o', 'i', 'e', 'a'},
{'z', 'v', 'g', 'p', 't'}
};
// We count right to left, so fast forward the pointer.
name += MICROBIT_DFU_HISTOGRAM_WIDTH;
uint32_t n = NRF_FICR->DEVICEID[1];
int ld = 1;
int d = MICROBIT_DFU_HISTOGRAM_HEIGHT;
int h;
for (int i=0; i<MICROBIT_DFU_HISTOGRAM_WIDTH;i++)
{
h = (n % d) / ld;
n -= h;
d *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
ld *= MICROBIT_DFU_HISTOGRAM_HEIGHT;
*--name = codebook[i][h];
}
return MICROBIT_DFU_HISTOGRAM_WIDTH;
}
void MicroBitDFUService::onButtonA(MicroBitEvent e)
{
if (flashCodeRequested)

View file

@ -26,7 +26,7 @@ MicroBitLEDService::MicroBitLEDService(BLEDevice &_ble) :
sizeof(scrollingSpeedCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
// Initialise our characteristic values.
matrixCharacteristicBuffer = 0;
memclr(matrixCharacteristicBuffer, sizeof(matrixCharacteristicBuffer));
textCharacteristicBuffer[0] = 0;
scrollingSpeedCharacteristicBuffer = MICROBIT_DEFAULT_SCROLL_SPEED;
@ -53,19 +53,13 @@ MicroBitLEDService::MicroBitLEDService(BLEDevice &_ble) :
*/
void MicroBitLEDService::onDataWritten(const GattWriteCallbackParams *params)
{
if (params->handle == matrixCharacteristicHandle && params->len >= 4)
{
uint32_t d = *((uint32_t *)params->data);
uint32_t mask = 0x01;
uint8_t *data = (uint8_t *)params->data;
for (int x=0; x<5; x++)
{
for (int y=0; y<5; y++)
{
uBit.display.image.setPixelValue(x, y, (d & mask) ? 255 : 0);
mask = mask << 1;
}
}
if (params->handle == matrixCharacteristicHandle && params->len > 0 && params->len < 6)
{
for (int y=0; y<params->len; y++)
for (int x=0; x<5; x++)
uBit.display.image.setPixelValue(x, y, (data[y] & (0x01 << 4-x)) ? 255 : 0);
}
else if (params->handle == textCharacteristicHandle)
@ -82,7 +76,7 @@ void MicroBitLEDService::onDataWritten(const GattWriteCallbackParams *params)
{
// Read the speed requested, and store it locally.
// We use this as the speed for all scroll operations subsquently initiated from BLE.
scrollingSpeedCharacteristicBuffer = *((uint8_t *)params->data);
scrollingSpeedCharacteristicBuffer = *((uint16_t *)params->data);
}
}
@ -93,18 +87,14 @@ void MicroBitLEDService::onDataRead(GattReadAuthCallbackParams *params)
{
if (params->handle == matrixCharacteristicHandle)
{
uint32_t mask = 0x01;
matrixCharacteristicBuffer = 0;
for (int x=0; x<5; x++)
for (int y=0; y<5; y++)
{
for (int y=0; y<5; y++)
matrixCharacteristicBuffer[y] = 0;
for (int x=0; x<5; x++)
{
if (uBit.display.image.getPixelValue(x, y))
matrixCharacteristicBuffer |= mask;
mask = mask << 1;
matrixCharacteristicBuffer[y] |= 0x01 << 4-x;
}
}

View file

@ -20,17 +20,25 @@ MicroBitTemperatureService::MicroBitTemperatureService(BLEDevice &_ble) :
GattCharacteristic temperatureDataCharacteristic(MicroBitTemperatureServiceDataUUID, (uint8_t *)&temperatureDataCharacteristicBuffer, 0,
sizeof(temperatureDataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic temperaturePeriodCharacteristic(MicroBitTemperatureServicePeriodUUID, (uint8_t *)&temperaturePeriodCharacteristicBuffer, 0,
sizeof(temperaturePeriodCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
// Initialise our characteristic values.
temperatureDataCharacteristicBuffer = 0;
temperaturePeriodCharacteristicBuffer = uBit.thermometer.getPeriod();
GattCharacteristic *characteristics[] = {&temperatureDataCharacteristic};
GattCharacteristic *characteristics[] = {&temperatureDataCharacteristic, &temperaturePeriodCharacteristic};
GattService service(MicroBitTemperatureServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));
ble.addService(service);
temperatureDataCharacteristicHandle = temperatureDataCharacteristic.getValueHandle();
ble.gattServer().write(temperatureDataCharacteristicHandle,(uint8_t *)&temperatureDataCharacteristicBuffer, sizeof(temperatureDataCharacteristicBuffer));
temperaturePeriodCharacteristicHandle = temperaturePeriodCharacteristic.getValueHandle();
ble.gattServer().write(temperatureDataCharacteristicHandle,(uint8_t *)&temperatureDataCharacteristicBuffer, sizeof(temperatureDataCharacteristicBuffer));
ble.gattServer().write(temperaturePeriodCharacteristicHandle,(uint8_t *)&temperaturePeriodCharacteristicBuffer, sizeof(temperaturePeriodCharacteristicBuffer));
ble.onDataWritten(this, &MicroBitTemperatureService::onDataWritten);
uBit.MessageBus.listen(MICROBIT_ID_THERMOMETER, MICROBIT_THERMOMETER_EVT_UPDATE, this, &MicroBitTemperatureService::temperatureUpdate, MESSAGE_BUS_LISTENER_IMMEDIATE);
}
@ -46,6 +54,24 @@ void MicroBitTemperatureService::temperatureUpdate(MicroBitEvent e)
}
}
/**
* Callback. Invoked when any of our attributes are written via BLE.
*/
void MicroBitTemperatureService::onDataWritten(const GattWriteCallbackParams *params)
{
if (params->handle == temperaturePeriodCharacteristicHandle && params->len >= sizeof(temperaturePeriodCharacteristicBuffer))
{
temperaturePeriodCharacteristicBuffer = *((uint16_t *)params->data);
uBit.thermometer.setPeriod(temperaturePeriodCharacteristicBuffer);
// The accelerometer will choose the nearest period to that requested that it can support
// Read back the ACTUAL period it is using, and report this back.
temperaturePeriodCharacteristicBuffer = uBit.accelerometer.getPeriod();
ble.gattServer().write(temperaturePeriodCharacteristicHandle, (const uint8_t *)&temperaturePeriodCharacteristicBuffer, sizeof(temperaturePeriodCharacteristicBuffer));
}
}
const uint8_t MicroBitTemperatureServiceUUID[] = {
0xe9,0x5d,0x61,0x00,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
};
@ -54,4 +80,8 @@ const uint8_t MicroBitTemperatureServiceDataUUID[] = {
0xe9,0x5d,0x92,0x50,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
};
const uint8_t MicroBitTemperatureServicePeriodUUID[] = {
0xe9,0x5d,0x1b,0x25,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
};