mbed.org local branch of microbit-dal. The real version lives in git at https://github.com/lancaster-university/microbit-dal
Dependencies: BLE_API nRF51822 mbed-dev-bin
Dependents: microbit Microbit IoTChallenge1 microbit ... more
MicroBitUARTService.cpp
00001 /* 00002 The MIT License (MIT) 00003 00004 Copyright (c) 2016 British Broadcasting Corporation. 00005 This software is provided by Lancaster University by arrangement with the BBC. 00006 00007 Permission is hereby granted, free of charge, to any person obtaining a 00008 copy of this software and associated documentation files (the "Software"), 00009 to deal in the Software without restriction, including without limitation 00010 the rights to use, copy, modify, merge, publish, distribute, sublicense, 00011 and/or sell copies of the Software, and to permit persons to whom the 00012 Software is furnished to do so, subject to the following conditions: 00013 00014 The above copyright notice and this permission notice shall be included in 00015 all copies or substantial portions of the Software. 00016 00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00023 DEALINGS IN THE SOFTWARE. 00024 */ 00025 00026 /** 00027 * Class definition for the custom MicroBit UART Service. 00028 * Provides a BLE service that acts as a UART port, enabling the reception and transmission 00029 * of an arbitrary number of bytes. 00030 */ 00031 00032 #include "ble/UUID.h" 00033 00034 #include "ExternalEvents.h" 00035 #include "MicroBitUARTService.h" 00036 #include "MicroBitFiber.h" 00037 #include "ErrorNo.h" 00038 #include "NotifyEvents.h" 00039 00040 static uint8_t txBufferHead = 0; 00041 static uint8_t txBufferTail = 0; 00042 00043 static GattCharacteristic* txCharacteristic = NULL; 00044 00045 /** 00046 * A callback function for whenever a Bluetooth device consumes our TX Buffer 00047 */ 00048 void on_confirmation(uint16_t handle) 00049 { 00050 if(handle == txCharacteristic->getValueAttribute().getHandle()) 00051 { 00052 txBufferTail = txBufferHead; 00053 MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY); 00054 } 00055 } 00056 00057 /** 00058 * Constructor for the UARTService. 00059 * @param _ble an instance of BLEDevice 00060 * @param rxBufferSize the size of the rxBuffer 00061 * @param txBufferSize the size of the txBuffer 00062 * 00063 * @note defaults to 20 00064 */ 00065 MicroBitUARTService::MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize, uint8_t txBufferSize) : ble(_ble) 00066 { 00067 rxBufferSize += 1; 00068 txBufferSize += 1; 00069 00070 txBuffer = (uint8_t *)malloc(txBufferSize); 00071 rxBuffer = (uint8_t *)malloc(rxBufferSize); 00072 00073 rxBufferHead = 0; 00074 rxBufferTail = 0; 00075 this->rxBufferSize = rxBufferSize; 00076 00077 txBufferHead = 0; 00078 txBufferTail = 0; 00079 this->txBufferSize = txBufferSize; 00080 00081 GattCharacteristic rxCharacteristic(UARTServiceRXCharacteristicUUID, rxBuffer, 1, rxBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); 00082 00083 txCharacteristic = new GattCharacteristic(UARTServiceTXCharacteristicUUID, txBuffer, 1, txBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE); 00084 00085 GattCharacteristic *charTable[] = {txCharacteristic, &rxCharacteristic}; 00086 00087 GattService uartService(UARTServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); 00088 00089 _ble.addService(uartService); 00090 00091 this->rxCharacteristicHandle = rxCharacteristic.getValueAttribute().getHandle(); 00092 00093 _ble.gattServer().onDataWritten(this, &MicroBitUARTService::onDataWritten); 00094 _ble.gattServer().onConfirmationReceived(on_confirmation); 00095 } 00096 00097 /** 00098 * A callback function for whenever a Bluetooth device writes to our RX characteristic. 00099 */ 00100 void MicroBitUARTService::onDataWritten(const GattWriteCallbackParams *params) { 00101 if (params->handle == this->rxCharacteristicHandle) 00102 { 00103 uint16_t bytesWritten = params->len; 00104 00105 for(int byteIterator = 0; byteIterator < bytesWritten; byteIterator++) 00106 { 00107 int newHead = (rxBufferHead + 1) % rxBufferSize; 00108 00109 if(newHead != rxBufferTail) 00110 { 00111 char c = params->data[byteIterator]; 00112 00113 int delimeterOffset = 0; 00114 int delimLength = this->delimeters.length(); 00115 00116 //iterate through our delimeters (if any) to see if there is a match 00117 while(delimeterOffset < delimLength) 00118 { 00119 //fire an event if there is to block any waiting fibers 00120 if(this->delimeters.charAt(delimeterOffset) == c) 00121 MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH); 00122 00123 delimeterOffset++; 00124 } 00125 00126 rxBuffer[rxBufferHead] = c; 00127 00128 rxBufferHead = newHead; 00129 00130 if(rxBufferHead == rxBuffHeadMatch) 00131 { 00132 rxBuffHeadMatch = -1; 00133 MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH); 00134 } 00135 } 00136 else 00137 MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_RX_FULL); 00138 } 00139 } 00140 } 00141 00142 /** 00143 * An internal method that copies values from a circular buffer to a linear buffer. 00144 * 00145 * @param circularBuff a pointer to the source circular buffer 00146 * @param circularBuffSize the size of the circular buffer 00147 * @param linearBuff a pointer to the destination linear buffer 00148 * @param tailPosition the tail position in the circular buffer you want to copy from 00149 * @param headPosition the head position in the circular buffer you want to copy to 00150 * 00151 * @note this method assumes that the linear buffer has the appropriate amount of 00152 * memory to contain the copy operation 00153 */ 00154 void MicroBitUARTService::circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition) 00155 { 00156 int toBuffIndex = 0; 00157 00158 while(tailPosition != headPosition) 00159 { 00160 linearBuff[toBuffIndex++] = circularBuff[tailPosition]; 00161 00162 tailPosition = (tailPosition + 1) % circularBuffSize; 00163 } 00164 } 00165 00166 /** 00167 * Retreives a single character from our RxBuffer. 00168 * 00169 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00170 * gives a different behaviour: 00171 * 00172 * ASYNC - Will attempt to read a single character, and return immediately 00173 * 00174 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00175 * 00176 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00177 * event is received. 00178 * 00179 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, a character or MICROBIT_NO_DATA 00180 */ 00181 int MicroBitUARTService::getc(MicroBitSerialMode mode) 00182 { 00183 if(mode == SYNC_SPINWAIT) 00184 return MICROBIT_INVALID_PARAMETER; 00185 00186 if(mode == ASYNC) 00187 { 00188 if(!isReadable()) 00189 return MICROBIT_NO_DATA; 00190 } 00191 00192 if(mode == SYNC_SLEEP) 00193 { 00194 if(!isReadable()) 00195 eventAfter(1, mode); 00196 } 00197 00198 char c = rxBuffer[rxBufferTail]; 00199 00200 rxBufferTail = (rxBufferTail + 1) % rxBufferSize; 00201 00202 return c; 00203 } 00204 00205 /** 00206 * places a single character into our transmission buffer, 00207 * 00208 * @param c the character to transmit 00209 * 00210 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00211 * gives a different behaviour: 00212 * 00213 * ASYNC - Will copy as many characters as it can into the buffer for transmission, 00214 * and return control to the user. 00215 * 00216 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00217 * 00218 * SYNC_SLEEP - Will perform a cooperative blocking wait until all 00219 * given characters have been received by the connected 00220 * device. 00221 * 00222 * @return the number of characters written, or MICROBIT_NOT_SUPPORTED if there is 00223 * no connected device, or the connected device has not enabled indications. 00224 */ 00225 int MicroBitUARTService::putc(char c, MicroBitSerialMode mode) 00226 { 00227 return (send((uint8_t *)&c, 1, mode) == 1) ? 1 : EOF; 00228 } 00229 00230 /** 00231 * Copies characters into the buffer used for Transmitting to the central device. 00232 * 00233 * @param buf a buffer containing length number of bytes. 00234 * @param length the size of the buffer. 00235 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00236 * gives a different behaviour: 00237 * 00238 * ASYNC - Will copy as many characters as it can into the buffer for transmission, 00239 * and return control to the user. 00240 * 00241 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00242 * 00243 * SYNC_SLEEP - Will perform a cooperative blocking wait until all 00244 * given characters have been received by the connected 00245 * device. 00246 * 00247 * @return the number of characters written, or MICROBIT_NOT_SUPPORTED if there is 00248 * no connected device, or the connected device has not enabled indications. 00249 */ 00250 int MicroBitUARTService::send(const uint8_t *buf, int length, MicroBitSerialMode mode) 00251 { 00252 if(length < 1 || mode == SYNC_SPINWAIT) 00253 return MICROBIT_INVALID_PARAMETER; 00254 00255 bool updatesEnabled = false; 00256 00257 ble.gattServer().areUpdatesEnabled(*txCharacteristic, &updatesEnabled); 00258 00259 if(!ble.getGapState().connected && !updatesEnabled) 00260 return MICROBIT_NOT_SUPPORTED; 00261 00262 int bytesWritten = 0; 00263 00264 while(bytesWritten < length && ble.getGapState().connected && updatesEnabled) 00265 { 00266 for(int bufferIterator = bytesWritten; bufferIterator < length; bufferIterator++) 00267 { 00268 int nextHead = (txBufferHead + 1) % txBufferSize; 00269 00270 if(nextHead != txBufferTail) 00271 { 00272 txBuffer[txBufferHead] = buf[bufferIterator]; 00273 00274 txBufferHead = nextHead; 00275 00276 bytesWritten++; 00277 } 00278 } 00279 00280 int size = txBufferedSize (); 00281 00282 uint8_t temp[size]; 00283 00284 memclr(&temp, size); 00285 00286 circularCopy(txBuffer, txBufferSize, temp, txBufferTail, txBufferHead); 00287 00288 00289 if(mode == SYNC_SLEEP) 00290 fiber_wake_on_event(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY); 00291 00292 ble.gattServer().write(txCharacteristic->getValueAttribute().getHandle(), temp, size); 00293 00294 if(mode == SYNC_SLEEP) 00295 schedule(); 00296 else 00297 break; 00298 00299 ble.gattServer().areUpdatesEnabled(*txCharacteristic, &updatesEnabled); 00300 } 00301 00302 return bytesWritten; 00303 } 00304 00305 /** 00306 * Copies characters into the buffer used for Transmitting to the central device. 00307 * 00308 * @param s the string to transmit 00309 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00310 * gives a different behaviour: 00311 * 00312 * ASYNC - Will copy as many characters as it can into the buffer for transmission, 00313 * and return control to the user. 00314 * 00315 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00316 * 00317 * SYNC_SLEEP - Will perform a cooperative blocking wait until all 00318 * given characters have been received by the connected 00319 * device. 00320 * 00321 * @return the number of characters written, or MICROBIT_NOT_SUPPORTED if there is 00322 * no connected device, or the connected device has not enabled indications. 00323 */ 00324 int MicroBitUARTService::send(ManagedString s, MicroBitSerialMode mode) 00325 { 00326 return send((uint8_t *)s.toCharArray(), s.length(), mode); 00327 } 00328 00329 /** 00330 * Reads a number of characters from the rxBuffer and fills user given buffer. 00331 * 00332 * @param buf a pointer to a buffer of len bytes. 00333 * @param len the size of the user allocated buffer 00334 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00335 * gives a different behaviour: 00336 * 00337 * ASYNC - Will attempt to read all available characters, and return immediately 00338 * until the buffer limit is reached 00339 * 00340 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00341 * 00342 * SYNC_SLEEP - Will first of all determine whether the given number of characters 00343 * are available in our buffer, if not, it will set an event and sleep 00344 * until the number of characters are avaialable. 00345 * 00346 * @return the number of characters digested 00347 */ 00348 int MicroBitUARTService::read(uint8_t *buf, int len, MicroBitSerialMode mode) 00349 { 00350 if(mode == SYNC_SPINWAIT) 00351 return MICROBIT_INVALID_PARAMETER; 00352 00353 int i = 0; 00354 00355 if(mode == ASYNC) 00356 { 00357 int c; 00358 00359 while((c = getc(mode)) > 0 && i < len) 00360 { 00361 buf[i] = c; 00362 i++; 00363 } 00364 } 00365 00366 if(mode == SYNC_SLEEP) 00367 { 00368 if(len > rxBufferedSize ()) 00369 eventAfter(len - rxBufferedSize (), mode); 00370 00371 while(i < len) 00372 { 00373 buf[i] = (char)getc(mode); 00374 i++; 00375 } 00376 } 00377 00378 return i; 00379 } 00380 00381 /** 00382 * Reads a number of characters from the rxBuffer and returns them as a ManagedString 00383 * 00384 * @param len the number of characters to read. 00385 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00386 * gives a different behaviour: 00387 * 00388 * ASYNC - Will attempt to read all available characters, and return immediately 00389 * until the buffer limit is reached 00390 * 00391 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00392 * 00393 * SYNC_SLEEP - Will first of all determine whether the given number of characters 00394 * are available in our buffer, if not, it will set an event and sleep 00395 * until the number of characters are avaialable. 00396 * 00397 * @return an empty ManagedString on error, or a ManagedString containing characters 00398 */ 00399 ManagedString MicroBitUARTService::read(int len, MicroBitSerialMode mode) 00400 { 00401 uint8_t buf[len + 1]; 00402 00403 memclr(&buf, len + 1); 00404 00405 int ret = read(buf, len, mode); 00406 00407 if(ret < 1) 00408 return ManagedString(); 00409 00410 return ManagedString((const char *)buf); 00411 } 00412 00413 /** 00414 * Reads characters until a character matches one of the given delimeters 00415 * 00416 * @param delimeters the number of characters to match against 00417 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00418 * gives a different behaviour: 00419 * 00420 * ASYNC - Will attempt read the immediate buffer, and look for a match. 00421 * If there isn't, an empty ManagedString will be returned. 00422 * 00423 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00424 * 00425 * SYNC_SLEEP - Will first of all consider the characters in the immediate buffer, 00426 * if a match is not found, it will block on an event, fired when a 00427 * character is matched. 00428 * 00429 * @return an empty ManagedString on error, or a ManagedString containing characters 00430 */ 00431 ManagedString MicroBitUARTService::readUntil(ManagedString delimeters, MicroBitSerialMode mode) 00432 { 00433 if(mode == SYNC_SPINWAIT) 00434 return MICROBIT_INVALID_PARAMETER; 00435 00436 int localTail = rxBufferTail; 00437 int preservedTail = rxBufferTail; 00438 00439 int foundIndex = -1; 00440 00441 //ASYNC mode just iterates through our stored characters checking for any matches. 00442 while(localTail != rxBufferHead && foundIndex == -1) 00443 { 00444 //we use localTail to prevent modification of the actual tail. 00445 char c = rxBuffer[localTail]; 00446 00447 for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++) 00448 if(delimeters.charAt(delimeterIterator) == c) 00449 foundIndex = localTail; 00450 00451 localTail = (localTail + 1) % rxBufferSize; 00452 } 00453 00454 //if our mode is SYNC_SLEEP, we set up an event to be fired when we see a 00455 //matching character. 00456 if(mode == SYNC_SLEEP && foundIndex == -1) 00457 { 00458 eventOn(delimeters, mode); 00459 00460 foundIndex = rxBufferHead - 1; 00461 00462 this->delimeters = ManagedString(); 00463 } 00464 00465 if(foundIndex >= 0) 00466 { 00467 //calculate our local buffer size 00468 int localBuffSize = (preservedTail > foundIndex) ? (rxBufferSize - preservedTail) + foundIndex : foundIndex - preservedTail; 00469 00470 uint8_t localBuff[localBuffSize + 1]; 00471 00472 memclr(&localBuff, localBuffSize + 1); 00473 00474 circularCopy(rxBuffer, rxBufferSize, localBuff, preservedTail, foundIndex); 00475 00476 //plus one for the character we listened for... 00477 rxBufferTail = (rxBufferTail + localBuffSize + 1) % rxBufferSize; 00478 00479 return ManagedString((char *)localBuff, localBuffSize); 00480 } 00481 00482 return ManagedString(); 00483 } 00484 00485 /** 00486 * Configures an event to be fired on a match with one of the delimeters. 00487 * 00488 * @param delimeters the characters to match received characters against e.g. ManagedString("\r\n") 00489 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00490 * gives a different behaviour: 00491 * 00492 * ASYNC - Will configure the event and return immediately. 00493 * 00494 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00495 * 00496 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00497 * event is received. 00498 * 00499 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK. 00500 * 00501 * @note delimeters are matched on a per byte basis. 00502 */ 00503 int MicroBitUARTService::eventOn(ManagedString delimeters, MicroBitSerialMode mode) 00504 { 00505 if(mode == SYNC_SPINWAIT) 00506 return MICROBIT_INVALID_PARAMETER; 00507 00508 //configure our head match... 00509 this->delimeters = delimeters; 00510 00511 //block! 00512 if(mode == SYNC_SLEEP) 00513 fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH); 00514 00515 return MICROBIT_OK; 00516 } 00517 00518 /** 00519 * Configures an event to be fired after "len" characters. 00520 * 00521 * @param len the number of characters to wait before triggering the event 00522 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00523 * gives a different behaviour: 00524 * 00525 * ASYNC - Will configure the event and return immediately. 00526 * 00527 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00528 * 00529 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00530 * event is received. 00531 * 00532 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK. 00533 */ 00534 int MicroBitUARTService::eventAfter(int len, MicroBitSerialMode mode) 00535 { 00536 if(mode == SYNC_SPINWAIT) 00537 return MICROBIT_INVALID_PARAMETER; 00538 00539 //configure our head match... 00540 this->rxBuffHeadMatch = (rxBufferHead + len) % rxBufferSize; 00541 00542 //block! 00543 if(mode == SYNC_SLEEP) 00544 fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH); 00545 00546 return MICROBIT_OK; 00547 } 00548 00549 /** 00550 * Determines if we have space in our rxBuff. 00551 * 00552 * @return 1 if we have space, 0 if we do not. 00553 * 00554 * @note the reason we do not wrap the super's readable() method is so that we 00555 * don't interfere with communities that use manual calls to uBit.serial.readable() 00556 */ 00557 int MicroBitUARTService::isReadable() 00558 { 00559 return (rxBufferTail != rxBufferHead) ? 1 : 0; 00560 } 00561 00562 /** 00563 * @return The currently buffered number of bytes in our rxBuff. 00564 */ 00565 int MicroBitUARTService::rxBufferedSize () 00566 { 00567 if(rxBufferTail > rxBufferHead) 00568 return (rxBufferSize - rxBufferTail) + rxBufferHead; 00569 00570 return rxBufferHead - rxBufferTail; 00571 } 00572 00573 /** 00574 * @return The currently buffered number of bytes in our txBuff. 00575 */ 00576 int MicroBitUARTService::txBufferedSize () 00577 { 00578 if(txBufferTail > txBufferHead) 00579 return (txBufferSize - txBufferTail) + txBufferHead; 00580 00581 return txBufferHead - txBufferTail; 00582 }
Generated on Tue Jul 12 2022 15:22:56 by 1.7.2