For a long time I have wanted a way to be able to run code on a raspberry pi and a esp32, and have the two programs talk to each other. Recently, I found a way to do that using sockets.
Sockets can be used for a lot of things, so to be more specific, today we will look at how to use wifi + sockets to connect two python programs together.
Hardware
Before we dive into the code, its important to mention the hardware required.
The Computer
I am using a raspberry pi 4B, but you can use any computer that runs python and has internet access. In fact when I started testing, I was using my laptop for both the RPI and ESP32 sides.
The Other Computer
I am using an upgraded esp32 with more flash and psram, but the code is not specific to my board. It should run on any esp32 that has micropython installed.
You don’t even have to use an esp32, you could use any other computer or microcontroller that has python and WIFI. You don’t even need python, you should be able to use c++ instead. That being said the esp32 code is hardware specific, so you will need to create or modify your own code, if you use a different computer.
The RPI Code
We will start with the raspberry pi’s code because its simpler.
import socket
s = socket.socket()
s.bind(('',100))
s.listen(5)
c,a = s.accept()
while True:
c.recv(5)
print('sending')
c.send(b'hello')
First we import the socket module, by default it should be installed with python.
import socket
Then we create a new socket, which we will call s.
s = socket.socket()
Then we need to bind our socket, which essentially turns it into a server. When you bind a socket you have to pass it a tuple, as a reminder tuples are like lists and they have the format (data1, data2, data3, …). The tuple we give it has two values Address and Port.
The Address can be an IP address,the host name of your computer, or some other connection to your computer. If you give ” for your address, the socket will manage all incoming traffic to your computer on its Port.
The Port number allows you to run multiple sockets at the same time. Its almost as if you are naming a socket. For this program we will use port 100.
If you choose to use a different port, it might be a good idea to research the meaning of port numbers for example if you used port 80 instead of 100, you might temporarily lose access to the internet on your computer .
I have rambled on long enough, here is the code we use to bind our socket to Address: ” and Port:100.
s.bind(('',100))
Then, we will tell the socket how many clients it can except at the same time. For this code, we will use 5, but there is really no point to that number, since the code will actually only be able to handle one client.
s.listen(5)
After that we tell the socket to wait until somebody connects to it. When somebody eventually connects to our socket, it will give us two new variables c and a. c stands for connection and a stands for address. For now we only care about c.
c,a = s.accept()
The Loop
Now the program will enter an infinite loop.
while True:
In that loop, we will wait for a message from the esp32 that is 5 characters long or shorter.
c.recv(5)
Then, we will print ‘sending’ to the terminal.
print('sending')
After that, we send hello back to the esp32.
c.send(b'hello')
The ESP32
Here is the esp32 code.
import network
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect('MYChannel','')
import time
while not wifi.isconnected():
time.sleep(0.1)
import socket
new_s = socket.socket()
new_s.connect(('Your IP Address',100))
def send(data):
data = str(data).encode()
left = len(data)
while left:
left -= new_s.send(data)
import select
def get():
result,_,_ = select.select([new_s],[],[])
if result:
print(result)
res = new_s.recv(5)
print(res)
new_s.send('hello')
time.sleep(0.5)
send(b'start')
while True:
get()
time.sleep(0.1)
The Network Connection
For our sockets to work we need an internet connection. I turned my raspberry pi into a WIFI hotspot or more accurately a access point, and gave it the name MYChannel and then had my esp32 connect to it.
On the raspberry pi once you get the connection started, you will need to run the command hostname -I this should return the IP address of it. We will need it latter. If you get more than one IP address, you might have to try each of them individually.
On the ESP32, I run the following code to connect to my raspberry pi access point.
import network
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect('MYChannel','')
import time
while not wifi.isconnected():
time.sleep(0.1)
The Socket
Once you find some way to get your esp32 and raspberry pi on the same internet connection, we can move on to the socket code.
First we are going to import the socket module and create a new socket like we did earlier.
import socket
new_s = socket.socket()
Then, we will connect to the raspberry pi on port 100. You will also need to add your raspberry pi’s IP address.
new_s.connect(('Your IP Address',100))
The socket send and recv functions aren’t perfect. Sometimes you might try to send or receive some data and its not possible. Because of this, it’s best if you run a little extra code to guarantee everything goes as expected.
Below is how you can make a more reliable send function.
def send(data):
data = str(data).encode()
left = len(data)
while left:
left -= new_s.send(data)
The next function will use the select module, so we need to import it.
import select
This function was supposed to be a more reliable receive function, but I kind of went over board and its not really a receive function. Its more of a respond function.
def get():
The following code uses the select module to tell if our socket has any data to read.
result,_,_ = select.select([new_s],[],[])
if result:
If there is something to read, the code will print the result variable.
print(result)
Then, it will ready 5 bytes of data from the esp32.
res = new_s.recv(5)
After that, it prints the data it received.
print(res)
And lastly it will send the text ‘hello’, and wait for half a second.
new_s.send('hello')
time.sleep(0.5)
We now use the fancy send function to send the text ‘start’.
send(b'start')
The last code we need to run is an infinite loop that repeats every 0.1 seconds and runs our get function.
while True:
get()
time.sleep(0.1)
The Close Function
The programs above have one vital thing missing from them. When your are done with a socket, you are supposed to call the close function on them like this.
YourSocketName.close()
The two programs above are infinite loops and never stop, so we never used the close function in the code and this can cause problems. For instance if you run the raspberry pi code, stop the program, and then try to run it again you will get errors, because the old program locked the socket.
To fix this, you will need to restart your pi. There are probably other ways to fix this, but that is the only way I now how to.
If you like playing around with esp32s and python, you will probably like LVGL. One of my favorite projects with LVGL is Building a Timer App with Micropython LVGL.