In this exercise, ESP32 (ESP32-DevKitC V4)/MicroPython play the role of AP, act as socket server. Raspberry Pi connect to ESP32 WiFi network, run Python code to load image, act as client and transmit the image to ESP32 server. The ESP32 server display the image on a 240*240 IPS (ST7789 SPI) LCD. It's is role reversed version of my previous exercise "Raspberry Pi/Python Server send image to ESP32/MicroPython Client via WiFi TCP socket".
protocol:
Client | | Server
(Raspberry Pi/Python) | | (ESP32/MicroPython)
| |
| | Reset
| | Setup AP
| | Setup socket server
(connect to ESP32 WiFi) | |
| |
connect to ESP32 server | | accepted
|<-- ACK ---|
send the 0th line |---------->| display the 0th line
|<-- ACK ---| send ACK
send the 1st line |---------->| display the 1st line
|<-- ACK ---| send ACK
.
.
.
send the 239th line |---------->| display the 239th line
|<-- ACK ---| send ACK
close socket | | close socket
| |
Server side:
(ESP32/MicroPython)
The ESP32 used is a ESP32-DevKitC V4, display is a 240*240 IPS (ST7789 SPI)
LCD. Library setup and connection, refer to former post "ESP32 (ESP32-DevKitC V4)/MicroPython + 240*240 IPS (ST7789 SPI) using
russhughes/st7789py_mpy lib".
upyESP32_ImgServer_AP_20210408a.py, MicroPython code run on ESP32. Save to ESP32 named main.py, such that it can run on power-up without host connected.
from os import uname
from sys import implementation
import machine
import network
import socket
import ubinascii
import utime
import st7789py as st7789
from fonts import vga1_16x32 as font
import ustruct as struct
"""
ST7789 Display ESP32-DevKitC (SPI2)
SCL GPIO18
SDA GPIO23
GPIO19 (miso not used)
ST7789_rst GPIO5
ST7789_dc GPIO4
"""
#ST7789 use SPI(2)
st7789_res = 5
st7789_dc = 4
pin_st7789_res = machine.Pin(st7789_res, machine.Pin.OUT)
pin_st7789_dc = machine.Pin(st7789_dc, machine.Pin.OUT)
disp_width = 240
disp_height = 240
ssid = "ssid"
AP_ssid = "ESP32"
password = "password"
serverIP = '192.168.1.30'
serverPort = 80
print(implementation.name)
print(uname()[3])
print(uname()[4])
print()
#spi2 = machine.SPI(2, baudrate=40000000, polarity=1)
pin_spi2_sck = machine.Pin(18, machine.Pin.OUT)
pin_spi2_mosi = machine.Pin(23, machine.Pin.OUT)
pin_spi2_miso = machine.Pin(19, machine.Pin.IN)
spi2 = machine.SPI(2, sck=pin_spi2_sck, mosi=pin_spi2_mosi, miso=pin_spi2_miso,
baudrate=40000000, polarity=1)
print(spi2)
display = st7789.ST7789(spi2, disp_width, disp_width,
reset=pin_st7789_res,
dc=pin_st7789_dc,
xstart=0, ystart=0, rotation=0)
display.fill(st7789.BLACK)
mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
print("MAC: " + mac)
print()
#init ESP32 as STA
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.disconnect()
utime.sleep(1)
def do_connect():
global wlan
print('connect to network...')
display.fill(st7789.BLACK)
display.text(font, "connect...", 10, 10)
wlan.active(True)
if not wlan.isconnected():
print('...')
wlan.connect(ssid, password)
while not wlan.isconnected():
pass
print()
print('network config:')
print("interface's IP/netmask/gw/DNS addresses")
#print(wlan.ifconfig())
myIP = wlan.ifconfig()[0]
print(myIP)
display.fill(st7789.BLACK)
display.text(font, myIP, 10, 10)
def do_setupAP():
global wlan
print('setup AP...')
display.fill(st7789.BLACK)
display.text(font, "setup AP...", 10, 10)
utime.sleep(1)
ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(essid=AP_ssid, password=password)
while ap.active() == False:
pass
print(ap.active())
print()
print('network config:')
myIP = ap.ifconfig()
print(myIP)
display.fill(st7789.BLACK)
display.text(font, myIP[0], 10, 10)
def do_setupServer():
global wlan
global display
addr = socket.getaddrinfo('0.0.0.0', serverPort)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
print('listening on', addr)
display.text(font, ':'+str(serverPort), 10, 50)
while True:
print('waiting connection...')
cl, addr = s.accept()
cl.settimeout(5)
print('client connected from:', addr)
display.fill(st7789.BLACK)
display.text(font, addr[0], 10, 10)
cl.sendall('ACK')
print('Firt ACK sent')
display.set_window(0, 0, disp_width-1, disp_height-1)
pin_st7789_dc.on()
for j in range(disp_height):
try:
buff = cl.recv(disp_width*3)
#print('recv ok: ' + str(j))
except:
print('except: ' + str(j))
for i in range(disp_width):
offset= i*3
spi2.write(struct.pack(st7789._ENCODE_PIXEL,
(buff[offset] & 0xf8) << 8 |
(buff[offset+1] & 0xfc) << 3 |
buff[offset+2] >> 3))
#print('send ACK : ' + str(j))
cl.sendall(bytes("ACK","utf-8"))
#print('ACK -> : ' + str(j))
utime.sleep(1)
cl.close()
print('socket closed')
#do_connect()
do_setupAP()
try:
do_setupServer()
except:
print('error')
display.text(font, "Error", 10, 200)
finally:
print('wlan.disconnect()')
wlan.disconnect()
print('\n- bye -')
Client Side:
(Raspberry Pi/Python)
Connect Raspberry Pi to ESP32 WiFi network before run the Python code.
Load and send images with fixed resolution 240x240 (match with the display in client side) to server. My former post "min. version of RPi/Python Code to control Camera Module with preview on local HDMI" is prepared for this purpose to capture using Raspberry Pi Camera Module .
pyqTCP_ImgClient_20210408a.py
import sys
from pkg_resources import require
import time
import matplotlib.image as mpimg
import socket
#HOST = '192.168.1.34' # The server's hostname or IP address
HOST = '192.168.4.1'
PORT = 80 # The port used by the server
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QLabel,
QFileDialog, QHBoxLayout, QVBoxLayout)
from PyQt5.QtGui import QPixmap, QImage
print(sys.version)
class AppWindow(QWidget):
camPreviewState = False #not in Preview
fileToUpload = ""
def __init__(self):
super().__init__()
lbSysInfo = QLabel('Python:\n' + sys.version)
vboxInfo = QVBoxLayout()
vboxInfo.addWidget(lbSysInfo)
#setup UI
btnOpenFile = QPushButton("Open File", self)
btnOpenFile.clicked.connect(self.evBtnOpenFileClicked)
self.btnUpload = QPushButton("Upload", self)
self.btnUpload.clicked.connect(self.evBtnUploadClicked)
self.btnUpload.setEnabled(False)
vboxCamControl = QVBoxLayout()
vboxCamControl.addWidget(btnOpenFile)
vboxCamControl.addWidget(self.btnUpload)
vboxCamControl.addStretch()
self.lbImg = QLabel(self)
self.lbImg.resize(240, 240)
self.lbImg.setStyleSheet("border: 1px solid black;")
hboxCam = QHBoxLayout()
hboxCam.addWidget(self.lbImg)
hboxCam.addLayout(vboxCamControl)
self.lbPath = QLabel(self)
vboxMain = QVBoxLayout()
vboxMain.addLayout(vboxInfo)
vboxMain.addLayout(hboxCam)
vboxMain.addWidget(self.lbPath)
vboxMain.addStretch()
self.setLayout(vboxMain)
self.setGeometry(100, 100, 500,400)
self.show()
#wait client response in 3 byte len
def wait_RESP(self, sock):
#sock.settimeout(10)
res = str()
data = sock.recv(4)
return data.decode("utf-8")
def sendImageToServer(self, imgFile):
print(imgFile)
imgArray = mpimg.imread(imgFile)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
print(self.wait_RESP(s))
for j in range(240):
time.sleep(0.1)
b = bytes(imgArray[j])
#print('send img : ' + str(j))
s.sendall(bytes(b))
#print('img sent : ' + str(j))
ans = self.wait_RESP(s)
#print(ans + " : " + str(j))
print('image sent finished')
s.close()
def evBtnUploadClicked(self):
print("evBtnUploadClicked()")
print(self.fileToUpload)
self.sendImageToServer(self.fileToUpload)
def evBtnOpenFileClicked(self):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
targetPath, _ = QFileDialog.getOpenFileName(
self, 'Open file', '/home/pi/Desktop',
'Image files (*.jpg)', options=options)
if targetPath:
print(targetPath)
self.lbPath.setText(targetPath)
with open(targetPath):
pixmap = QPixmap(targetPath)
#accept 240x240 image only
if pixmap.width()==240 and pixmap.height()==240:
self.lbImg.setPixmap(pixmap)
self.btnUpload.setEnabled(True)
self.fileToUpload = targetPath
else:
self.btnUpload.setEnabled(False)
def evBtnOpenFileClickedX(self):
targetPath="/home/pi/Desktop/image.jpg"
print(targetPath)
self.lbPath.setText(targetPath)
try:
with open(targetPath):
pixmap = QPixmap(targetPath)
self.lbImg.setPixmap(pixmap)
#as a exercise, get some info from pixmap
print('\npixmap:')
print(pixmap)
print(type(pixmap))
print(str(pixmap.width()) + " : " + str(pixmap.height()))
print()
print('convert to Image')
qim = pixmap.toImage()
print(qim)
print(type(qim))
print()
print('read a pixel from image')
qrgb = qim.pixel(0, 0)
print(hex(qrgb))
print(type(qrgb))
r, g, b = qRed(qrgb), qGreen(qrgb), qBlue(qrgb)
print([hex(r), hex(g), hex(b)])
print()
except FileNotFoundError:
print('File Not Found Error')
if __name__ == '__main__':
print('run __main__')
app = QApplication(sys.argv)
window = AppWindow()
sys.exit(app.exec_())
print("- bye -")
Remark:
At beginning, I tried to set ESP32 as STA, by calling do_connect(), connect to my home/mobile WiFi network. But the result is very unstable in: Pi client send 240 pixel but ESP32 server can't receive all. That's why you can find some commented debug code (using print()) in the program.
After then, set ESP32 as AP, Pi connect to ESP32 WiFi network. The result have great improved and very stable.
No comments:
Post a Comment