2015-09-22 15:13:08 +00:00
|
|
|
/**
|
|
|
|
* Class definition for the custom MicroBit IOPin Service.
|
|
|
|
* Provides a BLE service to remotely read the state of the ioPin, and configure its behaviour.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "MicroBit.h"
|
|
|
|
#include "ble/UUID.h"
|
|
|
|
|
|
|
|
#include "MicroBitIOPinService.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor.
|
|
|
|
* Create a representation of the IOPinService
|
|
|
|
* @param _ble The instance of a BLE device that we're running on.
|
|
|
|
*/
|
|
|
|
MicroBitIOPinService::MicroBitIOPinService(BLEDevice &_ble) :
|
|
|
|
ble(_ble)
|
|
|
|
{
|
|
|
|
// Create the AD characteristic, that defines whether each pin is treated as analogue or digital
|
|
|
|
GattCharacteristic ioPinServiceADCharacteristic(MicroBitIOPinServiceADConfigurationUUID, (uint8_t *)&ioPinServiceADCharacteristicBuffer, 0, sizeof(ioPinServiceADCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
|
|
|
|
|
|
|
|
// Create the IO characteristic, that defines whether each pin is treated as input or output
|
|
|
|
GattCharacteristic ioPinServiceIOCharacteristic(MicroBitIOPinServiceIOConfigurationUUID, (uint8_t *)&ioPinServiceIOCharacteristicBuffer, 0, sizeof(ioPinServiceIOCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
|
|
|
|
|
|
|
|
// Create the Data characteristic, that allows the actual read and write operations.
|
2015-09-28 20:40:44 +00:00
|
|
|
ioPinServiceDataCharacteristic = new GattCharacteristic(MicroBitIOPinServiceDataUUID, (uint8_t *)ioPinServiceDataCharacteristicBuffer, 0, sizeof(ioPinServiceDataCharacteristicBuffer), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
|
2015-09-22 15:13:08 +00:00
|
|
|
|
|
|
|
ioPinServiceDataCharacteristic->setReadAuthorizationCallback(this, &MicroBitIOPinService::onDataRead);
|
|
|
|
|
|
|
|
ioPinServiceADCharacteristicBuffer = 0;
|
|
|
|
ioPinServiceIOCharacteristicBuffer = 0;
|
|
|
|
memset(ioPinServiceIOData, 0, sizeof(ioPinServiceIOData));
|
2015-11-16 13:44:27 +00:00
|
|
|
|
|
|
|
// Set default security requirements
|
|
|
|
ioPinServiceADCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
|
|
|
|
ioPinServiceIOCharacteristic.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
|
|
|
|
ioPinServiceDataCharacteristic->requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_WITH_MITM);
|
2015-09-22 15:13:08 +00:00
|
|
|
|
|
|
|
GattCharacteristic *characteristics[] = {&ioPinServiceADCharacteristic, &ioPinServiceIOCharacteristic, ioPinServiceDataCharacteristic};
|
|
|
|
GattService service(MicroBitIOPinServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *));
|
|
|
|
|
|
|
|
ble.addService(service);
|
|
|
|
|
|
|
|
ioPinServiceADCharacteristicHandle = ioPinServiceADCharacteristic.getValueHandle();
|
|
|
|
ioPinServiceIOCharacteristicHandle = ioPinServiceIOCharacteristic.getValueHandle();
|
|
|
|
|
2015-09-28 20:40:44 +00:00
|
|
|
ble.gattServer().write(ioPinServiceADCharacteristicHandle, (const uint8_t *)&ioPinServiceADCharacteristicBuffer, sizeof(ioPinServiceADCharacteristicBuffer));
|
|
|
|
ble.gattServer().write(ioPinServiceIOCharacteristicHandle, (const uint8_t *)&ioPinServiceIOCharacteristicBuffer, sizeof(ioPinServiceIOCharacteristicBuffer));
|
2015-09-22 15:13:08 +00:00
|
|
|
|
|
|
|
ble.onDataWritten(this, &MicroBitIOPinService::onDataWritten);
|
2015-09-28 20:40:44 +00:00
|
|
|
uBit.addIdleComponent(this);
|
2015-09-22 15:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines if the given pin was configured as a digital pin by the BLE IOPinConfigurationCharacterisitic.
|
|
|
|
*
|
|
|
|
* @param pin the enumeration of the pin to test
|
|
|
|
* @return 1 if this pin is configured as a digital value, 0 otherwise
|
|
|
|
*/
|
|
|
|
int MicroBitIOPinService::isDigital(int i)
|
|
|
|
{
|
|
|
|
return ((ioPinServiceADCharacteristicBuffer & (1 << i)) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines if the given pin was configured as an analog pin by the BLE IOPinConfigurationCharacterisitic.
|
|
|
|
*
|
|
|
|
* @param pin the enumeration of the pin to test
|
|
|
|
* @return 1 if this pin is configured as a analog value, 0 otherwise
|
|
|
|
*/
|
|
|
|
int MicroBitIOPinService::isAnalog(int i)
|
|
|
|
{
|
|
|
|
return ((ioPinServiceADCharacteristicBuffer & (1 << i)) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines if the given pin was configured as an input by the BLE IOPinConfigurationCharacterisitic.
|
|
|
|
*
|
|
|
|
* @param pin the enumeration of the pin to test
|
|
|
|
* @return 1 if this pin is configured as an input, 0 otherwise
|
|
|
|
*/
|
|
|
|
int MicroBitIOPinService::isInput(int i)
|
|
|
|
{
|
2015-09-28 20:40:44 +00:00
|
|
|
return ((ioPinServiceIOCharacteristicBuffer & (1 << i)) != 0);
|
2015-09-22 15:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines if the given pin was configured as output by the BLE IOPinConfigurationCharacterisitic.
|
|
|
|
*
|
|
|
|
* @param pin the enumeration of the pin to test
|
|
|
|
* @return 1 if this pin is configured as an output, 0 otherwise
|
|
|
|
*/
|
|
|
|
int MicroBitIOPinService::isOutput(int i)
|
|
|
|
{
|
2015-09-28 20:40:44 +00:00
|
|
|
return ((ioPinServiceIOCharacteristicBuffer & (1 << i)) == 0);
|
2015-09-22 15:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback. Invoked when any of our attributes are written via BLE.
|
|
|
|
*/
|
|
|
|
void MicroBitIOPinService::onDataWritten(const GattWriteCallbackParams *params)
|
|
|
|
{
|
|
|
|
// Check for writes to the IO configuration characteristic
|
|
|
|
if (params->handle == ioPinServiceIOCharacteristicHandle && params->len >= sizeof(ioPinServiceIOCharacteristicBuffer))
|
|
|
|
{
|
|
|
|
uint32_t *value = (uint32_t *)params->data;
|
|
|
|
|
|
|
|
// Our IO configuration may be changing... read the new value, and push it back into the BLE stack.
|
|
|
|
ioPinServiceIOCharacteristicBuffer = *value;
|
2015-09-28 20:40:44 +00:00
|
|
|
ble.gattServer().write(ioPinServiceIOCharacteristicHandle, (const uint8_t *)&ioPinServiceIOCharacteristicBuffer, sizeof(ioPinServiceIOCharacteristicBuffer));
|
2015-09-22 15:13:08 +00:00
|
|
|
|
|
|
|
// Also, drop any selected pins into input mode, so we can pick up changes later
|
|
|
|
for (int i=0; i < MICROBIT_IO_PIN_SERVICE_PINCOUNT; i++)
|
|
|
|
{
|
|
|
|
if(isDigital(i) && isInput(i))
|
|
|
|
MicroBitIOPins[i]->getDigitalValue();
|
|
|
|
|
|
|
|
if(isAnalog(i) && isInput(i))
|
|
|
|
MicroBitIOPins[i]->getAnalogValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for writes to the IO configuration characteristic
|
|
|
|
if (params->handle == ioPinServiceADCharacteristicHandle && params->len >= sizeof(ioPinServiceADCharacteristicBuffer))
|
|
|
|
{
|
|
|
|
uint32_t *value = (uint32_t *)params->data;
|
|
|
|
|
|
|
|
// Our IO configuration may be changing... read the new value, and push it back into the BLE stack.
|
|
|
|
ioPinServiceADCharacteristicBuffer = *value;
|
2015-09-28 20:40:44 +00:00
|
|
|
ble.gattServer().write(ioPinServiceADCharacteristicHandle, (const uint8_t *)&ioPinServiceADCharacteristicBuffer, sizeof(ioPinServiceADCharacteristicBuffer));
|
2015-09-22 15:13:08 +00:00
|
|
|
|
|
|
|
// Also, drop any selected pins into input mode, so we can pick up changes later
|
|
|
|
for (int i=0; i < MICROBIT_IO_PIN_SERVICE_PINCOUNT; i++)
|
|
|
|
{
|
|
|
|
if(isDigital(i) && isInput(i))
|
|
|
|
MicroBitIOPins[i]->getDigitalValue();
|
|
|
|
|
|
|
|
if(isAnalog(i) && isInput(i))
|
|
|
|
MicroBitIOPins[i]->getAnalogValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params->handle == ioPinServiceDataCharacteristic->getValueHandle())
|
|
|
|
{
|
|
|
|
// We have some pin data to change...
|
2015-10-27 22:17:04 +00:00
|
|
|
uint16_t len = params->len;
|
2015-09-22 15:13:08 +00:00
|
|
|
IOData *data = (IOData *)params->data;
|
|
|
|
|
|
|
|
// There may be multiple write operaitons... take each in turn and update the pin values
|
|
|
|
while (len >= sizeof(IOData))
|
|
|
|
{
|
|
|
|
if (isOutput(data->pin))
|
|
|
|
{
|
|
|
|
if (isDigital(data->pin))
|
|
|
|
MicroBitIOPins[data->pin]->setDigitalValue(data->value);
|
|
|
|
else
|
|
|
|
MicroBitIOPins[data->pin]->setAnalogValue(data->value*4);
|
|
|
|
}
|
|
|
|
|
|
|
|
data++;
|
|
|
|
len -= sizeof(IOData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* read callback on data characteristic.
|
|
|
|
* reads all the pins marked as inputs, and updates the data stored in the BLE stack.
|
|
|
|
*/
|
|
|
|
void MicroBitIOPinService::onDataRead(GattReadAuthCallbackParams *params)
|
|
|
|
{
|
|
|
|
if (params->handle == ioPinServiceDataCharacteristic->getValueHandle())
|
|
|
|
{
|
|
|
|
|
|
|
|
// Scan through all pins that our BLE client may be listening for. If any have changed value, update the BLE characterisitc, and NOTIFY our client.
|
|
|
|
int pairs = 0;
|
|
|
|
|
|
|
|
for (int i=0; i < MICROBIT_IO_PIN_SERVICE_PINCOUNT; i++)
|
|
|
|
{
|
|
|
|
if (isInput(i))
|
|
|
|
{
|
2015-09-28 20:40:44 +00:00
|
|
|
uint8_t value;
|
2015-09-22 15:13:08 +00:00
|
|
|
|
|
|
|
if (isDigital(i))
|
|
|
|
value = MicroBitIOPins[i]->getDigitalValue();
|
|
|
|
else
|
|
|
|
value = MicroBitIOPins[i]->getAnalogValue();
|
|
|
|
|
|
|
|
ioPinServiceIOData[i] = value;
|
|
|
|
ioPinServiceDataCharacteristicBuffer[pairs].pin = i;
|
|
|
|
ioPinServiceDataCharacteristicBuffer[pairs].value = value;
|
|
|
|
|
|
|
|
pairs++;
|
|
|
|
|
|
|
|
if (pairs >= MICROBIT_IO_PIN_SERVICE_DATA_SIZE)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there's any data, issue a BLE notification.
|
|
|
|
if (pairs > 0)
|
2015-09-28 20:40:44 +00:00
|
|
|
ble.gattServer().notify(ioPinServiceDataCharacteristic->getValueHandle(), (uint8_t *)ioPinServiceDataCharacteristicBuffer, pairs * sizeof(IOData));
|
2015-09-22 15:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Periodic callback from MicroBit scheduler.
|
|
|
|
* Check if any of the pins we're watching need updating. Apply a BLE NOTIFY if so...
|
|
|
|
*/
|
|
|
|
void MicroBitIOPinService::idleTick()
|
|
|
|
{
|
|
|
|
// If we're not we're connected, then there's nothing to do...
|
|
|
|
if (!ble.getGapState().connected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Scan through all pins that our BLE client may be listening for. If any have changed value, update the BLE characterisitc, and NOTIFY our client.
|
|
|
|
int pairs = 0;
|
|
|
|
|
|
|
|
for (int i=0; i < MICROBIT_IO_PIN_SERVICE_PINCOUNT; i++)
|
|
|
|
{
|
|
|
|
if (isInput(i))
|
|
|
|
{
|
2015-09-28 20:40:44 +00:00
|
|
|
uint8_t value;
|
2015-09-22 15:13:08 +00:00
|
|
|
|
|
|
|
if (isDigital(i))
|
|
|
|
value = MicroBitIOPins[i]->getDigitalValue();
|
|
|
|
else
|
|
|
|
value = MicroBitIOPins[i]->getAnalogValue();
|
|
|
|
|
|
|
|
// If the data has changed, send an update.
|
|
|
|
if (value != ioPinServiceIOData[i])
|
|
|
|
{
|
|
|
|
ioPinServiceIOData[i] = value;
|
|
|
|
|
|
|
|
ioPinServiceDataCharacteristicBuffer[pairs].pin = i;
|
|
|
|
ioPinServiceDataCharacteristicBuffer[pairs].value = value;
|
|
|
|
|
|
|
|
pairs++;
|
|
|
|
|
|
|
|
if (pairs >= MICROBIT_IO_PIN_SERVICE_DATA_SIZE)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there were any changes, issue a BLE notification.
|
|
|
|
if (pairs > 0)
|
2015-09-28 20:40:44 +00:00
|
|
|
ble.gattServer().notify(ioPinServiceDataCharacteristic->getValueHandle(), (uint8_t *)ioPinServiceDataCharacteristicBuffer, pairs * sizeof(IOData));
|
2015-09-22 15:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const uint8_t MicroBitIOPinServiceUUID[] = {
|
|
|
|
0xe9,0x5d,0x12,0x7b,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
|
|
|
|
};
|
|
|
|
|
|
|
|
const uint8_t MicroBitIOPinServiceIOConfigurationUUID[] = {
|
2015-09-23 21:15:44 +00:00
|
|
|
0xe9,0x5d,0xb9,0xfe,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
|
2015-09-22 15:13:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const uint8_t MicroBitIOPinServiceADConfigurationUUID[] = {
|
2015-09-23 21:15:44 +00:00
|
|
|
0xe9,0x5d,0x58,0x99,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
|
2015-09-22 15:13:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const uint8_t MicroBitIOPinServiceDataUUID[] = {
|
2015-09-23 21:15:44 +00:00
|
|
|
0xe9,0x5d,0x8d,0x00,0x25,0x1d,0x47,0x0a,0xa0,0x62,0xfa,0x19,0x22,0xdf,0xa9,0xa8
|
2015-09-22 15:13:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
MicroBitPin * const MicroBitIOPins[] = {
|
|
|
|
&uBit.io.P0,
|
|
|
|
&uBit.io.P1,
|
|
|
|
&uBit.io.P2,
|
|
|
|
&uBit.io.P3,
|
|
|
|
&uBit.io.P4,
|
|
|
|
&uBit.io.P5,
|
|
|
|
&uBit.io.P6,
|
|
|
|
&uBit.io.P7,
|
|
|
|
&uBit.io.P8,
|
|
|
|
&uBit.io.P9,
|
|
|
|
&uBit.io.P10,
|
|
|
|
&uBit.io.P11,
|
|
|
|
&uBit.io.P12,
|
|
|
|
&uBit.io.P13,
|
|
|
|
&uBit.io.P14,
|
|
|
|
&uBit.io.P15,
|
|
|
|
&uBit.io.P16,
|
|
|
|
&uBit.io.P19,
|
|
|
|
&uBit.io.P20
|
|
|
|
};
|
|
|
|
|
|
|
|
|