-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Function added to detect baudrate #4978
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Congratulations on getting baud detection to work!
In addition to my review comments, please implement a method HardwareSerial::detectBaudrate() that wraps uart_detect_baudrate().
cores/esp8266/uart.c
Outdated
int | ||
uart_detect_baudrate(int uart_nr) | ||
{ | ||
static bool doTrigger = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doTrigger var is inited to true, then set to false on first run. In order to reset it to true, the detection must actually succeed in a subsequent call.
There is no way to abort the detection process, or reset a detection considered to have failed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Off the top of my head, I see the following possibilities:
- add an argument which resets doTrigger when true. The arg should default to false.
- make doTrigger a global static, and implement a resetter function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was done on purpose, to have only one function to do the detection. If no detection takes place between two subsequent calls to uart_detect_baudrate(), triggering is not necessary: the uart is still in detecting state, and the function returns 0. If (enough) data was presented to the uart, the correct baudrate is returned.
If you prefer a "reset", I suggest to make a void uart_start_detect_baudrate(int uart_nr), that does the triggering, and a int uart_detect_baudrate(uart_nr) that reads the baudrate.
What do you prefer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would have the same effect as the current code: it wouldn't be possible to abort the detection process. Consider the case where you want to retry a number of times, then give up and report an error. Say that the serial lines are multiplexed or shared in some way to communicate with multiple devices. Consider what happens if the detection of the last device fails. The triggering can't be disabled for communication with the devices that were detected correctly.
I suppose my solution 1 above has the same problem, which leaves only solution 2 as viable. It must be possible to clean up the triggering in case of failure.
Unless you can think of another solution?
If you're worried about usage, I think that is addressed by wrapping the whold thing in a HardwareSerial::detectBaudrate() method. In fact, you could even implement a timed loop with yield and max retries in there and avoid having the user do the whole logic inside loop(). Trivial usage :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if I get you right... The uart starts detecting by clearing and setting the UART_AUTOBAUD_EN bit. I do not know if it stops detecting by only clearing the bit. But why do you want to stop the uart detecting? It is not consuming any processor time. Retrying is just: call uart_detect_baudrate() again. Giving up is just stop calling that function. The detection of the baudrate does not change it. In the case of multiple devices, if you want to detect some device D after another device B was detected uart_detect_baudrate() will return the rate of device B and reinit. If you don't need the device B baudrate, just call uart_detect_baudrate once, ignore the return value, and start detecting in a loop until device D comes up. Am I not getting you???
I will implement a hard restart, in uart.h and uart.c, just in case.
About your last suggestion, that would block the processor in waiting for the uart. What about two wrappers? A non blocking detectBaudrate() and a blocking detectBaudrate(time_t timeoutMillis)?
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}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are all of these rates valid for both 80MHz and 160MHz CPU speed?
Or rather, has the detection process been tested with both CPU speeds and different baudrates?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These baud rates are common rates. Yes it has been tested with both 80MHz and 160 MHz, and with two devices, one at 9600 baud and one at 115200 baud. I did not test changing the crystal frequency, because I do not know the purpose of changing the crystal frequency.
One last thing: please add a minimalistic example sketch to show usage. |
Thanks for the congrats @devyte! There is already a minimalistic example at the top of this PR, is that what you're looking for? Because there is no /examples subdirectory in Arduino/cores/esp8266, I did not include the example in the code. |
Yes, more or less, although I would rather the example be done with the HardwareSerial wrapper method instead of the functions in uart.h. |
I will add a HardwareSerial example at the suggested location. |
…areSerial and an example usage SerialDetectBaudrate.ino
cores/esp8266/HardwareSerial.h
Outdated
@@ -184,6 +184,12 @@ class HardwareSerial: public Stream | |||
return uart_has_overrun(_uart); | |||
} | |||
|
|||
void startDetectBaudrate(); | |||
|
|||
unsigned long detectBaudrate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be renamed to detectBaudrateOnce() or similar. the usage semantic is very different from the overload with the timeout.
Personally, I would prefer to not expose this method and just make it private. I can't think of a usage example where some other time-critical process has to be ongoing at the same time as baudrate detection.
If you really need this to be public, please add another example with its usage from the loop().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will rename it to testBaudrate().
I can imagine that you have some kind of hot swappable serial device, and want to test every now and then if something has been connected. I prefer to keep it public.
I will add an example as a comment on this PR, not in the examples directory which seems overkill for me
@Jeroen88 some more comments: |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I missed the single line statements in my previous review.
Other than that, it looks ok to me to merge. If you fix the formatting today, this will make it into 2.4.2.
cores/esp8266/HardwareSerial.cpp
Outdated
time_t startMillis = millis(); | ||
unsigned long detectedBaudrate; | ||
while ((time_t) millis() - startMillis < timeoutMillis) { | ||
if ((detectedBaudrate = testBaudrate())) break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
break; on a new line please
cores/esp8266/uart.c
Outdated
} | ||
|
||
int32_t divisor = uart_baudrate_detect(uart_nr, 1); | ||
if (!divisor) return 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return 0; on a new line please
cores/esp8266/uart.c
Outdated
{ | ||
if (baudrate <= default_rates[i]) | ||
{ | ||
if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) i--; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i-- on a new line please
Done! Would be nice if it is merged into 2.4.2 :) |
Functions uart_detect_baudrate(uart_nr), uart_start_detect_baudrate(int uart_nr), wrappers for HardwareSerial and an example usage SerialDetectBaudrate.ino added.
These function detect the baudrate of the Serial port automatically. Of course there must be incoming data on the port. The function HardwareSerial::detectBaudrate() without a parameter returns inmediately. It should be called repeatedly until a non-zero value, the detected baudrate, is returned. The function HardwareSerial::detectBaudrate(time_t timeoutMillis) tries to detect the baudrate for maximum timeoutMillis ms and then returns zero if no baudrate was detected, or the detected baudrate otherwise. The detectBaudrate() functions may be called before Serial.begin() is called, they do not need the Rx 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 does not change the baudrate, after detection it should be set using Serial.begin(detectedBaudrate).
Detection is very fast, it takes only a few incoming bytes.
Example of usage (same as SerialDetectBaudrate.ino):
If you do not want to wait for a device responding on the Serial you can also use: