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

Thursday, July 15, 2021

Install PyQt5 for Python3 on Raspberry Pi OS

 To install PyQt5 on Raspberry Pi OS, for Python3, enter the command:

$ sudo apt install python3-pyqt5

Currently, it's 5.11.3.



Tuesday, July 13, 2021

Install bluepy on Raspberry Pi, for Python3, and examples.

bluepy is a Python interface to Bluetooth LE on Linux.

To install bluepy on Raspberry Pi for Python3, enter the command:

$ sudo apt-get install python3-pip libglib2.0-dev
$ sudo pip3 install bluepy


ref:


bluepy examples:

There are two examples in bluepy, scanner and notification. Here how I modify it for Python3 working on Raspberry Pi, work with ESP32 BLE_uart example.


scanner.py
from bluepy.btle import Scanner, DefaultDelegate

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)

    def handleDiscovery(self, dev, isNewDev, isNewData):
        if isNewDev:
            print("Discovered device", dev.addr)
        elif isNewData:
            print("Received new data from", dev.addr)

scanner = Scanner().withDelegate(ScanDelegate())
devices = scanner.scan(10.0)

for dev in devices:
    print("Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi))
    for (adtype, desc, value) in dev.getScanData():
        print("  %s = %s" % (desc, value))

notification.py
from bluepy import btle

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")
p.setDelegate( MyDelegate() )

# Setup to turn notifications on, e.g.
svc = p.getServiceByUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
ch = svc.getCharacteristics("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")[0]
#   ch.write( setup_data )

setup_data = b"\x01\00"
p.writeCharacteristic(ch.valHandle+1, setup_data)

# Main loop --------

while True:
    if p.waitForNotifications(1.0):
        # handleNotification() was called
        continue

    print("Waiting...")
    # Perhaps do something else here


Next:

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:

Saturday, February 20, 2021

Pico/MicroPython + ESP-01S (AT Command) act as TCP Client connect to Raspberry Pi/Python TCP Server

This exercise program Raspberry Pi Pico in MicroPython, using ESP-01S (ESP8266) as WiFi co-processor, act as TCP Client.

The ESP-01S is flashed with AT command firmware, version:

AT version:1.7.4.0(May 11 2020 19:13:04)
SDK version:3.0.4(9532ceb)
compile time:May 27 2020 10:12:17
Bin version(Wroom 02):1.7.4
For the connection between Pico and ESP-01S, refer to previous post "Connect ESP-01S (ESP8266) to Raspberry Pi Pico/MicroPython, using AT Command".

The TCP server side is implemented using Python3 socketserver. It's copied and modified from Python Document > socketserver.

Both TCP server and client have to be connected in the same WiFi network.



mpyPico_ESP-01S_TCPclient_2021-02-21a.py, client side run on Raspberry Pi Pico, programmed in MicroPython.
import uos
import machine
import utime
"""
Raspberry Pi Pico/MicroPython + ESP-01S exercise

ESP-01S(ESP8266) with AT-command firmware:
AT version:1.7.4.0(May 11 2020 19:13:04)

Pico send AT command to ESP-01S via UART,
- set in station mode
- join AP
- connect to server ip:port 9999
- send text and wait response
"""
#server port & ip hard-coded,
#have to match with server side setting
server_ip="192.168.12.147"
server_port=9999

print()
print("Machine: \t" + uos.uname()[4])
print("MicroPython: \t" + uos.uname()[3])

#indicate program started visually
led_onboard = machine.Pin(25, machine.Pin.OUT)
led_onboard.value(0)     # onboard LED OFF/ON for 0.5/1.0 sec
utime.sleep(0.5)
led_onboard.value(1)
utime.sleep(1.0)
led_onboard.value(0)

uart0 = machine.UART(0, baudrate=115200)
print(uart0)

def sendCMD_waitResp(cmd, uart=uart0, timeout=2000):
    print("CMD: " + cmd)
    uart.write(cmd)
    waitResp(uart, timeout)
    print()
    
def waitResp(uart=uart0, timeout=2000):
    prvMills = utime.ticks_ms()
    resp = b""
    while (utime.ticks_ms()-prvMills)<timeout:
        if uart.any():
            resp = b"".join([resp, uart.read(1)])
    print("resp:")
    try:
        print(resp.decode())
    except UnicodeError:
        print(resp)
        
# send CMD to uart,
# wait and show response without return
def sendCMD_waitAndShow(cmd, uart=uart0):
    print("CMD: " + cmd)
    uart.write(cmd)
    while True:
        print(uart.readline())
        
def espSend(text="test", uart=uart0):
    sendCMD_waitResp('AT+CIPSEND=' + str(len(text)) + '\r\n')
    sendCMD_waitResp(text)
    
sendCMD_waitResp('AT\r\n')          #Test AT startup
sendCMD_waitResp('AT+GMR\r\n')      #Check version information
#sendCMD_waitResp('AT+RESTORE\r\n')  #Restore Factory Default Settings

sendCMD_waitResp('AT+CWMODE?\r\n')  #Query the Wi-Fi mode
sendCMD_waitResp('AT+CWMODE=1\r\n') #Set the Wi-Fi mode 1 = Station mode
#sendCMD_waitResp('AT+CWMODE=2\r\n') #Set the Wi-Fi mode 2 = S0ftAP mode
sendCMD_waitResp('AT+CWMODE?\r\n')  #Query the Wi-Fi mode again

#sendCMD_waitResp('AT+CWLAP\r\n', timeout=10000) #List available APs
sendCMD_waitResp('AT+CWJAP="ssid","password"\r\n', timeout=5000) #Connect to AP
sendCMD_waitResp('AT+CIFSR\r\n')    #Obtain the Local IP Address
#sendCMD_waitResp('AT+CIPSTART="TCP","192.168.12.147",9999\r\n')
sendCMD_waitResp('AT+CIPSTART="TCP","' +
                 server_ip +
                 '",' +
                 str(server_port) +
                 '\r\n')
espSend()

while True:
    print('Enter something:')
    msg = input()
    #sendCMD_waitResp('AT+CIPSTART="TCP","192.168.12.147",9999\r\n')
    sendCMD_waitResp('AT+CIPSTART="TCP","' +
                 server_ip +
                 '",' +
                 str(server_port) +
                 '\r\n')
    espSend(msg)
pyMyTCPServer_2021-02-21a.py, run on Raspberry Pi, programmed in Python.
"""
ref:
https://docs.python.org/3/library/socketserver.html
"""
import socketserver
import platform

print("sys info:")
for info in platform.uname():
    print(info)

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The request handler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # just send back the same data, but upper-cased
        self.request.sendall(self.client_address[0].encode())
        self.request.sendall(self.data.upper())
        self.request.sendall(b'\r\n')

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()

Next:
TCP socket communication between (RPi Pico+ESP-01S) and ESP32 via WiFi

Friday, February 19, 2021

Python tips: print() text in color

Found a simple approach to print() text in color for Python REPL:

To make some of your text more readable, you can use ANSI escape codes to change the colour of the text output in your python program. A good use case for this is to to highlight errors.
~ read here: ozzmaker - Add Colour to Text in Python

Test on Raspberry Pi/Thonny with interpreter of MicroPython on Raspberry Pi Pico:



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:

Friday, December 25, 2020

Raspberry Pi/Python remote control ESP32/Servos via Bluetooth Classic

Last exercise show how Python (on Raspberry Pi) Bluetooth communicate with ESP32 SerialToSerialBT, using pybluez. This exercise extended to send simple single character command to ESP32 via Bluetooth Classic, to control remote servos; with GUI using Tkinter.


pyBTServoClient_20201226.py, for Python3.
import sys
from tkinter import *
from bluetooth import *

print("Python version: ")
print(sys.version)
print("tkinter version: ", TkVersion)
print("=============================");
print("Connect to ESP BT Servo Server")
addr = "24:0A:C4:E8:0F:9A"
print(addr)
service_matches = find_service( address = addr )

if len(service_matches) == 0:
    print("couldn't find the ESP32 BT Servo 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")

def close_window():
    sock.close()
    print("Socket closed")
    print("Window closed")
    root.destroy()

#prepare GUI
root = Tk()
root.geometry("380x200")
root.configure(bg="lightgray") 
root.wm_title("Bluetooth Servo Client")
root.protocol("WM_DELETE_WINDOW", close_window)

CMD_ORG = 'O'
CMD_XDEC = 'A'
CMD_XDEC10 = 'B'
CMD_XINC = 'C'
CMD_XINC10 = 'D'
CMD_YDEC = 'E'
CMD_YDEC10 = 'F'
CMD_YINC = 'G'
CMD_YINC10 = 'H'

def cmdO():
    sock.send(CMD_ORG)
    print("0, 0")

def cmdXdec():
    sock.send(CMD_XDEC)
    print("X-")
    
def cmdXdec10():
    sock.send(CMD_XDEC10)
    print("X-10")

def cmdXinc():
    sock.send(CMD_XINC)
    print("X+")
    
def cmdXinc10():
    sock.send(CMD_XINC10)
    print("X+10")
    
def cmdYdec():
    sock.send(CMD_YDEC)
    print("Y-")
    
def cmdYdec10():
    sock.send(CMD_YDEC10)
    print("Y-10")

def cmdYinc():
    sock.send(CMD_YINC)
    print("Y+")
    
def cmdYinc10():
    sock.send(CMD_YINC10)
    print("Y+10")


ButtonO = Button(root, text="0", command = cmdO, width=6)
ButtonXdec = Button(root, text="X-", command = cmdXdec, width=6)
ButtonXinc = Button(root, text="X+", command = cmdXinc, width=6)
ButtonYdec = Button(root, text="Y-", command = cmdYdec, width=6)
ButtonYinc = Button(root, text="Y+", command = cmdYinc, width=6)
ButtonXdec10 = Button(root, text="X-10", command = cmdXdec10, width=6)
ButtonXinc10 = Button(root, text="X+10", command = cmdXinc10, width=6)
ButtonYdec10 = Button(root, text="Y-10", command = cmdYdec10, width=6)
ButtonYinc10 = Button(root, text="Y+10", command = cmdYinc10, width=6)

ButtonO.grid(row=2, column=2)
ButtonXdec.grid(row=2, column=1)
ButtonXinc.grid(row=2, column=3)
ButtonYdec.grid(row=3, column=2)
ButtonYinc.grid(row=1, column=2)
ButtonXdec10.grid(row=2, column=0)
ButtonXinc10.grid(row=2, column=4)
ButtonYdec10.grid(row=4, column=2)
ButtonYinc10.grid(row=0, column=2)

root.mainloop()

print("\n--- bye ---\n")
The ESP32 code (run on Arduino framework) is in my another blog's post: Arduino-er - ESP32 receive Bluetooth Classic command from Raspberry Pi/Python, to control Servos.

Next:

Saturday, December 19, 2020

Python (on Raspberry Pi) Bluetooth communicate with ESP32 SerialToSerialBT, using pybluez

It's a simple Python example using pybluez extension module, run on Raspberry Pi 4/Raspberry Pi OS, to communicate with ESP32 SerialToSerialBT example.

pybluez (or https://github.com/pybluez/pybluez)is a Python extension module allowing access to system Bluetooth resources.

Install pybluez for Python3, enter the command in Terminal:

$ sudo pip3 install pybluez

Copy and modify pybluez/examples/simple/rfcomm-client.py in Raspberry Pi Thonny.

It's my modified version, pyBTSimpleClient.py

from bluetooth import *

def input_and_send():
    print("\nType something\n")
    while True:
        data = input()
        if len(data) == 0: break
        sock.send(data)
        sock.send("\n")
        
def rx_and_echo():
    sock.send("\nsend anything\n")
    while True:
        data = sock.recv(buf_size)
        if data:
            print(data)
            sock.send(data)
            
#MAC address of ESP32
addr = "24:0A:C4:E8:0F:9A"
#uuid = "94f39d29-7d6d-437d-973b-fba39e49d4ee"
#service_matches = find_service( uuid = uuid, address = addr )
service_matches = find_service( address = addr )

buf_size = 1024;

if len(service_matches) == 0:
    print("couldn't find the SampleServer 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")

#input_and_send()
rx_and_echo()

sock.close()
print("\n--- bye ---\n")


How it run on Raspberry Pi 4/Raspberry Pi OS, communicate with ESP32 (ESP32-DevKitC V4) SerialToSerialBT example. The code in ESP32 side is in my another blog Arduino-er: ESP32 Bluetooth serial example.
 

Next:


ImportError: libbluetooth.so.3


If you reported with error of:
ImportError: libbluetooth.so.3: cannot open shared object file: No such file or directory

Install required library by entering the command in Terminal:
$ sudo apt install bluetooth bluez libbluetooth-dev


Thursday, November 5, 2020

psutil, process and system utilities for Python

psutil (process and system utilities) is a cross-platform library for retrieving information on running processes and system utilization (CPU, memory, disks, network, sensors) in Python. 

In my trial on Ubuntu Desktop for Raspberry Pi, it's installed by default.

Links:
- GitHub - giampaolo /psutil
- psutil documentation



Install Thonny on Ubuntu for Raspberry Pi

Thonny is a very great cross-platform Python IDE. 

To install Thonny on Ubuntu Desktop for Raspberry Pi, simple enter the command in Terminal:

$ sudo apt install thonny

After installed, you can enter "thonny" in Terminal, or search "thonny" in Activities.


Monday, January 28, 2019

A friendly video tutorial to get started with TensorFlow and Deep Learning

A friendly introduction to Deep Learning, taught at the beginner level. Work through introductory exercises across several domains - including computer vision, natural language processing, and structured data classification. Introduce TensorFlow - the world’s most popular open source machine learning library - preview the latest APIs (including Eager Execution), discuss best practices, and point you to recommended educational resources you can use to learn more.

Getting Started with TensorFlow and Deep Learning | SciPy 2018 Tutorial | Josh Gordon

Monday, June 27, 2016

picamera updated 1.11


Picamera updated 1.11, now support Camera V2. If you not yet updated, run the command to update:
$ sudo apt-get update
$ sudo apt-get upgrade

Suggested to update firmware also:
$ sudo rpi-update


Release 1.11 (2016-06-19) Change log (https://picamera.readthedocs.io/en/release-1.11/changelog.html):

1.11 on the surface consists mostly of enhancements, but underneath includes a major re-write of picamera’s core:

  • Direct capture to buffer-protocol objects, such as numpy arrays (#241)
  • Add request_key_frame() method to permit manual request of an I-frame during H264 recording; this is now used implicitly by split_recording() (#257)
  • Added timestamp attribute to query camera’s clock (#212)
  • Added framerate_delta to permit small adjustments to the camera’s framerate to be performed “live” (#279)
  • Added clear() and copy_to() methods to PiCameraCircularIO (#216)
  • Prevent setting attributes on the main PiCamera class to ease debugging in educational settings (#240)
  • Due to the core re-writes in this version, you may require cutting edge firmware (sudo rpi-update) if you are performing unencoded captures, unencoded video recording, motion estimation vector sampling, or manual sensor mode setting.
  • Added property to control preview’s resolution separately from the camera’s resolution (required for maximum resolution previews on the V2 module - #296).

There are also several bug fixes:

  • Fixed basic stereoscopic operation on compute module (#218)
  • Fixed accessing framerate as a tuple (#228)
  • Fixed hang when invalid file format is specified (#236)
  • Fixed multiple bayer captures with capture_sequence() and capture_continuous() (#264)
  • Fixed usage of “falsy” custom outputs with motion_output (#281)
Many thanks to the community, and especially thanks to 6by9 (one of the firmware developers) who’s fielded seemingly endless questions and requests from me in the last couple of months!


Hardware Limits (http://picamera.readthedocs.io/en/release-1.11/fov.html#hardware-limits):

The are additional limits imposed by the GPU hardware that performs all image and video processing:

  • The maximum resolution for MJPEG recording depends partially on GPU memory. If you get “Out of resource” errors with MJPEG recording at high resolutions, try increasing gpu_mem in /boot/config.txt.
  • The maximum horizontal resolution for default H264 recording is 1920. Any attempt to recording H264 video at higher horizontal resolutions will fail.
  • However, H264 high profile level 4.2 has slightly higher limits and may succeed with higher resolutions.
  • The maximum resolution of the V2 camera can cause issues with previews. Currently, picamera runs previews at the same resolution as captures (equivalent to -fp in raspistill). You may need to increase gpu_mem in /boot/config.txt to achieve full resolution operation with the V2 camera module.
  • The maximum framerate of the camera depends on several factors. With overclocking, 120fps has been achieved on a V2 module but 90fps is the maximum supported framerate.
  • The maximum exposure time is currently 6 seconds on the V1 camera module, and 10 seconds on the V2 camera module. Remember that exposure time is limited by framerate, so you need to set an extremely slow framerate before setting shutter_speed.



Monday, June 20, 2016

Raspberry Pi display on 128x64 I2C OLED with SSD1306, using Python


This post show how to install rm-hull/ssd1306 on Raspberry Pi, and run the example to display on 0.96" 128x64 I2C OLED with SSD1306 driver, using Python.


rm-hull/ssd1306 interfacing OLED matrix displays with the SSD1306 (or SH1106) driver in Python using I2C on the Raspberry Pi.

Before install rm-hull/ssd1306, we have to enable I2C on the Raspberry Pi.

Connect a 0.96" 128x64 I2C OLED to Raspberry Pi 2 as shown:
3V3, GND, SDA and SCL respectively.
(my OLED support both 3.3V and 5V)

Download rm-hull/ssd1306 with:
$ wget https://github.com/rm-hull/ssd1306/archive/master.zip

Install the library, switch to the unpacked download folder:
$ sudo python setup.py install

Install some packages:

$ sudo apt-get install i2c-tools python-smbus python-pip
$ sudo pip install pillow

That's, now you can try the example, refer to the video.


Remark@2017-05-07:

Somebody commented with error of:
error in luma.oled setup command : 'extras_require' must be a dictionary whose values are strings or lists of strings containing valid project/version requirement specifiers.

I google to found that it may be because of the version of setuptools.

Install and upgrade setuptools:
$ sudo -H pip install --upgrade pip setuptools

(reference: https://github.com/rm-hull/luma.examples/issues/44)


Related:
NodeMCU/ESP8266 + OLED 0.96" 128x64 I2C SSD1306 using esp8266-oled-ssd1306 library


Updated@2017-05-14:
The driver renamed rm-hull/luma.oled, and updated to support SSD1306 / SSD1322 / SSD1325 / SSD1331 / SH1106 OLED.

Friday, April 29, 2016

Python on Raspberry Pi + Camera Module to take photos for timelapse

I finalized my Python exercise to take photos and generate timelapse mp4, run on Raspberry Pi (Raspbian Jessie) with Camera Module installed.


To run the Python on update Raspbian Jessie (2016-03-18), we have to install python-imaging-tk:
$ sudo apt-get install python-imaging-tk

In you desktop, create a shell script, doLapse.sh, and make it executable to run the Python code. Such that you can easy run it.

doLapse.sh
cd ~/pyLapse
python2 myPiLapse.py

The myPiLapse.py is the Python script (Python 2) to take photos. Select "Timelapse" tab and click the "Start TimeLapse" button to start and click the "Stop TimeLapse" button to stop. All the photos will be save in sub-directory named with timestamp.

myPiLapse.py
import picamera
import Tkinter as tk
import ttk
import time
from PIL import ImageTk, Image
from threading import Thread
import io
import sys
from pkg_resources import require
from fractions import Fraction
from time import sleep
import tkFont
import os

#reference:
# http://picamera.readthedocs.org/en/latest/api_camera.html

RQS_0=0
RQS_QUIT=1
RQS_CAPTURE=2
RQS_STARTLAPSE = 3
RQS_STOPLAPSE = 4
RQS_LAPSEEND = 5
rqs=RQS_0
rqsUpdateSetting=True

LAPSE_RQS_0 = 0
LAPSE_RQS_STOP = 1
lapse_rqs = LAPSE_RQS_0

PREFIX_IMAGE = "img_"
PREFIX_LAPSE = "lapse_"
prefix = PREFIX_IMAGE



def camHandler():
    global rqs
    rqs = RQS_0

    timelapsing = False
    
    camera = picamera.PiCamera()
    #stream = io.BytesIO()

    #set default
    camera.sharpness = 0
    camera.contrast = 0
    camera.brightness = 50
    camera.saturation = 0
    camera.ISO = 0
    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.rotation = 270
    camera.hflip = False
    camera.vflip = False
    camera.crop = (0.0, 0.0, 1.0, 1.0)
    #camera.resolution = (1024, 768)
    camera.resolution = (400, 300)
    #end of set default
    #camera.start_preview()

    while rqs != RQS_QUIT:
        #check if need update setting
        global rqsUpdateSetting
        if ((rqsUpdateSetting == True) and (timelapsing == False)):
            rqsUpdateSetting = False
            camera.sharpness = scaleSharpness.get()
            camera.contrast = scaleContrast.get()
            camera.brightness = scaleBrightness.get()
            camera.saturation = scaleSaturation.get()
            camera.exposure_compensation = scaleExpCompensation.get()
            camera.iso = varIso.get()
            camera.drc_strength = varDrc.get()
            camera.exposure_mode = varExpMode.get()
            camera.meter_mode = varMeterMode.get()
            camera.rotation = varRotation.get()
            camera.vflip = varVFlip.get()
            camera.hflip = varHFlip.get()
            camera.image_denoise = varDenoise.get()

            awb_mode_setting = varAwbMode.get()
            labelAwbVar.set(awb_mode_setting)
            camera.awb_mode = awb_mode_setting

            if awb_mode_setting == "off":
                gr = scaleGainRed.get()
                gb = scaleGainBlue.get()
                gAwb = (gr, gb)
                camera.awb_gains = gAwb
                labelAwbVar.set(awb_mode_setting + " : "
                    + str(gAwb))

            image_effect_setting = varImageEffect.get()
            labelImageEffectVar.set(image_effect_setting)
            camera.image_effect = image_effect_setting

            if image_effect_setting == 'solarize':
                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())
                labelImageEffectVar.set(image_effect_setting + " " + str(solarize_para))
                camera.image_effect_params = solarize_para
            elif image_effect_setting == 'colorpoint':
                camera.image_effect_params = quadrantVar.get()
                labelImageEffectVar.set(image_effect_setting + " " + str(quadrantVar.get()))
            elif image_effect_setting == 'colorbalance':
                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())
                labelImageEffectVar.set(image_effect_setting + " " + str(colorbalance_para))
                camera.image_effect_params = colorbalance_para
            elif image_effect_setting == 'colorswap':
                labelImageEffectVar.set(image_effect_setting + " " + str(cbColorswap_dir_Var.get()))
                camera.image_effect_params = cbColorswap_dir_Var.get()
            elif image_effect_setting == 'posterise':
                labelImageEffectVar.set(image_effect_setting + " " + str(scPosterise_steps_Var.get()))
                camera.image_effect_params = scPosterise_steps_Var.get()
            elif image_effect_setting == 'blur':
                labelImageEffectVar.set(image_effect_setting + " " + str(scBlur_size_Var.get()))
                camera.image_effect_params = scBlur_size_Var.get()
            elif image_effect_setting == 'film':
                film_para = (
                    scFilm_strength_Var.get(),
                    scFilm_u_Var.get(),
                    scFilm_v_Var.get())
                labelImageEffectVar.set(image_effect_setting + " " + str(film_para))
                camera.image_effect_params = film_para
            elif image_effect_setting == 'watercolor':
                if cbWatercolor_uv_Var.get():
                    watercolor_para = (
                        scWatercolor_u_Var.get(),
                        scWatercolor_v_Var.get())
                    labelImageEffectVar.set(image_effect_setting + " " + str(watercolor_para))
                    camera.image_effect_params = watercolor_para
                else:
                    watercolor_para = ()
                    labelImageEffectVar.set(image_effect_setting + " " + str(watercolor_para))
                    camera.image_effect_params = watercolor_para

        if rqs == RQS_CAPTURE:
            global prefix
            print("Capture")
            rqs=RQS_0
            timeStamp = time.strftime("%Y%m%d-%H%M%S")
            jpgFile=prefix+timeStamp+'.jpg'
            
            #camera.resolution = (2592, 1944)    #set photo size

            varRes = varResolution.get()
            if varRes == '640x480':
                settingResolution = (640, 480)
            elif varRes == '800x600':
                settingResolution = (800, 600)
            elif varRes == '1280x720':
                settingResolution = (1280, 720)
            elif varRes == '1296x730':
                settingResolution = (1296, 730)
            elif varRes == '1296x972':
                settingResolution = (1296, 972)
            elif varRes == '1600x1200':
                settingResolution = (1600, 1200)
            elif varRes == '1920x1080':
                settingResolution = (1920, 1080)
            else:
                settingResolution = (2592, 1944)

            camera.resolution = settingResolution

            shutterSpeedSetting = varShutterSpeed.get()
            settingQuality = varQuality.get()
            
            if shutterSpeedSetting == 'normal':
                camera.capture(jpgFile, quality=settingQuality)
            else:
                orgFrameRate = camera.framerate

                if shutterSpeedSetting == '6 sec':
                    camera.framerate = Fraction(1, 6)
                    camera.shutter_speed = 6000000
                elif shutterSpeedSetting == '5 sec':
                    camera.framerate = Fraction(1, 5)
                    camera.shutter_speed = 5000000
                elif shutterSpeedSetting == '4 sec':
                    camera.framerate = Fraction(1, 4)
                    camera.shutter_speed = 4000000
                elif shutterSpeedSetting == '3 sec':
                    camera.framerate = Fraction(1, 3)
                    camera.shutter_speed = 3000000
                elif shutterSpeedSetting == '2 sec':
                    camera.framerate = Fraction(1, 2)
                    camera.shutter_speed = 2000000
                elif shutterSpeedSetting == '1 sec':
                    camera.framerate = Fraction(1, 1)
                    camera.shutter_speed = 1000000
                elif shutterSpeedSetting == '1/2 sec':
                    camera.framerate = Fraction(1, 1)
                    camera.shutter_speed = 500000
                elif shutterSpeedSetting == '1/4 sec':
                    camera.framerate = Fraction(1, 1)
                    camera.shutter_speed = 250000
                #sleep(1)
                camera.capture(jpgFile, quality=settingQuality)
                camera.framerate = orgFrameRate
                camera.shutter_speed = 0

            camera.resolution = (400, 300)      #resume preview size
            
            labelCapVal.set(jpgFile)
        elif rqs == RQS_STARTLAPSE:
            rqs=RQS_0
            labelLapseText.set("Timelapse started.")
            btnStartLapse.config(state=tk.DISABLED)
            btnStopLapse.config(state=tk.NORMAL)

            timelapsing = True

            lapseDir = time.strftime("%Y%m%d-%H%M%S")
            os.makedirs(lapseDir)
                
            prefix = lapseDir + "/" + PREFIX_LAPSE

            camera.shutter_speed = camera.exposure_speed
            camera.exposure_mode = 'off'
            g = camera.awb_gains
            camera.awb_mode = 'off'
            camera.awb_gains = g
            
            startTimelapseHandle()
        elif rqs == RQS_STOPLAPSE:
            rqs=RQS_0
            global lapse_rqs
            lapse_rqs = LAPSE_RQS_STOP

            labelLapseText.set("Timelapse stopped.")
            btnStartLapse.config(state=tk.NORMAL)
            btnStopLapse.config(state=tk.DISABLED)

            prefix = PREFIX_IMAGE
            timelapsing = False
            rqsUpdateSetting = True
        elif rqs == RQS_LAPSEEND:
            rqs=RQS_0
            labelLapseText.set("Timelapse ended.")
            btnStartLapse.config(state=tk.NORMAL)
            btnStopLapse.config(state=tk.DISABLED)
            prefix = PREFIX_IMAGE
            timelapsing = False
            rqsUpdateSetting = True
        else:
            stream = io.BytesIO()
            camera.capture(stream, format='jpeg', quality=40)
            stream.seek(0)
            tmpImage = Image.open(stream)
            tmpImg = ImageTk.PhotoImage(tmpImage)
            previewPanel.configure(image = tmpImg)
            #sleep(0.5)
                
    print("Quit")        
    #camera.stop_preview()

def startCamHandler():
    camThread = Thread(target=camHandler)
    camThread.start()

def TimelapseHandle():
    global lapse_rqs
    global rqs
    global prefix

    print("TimelapseHandle started")

    numberOfShot = scaleNumOfShot.get()
    interval = scaleIntervalSec.get()
    
    while True:
        
        if lapse_rqs == LAPSE_RQS_STOP:
            lapse_rqs = LAPSE_RQS_0
            print('LAPSE_RQS_STOP')
            break
        print(numberOfShot)

        rqs = RQS_CAPTURE

        if numberOfShot != 0:
            numberOfShot = numberOfShot-1
            labelLapseText.set(str(numberOfShot))
            if numberOfShot == 0:
                break;
        
        sleep(interval)
        
    rqs = RQS_LAPSEEND
    print("TimelapseHandle ended")
    
def startTimelapseHandle():
    print("TimelapseHandle starting...")
    global lapse_rqs
    lapse_rqs = LAPSE_RQS_0
    timelapseThread = Thread(target=TimelapseHandle)
    timelapseThread.start()

def quit():
    print("quit()")
    global rqs
    global lapse_rqs
    rqs=RQS_QUIT
    lapse_rqs=LAPSE_RQS_STOP

    global tkTop
    tkTop.destroy()

def capture():
    global rqs
    rqs = RQS_CAPTURE
    labelCapVal.set("capturing")

def cbScaleSetting(new_value):
    global rqsUpdateSetting
    rqsUpdateSetting = True

def cbButtons():
    global rqsUpdateSetting
    rqsUpdateSetting = True
    
def lapseScaleSetting(new_value):
    #do nothing
    pass

def cmdStartLapse():
    global rqs
    labelLapseText.set("Timelapse starting...")
    rqs = RQS_STARTLAPSE

def cmdStopLapse():
    global rqs
    labelLapseText.set("Timelapse stopping...")
    rqs = RQS_STOPLAPSE

tkTop = tk.Tk()
tkTop.wm_title("helloraspberrypi.blogspot.com")
tkTop.geometry('1000x650')

myFont = tkFont.Font(size=16)

previewWin = tk.Toplevel(tkTop)
previewWin.title('Preview')
previewWin.geometry('400x300')
previewPanel = tk.Label(previewWin)
previewPanel.pack(side = "bottom", fill = "both", expand = "yes")

#tkButtonQuit = tk.Button(tkTop, text="Quit", command=quit).pack()

tkButtonCapture = tk.Button(
    tkTop, text="Capture", command=capture)
tkButtonCapture.pack()

SCALE_WIDTH = 980;

labelCapVal = tk.StringVar()
tk.Label(tkTop, textvariable=labelCapVal).pack()

notebook = ttk.Notebook(tkTop)
frame1 = ttk.Frame(notebook)
frame2 = ttk.Frame(notebook)
frame3 = ttk.Frame(notebook)
frame4 = ttk.Frame(notebook)
frame5 = ttk.Frame(notebook)
notebook.add(frame1, text='Setting')
notebook.add(frame2, text='White Balance')
notebook.add(frame3, text='Image Effect')
notebook.add(frame4, text='Control')
notebook.add(frame5, text='Timelapse')
notebook.pack()

# Tab Setting
tk.Label(frame1, text=require('picamera')).pack()

scaleSharpness = tk.Scale(
    frame1,
    from_=-100, to=100,
    length=SCALE_WIDTH,
    orient=tk.HORIZONTAL,
    label="sharpness",
    command=cbScaleSetting)
scaleSharpness.set(0)
scaleSharpness.pack(anchor=tk.CENTER)

scaleContrast = tk.Scale(
    frame1,
    from_=-100, to=100,
    length=SCALE_WIDTH,
    orient=tk.HORIZONTAL,
    label="contrast",
    command=cbScaleSetting)
scaleContrast.set(0)
scaleContrast.pack(anchor=tk.CENTER)

scaleBrightness = tk.Scale(
    frame1,
    from_=0, to=100,
    length=SCALE_WIDTH,
    orient=tk.HORIZONTAL,
    label="brightness",
    command=cbScaleSetting)
scaleBrightness.set(50)
scaleBrightness.pack(anchor=tk.CENTER)

scaleSaturation = tk.Scale(
    frame1,
    from_=-100, to=100,
    length=SCALE_WIDTH,
    orient=tk.HORIZONTAL,
    label="saturation",
    command=cbScaleSetting)
scaleSaturation.set(0)
scaleSaturation.pack(anchor=tk.CENTER)

scaleExpCompensation = tk.Scale(
    frame1,
    from_=-25, to=25,
    length=SCALE_WIDTH,
    orient=tk.HORIZONTAL,
    label="exposure_compensation",
    command=cbScaleSetting)
scaleExpCompensation.set(0)
scaleExpCompensation.pack(anchor=tk.CENTER)

lfExpMode = ttk.LabelFrame(frame1, text="Exposure Mode")
lfExpMode.pack(fill="x")
varExpMode = tk.StringVar()
varExpMode.set('auto')
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='off',value='off',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='auto',value='auto',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='night',value='night',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='nightpreview',value='nightpreview',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='backlight',value='backlight',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='spotlight',value='spotlight',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='sports',value='sports',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='snow',value='snow',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='beach',value='beach',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='verylong',value='verylong',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='fixedfps',value='fixedfps',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='antishake',value='antishake',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfExpMode, variable=varExpMode,
        text='fireworks',value='fireworks',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

lfMeterMode = ttk.LabelFrame(frame1, text="Meter Mode")
lfMeterMode.pack(fill="x")
varMeterMode = tk.StringVar()
varMeterMode.set('average')
tk.Radiobutton(lfMeterMode, variable=varMeterMode,
        text='average',value='average',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfMeterMode, variable=varMeterMode,
        text='spot',value='spot',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfMeterMode, variable=varMeterMode,
        text='backlit',value='backlit',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfMeterMode, variable=varMeterMode,
        text='matrix',value='matrix',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

# Tab White Balance

lfAwbMode = ttk.LabelFrame(frame2, text="awb_mode")
lfAwbMode.pack(fill="x")
lfAwbGains = ttk.LabelFrame(frame2, text="awb_gains")
lfAwbGains.pack(fill="x")

labelAwbVar = tk.StringVar()
tk.Label(lfAwbMode, textvariable=labelAwbVar).pack()

#--
AWB_MODES = [
    ("off (set by awb_gains)", "off"),
    ("auto", "auto"),
    ("sunlight", "sunlight"),
    ("cloudy", "cloudy"),
    ("shade", "shade"),
    ("tungsten", "tungsten"),
    ("fluorescent", "fluorescent"),
    ("incandescent", "incandescent"),
    ("flash", "flash"),
    ("horizon", "horizon"),
    ]

varAwbMode = tk.StringVar()
varAwbMode.set("auto")
for text, awbmode in AWB_MODES:
    awbModeBtns = tk.Radiobutton(
        lfAwbMode,
        text=text,
        variable=varAwbMode,
        value=awbmode,
        command=cbButtons)
    awbModeBtns.pack(anchor=tk.W)
#--
scaleGainRed = tk.Scale(
    lfAwbGains,
    from_=0.0, to=8.0,
    resolution=0.1,
    length=SCALE_WIDTH,
    orient=tk.HORIZONTAL,
    label="Red",
    command=cbScaleSetting)
scaleGainRed.set(0.0)
scaleGainRed.pack(anchor=tk.CENTER)

scaleGainBlue = tk.Scale(
    lfAwbGains,
    from_=0.0, to=8.0,
    resolution=0.1,
    length=SCALE_WIDTH,
    orient=tk.HORIZONTAL,
    label="Blue",
    command=cbScaleSetting)
scaleGainBlue.set(0.0)
scaleGainBlue.pack(anchor=tk.CENTER)

# Tab Image Effect
#For Image effects, ref:
#http://picamera.readthedocs.org/en/latest/api_camera.html?highlight=effect#picamera.camera.PiCamera.image_effect
labelImageEffectVar = tk.StringVar()
tk.Label(frame3, textvariable=labelImageEffectVar).pack()
#-- image_effect

varImageEffect = tk.StringVar()
varImageEffect.set('none')

lfNoParaOpts1 = ttk.Frame(frame3)
lfNoParaOpts1.pack(fill="x")

tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
        text='none',value='none',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
        text='negative',value='negative',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
        text='sketch',value='sketch',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
        text='denoise',value='denoise',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
        text='emboss',value='emboss',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
        text='oilpaint',value='oilpaint',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
        text='hatch',value='hatch',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts1, variable=varImageEffect,
        text='gpen',value='gpen',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

lfNoParaOpts2 = ttk.Frame(frame3)
lfNoParaOpts2.pack(fill="x")
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
        text='pastel',value='pastel',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
        text='saturation',value='saturation',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
        text='washedout',value='washedout',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
        text='cartoon',value='cartoon',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
        text='deinterlace1',value='deinterlace1',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfNoParaOpts2, variable=varImageEffect,
        text='deinterlace2',value='deinterlace2',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

lfSolarize = ttk.LabelFrame(frame3, text="solarize")
lfSolarize.pack(fill="x")

tk.Radiobutton(lfSolarize, variable=varImageEffect,
        text='solarize',value='solarize',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

cbSolarize_yuv_Var = tk.BooleanVar()
tk.Checkbutton(lfSolarize, text="yuv",
    variable=cbSolarize_yuv_Var, command=cbButtons).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=cbScaleSetting).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=cbScaleSetting).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=cbScaleSetting).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=cbScaleSetting).pack(anchor=tk.W, side=tk.LEFT)

lfwatercolor = ttk.LabelFrame(frame3, text="watercolor")
lfwatercolor.pack(fill="x")
tk.Radiobutton(lfwatercolor, variable=varImageEffect,
        text='watercolor',value='watercolor',command=cbButtons).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=cbButtons).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=cbScaleSetting).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=cbScaleSetting).pack(anchor=tk.W, side=tk.LEFT)

lffilm = ttk.LabelFrame(frame3, text="film")
lffilm.pack(fill="x")
tk.Radiobutton(lffilm, variable=varImageEffect,
        text='film',value='film',command=cbButtons).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=cbScaleSetting).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=cbScaleSetting).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=cbScaleSetting).pack(anchor=tk.W, side=tk.LEFT)

lfblur = ttk.LabelFrame(frame3, text="blur")
lfblur.pack(fill="x")
tk.Radiobutton(lfblur, variable=varImageEffect,
        text='blur',value='blur',command=cbButtons).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=cbScaleSetting).pack(anchor=tk.W, side=tk.LEFT)

lfcolorswap = ttk.LabelFrame(frame3, text="colorswap")
lfcolorswap.pack(fill="x")
tk.Radiobutton(lfcolorswap, variable=varImageEffect,
        text='colorswap',value='colorswap',command=cbButtons).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=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

lfposterise = ttk.LabelFrame(frame3, text="posterise")
lfposterise.pack(fill="x")
tk.Radiobutton(lfposterise, variable=varImageEffect,
        text='posterise',value='posterise',command=cbButtons).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=cbScaleSetting).pack(anchor=tk.W, side=tk.LEFT)

lfcolorpoint = ttk.LabelFrame(frame3, text="colorpoint")
lfcolorpoint.pack(fill="x")
tk.Radiobutton(lfcolorpoint, variable=varImageEffect,
        text='colorpoint',value='colorpoint',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
quadrantVar = tk.IntVar()
quadrantVar.set(0)
tk.Radiobutton(lfcolorpoint, text="green",
    variable=quadrantVar, value=0, command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfcolorpoint, text="red/yellow",
    variable=quadrantVar, value=1, command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfcolorpoint, text="blue",
    variable=quadrantVar, value=2, command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfcolorpoint, text="purple",
    variable=quadrantVar, value=3, command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

lfcolorbalance = ttk.LabelFrame(frame3, text="colorbalance: I can't see the effect!")
lfcolorbalance.pack(fill="x")
tk.Radiobutton(lfcolorbalance, variable=varImageEffect,
        text='colorbalance',value='colorbalance',command=cbButtons).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=cbScaleSetting).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=cbScaleSetting).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=cbScaleSetting).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=cbScaleSetting).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=cbScaleSetting).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=cbScaleSetting).pack(anchor=tk.W, side=tk.LEFT)
# Tab Control

lfISO = ttk.LabelFrame(frame4, text="ISO")
lfISO.pack(fill="x")
varIso = tk.IntVar()
tk.Radiobutton(lfISO, variable=varIso,
        text='auto',value='0',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfISO, variable=varIso,
        text='100',value='100',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfISO, variable=varIso,
        text='200',value='200',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfISO, variable=varIso,
        text='320',value='320',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfISO, variable=varIso,
        text='400',value='400',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfISO, variable=varIso,
        text='500',value='500',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfISO, variable=varIso,
        text='640',value='640',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfISO, variable=varIso,
        text='800',value='800',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

lfDRC = ttk.LabelFrame(frame4, text="Dynamic Range Compression")
lfDRC.pack(fill="x")
varDrc = tk.StringVar()
varDrc.set('off')
tk.Radiobutton(lfDRC, variable=varDrc,
        text='off',value='off',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfDRC, variable=varDrc,
        text='low',value='low',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfDRC, variable=varDrc,
        text='medium',value='medium',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfDRC, variable=varDrc,
        text='high',value='high',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

lfRotation = ttk.LabelFrame(frame4, text="Rotation")
lfRotation.pack(fill="x")
varRotation = tk.IntVar()
varRotation.set(270)
tk.Radiobutton(lfRotation, variable=varRotation,
        text='0',value='0',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfRotation, variable=varRotation,
        text='90',value='90',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfRotation, variable=varRotation,
        text='180',value='180',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfRotation, variable=varRotation,
        text='270',value='270',command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)


lfFlip = ttk.LabelFrame(frame4, text="Flip")
lfFlip.pack(fill="x")
varHFlip = tk.BooleanVar()
varHFlip.set(False)
tk.Checkbutton(lfFlip, text="hflip",
    variable=varHFlip, command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
varVFlip = tk.BooleanVar()
varVFlip.set(False)
tk.Checkbutton(lfFlip, text="vflip",
    variable=varVFlip, command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)
# Resolution
lfResolution = ttk.LabelFrame(frame4, text="Resolution")
lfResolution.pack(fill="x")
varResolution = tk.StringVar()
varResolution.set('1280x720')
tk.Radiobutton(lfResolution, variable=varResolution,
        text='640x480',value='640x480').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfResolution, variable=varResolution,
        text='800x400',value='800x600').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfResolution, variable=varResolution,
        text='1280x720',value='1280x720').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfResolution, variable=varResolution,
        text='1296x730',value='1296x730').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfResolution, variable=varResolution,
        text='1296x972',value='1296x972').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfResolution, variable=varResolution,
        text='1600x1200',value='1600x1200').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfResolution, variable=varResolution,
        text='1920x1080',value='1920x1080').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(lfResolution, variable=varResolution,
        text='2592x1944',value='2592x1944').pack(anchor=tk.W, side=tk.LEFT)

