Attempting to publish a tree
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
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* rxCharacteristic = NULL; 00044 00045 /** 00046 * A callback function for whenever a Bluetooth device consumes our RX Buffer 00047 */ 00048 void on_confirmation_received_callback(uint16_t handle) 00049 { 00050 #if CONFIG_ENABLED(MICROBIT_DBG) 00051 SERIAL_DEBUG->printf("RECEIVED!! %d \r\n",handle); 00052 #endif 00053 if(handle == rxCharacteristic->getValueAttribute().getHandle()) 00054 { 00055 txBufferTail = txBufferHead; 00056 MicroBitEvent(MICROBIT_ID_NOTIFY, MICROBIT_UART_S_EVT_TX_EMPTY); 00057 } 00058 } 00059 00060 /** 00061 * Constructor for the UARTService. 00062 * @param _ble an instance of BLEDevice 00063 * @param rxBufferSize the size of the rxBuffer 00064 * @param txBufferSize the size of the txBuffer 00065 * 00066 * @note defaults to 20 00067 */ 00068 MicroBitUARTService::MicroBitUARTService(BLEDevice &_ble, uint8_t rxBufferSize, uint8_t txBufferSize) : ble(_ble) 00069 { 00070 00071 txBuffer = (uint8_t *)malloc(txBufferSize); 00072 rxBuffer = (uint8_t *)malloc(rxBufferSize); 00073 00074 rxBufferHead = 0; 00075 rxBufferTail = 0; 00076 this->rxBufferSize = rxBufferSize; 00077 00078 txBufferHead = 0; 00079 txBufferTail = 0; 00080 this->txBufferSize = txBufferSize; 00081 00082 GattCharacteristic txCharacteristic(UARTServiceTXCharacteristicUUID, rxBuffer, 1, rxBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); 00083 00084 rxCharacteristic = new GattCharacteristic(UARTServiceRXCharacteristicUUID, txBuffer, 1, txBufferSize, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE); 00085 00086 GattCharacteristic *charTable[] = {&txCharacteristic, rxCharacteristic}; 00087 00088 GattService uartService(UARTServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); 00089 00090 _ble.addService(uartService); 00091 00092 this->txCharacteristicHandle = txCharacteristic.getValueAttribute().getHandle(); 00093 00094 _ble.gattServer().onDataWritten(this, &MicroBitUARTService::onDataWritten); 00095 _ble.gattServer().onConfirmationReceived(on_confirmation_received_callback); 00096 } 00097 00098 /** 00099 * A callback function for whenever a Bluetooth device writes to our TX characteristic. 00100 */ 00101 void MicroBitUARTService::onDataWritten(const GattWriteCallbackParams *params) { 00102 if (params->handle == this->txCharacteristicHandle) 00103 { 00104 uint16_t bytesWritten = params->len; 00105 00106 for(int byteIterator = 0; byteIterator < bytesWritten; byteIterator++) 00107 { 00108 int newHead = (rxBufferHead + 1) % rxBufferSize; 00109 00110 if(newHead != rxBufferTail) 00111 { 00112 char c = params->data[byteIterator]; 00113 00114 int delimeterOffset = 0; 00115 int delimLength = this->delimeters.length(); 00116 00117 //iterate through our delimeters (if any) to see if there is a match 00118 while(delimeterOffset < delimLength) 00119 { 00120 //fire an event if there is to block any waiting fibers 00121 if(this->delimeters.charAt(delimeterOffset) == c) 00122 MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH); 00123 00124 delimeterOffset++; 00125 } 00126 00127 rxBuffer[rxBufferHead] = c; 00128 00129 rxBufferHead = newHead; 00130 00131 if(rxBufferHead == rxBuffHeadMatch) 00132 { 00133 rxBuffHeadMatch = -1; 00134 MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH); 00135 } 00136 } 00137 else 00138 MicroBitEvent(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_RX_FULL); 00139 } 00140 } 00141 } 00142 00143 /** 00144 * An internal method that copies values from a circular buffer to a linear buffer. 00145 * 00146 * @param circularBuff a pointer to the source circular buffer 00147 * @param circularBuffSize the size of the circular buffer 00148 * @param linearBuff a pointer to the destination linear buffer 00149 * @param tailPosition the tail position in the circular buffer you want to copy from 00150 * @param headPosition the head position in the circular buffer you want to copy to 00151 * 00152 * @note this method assumes that the linear buffer has the appropriate amount of 00153 * memory to contain the copy operation 00154 */ 00155 void MicroBitUARTService::circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition) 00156 { 00157 int toBuffIndex = 0; 00158 00159 while(tailPosition != headPosition) 00160 { 00161 linearBuff[toBuffIndex++] = circularBuff[tailPosition]; 00162 00163 tailPosition = (tailPosition + 1) % circularBuffSize; 00164 } 00165 } 00166 00167 /** 00168 * Retreives a single character from our RxBuffer. 00169 * 00170 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00171 * gives a different behaviour: 00172 * 00173 * ASYNC - Will attempt to read a single character, and return immediately 00174 * 00175 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00176 * 00177 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00178 * event is received. 00179 * 00180 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, a character or MICROBIT_NO_DATA 00181 */ 00182 int MicroBitUARTService::getc(MicroBitSerialMode mode) 00183 { 00184 if(mode == SYNC_SPINWAIT) 00185 return MICROBIT_INVALID_PARAMETER; 00186 00187 if(mode == ASYNC) 00188 { 00189 if(!isReadable()) 00190 return MICROBIT_NO_DATA; 00191 } 00192 00193 if(mode == SYNC_SLEEP) 00194 { 00195 if(!isReadable()) 00196 eventAfter(1, mode); 00197 } 00198 00199 char c = rxBuffer[rxBufferTail]; 00200 00201 rxBufferTail = (rxBufferTail + 1) % rxBufferSize; 00202 00203 return c; 00204 } 00205 00206 /** 00207 * places a single character into our transmission buffer, 00208 * 00209 * @param c the character to transmit 00210 * 00211 * @return the number of characters written (0, or 1). 00212 */ 00213 int MicroBitUARTService::putc(char c) 00214 { 00215 return (send((uint8_t *)&c, 1) == 1) ? 1 : EOF; 00216 } 00217 00218 /** 00219 * Copies characters into the buffer used for Transmitting to the central device. 00220 * 00221 * @param buf a buffer containing length number of bytes. 00222 * @param length the size of the buffer. 00223 * 00224 * @return the number of characters copied into the buffer 00225 * 00226 * @note no modes for sending are available at the moment, due to interrupt overhead. 00227 */ 00228 int MicroBitUARTService::send(const uint8_t *buf, int length) 00229 { 00230 if(length < 1) 00231 return MICROBIT_INVALID_PARAMETER; 00232 00233 int bytesWritten = 0; 00234 00235 if (ble.getGapState().connected) { 00236 00237 for(int bufferIterator = 0; bufferIterator < length; bufferIterator++) 00238 { 00239 int nextHead = (txBufferHead + 1) % txBufferSize; 00240 00241 if(nextHead != txBufferTail) 00242 { 00243 txBuffer[txBufferHead] = buf[bufferIterator]; 00244 00245 txBufferHead = nextHead; 00246 00247 bytesWritten++; 00248 } 00249 } 00250 00251 int size = txBufferedSize (); 00252 00253 #if CONFIG_ENABLED(MICROBIT_DBG) 00254 SERIAL_DEBUG->printf("tx size: %d", size); 00255 #endif 00256 00257 uint8_t temp[size]; 00258 00259 memclr(&temp, size); 00260 00261 circularCopy(txBuffer, txBufferSize, temp, txBufferTail, txBufferHead); 00262 00263 #if CONFIG_ENABLED(MICROBIT_DBG) 00264 for(int i = 0; i < size; i++) 00265 SERIAL_DEBUG->printf("%c",temp[i]); 00266 #endif 00267 00268 ble.gattServer().write(rxCharacteristic->getValueAttribute().getHandle(), temp, size); 00269 } 00270 00271 #if CONFIG_ENABLED(MICROBIT_DBG) 00272 SERIAL_DEBUG->printf("written: %d \r\n",bytesWritten); 00273 #endif 00274 00275 return bytesWritten; 00276 } 00277 00278 /** 00279 * Copies characters into the buffer used for Transmitting to the central device. 00280 * 00281 * @param s the string to transmit 00282 * 00283 * @return the number of characters copied into the buffer 00284 * 00285 * @note no modes for sending are available at the moment, due to interrupt overhead. 00286 */ 00287 int MicroBitUARTService::send(ManagedString s) 00288 { 00289 return send((uint8_t *)s.toCharArray(), s.length()); 00290 } 00291 00292 /** 00293 * Reads a number of characters from the rxBuffer and fills user given buffer. 00294 * 00295 * @param buf a pointer to a buffer of len bytes. 00296 * @param len the size of the user allocated buffer 00297 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00298 * gives a different behaviour: 00299 * 00300 * ASYNC - Will attempt to read all available characters, and return immediately 00301 * until the buffer limit is reached 00302 * 00303 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00304 * 00305 * SYNC_SLEEP - Will first of all determine whether the given number of characters 00306 * are available in our buffer, if not, it will set an event and sleep 00307 * until the number of characters are avaialable. 00308 * 00309 * @return the number of characters digested 00310 */ 00311 int MicroBitUARTService::read(uint8_t *buf, int len, MicroBitSerialMode mode) 00312 { 00313 if(mode == SYNC_SPINWAIT) 00314 return MICROBIT_INVALID_PARAMETER; 00315 00316 int i = 0; 00317 00318 if(mode == ASYNC) 00319 { 00320 int c; 00321 00322 while((c = getc(mode)) > 0 && i < len) 00323 { 00324 buf[i] = c; 00325 i++; 00326 } 00327 } 00328 00329 if(mode == SYNC_SLEEP) 00330 { 00331 if(len > rxBufferedSize ()) 00332 eventAfter(len - rxBufferedSize (), mode); 00333 00334 while(i < len) 00335 { 00336 buf[i] = (char)getc(mode); 00337 i++; 00338 } 00339 } 00340 00341 return i; 00342 } 00343 00344 /** 00345 * Reads a number of characters from the rxBuffer and returns them as a ManagedString 00346 * 00347 * @param len the number of characters to read. 00348 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00349 * gives a different behaviour: 00350 * 00351 * ASYNC - Will attempt to read all available characters, and return immediately 00352 * until the buffer limit is reached 00353 * 00354 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00355 * 00356 * SYNC_SLEEP - Will first of all determine whether the given number of characters 00357 * are available in our buffer, if not, it will set an event and sleep 00358 * until the number of characters are avaialable. 00359 * 00360 * @return an empty ManagedString on error, or a ManagedString containing characters 00361 */ 00362 ManagedString MicroBitUARTService::read(int len, MicroBitSerialMode mode) 00363 { 00364 uint8_t buf[len + 1]; 00365 00366 memclr(&buf, len + 1); 00367 00368 int ret = read(buf, len, mode); 00369 00370 if(ret < 1) 00371 return ManagedString(); 00372 00373 return ManagedString((const char *)buf); 00374 } 00375 00376 /** 00377 * Reads characters until a character matches one of the given delimeters 00378 * 00379 * @param delimeters the number of characters to match against 00380 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00381 * gives a different behaviour: 00382 * 00383 * ASYNC - Will attempt read the immediate buffer, and look for a match. 00384 * If there isn't, an empty ManagedString will be returned. 00385 * 00386 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00387 * 00388 * SYNC_SLEEP - Will first of all consider the characters in the immediate buffer, 00389 * if a match is not found, it will block on an event, fired when a 00390 * character is matched. 00391 * 00392 * @return an empty ManagedString on error, or a ManagedString containing characters 00393 */ 00394 ManagedString MicroBitUARTService::readUntil(ManagedString delimeters, MicroBitSerialMode mode) 00395 { 00396 if(mode == SYNC_SPINWAIT) 00397 return MICROBIT_INVALID_PARAMETER; 00398 00399 int localTail = rxBufferTail; 00400 int preservedTail = rxBufferTail; 00401 00402 int foundIndex = -1; 00403 00404 //ASYNC mode just iterates through our stored characters checking for any matches. 00405 while(localTail != rxBufferHead && foundIndex == -1) 00406 { 00407 //we use localTail to prevent modification of the actual tail. 00408 char c = rxBuffer[localTail]; 00409 00410 for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++) 00411 if(delimeters.charAt(delimeterIterator) == c) 00412 foundIndex = localTail; 00413 00414 localTail = (localTail + 1) % rxBufferSize; 00415 } 00416 00417 //if our mode is SYNC_SLEEP, we set up an event to be fired when we see a 00418 //matching character. 00419 if(mode == SYNC_SLEEP && foundIndex == -1) 00420 { 00421 eventOn(delimeters, mode); 00422 00423 foundIndex = rxBufferHead - 1; 00424 00425 this->delimeters = ManagedString(); 00426 } 00427 00428 if(foundIndex >= 0) 00429 { 00430 //calculate our local buffer size 00431 int localBuffSize = (preservedTail > foundIndex) ? (rxBufferSize - preservedTail) + foundIndex : foundIndex - preservedTail; 00432 00433 uint8_t localBuff[localBuffSize + 1]; 00434 00435 memclr(&localBuff, localBuffSize + 1); 00436 00437 circularCopy(rxBuffer, rxBufferSize, localBuff, preservedTail, foundIndex); 00438 00439 //plus one for the character we listened for... 00440 rxBufferTail = (rxBufferTail + localBuffSize + 1) % rxBufferSize; 00441 00442 return ManagedString((char *)localBuff, localBuffSize); 00443 } 00444 00445 return ManagedString(); 00446 } 00447 00448 /** 00449 * Configures an event to be fired on a match with one of the delimeters. 00450 * 00451 * @param delimeters the characters to match received characters against e.g. ManagedString("\r\n") 00452 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00453 * gives a different behaviour: 00454 * 00455 * ASYNC - Will configure the event and return immediately. 00456 * 00457 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00458 * 00459 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00460 * event is received. 00461 * 00462 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK. 00463 * 00464 * @note delimeters are matched on a per byte basis. 00465 */ 00466 int MicroBitUARTService::eventOn(ManagedString delimeters, MicroBitSerialMode mode) 00467 { 00468 if(mode == SYNC_SPINWAIT) 00469 return MICROBIT_INVALID_PARAMETER; 00470 00471 //configure our head match... 00472 this->delimeters = delimeters; 00473 00474 //block! 00475 if(mode == SYNC_SLEEP) 00476 fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH); 00477 00478 return MICROBIT_OK; 00479 } 00480 00481 /** 00482 * Configures an event to be fired after "len" characters. 00483 * 00484 * @param len the number of characters to wait before triggering the event 00485 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00486 * gives a different behaviour: 00487 * 00488 * ASYNC - Will configure the event and return immediately. 00489 * 00490 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00491 * 00492 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00493 * event is received. 00494 * 00495 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK. 00496 */ 00497 int MicroBitUARTService::eventAfter(int len, MicroBitSerialMode mode) 00498 { 00499 if(mode == SYNC_SPINWAIT) 00500 return MICROBIT_INVALID_PARAMETER; 00501 00502 //configure our head match... 00503 this->rxBuffHeadMatch = (rxBufferHead + len) % rxBufferSize; 00504 00505 //block! 00506 if(mode == SYNC_SLEEP) 00507 fiber_wait_for_event(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_HEAD_MATCH); 00508 00509 return MICROBIT_OK; 00510 } 00511 00512 /** 00513 * Determines if we have space in our rxBuff. 00514 * 00515 * @return 1 if we have space, 0 if we do not. 00516 * 00517 * @note the reason we do not wrap the super's readable() method is so that we 00518 * don't interfere with communities that use manual calls to uBit.serial.readable() 00519 */ 00520 int MicroBitUARTService::isReadable() 00521 { 00522 return (rxBufferTail != rxBufferHead) ? 1 : 0; 00523 } 00524 00525 /** 00526 * @return The currently buffered number of bytes in our rxBuff. 00527 */ 00528 int MicroBitUARTService::rxBufferedSize () 00529 { 00530 if(rxBufferTail > rxBufferHead) 00531 return (rxBufferSize - rxBufferTail) + rxBufferHead; 00532 00533 return rxBufferHead - rxBufferTail; 00534 } 00535 00536 /** 00537 * @return The currently buffered number of bytes in our txBuff. 00538 */ 00539 int MicroBitUARTService::txBufferedSize () 00540 { 00541 if(txBufferTail > txBufferHead) 00542 return (txBufferSize - txBufferTail) + txBufferHead; 00543 00544 return txBufferHead - txBufferTail; 00545 }
Generated on Tue Jul 12 2022 19:58:09 by 1.7.2