diff --git a/cores/esp8266/HardwareSerial.cpp b/cores/esp8266/HardwareSerial.cpp index 416aa73c43..ba3d622bb9 100644 --- a/cores/esp8266/HardwareSerial.cpp +++ b/cores/esp8266/HardwareSerial.cpp @@ -108,6 +108,30 @@ void HardwareSerial::flush() delayMicroseconds(11000000 / uart_get_baudrate(_uart) + 1); } +void HardwareSerial::startDetectBaudrate() +{ + uart_start_detect_baudrate(_uart_nr); +} + +unsigned long HardwareSerial::testBaudrate() +{ + return uart_detect_baudrate(_uart_nr); +} + +unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis) +{ + time_t startMillis = millis(); + unsigned long detectedBaudrate; + while ((time_t) millis() - startMillis < timeoutMillis) { + if ((detectedBaudrate = testBaudrate())) { + break; + } + yield(); + delay(100); + } + return detectedBaudrate; +} + #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) HardwareSerial Serial(UART0); #endif diff --git a/cores/esp8266/HardwareSerial.h b/cores/esp8266/HardwareSerial.h index 0333d4ca87..cac9d35509 100644 --- a/cores/esp8266/HardwareSerial.h +++ b/cores/esp8266/HardwareSerial.h @@ -184,6 +184,12 @@ class HardwareSerial: public Stream return uart_has_overrun(_uart); } + void startDetectBaudrate(); + + unsigned long testBaudrate(); + + unsigned long detectBaudrate(time_t timeoutMillis); + protected: int _uart_nr; uart_t* _uart = nullptr; diff --git a/cores/esp8266/uart.c b/cores/esp8266/uart.c index 47814f7a9d..02960f4c90 100644 --- a/cores/esp8266/uart.c +++ b/cores/esp8266/uart.c @@ -44,6 +44,7 @@ #include "uart.h" #include "esp8266_peri.h" #include "user_interface.h" +#include "uart_register.h" const char overrun_str [] ICACHE_RODATA_ATTR STORE_ATTR = "uart input full!\r\n"; static int s_uart_debug_nr = UART0; @@ -739,3 +740,49 @@ uart_get_debug() { return s_uart_debug_nr; } + +/* +To start detection of baud rate with the UART the UART_AUTOBAUD_EN bit needs to be cleared and set. The ROM function uart_baudrate_detect() does this only once, so on a next call the UartDev.rcv_state is not equal to BAUD_RATE_DET. Instead of poking around in the UartDev struct with unknown effect, the UART_AUTOBAUD_EN bit is directly triggered by the function uart_detect_baudrate(). +*/ +void +uart_start_detect_baudrate(int uart_nr) +{ + USA(uart_nr) &= ~(UART_GLITCH_FILT << UART_GLITCH_FILT_S | UART_AUTOBAUD_EN); + USA(uart_nr) = 0x08 << UART_GLITCH_FILT_S | UART_AUTOBAUD_EN; +} + +int +uart_detect_baudrate(int uart_nr) +{ + static bool doTrigger = true; + + if (doTrigger) + { + uart_start_detect_baudrate(uart_nr); + doTrigger = false; + } + + int32_t divisor = uart_baudrate_detect(uart_nr, 1); + if (!divisor) { + return 0; + } + + doTrigger = true; // Initialize for a next round + int32_t baudrate = UART_CLK_FREQ / divisor; + + static const int default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400}; + + size_t i; + for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++) // find the nearest real baudrate + { + if (baudrate <= default_rates[i]) + { + if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) { + i--; + } + break; + } + } + + return default_rates[i]; +} diff --git a/cores/esp8266/uart.h b/cores/esp8266/uart.h index 127c5d0ebb..f430d88e1d 100644 --- a/cores/esp8266/uart.h +++ b/cores/esp8266/uart.h @@ -141,6 +141,9 @@ bool uart_has_overrun (uart_t* uart); // returns then clear overrun flag void uart_set_debug(int uart_nr); int uart_get_debug(); +void uart_start_detect_baudrate(int uart_nr); +int uart_detect_baudrate(int uart_nr); + #if defined (__cplusplus) } // extern "C" diff --git a/doc/reference.rst b/doc/reference.rst index 053d75d776..fbff990f93 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -155,6 +155,17 @@ current speed. For example | Note that this implementation is **only for ESP8266 based boards**, and will not works with other Arduino boards. + +To detect an unknown baudrate of data coming into Serial use ``Serial.detectBaudrate(time_t timeoutMillis)``. This method tries to detect the baudrate for a maximum of timeoutMillis ms. It returns zero if no baudrate was detected, or the detected baudrate otherwise. The ``detectBaudrate()`` function may be called before ``Serial.begin()`` is called, because it does not need the receive buffer nor the SerialConfig parameters. + +The uart can not detect other parameters like number of start- or stopbits, number of data bits or parity. + +The detection itself does not change the baudrate, after detection it should be set as usual using ``Serial.begin(detectedBaudrate)``. + +Detection is very fast, it takes only a few incoming bytes. + +SerialDetectBaudrate.ino is a full example of usage. + Progmem ------- diff --git a/libraries/esp8266/examples/SerialDetectBaudrate/SerialDetectBaudrate.ino b/libraries/esp8266/examples/SerialDetectBaudrate/SerialDetectBaudrate.ino new file mode 100644 index 0000000000..61967c691f --- /dev/null +++ b/libraries/esp8266/examples/SerialDetectBaudrate/SerialDetectBaudrate.ino @@ -0,0 +1,34 @@ +#define TIMEOUT (10000UL) // Maximum time to wait for serial activity to start + +void setup() { + // put your setup code here, to run once: + + Serial.begin(115200); + + // Serial.detectBaudrate() may also be called before Serial.begin() + // There must be activity on the serial port for the baudrate to be detected + unsigned long detectedBaudrate = Serial.detectBaudrate(TIMEOUT); + + if (detectedBaudrate) { + Serial.printf("\nDetected baudrate is %lu, switching to that baudrate now...\n", detectedBaudrate); + + // Wait for printf to finish + while (Serial.availableForWrite() != UART_TX_FIFO_SIZE) { + yield(); + } + + // Clear Tx buffer to avoid extra characters being printed + Serial.flush(); + + // After this, any writing to Serial will print gibberish on the serial monitor if the baudrate doesn't match + Serial.begin(detectedBaudrate); + } else { + Serial.println("\nNothing detected"); + } +} + +void loop() { + // put your main code here, to run repeatedly: + +} +