Added a serial implementation for bluetooth, needs a review at a later date.master
parent
728604423f
commit
b5c8714ea3
@ -0,0 +1,247 @@
|
||||
#ifndef MICROBIT_UART_SERVICE_H
|
||||
#define MICROBIT_UART_SERVICE_H
|
||||
|
||||
#include "mbed.h"
|
||||
#include "ble/UUID.h"
|
||||
#include "ble/BLE.h"
|
||||
#include "MicroBitConfig.h"
|
||||
#include "MicroBitSerial.h"
|
||||
|
||||
#define MICROBIT_UART_S_DEFAULT_BUF_SIZE 20
|
||||
|
||||
#define MICROBIT_UART_S_EVT_DELIM_MATCH 1
|
||||
#define MICROBIT_UART_S_EVT_HEAD_MATCH 2
|
||||
#define MICROBIT_UART_S_EVT_RX_FULL 3
|
||||
|
||||
class MicroBitUARTService
|
||||
{
|
||||
uint8_t* rxBuffer;
|
||||
|
||||
uint8_t* txBuffer;
|
||||
|
||||
uint8_t rxBufferHead;
|
||||
uint8_t rxBufferTail;
|
||||
uint8_t rxBufferSize;
|
||||
|
||||
uint8_t txBufferSize;
|
||||
|
||||
uint32_t txCharacteristicHandle;
|
||||
|
||||
// Bluetooth stack we're running on.
|
||||
BLEDevice &ble;
|
||||
|
||||
//delimeters used for matching on receive.
|
||||
ManagedString delimeters;
|
||||
|
||||
//a variable used when a user calls the eventAfter() method.
|
||||
int rxBuffHeadMatch;
|
||||
|
||||
/**
|
||||
* A callback function for whenever a Bluetooth device writes to our TX characteristic.
|
||||
*/
|
||||
void onDataWritten(const GattWriteCallbackParams *params);
|
||||
|
||||
/**
|
||||
* An internal method that copies values from a circular buffer to a linear buffer.
|
||||
*
|
||||
* @param circularBuff a pointer to the source circular buffer
|
||||
* @param circularBuffSize the size of the circular buffer
|
||||
* @param linearBuff a pointer to the destination linear buffer
|
||||
* @param tailPosition the tail position in the circular buffer you want to copy from
|
||||
* @param headPosition the head position in the circular buffer you want to copy to
|
||||
*
|
||||
* @note this method assumes that the linear buffer has the appropriate amount of
|
||||
* memory to contain the copy operation
|
||||
*/
|
||||
void circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition);
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for the UARTService.
|
||||
* @param _ble an instance of BLEDevice
|
||||
* @param rxBufferSize the size of the rxBuffer
|
||||
* @param txBufferSize the size of the txBuffer
|
||||
*
|
||||
* @note defaults to 20
|
||||
*/
|
||||
MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize = MICROBIT_UART_S_DEFAULT_BUF_SIZE, uint8_t txBufferSize = MICROBIT_UART_S_DEFAULT_BUF_SIZE);
|
||||
|
||||
/**
|
||||
* Retreives a single character from our RxBuffer.
|
||||
*
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will attempt to read a single character, and return immediately
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will configure the event and block the current fiber until the
|
||||
* event is received.
|
||||
*
|
||||
* @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, a character or MICROBIT_NO_DATA
|
||||
*/
|
||||
int getc(MicroBitSerialMode mode = SYNC_SLEEP);
|
||||
|
||||
/**
|
||||
* places a single character into our transmission buffer,
|
||||
*
|
||||
* @param c the character to transmit
|
||||
*
|
||||
* @return the number of characters written (0, or 1).
|
||||
*/
|
||||
int putc(char c);
|
||||
|
||||
/**
|
||||
* Copies characters into the buffer used for Transmitting to the central device.
|
||||
*
|
||||
* @param buf a buffer containing length number of bytes.
|
||||
* @param length the size of the buffer.
|
||||
*
|
||||
* @return the number of characters copied into the buffer
|
||||
*
|
||||
* @note no modes for sending are available at the moment, due to interrupt overhead.
|
||||
*/
|
||||
int send(const uint8_t *buf, int length);
|
||||
|
||||
/**
|
||||
* Copies characters into the buffer used for Transmitting to the central device.
|
||||
*
|
||||
* @param s the string to transmit
|
||||
*
|
||||
* @return the number of characters copied into the buffer
|
||||
*
|
||||
* @note no modes for sending are available at the moment, due to interrupt overhead.
|
||||
*/
|
||||
int send(ManagedString s);
|
||||
|
||||
/**
|
||||
* Reads a number of characters from the rxBuffer and fills user given buffer.
|
||||
*
|
||||
* @param buf a pointer to a buffer of len bytes.
|
||||
* @param len the size of the user allocated buffer
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will attempt to read all available characters, and return immediately
|
||||
* until the buffer limit is reached
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will first of all determine whether the given number of characters
|
||||
* are available in our buffer, if not, it will set an event and sleep
|
||||
* until the number of characters are avaialable.
|
||||
*
|
||||
* @return the number of characters digested
|
||||
*/
|
||||
int read(uint8_t *buf, int len, MicroBitSerialMode mode = SYNC_SLEEP);
|
||||
|
||||
/**
|
||||
* Reads a number of characters from the rxBuffer and returns them as a ManagedString
|
||||
*
|
||||
* @param len the number of characters to read.
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will attempt to read all available characters, and return immediately
|
||||
* until the buffer limit is reached
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will first of all determine whether the given number of characters
|
||||
* are available in our buffer, if not, it will set an event and sleep
|
||||
* until the number of characters are avaialable.
|
||||
*
|
||||
* @return an empty ManagedString on error, or a ManagedString containing characters
|
||||
*/
|
||||
ManagedString read(int len, MicroBitSerialMode mode = SYNC_SLEEP);
|
||||
|
||||
/**
|
||||
* Reads characters until a character matches one of the given delimeters
|
||||
*
|
||||
* @param delimeters the number of characters to match against
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will attempt read the immediate buffer, and look for a match.
|
||||
* If there isn't, an empty ManagedString will be returned.
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will first of all consider the characters in the immediate buffer,
|
||||
* if a match is not found, it will block on an event, fired when a
|
||||
* character is matched.
|
||||
*
|
||||
* @return an empty ManagedString on error, or a ManagedString containing characters
|
||||
*/
|
||||
ManagedString readUntil(ManagedString delimeters, MicroBitSerialMode mode = SYNC_SLEEP);
|
||||
|
||||
/**
|
||||
* Configures an event to be fired on a match with one of the delimeters.
|
||||
*
|
||||
* @param delimeters the characters to match received characters against e.g. ManagedString("\r\n")
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will configure the event and return immediately.
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will configure the event and block the current fiber until the
|
||||
* event is received.
|
||||
*
|
||||
* @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
|
||||
*
|
||||
* @note delimeters are matched on a per byte basis.
|
||||
*/
|
||||
int eventOn(ManagedString delimeters, MicroBitSerialMode mode = ASYNC);
|
||||
|
||||
/**
|
||||
* Configures an event to be fired after "len" characters.
|
||||
*
|
||||
* @param len the number of characters to wait before triggering the event
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will configure the event and return immediately.
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will configure the event and block the current fiber until the
|
||||
* event is received.
|
||||
*
|
||||
* @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
|
||||
*/
|
||||
int eventAfter(int len, MicroBitSerialMode mode = ASYNC);
|
||||
|
||||
/**
|
||||
* Determines if we have space in our rxBuff.
|
||||
*
|
||||
* @return 1 if we have space, 0 if we do not.
|
||||
*/
|
||||
int isReadable();
|
||||
|
||||
/**
|
||||
* @return The currently buffered number of bytes in our rxBuff.
|
||||
*/
|
||||
int rxBufferedSize();
|
||||
|
||||
/**
|
||||
* @return The currently buffered number of bytes in our txBuff.
|
||||
*/
|
||||
int txBufferedSize();
|
||||
};
|
||||
|
||||
extern const uint8_t UARTServiceBaseUUID[UUID::LENGTH_OF_LONG_UUID];
|
||||
extern const uint16_t UARTServiceShortUUID;
|
||||
extern const uint16_t UARTServiceTXCharacteristicShortUUID;
|
||||
extern const uint16_t UARTServiceRXCharacteristicShortUUID;
|
||||
|
||||
extern const uint8_t UARTServiceUUID[UUID::LENGTH_OF_LONG_UUID];
|
||||
extern const uint8_t UARTServiceUUID_reversed[UUID::LENGTH_OF_LONG_UUID];
|
||||
|
||||
extern const uint8_t UARTServiceTXCharacteristicUUID[UUID::LENGTH_OF_LONG_UUID];
|
||||
extern const uint8_t UARTServiceRXCharacteristicUUID[UUID::LENGTH_OF_LONG_UUID];
|
||||
|
||||
#endif
|
@ -0,0 +1,508 @@
|
||||
#include "ble/UUID.h"
|
||||
|
||||
#include "ExternalEvents.h"
|
||||
#include "MicroBitUARTService.h"
|
||||
#include "MicroBitFiber.h"
|
||||
#include "ErrorNo.h"
|
||||
#include "NotifyEvents.h"
|
||||
|
||||
static uint8_t txBufferHead = 0;
|
||||
static uint8_t txBufferTail = 0;
|
||||
|
||||
static GattCharacteristic* rxCharacteristic = NULL;
|
||||
|
||||
/**
|
||||
* A callback function for whenever a Bluetooth device consumes our RX Buffer
|
||||
*/
|
||||
void on_confirmation_received_callback(uint16_t handle)
|
||||
{
|
||||
#if CONFIG_ENABLED(MICROBIT_DBG)
|
||||
SERIAL_DEBUG.printf("RECEIVED!! %d \r\n",handle);
|
||||
#endif
|
||||
if(handle == rxCharacteristic->getValueAttribute().getHandle())
|
||||
{
|
||||
txBufferTail = txBufferHead;
|
||||
MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for the UARTService.
|
||||
* @param _ble an instance of BLEDevice
|
||||
* @param rxBufferSize the size of the rxBuffer
|
||||
* @param txBufferSize the size of the txBuffer
|
||||
*
|
||||
* @note defaults to 20
|
||||
*/
|
||||
MicroBitUARTService::MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize, uint8_t txBufferSize) : ble(_ble)
|
||||
{
|
||||
|
||||
txBuffer = (uint8_t *)malloc(txBufferSize);
|
||||
rxBuffer = (uint8_t *)malloc(rxBufferSize);
|
||||
|
||||
rxBufferHead = 0;
|
||||
rxBufferTail = 0;
|
||||
this->rxBufferSize = rxBufferSize;
|
||||
|
||||
txBufferHead = 0;
|
||||
txBufferTail = 0;
|
||||
this->txBufferSize = txBufferSize;
|
||||
|
||||
GattCharacteristic txCharacteristic(UARTServiceTXCharacteristicUUID, rxBuffer, 1, rxBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
|
||||
|
||||
rxCharacteristic = new GattCharacteristic(UARTServiceRXCharacteristicUUID, txBuffer, 1, txBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE);
|
||||
|
||||
GattCharacteristic *charTable[] = {&txCharacteristic, rxCharacteristic};
|
||||
|
||||
GattService uartService(UARTServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
|
||||
|
||||
_ble.addService(uartService);
|
||||
|
||||
this->txCharacteristicHandle = txCharacteristic.getValueAttribute().getHandle();
|
||||
|
||||
_ble.gattServer().onDataWritten(this, &MicroBitUARTService::onDataWritten);
|
||||
_ble.gattServer().onConfirmationReceived(on_confirmation_received_callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback function for whenever a Bluetooth device writes to our TX characteristic.
|
||||
*/
|
||||
void MicroBitUARTService::onDataWritten(const GattWriteCallbackParams *params) {
|
||||
if (params->handle == this->txCharacteristicHandle)
|
||||
{
|
||||
uint16_t bytesWritten = params->len;
|
||||
|
||||
for(int byteIterator = 0; byteIterator < bytesWritten; byteIterator++)
|
||||
{
|
||||
int newHead = (rxBufferHead + 1) % rxBufferSize;
|
||||
|
||||
if(newHead != rxBufferTail)
|
||||
{
|
||||
char c = params->data[byteIterator];
|
||||
|
||||
int delimeterOffset = 0;
|
||||
int delimLength = this->delimeters.length();
|
||||
|
||||
//iterate through our delimeters (if any) to see if there is a match
|
||||
while(delimeterOffset < delimLength)
|
||||
{
|
||||
//fire an event if there is to block any waiting fibers
|
||||
if(this->delimeters.charAt(delimeterOffset) == c)
|
||||
MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH);
|
||||
|
||||
delimeterOffset++;
|
||||
}
|
||||
|
||||
rxBuffer[rxBufferHead] = c;
|
||||
|
||||
rxBufferHead = newHead;
|
||||
|
||||
if(rxBufferHead == rxBuffHeadMatch)
|
||||
{
|
||||
rxBuffHeadMatch = -1;
|
||||
MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH);
|
||||
}
|
||||
}
|
||||
else
|
||||
MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_RX_FULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal method that copies values from a circular buffer to a linear buffer.
|
||||
*
|
||||
* @param circularBuff a pointer to the source circular buffer
|
||||
* @param circularBuffSize the size of the circular buffer
|
||||
* @param linearBuff a pointer to the destination linear buffer
|
||||
* @param tailPosition the tail position in the circular buffer you want to copy from
|
||||
* @param headPosition the head position in the circular buffer you want to copy to
|
||||
*
|
||||
* @note this method assumes that the linear buffer has the appropriate amount of
|
||||
* memory to contain the copy operation
|
||||
*/
|
||||
void MicroBitUARTService::circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition)
|
||||
{
|
||||
int toBuffIndex = 0;
|
||||
|
||||
while(tailPosition != headPosition)
|
||||
{
|
||||
linearBuff[toBuffIndex++] = circularBuff[tailPosition];
|
||||
|
||||
tailPosition = (tailPosition + 1) % circularBuffSize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives a single character from our RxBuffer.
|
||||
*
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will attempt to read a single character, and return immediately
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will configure the event and block the current fiber until the
|
||||
* event is received.
|
||||
*
|
||||
* @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, a character or MICROBIT_NO_DATA
|
||||
*/
|
||||
int MicroBitUARTService::getc(MicroBitSerialMode mode)
|
||||
{
|
||||
if(mode == SYNC_SPINWAIT)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
if(mode == ASYNC)
|
||||
{
|
||||
if(!isReadable())
|
||||
return MICROBIT_NO_DATA;
|
||||
}
|
||||
|
||||
if(mode == SYNC_SLEEP)
|
||||
{
|
||||
if(!isReadable())
|
||||
eventAfter(1, mode);
|
||||
}
|
||||
|
||||
char c = rxBuffer[rxBufferTail];
|
||||
|
||||
rxBufferTail = (rxBufferTail + 1) % rxBufferSize;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* places a single character into our transmission buffer,
|
||||
*
|
||||
* @param c the character to transmit
|
||||
*
|
||||
* @return the number of characters written (0, or 1).
|
||||
*/
|
||||
int MicroBitUARTService::putc(char c)
|
||||
{
|
||||
return (send((uint8_t *)&c, 1) == 1) ? 1 : EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies characters into the buffer used for Transmitting to the central device.
|
||||
*
|
||||
* @param buf a buffer containing length number of bytes.
|
||||
* @param length the size of the buffer.
|
||||
*
|
||||
* @return the number of characters copied into the buffer
|
||||
*
|
||||
* @note no modes for sending are available at the moment, due to interrupt overhead.
|
||||
*/
|
||||
int MicroBitUARTService::send(const uint8_t *buf, int length)
|
||||
{
|
||||
if(length < 1)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
int bytesWritten = 0;
|
||||
|
||||
if (ble.getGapState().connected) {
|
||||
|
||||
for(int bufferIterator = 0; bufferIterator < length; bufferIterator++)
|
||||
{
|
||||
int nextHead = (txBufferHead + 1) % txBufferSize;
|
||||
|
||||
if(nextHead != txBufferTail)
|
||||
{
|
||||
txBuffer[txBufferHead] = buf[bufferIterator];
|
||||
|
||||
txBufferHead = nextHead;
|
||||
|
||||
bytesWritten++;
|
||||
}
|
||||
}
|
||||
|
||||
int size = txBufferedSize();
|
||||
|
||||
#if CONFIG_ENABLED(MICROBIT_DBG)
|
||||
SERIAL_DEBUG.printf("tx size: %d", size);
|
||||
#endif
|
||||
|
||||
uint8_t temp[size] = { 0 };
|
||||
|
||||
circularCopy(txBuffer, txBufferSize, temp, txBufferTail, txBufferHead);
|
||||
|
||||
#if CONFIG_ENABLED(MICROBIT_DBG)
|
||||
for(int i = 0; i < size; i++)
|
||||
SERIAL_DEBUG.printf("%c",temp[i]);
|
||||
#endif
|
||||
|
||||
ble.gattServer().write(rxCharacteristic->getValueAttribute().getHandle(), temp, size);
|
||||
}
|
||||
|
||||
#if CONFIG_ENABLED(MICROBIT_DBG)
|
||||
SERIAL_DEBUG.printf("written: %d \r\n",bytesWritten);
|
||||
#endif
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies characters into the buffer used for Transmitting to the central device.
|
||||
*
|
||||
* @param s the string to transmit
|
||||
*
|
||||
* @return the number of characters copied into the buffer
|
||||
*
|
||||
* @note no modes for sending are available at the moment, due to interrupt overhead.
|
||||
*/
|
||||
int MicroBitUARTService::send(ManagedString s)
|
||||
{
|
||||
return send((uint8_t *)s.toCharArray(), s.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a number of characters from the rxBuffer and fills user given buffer.
|
||||
*
|
||||
* @param buf a pointer to a buffer of len bytes.
|
||||
* @param len the size of the user allocated buffer
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will attempt to read all available characters, and return immediately
|
||||
* until the buffer limit is reached
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will first of all determine whether the given number of characters
|
||||
* are available in our buffer, if not, it will set an event and sleep
|
||||
* until the number of characters are avaialable.
|
||||
*
|
||||
* @return the number of characters digested
|
||||
*/
|
||||
int MicroBitUARTService::read(uint8_t *buf, int len, MicroBitSerialMode mode)
|
||||
{
|
||||
if(mode == SYNC_SPINWAIT)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
int i = 0;
|
||||
|
||||
if(mode == ASYNC)
|
||||
{
|
||||
int c;
|
||||
|
||||
while((c = getc(mode)) > 0 && i < len)
|
||||
{
|
||||
buf[i] = c;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if(mode == SYNC_SLEEP)
|
||||
{
|
||||
if(len > rxBufferedSize())
|
||||
eventAfter(len - rxBufferedSize(), mode);
|
||||
|
||||
while(i < len)
|
||||
{
|
||||
buf[i] = (char)getc(mode);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a number of characters from the rxBuffer and returns them as a ManagedString
|
||||
*
|
||||
* @param len the number of characters to read.
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will attempt to read all available characters, and return immediately
|
||||
* until the buffer limit is reached
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will first of all determine whether the given number of characters
|
||||
* are available in our buffer, if not, it will set an event and sleep
|
||||
* until the number of characters are avaialable.
|
||||
*
|
||||
* @return an empty ManagedString on error, or a ManagedString containing characters
|
||||
*/
|
||||
ManagedString MicroBitUARTService::read(int len, MicroBitSerialMode mode)
|
||||
{
|
||||
uint8_t buf[len + 1] = { 0 };
|
||||
|
||||
int ret = read(buf, len, mode);
|
||||
|
||||
if(ret < 1)
|
||||
return ManagedString();
|
||||
|
||||
return ManagedString((const char *)buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads characters until a character matches one of the given delimeters
|
||||
*
|
||||
* @param delimeters the number of characters to match against
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will attempt read the immediate buffer, and look for a match.
|
||||
* If there isn't, an empty ManagedString will be returned.
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will first of all consider the characters in the immediate buffer,
|
||||
* if a match is not found, it will block on an event, fired when a
|
||||
* character is matched.
|
||||
*
|
||||
* @return an empty ManagedString on error, or a ManagedString containing characters
|
||||
*/
|
||||
ManagedString MicroBitUARTService::readUntil(ManagedString delimeters, MicroBitSerialMode mode)
|
||||
{
|
||||
if(mode == SYNC_SPINWAIT)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
int localTail = rxBufferTail;
|
||||
int preservedTail = rxBufferTail;
|
||||
|
||||
int foundIndex = -1;
|
||||
|
||||
//ASYNC mode just iterates through our stored characters checking for any matches.
|
||||
while(localTail != rxBufferHead && foundIndex == -1)
|
||||
{
|
||||
//we use localTail to prevent modification of the actual tail.
|
||||
char c = rxBuffer[localTail];
|
||||
|
||||
for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++)
|
||||
if(delimeters.charAt(delimeterIterator) == c)
|
||||
foundIndex = localTail;
|
||||
|
||||
localTail = (localTail + 1) % rxBufferSize;
|
||||
}
|
||||
|
||||
//if our mode is SYNC_SLEEP, we set up an event to be fired when we see a
|
||||
//matching character.
|
||||
if(mode == SYNC_SLEEP && foundIndex == -1)
|
||||
{
|
||||
eventOn(delimeters, mode);
|
||||
|
||||
foundIndex = rxBufferHead - 1;
|
||||
|
||||
this->delimeters = ManagedString();
|
||||
}
|
||||
|
||||
if(foundIndex >= 0)
|
||||
{
|
||||
//calculate our local buffer size
|
||||
int localBuffSize = (preservedTail > foundIndex) ? (rxBufferSize - preservedTail) + foundIndex : foundIndex - preservedTail;
|
||||
|
||||
uint8_t localBuff[localBuffSize + 1] = { 0 };
|
||||
|
||||
circularCopy(rxBuffer, rxBufferSize, localBuff, preservedTail, foundIndex);
|
||||
|
||||
//plus one for the character we listened for...
|
||||
rxBufferTail = (rxBufferTail + localBuffSize + 1) % rxBufferSize;
|
||||
|
||||
return ManagedString((char *)localBuff, localBuffSize);
|
||||
}
|
||||
|
||||
return ManagedString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an event to be fired on a match with one of the delimeters.
|
||||
*
|
||||
* @param delimeters the characters to match received characters against e.g. ManagedString("\r\n")
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will configure the event and return immediately.
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will configure the event and block the current fiber until the
|
||||
* event is received.
|
||||
*
|
||||
* @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
|
||||
*
|
||||
* @note delimeters are matched on a per byte basis.
|
||||
*/
|
||||
int MicroBitUARTService::eventOn(ManagedString delimeters, MicroBitSerialMode mode)
|
||||
{
|
||||
if(mode == SYNC_SPINWAIT)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
//configure our head match...
|
||||
this->delimeters = delimeters;
|
||||
|
||||
//block!
|
||||
if(mode == SYNC_SLEEP)
|
||||
fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH);
|
||||
|
||||
return MICROBIT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures an event to be fired after "len" characters.
|
||||
*
|
||||
* @param len the number of characters to wait before triggering the event
|
||||
* @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode
|
||||
* gives a different behaviour:
|
||||
*
|
||||
* ASYNC - Will configure the event and return immediately.
|
||||
*
|
||||
* SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER
|
||||
*
|
||||
* SYNC_SLEEP - Will configure the event and block the current fiber until the
|
||||
* event is received.
|
||||
*
|
||||
* @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK.
|
||||
*/
|
||||
int MicroBitUARTService::eventAfter(int len, MicroBitSerialMode mode)
|
||||
{
|
||||
if(mode == SYNC_SPINWAIT)
|
||||
return MICROBIT_INVALID_PARAMETER;
|
||||
|
||||
//configure our head match...
|
||||
this->rxBuffHeadMatch = (rxBufferHead + len) % rxBufferSize;
|
||||
|
||||
//block!
|
||||
if(mode == SYNC_SLEEP)
|
||||
fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH);
|
||||
|
||||
return MICROBIT_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we have space in our rxBuff.
|
||||
*
|
||||
* @return 1 if we have space, 0 if we do not.
|
||||
*
|
||||
* @note the reason we do not wrap the super's readable() method is so that we
|
||||
* don't interfere with communities that use manual calls to uBit.serial.readable()
|
||||
*/
|
||||
int MicroBitUARTService::isReadable()
|
||||
{
|
||||
return (rxBufferTail != rxBufferHead) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The currently buffered number of bytes in our rxBuff.
|
||||
*/
|
||||
int MicroBitUARTService::rxBufferedSize()
|
||||
{
|
||||
if(rxBufferTail > rxBufferHead)
|
||||
return (rxBufferSize - rxBufferTail) + rxBufferHead;
|
||||
|
||||
return rxBufferHead - rxBufferTail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The currently buffered number of bytes in our txBuff.
|
||||
*/
|
||||
int MicroBitUARTService::txBufferedSize()
|
||||
{
|
||||
if(txBufferTail > txBufferHead)
|
||||
return (txBufferSize - txBufferTail) + txBufferHead;
|
||||
|
||||
return txBufferHead - txBufferTail;
|
||||
}
|
Loading…
Reference in new issue