Monday, July 18, 2022

Serial Multi-drop on an ESP8266

My servo controller is being moved from a PIC to an ESP module. Each servo motor has its own controller connected to a bus. The bus is a TTL serial line. In order to connect multiple controllers on the same bus, the transmit line of each slave controller has to be floated. They are pulled up at the host and a slave controller enables it only when it is ready to respond to a host request. This ensures that only one slave is transmitting at any given time.

I am, for now, using the Arduino framework. This puts a couple of layers between my code and the hardware. This blog looks at how to implement a floating transmit line on an ESP. To test the prototype I have the line set to half the power supply voltage of 3.3V using a couple of resistors. This helps me check if the output is floating or not. In the trace below, you can see a floating line starting to transmit a response.

The communication between the host and individual controllers is half duplex. The host requests for some information or sends some information. The slave responds with the information or acknowledges it. The slave never transmits without a request and only one slave transmits at any given time. This means the slave lets the transmit line float, asserting it only when it needs to transmit something. This is possible on the PIC by switching the transmit line to an input. But wuth a few layers between my code and the hardware in the Arduino framework, this is a problem.

I did dig through the code to track down functions on the ESP that are available. The Serial clss isused for all serial port operations. For the esp9266, this is further implemented in HardwareSerial.h and HardwareSerial.cpp. This, in turn, uses functions that start with uart_*. These are coded in the file uart.h and uart.cpp. Most of the code there is to manipulate the software FIFO buffers. But there are some operations that deal with the ESP8266 hardware. Most of these are macros like USC0(). These are references to ESP8266 UART and other registers and are defined in esp8266_peri.h. Some basic GPIO control is also in core_esp8266_wiring_digital.cpp.

After fossicking through all that code, I came to a couple of conclusions. Firstly, there seems to be no simple way to tell if the transmit is finished. This may be a limitation of the ESP8266 hardware itself. There is a flag when the internal buffer is empty but not when the transmit is complete. Secondly, there is a receive only mode for he Serial object. This can probably be used to float the transmit line.

The Serial.begin() can be safely called more than once without calling Serial.end(). So to out the transmit line in float mode, set the Serial to receive only mode and make the transmit pin an input. You also need to pass a parameter when changing the mode - as per the source this is the number of data bits.

    Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_RX_ONLY);
    pinMode(TX_GPIO, INPUT);
  

To enable it before transmitting, set Serial back to full mode - the pin is set to output as part of begin().

    Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_FULL);
  

Now we come to the problem of when to float the transmit line when we don't know when it has finished transmitting. The communication is line based. A command is a series of characters terminated by a return. No response is expected from any slave till that return goes out on the line. So the solution is to transmit a response and not float the transmit line. Then continue to listen on the input and the moment a return is detected, float the transmit line, yeilding the transmit to the controller being addressed, even if it is itself.

I put two 12K resistors on the transmit line, tying one to ground and other to +3.3V. This pulls the line to 1.65V as long as the line is floating. Below, we can see the line is floating right up to the moment it starts to transmit. Note that, in this case, the previous command was to a different (non-existent) controller, hence the line was floating.

And as soon as a return is received the line starts to float as you can see below. I am using PlatformIO and the Serial Monitor sends a carriage return (0x0d or \r) and a line feed (0x0a or \n) when you hit return. You can see the \r go out and the line floats. After that the \n is sent out.

We are looking at the transmit line of the controller. The story on the receive line is a bit different. It is driven high throughout and is out of the control of the ESP chip. This is driven by the FTDI chip on the devkit. This does mean that when time comes to daisy chain many of these units, I will have to move away from using dev kits and use bare modules. Ortherwise the CH340s on each devkit will assert the transmit line whether or not it is hooked up on the USB port.

No comments:

Post a Comment