# Quality
lfQuality = ttk.LabelFrame(frame4, text="Quality")
lfQuality.pack(fill="x")
varQuality = tk.IntVar()
varQuality.set(85)
tk.Scale(lfQuality, from_=10, to=100,
    orient=tk.HORIZONTAL, length=600, label="%",
    variable=varQuality).pack(anchor=tk.W, side=tk.LEFT)

lfDenoise = ttk.LabelFrame(frame4, text="image_denoise")
lfDenoise.pack(fill="x")
varDenoise = tk.BooleanVar()
varDenoise.set(True)
tk.Checkbutton(lfDenoise, text="image_denoise",
    variable=varDenoise, command=cbButtons).pack(anchor=tk.W, side=tk.LEFT)

# Shutter Speed
lfShuuterSpeed = ttk.LabelFrame(frame4, text="Slow Shutter (caution: NOT work as expected)")
lfShuuterSpeed.pack(fill="x")
frameShuuterSpeed1 = tk.Frame(lfShuuterSpeed)
frameShuuterSpeed1.pack(fill="x")
frameShuuterSpeed2 = tk.Frame(lfShuuterSpeed)
frameShuuterSpeed2.pack(fill="x")

varShutterSpeed = tk.StringVar()
varShutterSpeed.set('normal')
tk.Radiobutton(frameShuuterSpeed1, variable=varShutterSpeed,
        text='normal',value='normal').pack(anchor=tk.W, side=tk.LEFT)

