Friday, August 16, 2024

Let two microcontrollers talk over Uart

For an index to all my stories click this text.

In a previous story I demonstrated how you could connect an ESP32 to a Raspberry Pi Pico and send data from the ESP to the Pico. This was not limited to communication between an ESP32 and A Raspberry Pico but could be used by most microcontrollers. The only thing you have to do is to adapt the pin numbers of the connected IO pins in the MicroPython programs.
You can find that story here : http://lucstechblog.blogspot.com/2024/08/let-microcontrollers-talk-to-eachother.html

As this works fine there are limitations: You can only send a few dedicated signals from one micro controller to the other.

If you want to have a full fledged communication between two micro's you'll need a different approach.

UART

The word UART is an abbrevation of universal asynchronous receiver-transmitter. It is a serial protocol develloped for communication between different devices. And that is just what we are going to do.

UART can not simply transfer text. It transfers binary data. Luckily there is a lot of intelligence build into MicroPython that makes life a lot easier for us.

To send data over an UART connection you can simply use:
uart.write("This is my text")

The second microcontroller receives this as a binary stream. So we have to decode it into something we can understand. That is done as follows:

        serinput = uart0.read(20)
        if serinput != None:
          received_data = serinput.decode("utf-8")

The number 20 in uart0.read(20) defines the length of the UART buffer. If the amount of information received is larger as 20 characters it wil be divided into multiple parts.

For safety in the following simple examples a buffer size of 20 is enough.

The breadboard setup

For this experiment I going to connect UART2 of the ESP32 to UART0 of the Raspberry Pi Pico. Both microcontrollers have multiple UART ports. You may use different ports. just make sure to adjust the pins accordingly.

For testing the communication I connected a pusbutton to the ESP32 and a led to the Raspberry Pi Pico.


The pushbutton is with a 10K pull-up resistor connected to IO pin 34 of the ESP32.
The led is connected with a 220 ohm current delimiting resistor to IO pin 15 of the Raspberry Pi Pico.

The UART2 pins of the ESP32 are IO Pin 2 and 15. IO Pin 2 is TX (transmit) and IO Pin 15 is RX (receive).
The UART0 pins of the Raspberry Pi pico are IO pin ) and 1. Pin 0 is TX (transmit) and pin 1 is RX (receive).

To have the two microcontrollers talk to eachother you need to connect the Transmit Pin from the ESP32 to the Receive Pin of the Raspberry Pi Pico and the otherway round. That is what I did in this breadboard setup.

The ESP32 TX pin (2) is connected to the Raspberry Pi Pico's RX pin (1)
The ESP32 RX pin (15) is connected to the Raspberry Pi Pico's TX pin (0)

The ESP32 MicroPython program

The ESP32 is programmed to send the text "led on" to the Raspberry Pi Pico when the button is pressed. here is the fullo code:

'''
ESP32 program to send data over uart
'''

from machine import UART, Pin
import time

button = Pin(34, Pin.IN)

uart = UART(2, baudrate=9600, tx=2, rx=15)

status = 0

while True:
      if (button.value() == 0) and status == 0:
          uart.write("led on")
          print("The led goes ON")
          status = 1
      if (button.value() == 1) and status == 1:
          uart.write("led off")
          print("The led is OFF")
          status = 0
      time.sleep(.3)

This code mostly speaks for itself but I will pick out a few important lines.

uart = UART(2, baudrate=9600, tx=2, rx=15)

This line creates an instance with the name uart. UART 2 is chosen, the baudrate is set to 9600 baud and the transmit (tx) pin is GPIO 2 and the receive pin is set to GPIO 15. Make sure the baudrate in the ESP32 and the Pico program are set to the same. Higher baudrates are of course allowed.

          uart.write("led on")

This is all it takes to send the text "led on" to the Raspberry Pi pico.

The status variable makes sure the the command "led on" or "led off" is only send once.
First time status is zero. And when you press the button the button.value = 0 and status is 0 so "led on" is send. The next time in the loop (if the button is still pressed) status is 1 and button.value is 0 so nothing happens.
The same procedure is copied for when the button is not pressed.
This makes sure that "led on" or "led off" is only send once. So the Uart communication is limited which gives us lots of time to do other things in between.


The Raspberry Pi Pico MicroPython program

The Raspberry Pi Pico is programmed to receive data from the ESP32. When the text "led on" is received the attached led will be set on. When the text "led off" is received the led is set off again.

'''
program to receive data over uart
from raspberry pico
'''

from machine import UART, Pin
import time

led = Pin(15, Pin.OUT)

uart0 = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))

while True:
    if uart0.any():
        print("Serial data available")
        serinput = uart0.read(200)
        if serinput != None:
          print(serinput.decode("utf-8"))
          if serinput.decode("utf-8") == "led on":
            led.on()
          if serinput.decode("utf-8") == "led off":
            led.off()
        time.sleep(.1)

Ok this needs some more explanation.

    if uart0.any():

This line tests if there is any data received on the UART pins. If not the whole routine is skipped. This gives us time to do other things in between when needed.

        serinput = uart0.read(200)

A serial buffer is created of 200 characters.
If more characters are received only the first 200 characters are remembered. You can expand the buffer or make it smaller adjusting to your needs.
For this small example a buffer of 20 caharcters would laready be too much.

        if serinput != None:

We skip the rest of the routine if None is received. That means the buffer is empty.

          print(serinput.decode("utf-8"))
          if serinput.decode("utf-8") == "led on":


The text in the buffer is printed in Thonny's shell for our convenience.
The received information that is contained in the buffer and stored in the serinput variable is in binary form. So it is decoded to human readable information by the serinput.decode("utf-8") command.

That's all there is to it.

Testing.

Just press the button and after a short delay the led wil go on. Keep the button pressed and the led wil stay on. Release the button and the led will go off.

You can see the status of the led printed in Thonny's shell.


Best is to open multiple instances of Thonny so you can look at the shell from the ESP32 and the shell of the Raspberry Pi Pico at the same time.


Experiment succeeded.

This gives lots of opportunities to expand and experiment.
It is easy to expand this to have two way communication so that the Pico can send data back to the ESP.
Both Micro's now can have a lot of sensors and actuators that can be controlled from the other micro.

Till next time
have fun


Luc Volders