#!/usr/bin/env python3
# coding: Latin-1
# Creates a web-page interface for DiddyBorg
# Import library functions we need
import PicoBorgRev3 as PicoBorgRev
import time
import sys
import threading
import socketserver as SocketServer
import picamera
import picamera.array
import cv2
import datetime
# Settings for the web-page
webPort = 8080 # Port number for the web-page, 80 is what web-pages normally use
imageWidth = 192 # Width of the captured image in pixels
imageHeight = 192 # Height of the captured image in pixels
frameRate = 10 # Number of images to capture per second
displayRate = 2 # Number of images to request per second
photoDirectory = '/home/pi' # Directory to save photos to
# Global values
global PBR
global lastFrame
global lockFrame
global camera
global processor
global running
global watchdog
running = True
# Setup the PicoBorg Reverse
PBR = PicoBorgRev.PicoBorgRev()
#PBR.i2cAddress = 0x44 # Uncomment and change the value if you have changed the board address
PBR.Init()
if not PBR.foundChip:
boards = PicoBorgRev.ScanForPicoBorgReverse()
if len(boards) == 0:
print ('No PicoBorg Reverse found, check you are attached :)')
else:
print ('No PicoBorg Reverse at address %02X, but we did find boards:') % (PBR.i2cAddress)
for board in boards:
print (' %02X (%d)' )% (board, board)
print ('If you need to change the I²C address change the setup line so it is correct, e.g.')
print ('PBR.i2cAddress = 0x%02X') % (boards[0])
sys.exit()
#PBR.SetEpoIgnore(True) # Uncomment to disable EPO latch, needed if you do not have a switch / jumper
PBR.SetCommsFailsafe(False) # Disable the communications failsafe
PBR.ResetEpo()
# Power settings
voltageIn = 1.2 * 10 # Total battery voltage to the PicoBorg Reverse
voltageOut = 6.0 # Maximum motor voltage
# Setup the power limits
if voltageOut > voltageIn:
maxPower = 1.0
else:
maxPower = voltageOut / float(voltageIn)
# Timeout thread
class Watchdog(threading.Thread):
def __init__(self):
super(Watchdog, self).__init__()
self.event = threading.Event()
self.terminated = False
self.start()
self.timestamp = time.time()
def run(self):
timedOut = True
# This method runs in a separate thread
while not self.terminated:
# Wait for a network event to be flagged for up to one second
if timedOut:
if self.event.wait(1):
# Connection
print ('Reconnected...')
timedOut = False
self.event.clear()
else:
if self.event.wait(1):
self.event.clear()
else:
# Timed out
print ('Timed out...')
timedOut = True
PBR.MotorsOff()
# Image stream processing thread
class StreamProcessor(threading.Thread):
def __init__(self):
super(StreamProcessor, self).__init__()
self.stream = picamera.array.PiRGBArray(camera)
self.event = threading.Event()
self.terminated = False
self.start()
self.begin = 0
def run(self):
global lastFrame
global lockFrame
# This method runs in a separate thread
while not self.terminated:
# Wait for an image to be written to the stream
if self.event.wait(1):
try:
# Read the image and save globally
self.stream.seek(0)
flippedArray = cv2.flip(self.stream.array, -1) # Flips X and Y
retval, thisFrame = cv2.imencode('.jpg', flippedArray)
del flippedArray
lockFrame.acquire()
lastFrame = thisFrame
lockFrame.release()
finally:
# Reset the stream and event
self.stream.seek(0)
self.stream.truncate()
self.event.clear()
# Image capture thread
class ImageCapture(threading.Thread):
def __init__(self):
super(ImageCapture, self).__init__()
self.start()
def run(self):
global camera
global processor
print ('Start the stream using the video port')
camera.capture_sequence(self.TriggerStream(), format='bgr', use_video_port=True)
print ('Terminating camera processing...')
processor.terminated = True
processor.join()
print ('Processing terminated.')
# Stream delegation loop
def TriggerStream(self):
global running
while running:
if processor.event.is_set():
time.sleep(0.01)
else:
yield processor.stream
processor.event.set()
# Class used to implement the web server
class WebServer(SocketServer.BaseRequestHandler):
def handle(self):
global PBR
global lastFrame
global watchdog
# Get the HTTP request data
reqData = self.request.recv(1024).strip()
reqData = reqData.decode('utf-8')
reqData = reqData.split('\n')
# Get the URL requested
getPath = ''
for line in reqData:
if line.startswith('GET'):
parts = line.split(' ')
getPath = parts[1]
break
watchdog.event.set()
if getPath.startswith('/cam.jpg'):
# Camera snapshot
lockFrame.acquire()
sendFrame = lastFrame
lockFrame.release()
if sendFrame is not None:
self.send(sendFrame.tobytes())
elif getPath.startswith('/off'):
# Turn the drives off
httpText = '
'
httpText += 'Speeds: 0 %, 0 %'
httpText += '
'
self.send(httpText)
PBR.MotorsOff()
elif getPath.startswith('/set/'):
# Motor power setting: /set/driveLeft/driveRight
parts = getPath.split('/')
# Get the power levels
if len(parts) >= 4:
try:
driveLeft = float(parts[2])
driveRight = float(parts[3])
except:
# Bad values
driveRight = 0.0
driveLeft = 0.0
else:
# Bad request
driveRight = 0.0
driveLeft = 0.0
# Ensure settings are within limits
if driveRight < -1:
driveRight = -1
elif driveRight > 1:
driveRight = 1
if driveLeft < -1:
driveLeft = -1
elif driveLeft > 1:
driveLeft = 1
# Report the current settings
percentLeft = driveLeft * 100.0;
percentRight = driveRight * 100.0;
httpText = '