tk.Radiobutton(frameShuuterSpeed2, variable=varShutterSpeed,
        text='1/4 sec',value='1/4 sec').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(frameShuuterSpeed2, variable=varShutterSpeed,
        text='1/2 sec',value='1/2 sec').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(frameShuuterSpeed2, variable=varShutterSpeed,
        text='1 sec',value='1 sec').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(frameShuuterSpeed2, variable=varShutterSpeed,
        text='2 sec',value='2 sec').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(frameShuuterSpeed2, variable=varShutterSpeed,
        text='3 sec',value='3 sec').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(frameShuuterSpeed2, variable=varShutterSpeed,
        text='4 sec',value='4 sec').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(frameShuuterSpeed2, variable=varShutterSpeed,
        text='5 sec',value='5 sec').pack(anchor=tk.W, side=tk.LEFT)
tk.Radiobutton(frameShuuterSpeed2, variable=varShutterSpeed,
        text='6 sec',value='6 sec').pack(anchor=tk.W, side=tk.LEFT)

#---------------------------------------
#TimeLapse

lfTimelapse = ttk.LabelFrame(frame5, text="Timelapse")
lfTimelapse.pack(fill="x")

btnStartLapse = tk.Button(
    lfTimelapse, text="Start TimeLapse", state=tk.NORMAL,
    font=myFont, command=cmdStartLapse)
