/** * Prints the given string to the display, one character at a time. * * Blocks the calling thread until all the text has been displayed. * * @param s The string to display. * * @param delay The time to delay between characters, in milliseconds. Defaults * to: MICROBIT_DEFAULT_PRINT_SPEED. * * @return MICROBIT_OK, MICROBIT_CANCELLED or MICROBIT_INVALID_PARAMETER. * * @code * display.print("abc123",400); * @endcode */ int MicroBitDisplay::print(ManagedString s, int delay) { //sanitise this value if(delay <= 0 ) return MICROBIT_INVALID_PARAMETER; // If there's an ongoing animation, wait for our turn to display. this->waitForFreeDisplay(); // If the display is free, it's our turn to display. // If someone called stopAnimation(), then we simply skip... if (animationMode == ANIMATION_MODE_NONE) { if (s.length() == 1) { return printCharAsync(s.charAt(0)); } else { this->printAsync(s, delay); fiberWait(); } } else { return MICROBIT_CANCELLED; } return MICROBIT_OK; }
/** * Prints the given ManagedString to the display, one character at a time. * Returns immediately, and executes the animation asynchronously. * * @param s The string to display. * * @param delay The time to delay between characters, in milliseconds. Must be > 0. * Defaults to: MICROBIT_DEFAULT_PRINT_SPEED. * * @return MICROBIT_OK, or MICROBIT_INVALID_PARAMETER. * * @code * display.printAsync("abc123",400); * @endcode */ int MicroBitDisplay::printAsync(ManagedString s, int delay) { if (s.length() == 1) return printCharAsync(s.charAt(0)); //sanitise this value if (delay <= 0 ) return MICROBIT_INVALID_PARAMETER; if (animationMode == ANIMATION_MODE_NONE || animationMode == ANIMATION_MODE_STOPPED) { printingChar = 0; printingText = s; animationDelay = delay; animationTick = 0; animationMode = ANIMATION_MODE_PRINT_TEXT; } else { return MICROBIT_BUSY; } return MICROBIT_OK; }
/** * 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]; memclr(&localBuff, localBuffSize + 1); 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(); }
/** * Enter pairing mode. This is mode is called to initiate pairing, and to enable FOTA programming * of the micro:bit in cases where BLE is disabled during normal operation. */ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display) { ManagedString namePrefix("BBC micro:bit ["); ManagedString namePostfix("]"); ManagedString BLEName = namePrefix + deviceName + namePostfix; ManagedString msg("PAIRING MODE!"); int timeInPairingMode = 0; int brightness = 255; int fadeDirection = 0; ble->gap().stopAdvertising(); // Clear the whitelist (if we have one), so that we're discoverable by all BLE devices. #if CONFIG_ENABLED(MICROBIT_BLE_WHITELIST) BLEProtocol::Address_t addresses[MICROBIT_BLE_MAXIMUM_BONDS]; Gap::Whitelist_t whitelist; whitelist.addresses = addresses; whitelist.capacity = MICROBIT_BLE_MAXIMUM_BONDS; whitelist.size = 0; ble->gap().setWhitelist(whitelist); ble->gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST); #endif // Update the advertised name of this micro:bit to include the device name ble->clearAdvertisingPayload(); ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)BLEName.toCharArray(), BLEName.length()); ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); ble->setAdvertisingInterval(200); ble->gap().setAdvertisingTimeout(0); ble->gap().startAdvertising(); // Stop any running animations on the display display.stopAnimation(); display.scroll(msg); // Display our name, visualised as a histogram in the display to aid identification. showNameHistogram(display); while(1) { if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST) { timeInPairingMode = 0; MicroBitImage arrow("0,0,255,0,0\n0,255,0,0,0\n255,255,255,255,255\n0,255,0,0,0\n0,0,255,0,0\n"); display.print(arrow,0,0,0); if (fadeDirection == 0) brightness -= MICROBIT_PAIRING_FADE_SPEED; else brightness += MICROBIT_PAIRING_FADE_SPEED; if (brightness <= 40) display.clear(); if (brightness <= 0) fadeDirection = 1; if (brightness >= 255) fadeDirection = 0; if (uBit.buttonA.isPressed()) { pairingStatus &= ~MICROBIT_BLE_PAIR_REQUEST; pairingStatus |= MICROBIT_BLE_PAIR_PASSCODE; } } if (pairingStatus & MICROBIT_BLE_PAIR_PASSCODE) { timeInPairingMode = 0; display.setBrightness(255); for (int i=0; i<passKey.length(); i++) { display.image.print(passKey.charAt(i),0,0); uBit.sleep(800); display.clear(); uBit.sleep(200); if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE) break; } uBit.sleep(1000); } if (pairingStatus & MICROBIT_BLE_PAIR_COMPLETE) { if (pairingStatus & MICROBIT_BLE_PAIR_SUCCESSFUL) { MicroBitImage tick("0,0,0,0,0\n0,0,0,0,255\n0,0,0,255,0\n255,0,255,0,0\n0,255,0,0,0\n"); display.print(tick,0,0,0); uBit.sleep(5000); /* * Disabled, as the API to return the number of active bonds is not reliable at present... * display.clear(); ManagedString c(getBondCount()); ManagedString c2("/"); ManagedString c3(MICROBIT_BLE_MAXIMUM_BONDS); ManagedString c4("USED"); display.scroll(c+c2+c3+c4); * * */ } else { MicroBitImage cross("255,0,0,0,255\n0,255,0,255,0\n0,0,255,0,0\n0,255,0,255,0\n255,0,0,0,255\n"); display.print(cross,0,0,0); } } uBit.sleep(30); timeInPairingMode++; if (timeInPairingMode >= MICROBIT_BLE_PAIRING_TIMEOUT * 30) microbit_reset(); } }
/** * Copies characters into the buffer used for Transmitting to the central device. * * @param s the string to transmit * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode * gives a different behaviour: * * ASYNC - Will copy as many characters as it can into the buffer for transmission, * and return control to the user. * * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER * * SYNC_SLEEP - Will perform a cooperative blocking wait until all * given characters have been received by the connected * device. * * @return the number of characters written, or MICROBIT_NOT_SUPPORTED if there is * no connected device, or the connected device has not enabled indications. */ int MicroBitUARTService::send(ManagedString s, MicroBitSerialMode mode) { return send((uint8_t *)s.toCharArray(), s.length(), mode); }
/** * Reads until one of the delimeters matches a character in the rxBuff * * @param delimeters a ManagedString containing a sequence of delimeter characters e.g. ManagedString("\r\n") * * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode * gives a different behaviour: * * ASYNC - If one of the delimeters matches a character already in the rxBuff * this method will return a ManagedString up to the delimeter. * Otherwise, it will return an Empty ManagedString. * * SYNC_SPINWAIT - If one of the delimeters matches a character already in the rxBuff * this method will return a ManagedString up to the delimeter. * Otherwise, this method will spin (lock up the processor) until a * received character matches one of the delimeters. * * SYNC_SLEEP - If one of the delimeters matches a character already in the rxBuff * this method will return a ManagedString up to the delimeter. * Otherwise, the calling fiber sleeps until a character matching one * of the delimeters is seen. * * Defaults to SYNC_SLEEP. * * @return A ManagedString containing the characters up to a delimeter, or an Empty ManagedString, * if another fiber is currently using this instance for reception. * * @note delimeters are matched on a per byte basis. */ ManagedString MicroBitSerial::readUntil(ManagedString delimeters, MicroBitSerialMode mode) { if(rxInUse()) return ManagedString(); //lazy initialisation of our rx buffer if(!(status & MICROBIT_SERIAL_RX_BUFF_INIT)) { int result = initialiseRx(); if(result != MICROBIT_OK) return result; } lockRx(); int localTail = rxBuffTail; int preservedTail = rxBuffTail; int foundIndex = -1; //ASYNC mode just iterates through our stored characters checking for any matches. while(localTail != rxBuffHead && foundIndex == -1) { //we use localTail to prevent modification of the actual tail. char c = rxBuff[localTail]; for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++) if(delimeters.charAt(delimeterIterator) == c) foundIndex = localTail; localTail = (localTail + 1) % rxBuffSize; } //if our mode is SYNC_SPINWAIT and we didn't see any matching characters in our buffer //spin until we find a match! if(mode == SYNC_SPINWAIT) { while(foundIndex == -1) { while(localTail == rxBuffHead); char c = rxBuff[localTail]; for(int delimeterIterator = 0; delimeterIterator < delimeters.length(); delimeterIterator++) if(delimeters.charAt(delimeterIterator) == c) foundIndex = localTail; localTail = (localTail + 1) % rxBuffSize; } } //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 = rxBuffHead - 1; this->delimeters = ManagedString(); } if(foundIndex >= 0) { //calculate our local buffer size int localBuffSize = (preservedTail > foundIndex) ? (rxBuffSize - preservedTail) + foundIndex : foundIndex - preservedTail; uint8_t localBuff[localBuffSize + 1]; memclr(&localBuff, localBuffSize + 1); circularCopy(rxBuff, rxBuffSize, localBuff, preservedTail, foundIndex); //plus one for the character we listened for... rxBuffTail = (rxBuffTail + localBuffSize + 1) % rxBuffSize; unlockRx(); return ManagedString((char *)localBuff, localBuffSize); } unlockRx(); return ManagedString(); }
/** * Transmits the given string onto the broadcast radio. * * This is a synchronous call that will wait until the transmission of the packet * has completed before returning. * * @param data The packet contents to transmit. * * @return MICROBIT_OK on success, or MICROBIT_INVALID_PARAMETER if the buffer is invalid, * or the number of bytes to transmit is greater than `MICROBIT_RADIO_MAX_PACKET_SIZE + MICROBIT_RADIO_HEADER_SIZE`. */ int MicroBitRadioDatagram::send(ManagedString data) { return send((uint8_t *)data.toCharArray(), data.length()); }