Showing posts with label example code.Python. Show all posts
Showing posts with label example code.Python. Show all posts

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:
$ gcc blink.c -o blink -lpigpio

To run it with sudo:
$ sudo ./blink


py_LED.py, in Python:
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 -
- Click "Start BLE" button to connect to BLE_uart.
- Display received data on QPlainTextEdit, and send user entered data to BLE



pyqt5_bluepy_thread_.py
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

Now, replace ESP32 with Arduino Nano RP2040 Connect, running following code using ArduinoBLE library. Please note that you have to modify Python code to match with Arduino Nano RP2040 Connect's MAC address.


BLE_peripheral_uart.ino
/*
 * 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
			|	 |
	
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.




Wednesday, March 24, 2021

Raspberry Pi/Python Server send image to ESP32/MicroPython Client via WiFi TCP socket


In this exercise, Raspberry Pi/Python3 act as socket server, ESP32/MicroPython act as client connect to server via WiFi TCP. Once received, server (Pi/Python) send a image (240x240) to client (ESP32/MicroPython), then the client display the image on a 240*240 IPS (ST7789 SPI) LCD.


To make it more flexible, the image is in 240 batch of 240 pixel x 3 color (r, g, b).

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

Python code to get 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

It's a Python3 code run on Raspberry Pi to control Camera Module with various setting. The preview display on local HDMI.


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 and change setting on remote Android device, adjust focus/aperture on the lens, and check the effect on local HDMI preview at real-time.

It's a long time ago, I make a similar code with preview stream video, both control and preview on remote desktop, Python to capture image from Pi Camera Module, with image effects. But I'm not satisfied by the delay of stream video, that's why I re-develop this code again.


The lens shown on the video is a Nikkor ais 28mm f2.8 manual focus lens, connected to HQ Camera Module via a Nikon F to C mount adapter.

rpiCam_20210309a.py
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 -")
Remark:
As this is writing, picamera is still 1.13, so the maximum resolution is 3280x2464 for V2, not HQ.



~ 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.


pyBLE_test_waitNotification_AIN.py
# 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:

Subscribe to: Comments (Atom)

AltStyle によって変換されたページ (->オリジナル) /