btnStartLapse.pack(anchor=tk.W, side=tk.LEFT)

btnStopLapse = tk.Button(
    lfTimelapse, text="Stop TimeLapse", state=tk.DISABLED,
    font=myFont, command=cmdStopLapse)
btnStopLapse.pack(anchor=tk.W, side=tk.LEFT)

labelLapseText = tk.StringVar()
tk.Label(lfTimelapse, textvariable=labelLapseText).pack(anchor=tk.W, side=tk.LEFT)

lfInterval = ttk.LabelFrame(frame5, text="interval")
lfInterval.pack(fill="x")

scaleIntervalSec = tk.Scale(
    lfInterval,
    from_=5, to=60,
    resolution=5,
    length=SCALE_WIDTH,
    orient=tk.HORIZONTAL,
    label="second",
    font=myFont,
    command=lapseScaleSetting)
scaleIntervalSec.set(5)
scaleIntervalSec.pack(anchor=tk.CENTER)

lfNumOfShot = ttk.LabelFrame(frame5, text="Number of shot")
lfNumOfShot.pack(fill="x")

scaleNumOfShot= tk.Scale(
    lfNumOfShot,
    from_=0, to=300,
    resolution=1,
    length=SCALE_WIDTH,
    orient=tk.HORIZONTAL,
    label="# (0 = continue until 'Stop Timelapse' button pressed)",
    font=myFont,
    command=lapseScaleSetting)
scaleNumOfShot.set(0)
scaleNumOfShot.pack(anchor=tk.CENTER)

labelLockedSetting = tk.StringVar()
tk.Label(frame5, textvariable=labelLockedSetting).pack()
#=======================================
print("start")
startCamHandler()

tk.mainloop()


Once photos taking stopped, switch to the new directory under pyLapse. All the photos named with timestamp.


Samples:
- Raspberry Pi Camera Module Timelapse video
Assembling of 2WD Smart Robot Car
Thunder (timelapse) captured by Raspberry Pi NoIR Camera Module
Raspberry Pi NoIR Camera V2 Timelapse
RPi Camera Module NoIR V2 Timelapse - 2016-05-08