fsdfds
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
MicroBitSerial.h
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 #ifndef MICROBIT_SERIAL_H 00027 #define MICROBIT_SERIAL_H 00028 00029 #include "mbed.h" 00030 #include "ManagedString.h" 00031 00032 #define MICROBIT_SERIAL_DEFAULT_BAUD_RATE 115200 00033 #define MICROBIT_SERIAL_DEFAULT_BUFFER_SIZE 20 00034 00035 #define MICROBIT_SERIAL_EVT_DELIM_MATCH 1 00036 #define MICROBIT_SERIAL_EVT_HEAD_MATCH 2 00037 #define MICROBIT_SERIAL_EVT_RX_FULL 3 00038 00039 #define MICROBIT_SERIAL_RX_IN_USE 1 00040 #define MICROBIT_SERIAL_TX_IN_USE 2 00041 #define MICROBIT_SERIAL_RX_BUFF_INIT 4 00042 #define MICROBIT_SERIAL_TX_BUFF_INIT 8 00043 00044 00045 enum MicroBitSerialMode 00046 { 00047 ASYNC, 00048 SYNC_SPINWAIT, 00049 SYNC_SLEEP 00050 }; 00051 00052 /** 00053 * Class definition for MicroBitSerial. 00054 * 00055 * Represents an instance of RawSerial which accepts micro:bit specific data types. 00056 */ 00057 class MicroBitSerial : public RawSerial 00058 { 00059 00060 //holds that state of the mutex locks for all MicroBitSerial instances. 00061 static uint8_t status; 00062 00063 //holds the state of the baudrate for all MicroBitSerial instances. 00064 static int baudrate; 00065 00066 //delimeters used for matching on receive. 00067 ManagedString delimeters; 00068 00069 //a variable used when a user calls the eventAfter() method. 00070 int rxBuffHeadMatch; 00071 00072 uint8_t *rxBuff; 00073 uint8_t rxBuffSize; 00074 volatile uint16_t rxBuffHead; 00075 uint16_t rxBuffTail; 00076 00077 00078 uint8_t *txBuff; 00079 uint8_t txBuffSize; 00080 uint16_t txBuffHead; 00081 volatile uint16_t txBuffTail; 00082 00083 /** 00084 * An internal interrupt callback for MicroBitSerial configured for when a 00085 * character is received. 00086 * 00087 * Each time a character is received fill our circular buffer! 00088 */ 00089 void dataReceived(); 00090 00091 /** 00092 * An internal interrupt callback for MicroBitSerial. 00093 * 00094 * Each time the Serial module's buffer is empty, write a character if we have 00095 * characters to write. 00096 */ 00097 void dataWritten(); 00098 00099 /** 00100 * An internal method to configure an interrupt on tx buffer and also 00101 * a best effort copy operation to move bytes from a user buffer to our txBuff 00102 * 00103 * @param string a pointer to the first character of the users' buffer. 00104 * 00105 * @param len the length of the string, and ultimately the maximum number of bytes 00106 * that will be copied dependent on the state of txBuff 00107 * 00108 * @param mode determines whether to configure the current fiber context or not. If 00109 * The mode is SYNC_SPINWAIT, the context will not be configured, otherwise 00110 * no context will be configured. 00111 * 00112 * @return the number of bytes copied into the buffer. 00113 */ 00114 int setTxInterrupt(uint8_t *string, int len, MicroBitSerialMode mode); 00115 00116 /** 00117 * Locks the mutex so that others can't use this serial instance for reception 00118 */ 00119 void lockRx(); 00120 00121 /** 00122 * Locks the mutex so that others can't use this serial instance for transmission 00123 */ 00124 void lockTx(); 00125 00126 /** 00127 * Unlocks the mutex so that others can use this serial instance for reception 00128 */ 00129 void unlockRx(); 00130 00131 /** 00132 * Unlocks the mutex so that others can use this serial instance for transmission 00133 */ 00134 void unlockTx(); 00135 00136 /** 00137 * We do not want to always have our buffers initialised, especially if users to not 00138 * use them. We only bring them up on demand. 00139 */ 00140 int initialiseRx(); 00141 00142 /** 00143 * We do not want to always have our buffers initialised, especially if users to not 00144 * use them. We only bring them up on demand. 00145 */ 00146 int initialiseTx(); 00147 00148 /** 00149 * An internal method that either spin waits if mode is set to SYNC_SPINWAIT 00150 * or puts the fiber to sleep if the mode is set to SYNC_SLEEP 00151 * 00152 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP 00153 */ 00154 void send(MicroBitSerialMode mode); 00155 00156 /** 00157 * Reads a single character from the rxBuff 00158 * 00159 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00160 * gives a different behaviour: 00161 * 00162 * ASYNC - A character is read from the rxBuff if available, if there 00163 * are no characters to be read, a value of zero is returned immediately. 00164 * 00165 * SYNC_SPINWAIT - A character is read from the rxBuff if available, if there 00166 * are no characters to be read, this method will spin 00167 * (lock up the processor) until a character is available. 00168 * 00169 * SYNC_SLEEP - A character is read from the rxBuff if available, if there 00170 * are no characters to be read, the calling fiber sleeps 00171 * until there is a character available. 00172 * 00173 * Defaults to SYNC_SLEEP. 00174 * 00175 * @return a character from the circular buffer, or MICROBIT_NO_DATA is there 00176 * are no characters in the buffer. 00177 */ 00178 int getChar(MicroBitSerialMode mode); 00179 00180 /** 00181 * An internal method that copies values from a circular buffer to a linear buffer. 00182 * 00183 * @param circularBuff a pointer to the source circular buffer 00184 * 00185 * @param circularBuffSize the size of the circular buffer 00186 * 00187 * @param linearBuff a pointer to the destination linear buffer 00188 * 00189 * @param tailPosition the tail position in the circular buffer you want to copy from 00190 * 00191 * @param headPosition the head position in the circular buffer you want to copy to 00192 * 00193 * @note this method assumes that the linear buffer has the appropriate amount of 00194 * memory to contain the copy operation 00195 */ 00196 void circularCopy(uint8_t *circularBuff, uint8_t circularBuffSize, uint8_t *linearBuff, uint16_t tailPosition, uint16_t headPosition); 00197 00198 public: 00199 00200 /** 00201 * Constructor. 00202 * Create an instance of MicroBitSerial 00203 * 00204 * @param tx the Pin to be used for transmission 00205 * 00206 * @param rx the Pin to be used for receiving data 00207 * 00208 * @param rxBufferSize the size of the buffer to be used for receiving bytes 00209 * 00210 * @param txBufferSize the size of the buffer to be used for transmitting bytes 00211 * 00212 * @code 00213 * MicroBitSerial serial(USBTX, USBRX); 00214 * @endcode 00215 * @note the default baud rate is 115200. More API details can be found: 00216 * -https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/api/SerialBase.h 00217 * -https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/api/RawSerial.h 00218 * 00219 * Buffers aren't allocated until the first send or receive respectively. 00220 */ 00221 MicroBitSerial(PinName tx, PinName rx, uint8_t rxBufferSize = MICROBIT_SERIAL_DEFAULT_BUFFER_SIZE, uint8_t txBufferSize = MICROBIT_SERIAL_DEFAULT_BUFFER_SIZE); 00222 00223 /** 00224 * Sends a single character over the serial line. 00225 * 00226 * @param c the character to send 00227 * 00228 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00229 * gives a different behaviour: 00230 * 00231 * ASYNC - the character is copied into the txBuff and returns immediately. 00232 * 00233 * SYNC_SPINWAIT - the character is copied into the txBuff and this method 00234 * will spin (lock up the processor) until the character has 00235 * been sent. 00236 * 00237 * SYNC_SLEEP - the character is copied into the txBuff and the fiber sleeps 00238 * until the character has been sent. This allows other fibers 00239 * to continue execution. 00240 * 00241 * Defaults to SYNC_SLEEP. 00242 * 00243 * @return the number of bytes written, or MICROBIT_SERIAL_IN_USE if another fiber 00244 * is using the serial instance for transmission. 00245 */ 00246 int sendChar(char c, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE); 00247 00248 /** 00249 * Sends a ManagedString over the serial line. 00250 * 00251 * @param s the string to send 00252 * 00253 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00254 * gives a different behaviour: 00255 * 00256 * ASYNC - bytes are copied into the txBuff and returns immediately. 00257 * 00258 * SYNC_SPINWAIT - bytes are copied into the txBuff and this method 00259 * will spin (lock up the processor) until all bytes 00260 * have been sent. 00261 * 00262 * SYNC_SLEEP - bytes are copied into the txBuff and the fiber sleeps 00263 * until all bytes have been sent. This allows other fibers 00264 * to continue execution. 00265 * 00266 * Defaults to SYNC_SLEEP. 00267 * 00268 * @return the number of bytes written, MICROBIT_SERIAL_IN_USE if another fiber 00269 * is using the serial instance for transmission, MICROBIT_INVALID_PARAMETER 00270 * if buffer is invalid, or the given bufferLen is <= 0. 00271 */ 00272 int send(ManagedString s, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE); 00273 00274 /** 00275 * Sends a buffer of known length over the serial line. 00276 * 00277 * @param buffer a pointer to the first character of the buffer 00278 * 00279 * @param len the number of bytes that are safely available to read. 00280 * 00281 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00282 * gives a different behaviour: 00283 * 00284 * ASYNC - bytes are copied into the txBuff and returns immediately. 00285 * 00286 * SYNC_SPINWAIT - bytes are copied into the txBuff and this method 00287 * will spin (lock up the processor) until all bytes 00288 * have been sent. 00289 * 00290 * SYNC_SLEEP - bytes are copied into the txBuff and the fiber sleeps 00291 * until all bytes have been sent. This allows other fibers 00292 * to continue execution. 00293 * 00294 * Defaults to SYNC_SLEEP. 00295 * 00296 * @return the number of bytes written, MICROBIT_SERIAL_IN_USE if another fiber 00297 * is using the serial instance for transmission, MICROBIT_INVALID_PARAMETER 00298 * if buffer is invalid, or the given bufferLen is <= 0. 00299 */ 00300 int send(uint8_t *buffer, int bufferLen, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE); 00301 00302 /** 00303 * Reads a single character from the rxBuff 00304 * 00305 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00306 * gives a different behaviour: 00307 * 00308 * ASYNC - A character is read from the rxBuff if available, if there 00309 * are no characters to be read, a value of MICROBIT_NO_DATA is returned immediately. 00310 * 00311 * SYNC_SPINWAIT - A character is read from the rxBuff if available, if there 00312 * are no characters to be read, this method will spin 00313 * (lock up the processor) until a character is available. 00314 * 00315 * SYNC_SLEEP - A character is read from the rxBuff if available, if there 00316 * are no characters to be read, the calling fiber sleeps 00317 * until there is a character available. 00318 * 00319 * Defaults to SYNC_SLEEP. 00320 * 00321 * @return a character, MICROBIT_SERIAL_IN_USE if another fiber is using the serial instance for reception, 00322 * MICROBIT_NO_RESOURCES if buffer allocation did not complete successfully, or MICROBIT_NO_DATA if 00323 * the rx buffer is empty and the mode given is ASYNC. 00324 */ 00325 int read(MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE); 00326 00327 /** 00328 * Reads multiple characters from the rxBuff and returns them as a ManagedString 00329 * 00330 * @param size the number of characters to read. 00331 * 00332 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00333 * gives a different behaviour: 00334 * 00335 * ASYNC - If the desired number of characters are available, this will return 00336 * a ManagedString with the expected size. Otherwise, it will read however 00337 * many characters there are available. 00338 * 00339 * SYNC_SPINWAIT - If the desired number of characters are available, this will return 00340 * a ManagedString with the expected size. Otherwise, this method will spin 00341 * (lock up the processor) until the desired number of characters have been read. 00342 * 00343 * SYNC_SLEEP - If the desired number of characters are available, this will return 00344 * a ManagedString with the expected size. Otherwise, the calling fiber sleeps 00345 * until the desired number of characters have been read. 00346 * 00347 * Defaults to SYNC_SLEEP. 00348 * 00349 * @return A ManagedString, or an empty ManagedString if an error was encountered during the read. 00350 */ 00351 ManagedString read(int size, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE); 00352 00353 /** 00354 * Reads multiple characters from the rxBuff and fills a user buffer. 00355 * 00356 * @param buffer a pointer to a user allocated buffer. 00357 * 00358 * @param bufferLen the amount of data that can be safely stored 00359 * 00360 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00361 * gives a different behaviour: 00362 * 00363 * ASYNC - If the desired number of characters are available, this will fill 00364 * the given buffer. Otherwise, it will fill the buffer with however 00365 * many characters there are available. 00366 * 00367 * SYNC_SPINWAIT - If the desired number of characters are available, this will fill 00368 * the given buffer. Otherwise, this method will spin (lock up the processor) 00369 * and fill the buffer until the desired number of characters have been read. 00370 * 00371 * SYNC_SLEEP - If the desired number of characters are available, this will fill 00372 * the given buffer. Otherwise, the calling fiber sleeps 00373 * until the desired number of characters have been read. 00374 * 00375 * Defaults to SYNC_SLEEP. 00376 * 00377 * @return the number of characters read, or MICROBIT_SERIAL_IN_USE if another fiber 00378 * is using the instance for receiving. 00379 */ 00380 int read(uint8_t *buffer, int bufferLen, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE); 00381 00382 /** 00383 * Reads until one of the delimeters matches a character in the rxBuff 00384 * 00385 * @param delimeters a ManagedString containing a sequence of delimeter characters e.g. ManagedString("\r\n") 00386 * 00387 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00388 * gives a different behaviour: 00389 * 00390 * ASYNC - If one of the delimeters matches a character already in the rxBuff 00391 * this method will return a ManagedString up to the delimeter. 00392 * Otherwise, it will return an Empty ManagedString. 00393 * 00394 * SYNC_SPINWAIT - If one of the delimeters matches a character already in the rxBuff 00395 * this method will return a ManagedString up to the delimeter. 00396 * Otherwise, this method will spin (lock up the processor) until a 00397 * received character matches one of the delimeters. 00398 * 00399 * SYNC_SLEEP - If one of the delimeters matches a character already in the rxBuff 00400 * this method will return a ManagedString up to the delimeter. 00401 * Otherwise, the calling fiber sleeps until a character matching one 00402 * of the delimeters is seen. 00403 * 00404 * Defaults to SYNC_SLEEP. 00405 * 00406 * @return A ManagedString containing the characters up to a delimeter, or an Empty ManagedString, 00407 * if another fiber is currently using this instance for reception. 00408 * 00409 * @note delimeters are matched on a per byte basis. 00410 */ 00411 ManagedString readUntil(ManagedString delimeters, MicroBitSerialMode mode = MICROBIT_DEFAULT_SERIAL_MODE); 00412 00413 /** 00414 * A wrapper around the inherited method "baud" so we can trap the baud rate 00415 * as it changes and restore it if redirect() is called. 00416 * 00417 * @param baudrate the new baudrate. See: 00418 * - https://github.com/mbedmicro/mbed/blob/master/libraries/mbed/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/serial_api.c 00419 * for permitted baud rates. 00420 * 00421 * @return MICROBIT_INVALID_PARAMETER if baud rate is less than 0, otherwise MICROBIT_OK. 00422 * 00423 * @note the underlying implementation chooses the first allowable rate at or above that requested. 00424 */ 00425 void baud(int baudrate); 00426 00427 /** 00428 * A way of dynamically configuring the serial instance to use pins other than USBTX and USBRX. 00429 * 00430 * @param tx the new transmission pin. 00431 * 00432 * @param rx the new reception pin. 00433 * 00434 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently transmitting or receiving, otherwise MICROBIT_OK. 00435 */ 00436 int redirect(PinName tx, PinName rx); 00437 00438 /** 00439 * Configures an event to be fired after "len" characters. 00440 * 00441 * Will generate an event with the ID: MICROBIT_ID_SERIAL and the value MICROBIT_SERIAL_EVT_HEAD_MATCH. 00442 * 00443 * @param len the number of characters to wait before triggering the event. 00444 * 00445 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00446 * gives a different behaviour: 00447 * 00448 * ASYNC - Will configure the event and return immediately. 00449 * 00450 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00451 * 00452 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00453 * event is received. 00454 * 00455 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK. 00456 */ 00457 int eventAfter(int len, MicroBitSerialMode mode = ASYNC); 00458 00459 /** 00460 * Configures an event to be fired on a match with one of the delimeters. 00461 * 00462 * Will generate an event with the ID: MICROBIT_ID_SERIAL and the value MICROBIT_SERIAL_EVT_DELIM_MATCH. 00463 * 00464 * @param delimeters the characters to match received characters against e.g. ManagedString("\n") 00465 * 00466 * @param mode the selected mode, one of: ASYNC, SYNC_SPINWAIT, SYNC_SLEEP. Each mode 00467 * gives a different behaviour: 00468 * 00469 * ASYNC - Will configure the event and return immediately. 00470 * 00471 * SYNC_SPINWAIT - will return MICROBIT_INVALID_PARAMETER 00472 * 00473 * SYNC_SLEEP - Will configure the event and block the current fiber until the 00474 * event is received. 00475 * 00476 * @return MICROBIT_INVALID_PARAMETER if the mode given is SYNC_SPINWAIT, otherwise MICROBIT_OK. 00477 * 00478 * @note delimeters are matched on a per byte basis. 00479 */ 00480 int eventOn(ManagedString delimeters, MicroBitSerialMode mode = ASYNC); 00481 00482 /** 00483 * Determines whether there is any data waiting in our Rx buffer. 00484 * 00485 * @return 1 if we have space, 0 if we do not. 00486 * 00487 * @note We do not wrap the super's readable() method as we don't want to 00488 * interfere with communities that use manual calls to serial.readable(). 00489 */ 00490 int isReadable(); 00491 00492 /** 00493 * Determines if we have space in our txBuff. 00494 * 00495 * @return 1 if we have space, 0 if we do not. 00496 * 00497 * @note We do not wrap the super's writeable() method as we don't want to 00498 * interfere with communities that use manual calls to serial.writeable(). 00499 */ 00500 int isWriteable(); 00501 00502 /** 00503 * Reconfigures the size of our rxBuff 00504 * 00505 * @param size the new size for our rxBuff 00506 * 00507 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance 00508 * for reception, otherwise MICROBIT_OK. 00509 */ 00510 int setRxBufferSize(uint8_t size); 00511 00512 /** 00513 * Reconfigures the size of our txBuff 00514 * 00515 * @param size the new size for our txBuff 00516 * 00517 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance 00518 * for transmission, otherwise MICROBIT_OK. 00519 */ 00520 int setTxBufferSize(uint8_t size); 00521 00522 /** 00523 * The size of our rx buffer in bytes. 00524 * 00525 * @return the current size of rxBuff in bytes 00526 */ 00527 int getRxBufferSize(); 00528 00529 /** 00530 * The size of our tx buffer in bytes. 00531 * 00532 * @return the current size of txBuff in bytes 00533 */ 00534 int getTxBufferSize(); 00535 00536 /** 00537 * Sets the tail to match the head of our circular buffer for reception, 00538 * effectively clearing the reception buffer. 00539 * 00540 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance 00541 * for reception, otherwise MICROBIT_OK. 00542 */ 00543 int clearRxBuffer(); 00544 00545 /** 00546 * Sets the tail to match the head of our circular buffer for transmission, 00547 * effectively clearing the transmission buffer. 00548 * 00549 * @return MICROBIT_SERIAL_IN_USE if another fiber is currently using this instance 00550 * for transmission, otherwise MICROBIT_OK. 00551 */ 00552 int clearTxBuffer(); 00553 00554 /** 00555 * The number of bytes currently stored in our rx buffer waiting to be digested, 00556 * by the user. 00557 * 00558 * @return The currently buffered number of bytes in our rxBuff. 00559 */ 00560 int rxBufferedSize(); 00561 00562 /** 00563 * The number of bytes currently stored in our tx buffer waiting to be transmitted 00564 * by the hardware. 00565 * 00566 * @return The currently buffered number of bytes in our txBuff. 00567 */ 00568 int txBufferedSize(); 00569 00570 /** 00571 * Determines if the serial bus is currently in use by another fiber for reception. 00572 * 00573 * @return The state of our mutex lock for reception. 00574 * 00575 * @note Only one fiber can call read at a time 00576 */ 00577 int rxInUse(); 00578 00579 /** 00580 * Determines if the serial bus is currently in use by another fiber for transmission. 00581 * 00582 * @return The state of our mutex lock for transmition. 00583 * 00584 * @note Only one fiber can call send at a time 00585 */ 00586 int txInUse(); 00587 00588 /** 00589 * Detaches a previously configured interrupt 00590 * 00591 * @param interruptType one of Serial::RxIrq or Serial::TxIrq 00592 */ 00593 void detach(Serial::IrqType interuptType); 00594 00595 }; 00596 00597 #endif
Generated on Wed Jul 13 2022 00:58:03 by 1.7.2