Monday, November 29, 2021
Simple exercise to control GPIO on Raspberry Pi, C and Python
Simple exercise to control GPIO on Raspberry Pi, tested on Raspberry Pi Zero 2 W running "Raspbian GNU/Linux 11 (bullseye)".
blink.c, in C language:#include <stdio.h>
#include <pigpio.h>
#define led 17
int main(void){
printf("- IO Test on\n");
printf("- Raspberry Pi Zero 2 W\n");
gpioInitialise();
gpioSetMode(led, PI_OUTPUT);
while(1){
gpioWrite(led, 1);
time_sleep(1);
gpioWrite(led, 0);
time_sleep(1);
}
}
To compile it link to pigpio library:
To run it with sudo:
$ sudo ./blink
from gpiozero import LED
from time import sleep
led = LED(17)
while True:
led.on()
sleep(1)
led.off()
sleep(1)
Wednesday, August 25, 2021
Bi-direction BLE communication between Raspberry Pi/Python (with PyQt5 GUI) and ESP32/Arduino Nano RP2040 Connect
Raspberry Pi/Python/bluepy + ESP32
With bluepy installed, this exercise implement BLE client side on Raspberry Pi using Python, connect to ESP32 BLE uart server, send and receive data in between.
Prepare ESP32 BLE_uart
ESP32 side (NodeMCU ESP-32S) is programmed in Arduino framework.
In Arduino IDE
- Open Examples > ESP32 BLE Arduino > BLE_uart in
Arduino IDE, and upload to ESP32 board.
It's a BLE setup as server, wait connection. Once connected, send data to client repeatedly and display received data to Serial Monitor.
Test with nRF Connect App
To verify the function of ESP32 BLE_art on Android device, install nRF Connect for Mobile App by Nordic Semiconductor ASA.
Simple test Python code -
Automatically connect to BLE_uart, send and receive data repeatedly.
ex_bluepy_uart.py
from bluepy import btle
import time
class MyDelegate(btle.DefaultDelegate):
def __init__(self):
btle.DefaultDelegate.__init__(self)
# ... initialise here
def handleNotification(self, cHandle, data):
#print("\n- handleNotification -\n")
print(data)
# ... perhaps check cHandle
# ... process 'data'
# Initialisation -------
p = btle.Peripheral("3c:71:bf:0d:dd:6a") #NodeMCU-32S
#p = btle.Peripheral("24:0a:c4:e8:0f:9a") #ESP32-DevKitC V4
# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
ch_Tx = svc.getCharacteristics("6E400002-B5A3-F393-E0A9-E50E24DCCA9E")[0]
ch_Rx = svc.getCharacteristics("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")[0]
p.setDelegate( MyDelegate())
setup_data = b"\x0100円"
p.writeCharacteristic(ch_Rx.valHandle+1, setup_data)
lasttime = time.localtime()
while True:
"""
if p.waitForNotifications(1.0):
pass #continue
print("Waiting...")
"""
nowtime = time.localtime()
if(nowtime > lasttime):
lasttime = nowtime
stringtime = time.strftime("%H:%M:%S", nowtime)
btime = bytes(stringtime, 'utf-8')
try:
ch_Tx.write(btime, True)
except btle.BTLEException:
print("btle.BTLEException");
#print(stringtime)
#ch_Tx.write(b'wait...', True)
# Perhaps do something else here
Python code with PyQt5 GUI -
import sys
import time
import requests
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, QTimer, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (
QApplication, QLabel, QMainWindow, QPlainTextEdit, QPushButton, QVBoxLayout, QWidget,
)
from bluepy import btle
class WorkerSignals(QObject):
signalMsg = pyqtSignal(str)
signalRes = pyqtSignal(str)
class MyDelegate(btle.DefaultDelegate):
def __init__(self, sgn):
btle.DefaultDelegate.__init__(self)
self.sgn = sgn
def handleNotification(self, cHandle, data):
try:
dataDecoded = data.decode()
self.sgn.signalRes.emit(dataDecoded)
except UnicodeError:
print("UnicodeError: ", data)
class WorkerBLE(QRunnable):
def __init__(self):
super().__init__()
self.signals = WorkerSignals()
self.rqsToSend = False
@pyqtSlot()
def run(self):
self.signals.signalMsg.emit("WorkerBLE start")
#---------------------------------------------
p = btle.Peripheral("3c:71:bf:0d:dd:6a")
p.setDelegate( MyDelegate(self.signals) )
svc = p.getServiceByUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
self.ch_Tx = svc.getCharacteristics("6E400002-B5A3-F393-E0A9-E50E24DCCA9E")[0]
ch_Rx = svc.getCharacteristics("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")[0]
setup_data = b"\x0100円"
p.writeCharacteristic(ch_Rx.valHandle+1, setup_data)
# BLE loop --------
while True:
"""
if p.waitForNotifications(1.0):
# handleNotification() was called
continue
print("Waiting...")
"""
p.waitForNotifications(1.0)
if self.rqsToSend:
self.rqsToSend = False
try:
self.ch_Tx.write(self.bytestosend, True)
except btle.BTLEException:
print("btle.BTLEException");
#---------------------------------------------hellohello
self.signals.signalMsg.emit("WorkerBLE end")
def toSendBLE(self, tosend):
self.bytestosend = bytes(tosend, 'utf-8')
self.rqsToSend = True
"""
try:
self.ch_Tx.write(bytestosend, True)
except BTLEException:
print("BTLEException");
"""
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
buttonStartBLE = QPushButton("Start BLE")
buttonStartBLE.pressed.connect(self.startBLE)
self.console = QPlainTextEdit()
self.console.setReadOnly(True)
self.outconsole = QPlainTextEdit()
buttonSendBLE = QPushButton("Send message")
buttonSendBLE.pressed.connect(self.sendBLE)
layout.addWidget(buttonStartBLE)
layout.addWidget(self.console)
layout.addWidget(self.outconsole)
layout.addWidget(buttonSendBLE)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.show()
self.threadpool = QThreadPool()
print(
"Multithreading with Maximum %d threads" % self.threadpool.maxThreadCount())
def startBLE(self):
self.workerBLE = WorkerBLE()
self.workerBLE.signals.signalMsg.connect(self.slotMsg)
self.workerBLE.signals.signalRes.connect(self.slotRes)
self.threadpool.start(self.workerBLE)
def sendBLE(self):
strToSend = self.outconsole.toPlainText()
self.workerBLE.toSendBLE(strToSend)
def slotMsg(self, msg):
print(msg)
def slotRes(self, res):
self.console.appendPlainText(res)
app = QApplication(sys.argv)
window = MainWindow()
app.exec()
Raspberry Pi/Python/bluepy + Arduino Nano RP2040 Connect
/*
* BLE_peripheral_uart:
* modifid from Examples > ArduinoBLE > Peripheral > CallbackLED
*
* Bi-direction BLE communication between Raspberry Pi/Python (with PyQt5 GUI)
* and ESP32/Arduino Naon RP2040 Connect
* http://helloraspberrypi.blogspot.com/2021/08/bi-direction-ble-communication-between.html
*
*/
#include <ArduinoBLE.h>
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
//BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service
BLEService uartService(SERVICE_UUID); // create service
// create switch characteristic and allow remote device to read and write
//BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
BLEStringCharacteristic rxCharacteristic(CHARACTERISTIC_UUID_RX, BLEWrite, 30);
BLEStringCharacteristic txCharacteristic(CHARACTERISTIC_UUID_TX, BLENotify, 30);
// const int ledPin = LED_BUILTIN; // pin to use for the LED
void setup() {
Serial.begin(115200);
while (!Serial);
// pinMode(ledPin, OUTPUT); // use the LED pin as an output
// begin initialization
if (!BLE.begin()) {
Serial.println("starting BLE failed!");
while (1);
}
// set the local name peripheral advertises
BLE.setLocalName("BLE_peripheral_uart");
// set the UUID for the service this peripheral advertises
BLE.setAdvertisedService(uartService);
// add the characteristic to the service
uartService.addCharacteristic(rxCharacteristic);
uartService.addCharacteristic(txCharacteristic);
// add service
BLE.addService(uartService);
// assign event handlers for connected, disconnected to peripheral
BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
// assign event handlers for characteristic
rxCharacteristic.setEventHandler(BLEWritten, rxCharacteristicWritten);
// set an initial value for the characteristic
rxCharacteristic.setValue("BLE_peripheral_uart");
// start advertising
BLE.advertise();
Serial.println(("Bluetooth device active, waiting for connections..."));
Serial.println(BLE.address());
}
void loop() {
// poll for BLE events
BLE.poll();
}
void blePeripheralConnectHandler(BLEDevice central) {
// central connected event handler
Serial.print("Connected event, central: ");
Serial.println(central.address());
}
void blePeripheralDisconnectHandler(BLEDevice central) {
// central disconnected event handler
Serial.print("Disconnected event, central: ");
Serial.println(central.address());
}
void rxCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) {
// central wrote new value to characteristic, update LED
Serial.print("Characteristic event, written: ");
Serial.println("len=" +
String(rxCharacteristic.valueLength()));
String valString = rxCharacteristic.value();
Serial.println(valString);
valString.toUpperCase();
Serial.println(valString);
txCharacteristic.setValue(valString);
}
Thursday, April 8, 2021
ESP32/MicroPython server + Raspberry Pi/Python client, transmit image via WiFi TCP socket.
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
| |
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 -')
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 -")
Wednesday, March 24, 2021
Raspberry Pi/Python Server send image to ESP32/MicroPython Client via WiFi TCP socket
protocol:
Server | | Client
(Raspberry Pi/Python) | | (ESP32/MicroPython)
| |
Start | | Reset
| |
Setup as | |
socketserver.TCPServer | |
| |
| | Join the WiFi network
| | Connect to server with socket
| |
|<-- ACK ---| send ACK
send the 0th line |---------->| display the 0th line
|<-- ACK ---| send ACK
send the 1st line |---------->| display the 1st line
.
.
.
send the 239th line |---------->| display the 239th line
|<-- ACK ---| send ACK
close socket | | close socket
| |
wait next | | bye
Client 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_ImgClient_20210324c.py, MicroPython code run on ESP32. Modify ssid/password and serverIP for your WiFi network.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 = "your ssid"
password = "your password"
serverIP = '192.168.1.30'
serverPort = 9999
print(implementation.name)
print(uname()[3])
print(uname()[4])
print()
spi2 = machine.SPI(2, 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())
display.fill(st7789.BLACK)
display.text(font, "connected", 10, 10)
def do_scan():
global wlan
print('scan network...')
wlan.active(True)
for network in wlan.scan():
print(network)
def do_connectServer():
global wlan
global display
addr = socket.getaddrinfo(serverIP, serverPort)[0][-1]
print(addr)
s = socket.socket()
s.connect(addr)
print('---')
display.fill(st7789.BLACK)
display.text(font, "waiting...", 10, 10)
print('Send ACK')
s.sendall(bytes("ACK","utf-8"))
display.set_window(0, 0, disp_width-1, disp_height-1)
pin_st7789_dc.on()
for j in range(disp_height):
buff = s.recv(disp_width*3)
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))
s.sendall(bytes("ACK","utf-8"))
s.close()
print('socket closed')
do_connect()
try:
do_connectServer()
except:
print('error')
display.text(font, "Error", 10, 200)
finally:
print('wlan.disconnect()')
wlan.disconnect()
print('\n- bye -')
Server Side:
(Raspberry Pi/Python)
The server will send Desktop/image.jpg with fixed resolution 240x240 (match with the display in client side). 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 .
pyMyTCP_ImgServer_20210324c.py, Python3 code run on Raspberry Pi.import socketserver
import platform
import matplotlib.image as mpimg
imageFile = '/home/pi/Desktop/image.jpg'
print("sys info:")
for info in platform.uname():
print(info)
class MyTCPHandler(socketserver.BaseRequestHandler):
#wait client response in 3 byte len
def wait_RESPONSE(self, client):
client.settimeout(10)
res = str()
data = client.recv(4)
return data.decode("utf-8")
def handle(self):
msocket = self.request
print("{} connected:".format(self.client_address[0]))
imgArray = mpimg.imread(imageFile)
self.wait_RESPONSE(msocket) #dummy assume 'ACK' received
print('first RESPONSE received')
for j in range(240):
b = bytes(imgArray[j])
msocket.sendall(bytes(b))
self.wait_RESPONSE(msocket) #dummy assume 'ACK' received
print('image sent finished')
msocket.close()
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
# Create the server, binding to localhost on port 9999
#with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
with socketserver.TCPServer(('', PORT), MyTCPHandler) as server:
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
This socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server example is
modify from
Python 3 socketserver.TCPServer Example. I assume socketserver.TCPServer will handle Ctrl-C with port close. But
in my test, SOMETIMES throw OSError of "Address already in use". In my practice,
try pressing Ctrl-C in REPL/restart repeatedly.
Next:
Monday, March 22, 2021
min. version of RPi/Python Code to control Camera Module with preview on local HDMI
It's a minimum version of my another exercise "RPi/Python Code to control Camera Module with preview on local HDMI"; with minimum functions preview and capture only, using PyQt5 GUI, fixed resolution 240x240, and display the captured image on GUI. You can also choice save file name; image.jpg or img_<timestamp>.jpg, under Desktop folder. It's used to prepare images for my coming exercise "Raspberry Pi/Python send image to ESP32/MicroPython via WiFi TCP socket".
In my usage scenario: The Raspberry Pi 4B/8G is installed with HQ Camera Module (mount with manual focus lens) and a 4 inch HDMI IPS Touch Display. Remote control with Android with xrdp/Microsoft Remote Desktop. Such that I can control camera on remote Android device, adjust focus/aperture on the lens, and check the effect on local HDMI preview at real-time.
Python3 code, qCam240_20210323.py
import sys
import picamera
from pkg_resources import require
import time
import picamera
from PyQt5.QtWidgets import (QApplication, QWidget,
QPushButton, QLabel, QRadioButton,
QMessageBox, QHBoxLayout, QVBoxLayout)
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.Qt import qRed, qGreen, qBlue
from signal import signal, SIGINT
print(sys.version)
print(require('picamera'))
rpi_icon = 'rpi_icon_240.png'
class AppWindow(QWidget):
camPreviewState = False #not in Preview
def __init__(self):
super().__init__()
self.camera = picamera.PiCamera()
self.camera.resolution = (240, 240)
lbSysInfo = QLabel('Python:\n' + sys.version)
lbPicameraInfo = QLabel(str(require('picamera')))
vboxInfo = QVBoxLayout()
vboxInfo.addWidget(lbSysInfo)
vboxInfo.addWidget(lbPicameraInfo)
#setup UI
btnPreview = QPushButton("Start Preview", self)
btnPreview.clicked.connect(self.evBtnPreviewClicked)
btnCapture = QPushButton("Capture", self)
btnCapture.clicked.connect(self.evBtnCaptureClicked)
lbFileName = QLabel('save as (Desktop/):')
self.rbtnImage = QRadioButton('image.jpg')
self.rbtnImage.setChecked(True)
self.rbtnStamp = QRadioButton('img_<timestamp>.jpg')
vboxCamControl = QVBoxLayout()
vboxCamControl.addWidget(btnPreview)
vboxCamControl.addWidget(btnCapture)
vboxCamControl.addWidget(lbFileName)
vboxCamControl.addWidget(self.rbtnImage)
vboxCamControl.addWidget(self.rbtnStamp)
vboxCamControl.addStretch()
self.lbImg = QLabel(self)
self.lbImg.resize(240, 240)
self.lbImg.setStyleSheet("border: 1px solid black;")
try:
with open(rpi_icon):
pixmap = QPixmap(rpi_icon)
self.lbImg.setPixmap(pixmap)
except FileNotFoundError:
print('File Not Found Error')
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()
def evBtnPreviewClicked(self):
if self.camPreviewState:
print('Stop Preview')
self.camera.stop_preview()
self.sender().setText('Start Preview')
self.camPreviewState = False
else:
print('Start Preview')
self.camera.start_preview()
self.sender().setText('Stop Preview')
self.camPreviewState = True
def evBtnCaptureClicked(self):
print('evBtnCaptureClicked()')
print("Capture")
if self.rbtnImage.isChecked():
targetPath="/home/pi/Desktop/image.jpg"
else:
timeStamp = time.strftime("%Y%m%d-%H%M%S")
targetPath="/home/pi/Desktop/img_"+timeStamp+".jpg"
print(targetPath)
self.camera.capture(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')
def closeEvent(self, event):
confirmClose = QMessageBox.question(self,
"Quit App?",
"Confirm to Quit?",
QMessageBox.No | QMessageBox.Yes,
QMessageBox.Yes)
if confirmClose == QMessageBox.Yes:
print('Confirmed Close')
self.camera.close()
event.accept()
else:
event.ignore()
if __name__ == '__main__':
print('run __main__')
app = QApplication(sys.argv)
window = AppWindow()
sys.exit(app.exec_())
print("- bye -")
Download the thumbnail image, save as "rpi_icon_240.png" in the same folder. Used as a default image only.
Thursday, March 18, 2021
Python exercise: get RGB array of image using matplotlib
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
"""
ref:
A short tutorial on plotting images with Matplotlib:
https://matplotlib.org/stable/tutorials/introductory/images.html
numpy.ndarray:
https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html
"""
imageFile = '/home/pi/Desktop/image.jpg'
img = mpimg.imread(imageFile)
print(type(img))
print(dir(img))
print('shape (= length of each dimension):')
print(img.shape)
print()
print(img[0][0])
print(img[0][249])
print(img[249][0])
print(img[249][249])
print()
print(img)
imgplot = plt.imshow(img)
plt.show()
Tuesday, March 9, 2021
RPi/Python Code to control Camera Module with preview on local HDMI
import sys
import picamera
from pkg_resources import require
import time
"""
ref: Picamera
https://picamera.readthedocs.io/en/release-1.13/
"""
#from tkinter import *
#tkinter for Python 3
import tkinter as tk
from tkinter import ttk
def close_window():
#close tasks
camera.close()
print("Camera closed")
print("close_window()")
print("Window closed")
root.destroy()
#event callback functions
def evStartPreviewBtnPressed():
print("Start Preview")
camera.start_preview()
def evStopPreviewBtnPressed():
print("Stop Preview")
camera.stop_preview()
def evCaptureBtnPressed():
print("Capture")
timeStamp = time.strftime("%Y%m%d-%H%M%S")
targetPath="/home/pi/Desktop/img_"+timeStamp+".jpg"
print(targetPath)
camera.capture(targetPath)
def cmdScaleSharpness(new_value):
camera.sharpness = scaleSharpness.get()
print("sharpness: " + str(camera.sharpness))
def cmdScaleContrast(new_value):
camera.contrast = scaleContrast.get()
print("contrast: " + str(camera.contrast))
def cmdScaleBrightness(new_value):
camera.brightness = scaleBrightness.get()
print("brightness: " + str(camera.brightness))
def cmdScaleSaturation(new_value):
camera.saturation = scaleSaturation.get()
print("saturation: " + str(camera.saturation))
def cmdScaleExpCompensation(NEW_VALUE):
camera.exposure_compensation = scaleExpCompensation.get()
print("exposure_compensation: " + str(camera.exposure_compensation))
def cmdRB_Iso():
camera.iso = varIso.get()
print("iso: " + str(camera.iso))
print(sys.version)
print(require('picamera'))
strInfo = str(sys.version) + str(require('picamera'))
type(sys.version)
type(require('picamera'))
#Prepare camera
camera = picamera.PiCamera()
#set default
camera.sharpness = +5
camera.contrast = 0
camera.brightness = 50
camera.saturation = 0
camera.iso = 100
camera.video_stabilization = False
camera.exposure_compensation = 0
camera.exposure_mode = 'auto'
camera.meter_mode = 'average'
camera.awb_mode = 'auto'
camera.image_effect = 'none'
camera.color_effects = None
camera.rotation = 0
camera.hflip = False
camera.vflip = False
camera.crop = (0.0, 0.0, 1.0, 1.0)
# not work for HQ
#camera.resolution = (4056, 3040) # HQ
#camera.resolution = (2592, 1944) # V1
camera.resolution = (3280, 2464) # V2
#end of set default
SCALE_WIDTH = 940;
#Prepare GUI
root = tk.Tk()
#root.geometry("650x550")
root.geometry("960x800")
root.wm_title("doCamII")
root.protocol("WM_DELETE_WINDOW", close_window)
labelInfo = tk.Label(root,
text=strInfo, fg="gray",
font=("Helvetica", 14))
labelInfo.pack()
#Main control
frameMain = tk.Frame(root, bg="lightgray")
btnStartPreview = tk.Button(frameMain, text="Start Preview",
command=evStartPreviewBtnPressed)
btnStopPreview = tk.Button(frameMain, text="Stop Preview",
command=evStopPreviewBtnPressed)
btnStartPreview.grid(row=2, column=0, sticky=tk.W+tk.E)
btnStopPreview.grid(row=2, column=1, sticky=tk.W+tk.E)
btnCapture = tk.Button(frameMain, text="Capture",
command=evCaptureBtnPressed)
btnCapture.grid(row=3, columnspan=2, sticky=tk.W+tk.E)
frameMain.pack(padx=10,pady=10)
#Setting
notebook = ttk.Notebook(root)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
frame3 = ttk.Frame(notebook)
notebook.add(frame1, text='Setting 1')
notebook.add(frame2, text='Setting 2')
notebook.add(frame3, text='Setting 3')
notebook.pack()
lfSharpness = ttk.LabelFrame(frame1, text="sharpness")
lfSharpness.pack(fill="x", expand="no", anchor=tk.N)
scaleSharpness = tk.Scale(
lfSharpness,
from_=-100, to=100,
length=SCALE_WIDTH,
orient=tk.HORIZONTAL,
command=cmdScaleSharpness)
scaleSharpness.set(camera.sharpness)
scaleSharpness.pack()
lfContrast = ttk.LabelFrame(frame1, text="contrast")
lfContrast.pack(fill="x", expand="no", anchor=tk.N)
scaleContrast = tk.Scale(
lfContrast,
from_=-100, to=100,
length=SCALE_WIDTH,
orient=tk.HORIZONTAL,
command=cmdScaleContrast)
scaleContrast.set(camera.contrast)
scaleContrast.pack()
lfBrightness = ttk.LabelFrame(frame1, text="brightness")
lfBrightness.pack(fill="x", expand="no", anchor=tk.N)
scaleBrightness = tk.Scale(
lfBrightness,
from_=0, to=100,
length=SCALE_WIDTH,
orient=tk.HORIZONTAL,
command=cmdScaleBrightness)
scaleBrightness.set(camera.brightness)
scaleBrightness.pack()
lfSaturation = ttk.LabelFrame(frame1, text="saturation")
lfSaturation.pack(fill="x", expand="no", anchor=tk.N)
scaleSaturation = tk.Scale(
lfSaturation,
from_=-100, to=100,
length=SCALE_WIDTH,
orient=tk.HORIZONTAL,
command=cmdScaleSaturation)
scaleSaturation.set(camera.saturation)
scaleSaturation.pack()
lfExpCompensation = ttk.LabelFrame(frame1, text="exposure_compensation")
lfExpCompensation.pack(fill="x", expand="no", anchor=tk.N)
scaleExpCompensation = tk.Scale(
lfExpCompensation,
from_=-25, to=25,
length=SCALE_WIDTH,
orient=tk.HORIZONTAL,
command=cmdScaleExpCompensation)
scaleExpCompensation.set(camera.exposure_compensation)
scaleExpCompensation.pack()
#==========================================================
varIso = tk.IntVar()
varIso.set(camera.iso)
lfIso = ttk.LabelFrame(frame2, text="iso")
lfIso.pack(fill="x", expand="no", anchor=tk.N)
tk.Radiobutton(lfIso, variable=varIso,
text='0 (auto)',value=0,command=cmdRB_Iso).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfIso, variable=varIso,
text='100',value=100,command=cmdRB_Iso).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfIso, variable=varIso,
text='200',value=200,command=cmdRB_Iso).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfIso, variable=varIso,
text='400',value=400,command=cmdRB_Iso).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfIso, variable=varIso,
text='800',value=800,command=cmdRB_Iso).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfIso, variable=varIso,
text='1600',value=1600,command=cmdRB_Iso).pack(
anchor=tk.W, side=tk.LEFT)
#==========================================================
#-- meter_mode command
def cmdMeterMode():
camera.meter_mode = varMeterMode.get()
print("meter_mode: " + str(camera.meter_mode))
#-- exposure_mode
lfMeterMode = ttk.LabelFrame(frame2, text="meter_mode")
lfMeterMode.pack(fill="x", expand="no", anchor=tk.N)
varMeterMode = tk.StringVar()
varMeterMode.set(camera.meter_mode)
tk.Radiobutton(lfMeterMode, variable=varMeterMode,
text='average',value='average',command=cmdMeterMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfMeterMode, variable=varMeterMode,
text='spot',value='spot',command=cmdMeterMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfMeterMode, variable=varMeterMode,
text='backlit',value='backlit',command=cmdMeterMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfMeterMode, variable=varMeterMode,
text='matrix',value='matrix',command=cmdMeterMode).pack(
anchor=tk.W, side=tk.LEFT)
#==========================================================
#-- exposure_mode command
def cmdEposureMode():
camera.exposure_mode = varExposureMode.get()
print("exposure_mode: " + str(camera.exposure_mode))
#-- exposure_mode
lfExpMode = ttk.LabelFrame(frame2, text="exposure_mode")
lfExpMode.pack(fill="x", expand="no", anchor=tk.N)
varExposureMode = tk.StringVar()
varExposureMode.set(camera.exposure_mode)
lfExposure_mode1 = ttk.Frame(lfExpMode)
lfExposure_mode1.pack(fill="x", expand="no", anchor=tk.N)
lfExposure_mode2 = ttk.Frame(lfExpMode)
lfExposure_mode2.pack(fill="x", expand="no", anchor=tk.N)
tk.Radiobutton(lfExposure_mode1, variable=varExposureMode,
text='off',value='off',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode1, variable=varExposureMode,
text='auto',value='auto',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode1, variable=varExposureMode,
text='night',value='night',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode1, variable=varExposureMode,
text='nightpreview',value='nightpreview',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode1, variable=varExposureMode,
text='backlight',value='backlight',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode1, variable=varExposureMode,
text='spotlight',value='spotlight',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode1, variable=varExposureMode,
text='sports',value='sports',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode1, variable=varExposureMode,
text='snow',value='snow',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode2, variable=varExposureMode,
text='beach',value='beach',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode2, variable=varExposureMode,
text='verylong',value='verylong',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode2, variable=varExposureMode,
text='fixedfps',value='fixedfps',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode2, variable=varExposureMode,
text='antishake',value='antishake',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExposure_mode2, variable=varExposureMode,
text='fireworks',value='fireworks',command=cmdEposureMode).pack(
anchor=tk.W, side=tk.LEFT)
#==========================================================
#common button handler for ImageEffect without parameter setting,
#simple set camera.image_effect
def butComImageEffect():
camera.image_effect=varImageEffect.get()
labelImageEffectVar.set(camera.image_effect)
#----- ImageEffect 'solarise' ui event
def butImageEffect_solarize():
camera.image_effect=varImageEffect.get()
if cbSolarize_yuv_Var.get():
yuv = 1
else:
yuv = 0
solarize_para = (yuv,
scSolarize_x0_Var.get(),
scSolarize_y0_Var.get(),
scSolarize_y1_Var.get(),
scSolarize_y2_Var.get())
camera.image_effect_params = solarize_para
labelImageEffectVar.set(camera.image_effect +
" " + str(camera.image_effect_params))
def ev_solarizePara(new_value=None):
varImageEffect.set("solarize")
butImageEffect_solarize()
#----- ImageEffect 'watercolor' ui event ---
def butImageEffect_watercolor():
camera.image_effect=varImageEffect.get()
if cbWatercolor_uv_Var.get():
watercolor_para = (scWatercolor_u_Var.get(),
scWatercolor_v_Var.get())
camera.image_effect_params = watercolor_para
else:
watercolor_para = ()
camera.image_effect_params = watercolor_para
labelImageEffectVar.set(camera.image_effect +
" " + str(camera.image_effect_params))
def ev_watercolorPara(new_value=None):
varImageEffect.set("watercolor")
butImageEffect_watercolor()
#----- ImageEffect 'film' ui event ---
def butImageEffect_film():
camera.image_effect=varImageEffect.get()
film_para = (scFilm_strength_Var.get(),
scFilm_u_Var.get(),
scFilm_v_Var.get())
camera.image_effect_params = film_para
labelImageEffectVar.set(camera.image_effect +
" " + str(camera.image_effect_params))
def ev_filmPara(new_value=None):
varImageEffect.set("film")
butImageEffect_film()
#----- ImageEffect 'blur' ui event ---
def butImageEffect_blur():
camera.image_effect=varImageEffect.get()
camera.image_effect_params = scBlur_size_Var.get()
labelImageEffectVar.set(camera.image_effect +
" " + str(camera.image_effect_params))
def ev_blurPara(new_value=None):
varImageEffect.set("blur")
butImageEffect_blur()
#----- ImageEffect 'colorswap' ui event ---
def butImageEffect_colorswap():
camera.image_effect=varImageEffect.get()
camera.image_effect_params = cbColorswap_dir_Var.get()
labelImageEffectVar.set(camera.image_effect +
" " + str(camera.image_effect_params))
def ev_colorswapPara(new_value=None):
varImageEffect.set("colorswap")
butImageEffect_colorswap()
#----- ImageEffect 'posterise' ui event ---
def butImageEffect_posterise():
camera.image_effect=varImageEffect.get()
camera.image_effect_params = scPosterise_steps_Var.get()
labelImageEffectVar.set(camera.image_effect +
" " + str(camera.image_effect_params))
def ev_posterisePara(new_value=None):
varImageEffect.set("posterise")
butImageEffect_posterise()
#----- ImageEffect 'colorpoint' ui event ---
def butImageEffect_colorpoint():
camera.image_effect=varImageEffect.get()
camera.image_effect_params = quadrantVar.get()
labelImageEffectVar.set(camera.image_effect +
" " + str(camera.image_effect_params))
def ev_colorpointPara(new_value=None):
varImageEffect.set("colorpoint")
butImageEffect_colorpoint()
#----- ImageEffect 'colorbalance' ui event ---
def butImageEffect_colorbalance():
camera.image_effect=varImageEffect.get()
colorbalance_para = (scColorbalance_lens_Var.get(),
scColorbalance_r_Var.get(),
scColorbalance_g_Var.get(),
scColorbalance_b_Var.get(),
scColorbalance_u_Var.get(),
scColorbalance_v_Var.get())
camera.image_effect_params = colorbalance_para
labelImageEffectVar.set(camera.image_effect +
" " + str(camera.image_effect_params))
def ev_colorbalancePara(new_value=None):
varImageEffect.set("colorbalance")
butImageEffect_colorbalance()
#-----------------------------------------------------
#-----------------------------------------------------
# Tab Image Effect
varImageEffect = tk.StringVar()
labelImageEffectVar = tk.StringVar()
image_effect_setting = camera.image_effect
varImageEffect.set(image_effect_setting)
labelImageEffectVar.set(image_effect_setting)
tk.Label(frame3, textvariable=labelImageEffectVar).pack(anchor=tk.N)
#-- image_effect
lfNoParaOpts1 = ttk.Frame(frame3)
lfNoParaOpts1.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
text='none',value='none',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
text='negative',value='negative',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
text='sketch',value='sketch',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
text='denoise',value='denoise',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
text='emboss',value='emboss',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
text='oilpaint',value='oilpaint',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
text='hatch',value='hatch',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
text='gpen',value='gpen',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
lfNoParaOpts2 = ttk.Frame(frame3)
lfNoParaOpts2.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
text='pastel',value='pastel',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
text='saturation',value='saturation',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
text='washedout',value='washedout',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
text='cartoon',value='cartoon',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
text='deinterlace1',value='deinterlace1',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
text='deinterlace2',value='deinterlace2',command=butComImageEffect).pack(
anchor=tk.W, side=tk.LEFT)
lfSolarize = ttk.LabelFrame(frame3, text="solarize")
lfSolarize.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lfSolarize, variable=varImageEffect,
text='solarize',value='solarize',command=butImageEffect_solarize).pack(
anchor=tk.W, side=tk.LEFT)
cbSolarize_yuv_Var = tk.BooleanVar()
tk.Checkbutton(lfSolarize, text="yuv",
variable=cbSolarize_yuv_Var, command=ev_solarizePara).pack(
anchor=tk.W, side=tk.LEFT)
scSolarize_x0_Var = tk.IntVar()
scSolarize_x0_Var.set(128)
tk.Scale(lfSolarize, from_=0, to=255,
orient=tk.HORIZONTAL, length=200, label="x0",
variable=scSolarize_x0_Var, command=ev_solarizePara).pack(
anchor=tk.W, side=tk.LEFT)
scSolarize_y0_Var = tk.IntVar()
scSolarize_y0_Var.set(128)
tk.Scale(lfSolarize, from_=0, to=255,
orient=tk.HORIZONTAL, length=200, label="y0",
variable=scSolarize_y0_Var, command=ev_solarizePara).pack(
anchor=tk.W, side=tk.LEFT)
scSolarize_y1_Var = tk.IntVar()
scSolarize_y1_Var.set(128)
tk.Scale(lfSolarize, from_=0, to=255,
orient=tk.HORIZONTAL, length=200, label="y1",
variable=scSolarize_y1_Var, command=ev_solarizePara).pack(
anchor=tk.W, side=tk.LEFT)
scSolarize_y2_Var = tk.IntVar()
scSolarize_y2_Var.set(0)
tk.Scale(lfSolarize, from_=0, to=255,
orient=tk.HORIZONTAL, length=200, label="y2",
variable=scSolarize_y2_Var, command=ev_solarizePara).pack(
anchor=tk.W, side=tk.LEFT)
lfwatercolor = ttk.LabelFrame(frame3, text="watercolor")
lfwatercolor.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lfwatercolor, variable=varImageEffect,
text='watercolor',value='watercolor',command=butImageEffect_watercolor
).pack(anchor=tk.W, side=tk.LEFT)
cbWatercolor_uv_Var = tk.BooleanVar()
cbWatercolor_uv_Var.set(False)
tk.Checkbutton(lfwatercolor, text="uv",
variable=cbWatercolor_uv_Var, command=ev_watercolorPara).pack(
anchor=tk.W, side=tk.LEFT)
scWatercolor_u_Var = tk.IntVar()
scWatercolor_u_Var.set(0)
tk.Scale(lfwatercolor, from_=0, to=255,
orient=tk.HORIZONTAL, length=200, label="u",
variable=scWatercolor_u_Var, command=ev_watercolorPara).pack(
anchor=tk.W, side=tk.LEFT)
scWatercolor_v_Var = tk.IntVar()
scWatercolor_v_Var.set(0)
tk.Scale(lfwatercolor, from_=0, to=255,
orient=tk.HORIZONTAL, length=200, label="v",
variable=scWatercolor_v_Var, command=ev_watercolorPara).pack(
anchor=tk.W, side=tk.LEFT)
lffilm = ttk.LabelFrame(frame3, text="film")
lffilm.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lffilm, variable=varImageEffect,
text='film',value='film',command=butImageEffect_film).pack(
anchor=tk.W, side=tk.LEFT)
scFilm_strength_Var = tk.IntVar()
scFilm_strength_Var.set(0)
tk.Scale(lffilm, from_=0, to=255,
orient=tk.HORIZONTAL, length=200, label="strength",
variable=scFilm_strength_Var, command=ev_filmPara).pack(
anchor=tk.W, side=tk.LEFT)
scFilm_u_Var = tk.IntVar()
scFilm_u_Var.set(0)
tk.Scale(lffilm, from_=0, to=255,
orient=tk.HORIZONTAL, length=200, label="u",
variable=scFilm_u_Var, command=ev_filmPara).pack(anchor=tk.W, side=tk.LEFT)
scFilm_v_Var = tk.IntVar()
scFilm_v_Var.set(0)
tk.Scale(lffilm, from_=0, to=255,
orient=tk.HORIZONTAL, length=200, label="v",
variable=scFilm_v_Var, command=ev_filmPara).pack(anchor=tk.W, side=tk.LEFT)
lfblur = ttk.LabelFrame(frame3, text="blur")
lfblur.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lfblur, variable=varImageEffect,
text='blur',value='blur',command=butImageEffect_blur).pack(
anchor=tk.W, side=tk.LEFT)
scBlur_size_Var = tk.IntVar()
scBlur_size_Var.set(1)
tk.Scale(lfblur, from_=1, to=2,
orient=tk.HORIZONTAL, length=100, label="size",
variable=scBlur_size_Var, command=ev_blurPara).pack(anchor=tk.W, side=tk.LEFT)
lfcolorswap = ttk.LabelFrame(frame3, text="colorswap")
lfcolorswap.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lfcolorswap, variable=varImageEffect,
text='colorswap',value='colorswap',command=butImageEffect_colorswap).pack(
anchor=tk.W, side=tk.LEFT)
cbColorswap_dir_Var = tk.BooleanVar()
cbColorswap_dir_Var.set(False)
tk.Checkbutton(lfcolorswap, text="dir - 0:RGB to BGR/1:RGB to BRG",
variable=cbColorswap_dir_Var, command=ev_colorswapPara).pack(
anchor=tk.W, side=tk.LEFT)
lfposterise = ttk.LabelFrame(frame3, text="posterise")
lfposterise.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lfposterise, variable=varImageEffect,
text='posterise',value='posterise',command=butImageEffect_posterise).pack(
anchor=tk.W, side=tk.LEFT)
scPosterise_steps_Var = tk.IntVar()
scPosterise_steps_Var.set(4)
tk.Scale(lfposterise, from_=2, to=32,
orient=tk.HORIZONTAL, length=200, label="steps",
variable=scPosterise_steps_Var, command=ev_posterisePara).pack(
anchor=tk.W, side=tk.LEFT)
lfcolorpoint = ttk.LabelFrame(frame3, text="colorpoint")
lfcolorpoint.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lfcolorpoint, variable=varImageEffect,
text='colorpoint',value='colorpoint',command=butImageEffect_colorpoint).pack(
anchor=tk.W, side=tk.LEFT)
quadrantVar = tk.IntVar()
quadrantVar.set(0)
tk.Radiobutton(lfcolorpoint, text="green",
variable=quadrantVar, value=0, command=ev_colorpointPara).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfcolorpoint, text="red/yellow",
variable=quadrantVar, value=1, command=ev_colorpointPara).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfcolorpoint, text="blue",
variable=quadrantVar, value=2, command=ev_colorpointPara).pack(
anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfcolorpoint, text="purple",
variable=quadrantVar, value=3, command=ev_colorpointPara).pack(
anchor=tk.W, side=tk.LEFT)
lfcolorbalance = ttk.LabelFrame(frame3, text="colorbalance: I can't see any effect!")
lfcolorbalance.pack(fill="x", expand="yes", anchor=tk.N)
tk.Radiobutton(lfcolorbalance, variable=varImageEffect,
text='colorbalance',value='colorbalance',command=butImageEffect_colorbalance).pack(
anchor=tk.W, side=tk.LEFT)
scColorbalance_lens_Var = tk.DoubleVar()
scColorbalance_lens_Var.set(0)
tk.Scale(lfcolorbalance, from_=0, to=256,
orient=tk.HORIZONTAL, length=140, label="lens",
variable=scColorbalance_lens_Var, command=ev_colorbalancePara).pack(
anchor=tk.W, side=tk.LEFT)
scColorbalance_r_Var = tk.DoubleVar()
scColorbalance_r_Var.set(1)
tk.Scale(lfcolorbalance, from_=0, to=256,
orient=tk.HORIZONTAL, length=140, label="r",
variable=scColorbalance_r_Var, command=ev_colorbalancePara).pack(
anchor=tk.W, side=tk.LEFT)
scColorbalance_g_Var = tk.DoubleVar()
scColorbalance_g_Var.set(1)
tk.Scale(lfcolorbalance, from_=0, to=256,
orient=tk.HORIZONTAL, length=140, label="g",
variable=scColorbalance_g_Var, command=ev_colorbalancePara).pack(
anchor=tk.W, side=tk.LEFT)
scColorbalance_b_Var = tk.DoubleVar()
scColorbalance_b_Var.set(1)
tk.Scale(lfcolorbalance, from_=0, to=256,
orient=tk.HORIZONTAL, length=140, label="b",
variable=scColorbalance_b_Var, command=ev_colorbalancePara).pack(
anchor=tk.W, side=tk.LEFT)
scColorbalance_u_Var = tk.IntVar()
scColorbalance_u_Var.set(0)
tk.Scale(lfcolorbalance, from_=0, to=255,
orient=tk.HORIZONTAL, length=140, label="u",
variable=scColorbalance_u_Var, command=ev_colorbalancePara).pack(
anchor=tk.W, side=tk.LEFT)
scColorbalance_v_Var = tk.IntVar()
scColorbalance_v_Var.set(0)
tk.Scale(lfcolorbalance, from_=0, to=255,
orient=tk.HORIZONTAL, length=140, label="v",
variable=scColorbalance_v_Var, command=ev_colorbalancePara).pack(
anchor=tk.W, side=tk.LEFT)
#==========================================================
root.mainloop()
print("- bye -")
~ Another minimum version: with minimum functions preview and capture only, using PyQt5 GUI, fixed resolution 240x240, and display the captured image on GUI. You can also choice save file name; image.jpg or img_<timestamp>.jpg, under Desktop folder.
Related:
Wednesday, February 3, 2021
Raspberry Pi/Python remote control LED on Raspberry Pi Pico/MicroPython with HC-08 + ILI9341 SPI Display via Bluetooth
Raspberry Pi Pico/MicroPython exercise to work with HC-08 BLE 4.0 module, to communication with Raspberry Pi/Python. The ILI9341 SPI screen is used to display related information only. HC-08 connect to Pico via UART(0), as a BLE server. Once connected, Pico monitor the incoming data, if matched pattern of "#LEDON\r\n"/"#LEDOFF\r\n" received, it turn ON/OFF its onboard LED accordingly.
In the Raspberry Pi/Python, using bluepy library, act as client connect to Pico/HC-08 BLE server, setup a simple tkinter GUI, send "#LEDON\r\n"/"#LEDOFF\r\n" command if user click on the toggle button.
It's a group result of my previouse exercises:
~ Python on Raspberry Pi to read ESP32 BLE_server, using bluepy
~ Raspberry Pi Pico/MicroPython + 320x240 ILI9341 SPI Display, using jeffmer/micropython-ili9341 library
~ Raspberry Pi Pico/MicroPython + HC-08 BLE 4.0 UART Module
Connection between Raspberry Pi Pico and HC-08/ILI9341:
MicroPython/Python Code:MicrcoPython code run on Raspberry Pi Pico, mpyPico_ili9341_HC08.py:
"""
Exercise on Raspberry Pi Pico/MicroPython
with 320x240 ILI9341 SPI Display
+ HC-08 Bluetooth UART Module(ver:HC-08 V3.1,2017年07月07日)
"""
from ili934xnew import ILI9341, color565
from machine import Pin, SPI
from micropython import const
import os
import glcdfont
import tt14
import tt24
import tt32
import time
#2 sec timeout is arbitrarily chosen
def sendCMD_waitResp(cmd, timeout=2000):
print("CMD: " + cmd)
uart.write(cmd)
waitResp(timeout)
print()
def waitResp(timeout=2000):
prvMills = time.ticks_ms()
resp = b""
while (time.ticks_ms()-prvMills)<timeout:
if uart.any():
resp = b"".join([resp, uart.read(1)])
print(resp)
display.print(resp.decode('utf_8'))
def sendCMD_waitRespLine(cmd, timeout=2000):
print("CMD: " + cmd)
uart.write(cmd)
waitRespLine(timeout)
print()
def waitRespLine(timeout=2000):
prvMills = time.ticks_ms()
line = b""
while (time.ticks_ms()-prvMills)<timeout:
if uart.any():
line = line+uart.readline()
print(line)
display.print(line.decode('utf_8'))
SCR_WIDTH = const(320)
SCR_HEIGHT = const(240)
SCR_ROT = const(2)
CENTER_Y = int(SCR_WIDTH/2)
CENTER_X = int(SCR_HEIGHT/2)
print(os.uname())
TFT_CLK_PIN = const(6)
TFT_MOSI_PIN = const(7)
TFT_MISO_PIN = const(4)
TFT_CS_PIN = const(13)
TFT_RST_PIN = const(14)
TFT_DC_PIN = const(15)
fonts = [glcdfont,tt14,tt24,tt32]
text = 'RPi Pico+ili9341+HC-08'
print(text)
#print uart info
#using UART(0)
#UART_TX = GP0
#UART_RX = GP1
uart = machine.UART(0, baudrate=9600,
bits=8, parity=None, stop=1)
print(uart)
led_onboard = machine.Pin(25, machine.Pin.OUT)
led_onboard.value(0)
spi = SPI(
0,
baudrate=40000000,
miso=Pin(TFT_MISO_PIN),
mosi=Pin(TFT_MOSI_PIN),
sck=Pin(TFT_CLK_PIN))
print(spi)
display = ILI9341(
spi,
cs=Pin(TFT_CS_PIN),
dc=Pin(TFT_DC_PIN),
rst=Pin(TFT_RST_PIN),
w=SCR_WIDTH,
h=SCR_HEIGHT,
r=SCR_ROT)
display.erase()
display.set_pos(0,0)
#indicate program started visually
display.set_font(tt24)
display.print(text)
time.sleep(1)
"""
for i in range(20):
display.scroll(1)
time.sleep(0.01)
"""
led_onboard.value(1)
time.sleep(0.5)
led_onboard.value(0)
"""
for i in range(20):
display.scroll(-1)
time.sleep(0.01)
"""
#-----------------------------------
#clear bufer in UART
display.set_font(tt14)
waitResp()
sendCMD_waitResp("AT")
sendCMD_waitRespLine("AT+RX")
display.set_font(tt24)
while True:
char = uart.read(1)
print(char)
if char == b'#':
print("match")
bleCmd = uart.readline()
if bleCmd == b'LEDON\r\n':
led_onboard.value(1)
display.fill_rectangle(0, 280, 200, 20, color565(0, 0, 0))
display.set_pos(0, 280)
display.print("- LED ON -")
print("- LED ON -")
if bleCmd == b'LEDOFF\r\n':
led_onboard.value(0)
display.fill_rectangle(0, 280, 200, 20, color565(0, 0, 0))
display.set_pos(0, 280)
display.print("- LED OFF -")
print("- LED OFF -")
else:
print(bleCmd)
#display.print(char.decode('utf_8'))
print("- bye-")
Python code run on Raspberry Pi 4, pyBLE_Pico_HC08.py:
from bluepy import btle
import time
import tkinter as tk
HC08_Char = None
def toggle():
if toggle_btn.config('relief')[-1] == 'sunken':
toggle_btn.config(relief="raised")
HC08_Char.write(b'#LEDOFF\r\n')
toggle_btn['text'] = 'Turn LED ON'
else:
toggle_btn.config(relief="sunken")
HC08_Char.write(b'#LEDON\r\n')
toggle_btn['text'] = 'Turn LED OFF'
MAC = "F8:33:31:E2:A0:42"
SERVICE_UUID = "FFE0"
CHARACTERISTIC_UUID = "FFE1"
print("Connect to:" + MAC)
dev = btle.Peripheral(MAC)
print("\n--- dev ----------------------------")
print(type(dev))
print(dev)
print("\n--- dev.services -------------------")
for svc in dev.services:
print(str(svc))
print("\n------------------------------------")
print("Get Serice By UUID: " + SERVICE_UUID)
service_uuid = btle.UUID(SERVICE_UUID)
service = dev.getServiceByUUID(service_uuid)
print(service)
print("\n--- service.getCharacteristics() ---")
print(type(service.getCharacteristics()))
print(service.getCharacteristics())
#----------------------------------------------
characteristics = dev.getCharacteristics()
print("\n--- dev.getCharacteristics() -------")
print(type(characteristics))
print(characteristics)
for char in characteristics:
print("----------")
print(type(char))
print(char)
print(char.uuid)
if(char.uuid == CHARACTERISTIC_UUID ):
print("=== !CHARACTERISTIC_UUID matched! ==")
HC08_Char = char
print(char)
print(dir(char))
print(char.getDescriptors)
print(char.propNames)
print(char.properties)
print(type(char.read()))
print(char.read())
if HC08_Char != None:
for i in range(3):
HC08_Char.write(b'#LEDON\r\n')
time.sleep(0.5)
HC08_Char.write(b'#LEDOFF\r\n')
time.sleep(0.5)
root = tk.Tk()
label = tk.Label( root, text="Toggle button to Turn ON/OFF the Pico LED via HC-08/BLE")
label.pack(pady=10)
toggle_btn = tk.Button(text="Turn LED ON", width=12, relief="raised", command=toggle)
toggle_btn.pack(pady=10)
root.geometry("500x200")
#Place tkinter window center
root.eval('tk::PlaceWindow %s center' % root.winfo_pathname(root.winfo_id()))
root.title("helloraspberrypi.blogspot.com")
root.mainloop()
else:
print("Target HC-08 Not found!!!")
dev.disconnect()
print("--- bye ---")
Next:
~ ESP32 BLE Client remote control Raspberry Pi Pico/HC-08 via Bluetooth, almost same function to this exercise have ESP32 BLE client to replace Raspberry Pi/Python.
~ More exercise for Raspberry Pi Pico
Sunday, January 24, 2021
BLE Notification example: Python/Raspberry Pi read notification from ESP32 BLE server to display Analog Input
It's a Python example run on Raspberry Pi, using libraries bluepy/matplotlib, connect to ESP32 BLE Server (run on ESP32-DevKitC V4), handle notification, and plot the value graphically. It's modified from last exercise of Python/Raspberry Pi handle Notification from ESP32 BLE_notify example.
# Python3 example on Raspberry Pi to handle notification from
# ESP32 BLE_notify example.
#
# To install bluepy for Python3:
# $ sudo pip3 install bluepy
from bluepy import btle
import matplotlib.pyplot as plt
value = [0]*30
plt.ylim([0, 256])
plt.plot(value)
plt.draw()
plt.pause(0.01)
class MyDelegate(btle.DefaultDelegate):
def __init__(self):
btle.DefaultDelegate.__init__(self)
# ... initialise here
def handleNotification(self, cHandle, data):
# ... perhaps check cHandle
# ... process 'data'
#print(type(data))
#print(dir(data))
print(data)
print(data[0])
value.pop(0)
value.append(data[0])
plt.clf()
plt.ylim([0, 256])
plt.plot(value)
plt.draw()
plt.pause(0.01)
# Initialisation -------
address = "24:0a:c4:e8:0f:9a"
service_uuid = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
char_uuid = "beb5483e-36e1-4688-b7f5-ea07361b26a8"
p = btle.Peripheral(address)
p.setDelegate(MyDelegate())
# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID(service_uuid)
ch = svc.getCharacteristics(char_uuid)[0]
"""
setup_data for bluepy noification-
"""
setup_data = b"\x01\x00"
#ch.write(setup_data)
p.writeCharacteristic(ch.valHandle + 1, setup_data)
ch_data = p.readCharacteristic(ch.valHandle + 1)
print(type(ch_data))
print(ch_data)
print("=== Main Loop ===")
while True:
if p.waitForNotifications(1.0):
# handleNotification() was called
continue
#print("Waiting...")
# Perhaps do something else here
Wednesday, January 20, 2021
Python/Raspberry Pi handle Notification from ESP32 BLE_notify example
It's a Python3 example code (using bluepy library) run on Raspberry Pi, connect to ESP32 BLE server (programmed with BLE_notify example), and handle the Notification from ESP32.
Where address, service_uuid and char_uuid have to match with ESP32 side.
pyBLE_test_waitNotification_20210120a.py"""
ref:
bluepy Documentation: Working with notifications
http://ianharvey.github.io/bluepy-doc/notifications.html
"""
# Python3 example on Raspberry Pi to handle notification from
# ESP32 BLE_notify example.
#
# To install bluepy for Python3:
# $ sudo pip3 install bluepy
from bluepy import btle
class MyDelegate(btle.DefaultDelegate):
def __init__(self):
btle.DefaultDelegate.__init__(self)
# ... initialise here
def handleNotification(self, cHandle, data):
# ... perhaps check cHandle
# ... process 'data'
#print(type(data))
#print(dir(data))
print(data)
print(data[0])
# Initialisation -------
address = "24:0a:c4:e8:0f:9a"
service_uuid = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
char_uuid = "beb5483e-36e1-4688-b7f5-ea07361b26a8"
p = btle.Peripheral(address)
p.setDelegate(MyDelegate())
# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID(service_uuid)
ch = svc.getCharacteristics(char_uuid)[0]
"""
print(type(ch))
print(ch)
print(dir(ch))
peripheral = ch.peripheral
print(type(peripheral))
print(peripheral)
propNames = ch.propNames
print(type(propNames))
print(propNames)
properties = ch.properties
print(type(properties))
print(properties)
"""
"""
Remark for setup_data for bluepy noification-
Actually I don't understand how come setup_data = b"\x01\x00",
and ch.valHandle + 1.
Just follow suggestion by searching in internet:
https://stackoverflow.com/questions/32807781/
ble-subscribe-to-notification-using-gatttool-or-bluepy
"""
setup_data = b"\x01\x00"
#ch.write(setup_data)
p.writeCharacteristic(ch.valHandle + 1, setup_data)
ch_data = p.readCharacteristic(ch.valHandle + 1)
print(type(ch_data))
print(ch_data)
print("=== Main Loop ===")
while True:
if p.waitForNotifications(1.0):
# handleNotification() was called
continue
#print("Waiting...")
# Perhaps do something else here
Next:
Sunday, January 17, 2021
Python on Raspberry Pi to read ESP32 BLE_server, using bluepy
This Python exercise run on Raspberry Pi using bluepy library, read ESP32 (Arduino Framework) BLE_server example (with BLE_client also) .
To install bluepy for Python3, enter the command:
$ sudo pip3 install bluepy
Python code, pyBLE_test_20210117a.py
from bluepy import btle
MAC = "24:0a:c4:e8:0f:9a"
SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
CHARACTERISTIC_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8"
print("Connect to:" + MAC)
dev = btle.Peripheral(MAC)
print("\n--- dev ----------------------------")
print(type(dev))
print(dev)
print("\n--- dev.services -------------------")
for svc in dev.services:
print(str(svc))
print("\n------------------------------------")
print("Get Serice By UUID: " + SERVICE_UUID)
service_uuid = btle.UUID(SERVICE_UUID)
service = dev.getServiceByUUID(service_uuid)
print(service)
print("\n--- service.getCharacteristics() ---")
print(type(service.getCharacteristics()))
print(service.getCharacteristics())
#----------------------------------------------
characteristics = dev.getCharacteristics()
print("\n--- dev.getCharacteristics() -------")
print(type(characteristics))
print(characteristics)
for char in characteristics:
print("----------")
print(type(char))
print(char)
print(char.uuid)
if(char.uuid == CHARACTERISTIC_UUID ):
print("=== !CHARACTERISTIC_UUID matched! ==")
print(char)
print(dir(char))
print(char.getDescriptors)
print(char.propNames)
print(char.properties)
print(type(char.read()))
print(char.read())
#print("=== dev ============================")
#print(dir(dev))
#print("=== service ========================")
#print(dir(service))
Tuesday, December 29, 2020
Raspberry Pi/Python as Bluetooth classic client, bi-direction communication with ESP32
Raspberry Pi/Python act as GUI Bluetooth classic client, using tkinter/pybluez:
Connect to bluetooth classic server with hard-coded MAC.
User enter text to sent on bottom Text Frame, send to server.
Display the data received from server on upper Text frame.
The server side, run on ESP32 (NodeMCU ESP-32S), with SPI ST7735 IPS screen. It echo back received data. ESP32 (Arduino framework) code: Arduino-er: ESP-32S as Bluetooth classic Server, bi-direction communication with Raspberry Pi/Python.
Python code, pySPPClient.py.
import sys
from tkinter import *
from bluetooth import *
from threading import Thread
import time
rqsStopBtHandler = False
buf_size = 255
def btHandler():
global rqsStopBtHandler
rqsStopBtHandler = False
print("btHandler Started")
#Set sock.settimeout(),
#to prevent program blocked by sock.recv()
#and cannot end btHandler
sock.settimeout(1.0)
while rqsStopBtHandler!=True:
try:
datarx = sock.recv(buf_size)
datarxToStr = datarx.decode("utf-8")
print(datarxToStr)
textCenter.insert(INSERT, datarxToStr)
except Exception:
continue
print("btHandler End")
def startBtHandler():
btThread = Thread(target=btHandler)
btThread.start()
def close_window():
global rqsStopBtHandler
rqsStopBtHandler = True
sock.close()
print("Socket closed")
print("Window closed")
root.destroy()
def cmdSend():
stringSent = textBottom.get(1.0, END)
print(stringSent)
sock.send(stringSent)
#sock.send("\n")
print("- Sent")
#===============================================
#Prepare Bluetooth Classic
print("Python version: ")
print(sys.version)
print("tkinter version: ", TkVersion)
print("=============================")
print("Connect to ESP32 Bluetooth Classic SPP Server")
#addr = "24:0A:C4:E8:0F:9A"
addr = "3C:71:BF:0D:DD:6A"
print(addr)
service_matches = find_service( address = addr )
if len(service_matches) == 0:
print("couldn't find the ESP32 Bluetooth Classic SPP service")
sys.exit(0)
for s in range(len(service_matches)):
print("\nservice_matches: [" + str(s) + "]:")
print(service_matches[s])
first_match = service_matches[0]
port = first_match["port"]
name = first_match["name"]
host = first_match["host"]
port=1
print("connecting to \"%s\" on %s, port %s" % (name, host, port))
# Create the client socket
sock=BluetoothSocket(RFCOMM)
sock.connect((host, port))
print("connected")
#===============================================
#Prepare GUI
root = Tk()
root.configure(bg="darkgray")
root.wm_title("SPP Client")
root.protocol("WM_DELETE_WINDOW", close_window)
rootFrame = Frame(root)
rootFrame.pack()
labelTitle = Label(root,
text="helloraspberrypi.blogspot.com",
font=("Helvetica", 18),
fg="White", bg="darkgray")
labelTitle.pack()
frameCenter = Frame(root, bg="lightgray")
frameCenter.pack()
textCenter= Text(frameCenter, width=26, height=10, font=("Helvetica", 18))
textCenter.pack(padx=10,pady=5)
frameBottom = Frame(root, bg="gray")
frameBottom.pack(expand=True, fill='both')
textBottom = Text(frameBottom, width=26, height=10, font=("Helvetica", 18))
textBottom.insert(INSERT, "Enter text here")
textBottom.pack(padx=10,pady=5)
buttonSend = Button(frameBottom, text="Send", command=cmdSend)
buttonSend.pack(padx=10,pady=5)
startBtHandler()
root.mainloop()
print("--- bye ---")
Related: