ESP8266 - Quiet, UART!

13 Nov 2016

This is the sixth in a series of posts regarding my experiences with the ESP8266 microcontroller. Previous posts on the ESP8266 can be found at the bottom of this page.

One of my earlier posts introduced the serial connection, which is controlled by a UART (Universal Asynchonous Receiver Transmitter). Once the appropriate steps are followed, it’s actually quite easy to use the serial connection from an ESP8266.

Well, mostly… The ESP8266 has an annoying habit of writing start-up messages out the serial port at an entirely non-standard speed of 76,923 bps (bits per second). These boot messages can be very helpful for debugging purposes, as they can tell you why it is starting up, which can include information such as low power (e.g. if you’re trying to power it directly from a USB<–>serial converter, which often doesn’t work) or a watchdog timer expiry.

This information, while helpful, can cause problems. Some circuits will hook the UART directly up to a radio, and you don’t want to be sending the start-up messages over a radio - especially if you’re putting your own packet frame around it! I’ve once worked on serial communications with some incredibly expensive commercial industrial equipment that would crash if you sent something that wasn’t in exactly the right format that it’s expecting. And once it crashed, one of the plant engineers had to be sent through a somewhat hazardous area to go and physically reset it. I always felt bad when a bug in my code caused a poor fellow engineer to be sent out, especially if I’d crashed it before they’d even got back from resetting it the last time!

So how do we get the UART to be quiet on start-up? The short answer is that we can’t - it’s part of the boot loader program built into the chip itself which runs before the ESP8266 begins to run the code we upload to it. For a while, I thought that this meant that the ESP8266 is not suitable for such installations, but after much reading of various posts and blogs from around the Internet, and some trial and error, I now know of two work-arounds. Sadly, both of these work-arounds require you to ignore the Tx (Transmit) pin on the microcontroller, and instead use another pin for serial communications.

There are two UARTs on the ESP8266, imaginatively called UART 0 and UART 1. As described in my first blog post on the UARTs, UART 1 only has a transmit line available, so we can’t avoid the problem simply by using UART 1 instead of UART 0. So if we can’t use the Tx pin from UART 0, and there is no Rx pin in UART 1, how can we possibly solve the problem? Here are two work-arounds that I know of:

  1. Move the UART’s Rx (receive) and Tx (transmit) lines to other pins on the ESP8266
  2. Use UART 1’s Tx pin, and UART 0’s Rx pin

I’ll explain each of these in more detail below.

Moving the UART Rx and Tx pins

The main UART’s pins pass through a multiplexer or MUX. This MUX is able to reassign the functions of the scarce output pins on the ESP8266. For this work-around, we use the MUX to move the Rx and Tx functions to other pins by swapping them with the CTS (Clear To Send) and RTS (Ready To Send) pins. This swapping is performed by a call to the “system_uart_swap” function.

Before effects of calling “system_uart_swap” on the pins are as follows:

Pin Pin Name Before Swap After Swap
13 GPIO13 CTS Rx
10 GPIO15 RTS Tx
15 Tx Tx RTS
16 Rx Rx CTS

The important thing to note here is that this swap only takes effect after your code has started running, and asked it to swap. This means that at start-up time, the original UART Tx pin will still get the boot loader debug output. So this pin basically becomes useless, and you need to ignore it in your circuit, and just hook up your device to the new Rx/Tx pins.

To set this up in your code, something like the following porting of the “user_init” function is needed:

1
2
3
4
5
6
7
8
9
void user_init(void) {
    // Initialise the serial port.
    uart_init(BIT_RATE_19200, BIT_RATE_19200);

    // Swap the UART over, to suppress it's output.
    system_uart_swap();

    ...
}

Using UART 1’s Tx pin

This work-around is to use the working Rx pin of UART 0, along with the Tx pin of UART 1 which doesn’t send out the messages at start-up. Combined, you end up with one good bi-directional serial connection. You can continue to use UART 0’s Tx pin if you want a separate output for debugging purposes, as “os_printf” will continue to use UART 0’s Tx pin by default. It is rather unconventional to mix you communications across UARTs like this, the normal and recommended way is to use the first work-around, but it’s always nice to have other options for completeness, especially if you can’t afford to lose GPIO12/13. Make sure that you set the UART rates the same for the two UARTs, or weird things will likely happen!

Unfortunately, in the default “uart.h” supplied in the SDK, there is no simple equivalent of “uart0_tx_buffer”, to send a whole byte array at once, instead you have to use the “uart_tx_one_char” function, and call it from your own loop. It’s not too much worse, and that function can be easily swapped between UARTs just by changing the first parameter. Tip: Use the “UART0” and “UART1” #defines in calls to this function, rather than just 0/1, as it makes things clearer. The sample code below shows how you can send to UART 1.

Sample code

I have created a very simple program to demonstrate the above two options. What it does is to swap the pins around for UART 0, and transmit messages to both UART 0 and UART 1 every second.

The sending code in the 1 second call-back function is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
 * Call-back for when the message timer expires.
 */
LOCAL void ICACHE_FLASH_ATTR message_cb(void *arg) {
    // Write a message to UART 0.
    uint8_t message[] = {'T', 'i', 'm', 'e', 'r', ' ', 'e', 'x', 'p', 'i', 'r', 'e', 'd', '.', '\n'};
    uart0_tx_buffer(message, 15);

    // Write a message to UART 1.
    uart_tx_one_char(UART1, 'U');
    uart_tx_one_char(UART1, 'A');
    uart_tx_one_char(UART1, 'R');
    uart_tx_one_char(UART1, 'T');
    uart_tx_one_char(UART1, '-');
    uart_tx_one_char(UART1, '1');
    uart_tx_one_char(UART1, ' ');
    uart_tx_one_char(UART1, 'E');
    uart_tx_one_char(UART1, 'x');
    uart_tx_one_char(UART1, 'p');
    uart_tx_one_char(UART1, 'i');
    uart_tx_one_char(UART1, 'r');
    uart_tx_one_char(UART1, 'y');
    uart_tx_one_char(UART1, '.');
    uart_tx_one_char(UART1, '\n');
    
    os_printf("Timer expiry - %d.\n", ++message_count);
}

If you’re just looking at the above fragment, or you haven’t read my remote debug blog yet, you may be surprised to see the “os_printf” call at the end, thinking that it will also be sending a string over UART 0. In the GitHub link below, you an see that I have diverted all “os_printf” output to be sent via UDP to another system, so that I can debug my code without having to be physically connected to the ESP8266.

The full source code for the “uart-suppression” demonstration project is available in GitHub here.

Previous ESP8266 blogs

comments powered by Disqus