Diddiborg with Ultraborg over Web UI
Forums:
Hi !
Need some help with my project.
I like to run the Diddiborg with Ultraborg over Web UI. I'm using the code I found on this Forum. https://www.piborg.org/node/1846
Two HC-SR04 are in the front of the Diddiborg. I get correct readings in the Web UI.
But when I try to use "Semi" or "Auto-mode" there is no reaction.
Another thing is to control LED lights over the Web UI but I'm not sure how to put inside. Right now I turn on the lights when I start the script and turn off when the script ends. >> # Settings Light
Thanks :-)
Andreas

piborg
Sat, 01/23/2016 - 20:43
Permalink
Diddiborg with Ultraborg over Web UI
The reason that semi-automatic and automatic modes are not really working is that they are currently unfinished.
In its current state the automatic mode does nothing at all.
The semi-automatic mode should:
I do not remember if they have made any progress with the functionality since that version, but I think they were intending to make the automatic mode move around in a simlar fashion to the automaic carpet cleaners.
What you would need to do to make the LEDs do something entirely depends on what you want them to do.
For example if you were to change the values inside the various movement mode sections then they would show you which mode you are in.
e.g.
if
movementMode
=
=
MANUAL_MODE:
# User movement, wait a second before checking again
GPIO.output(
40
,
False
)
GPIO.output(
38
,
False
)
GPIO.output(
36
,
True
)
time.sleep(
1.0
)
4ndr345
Sat, 01/23/2016 - 23:14
Permalink
Hi again !
Thanks for the hint to see which mode I'm in.
The semi-automatic mode doesn't work for me and I have no idea whats wrong.
Doesn't slow down and don't stop below 50 mm.
elif
movementMode
=
=
SEMI_AUTO_MODE:
# Semi-automatic movement mode, checks twice per second
# Ultrasonic distance readings semi auto mode
print
'Semi auto mode'
GPIO.output(
40
,
True
)
GPIO.output(
38
,
False
)
GPIO.output(
36
,
True
)
# Get the readings from ultra sensors
print
'Reading distance'
distance1
=
int
(UB.GetDistance1())
distance2
=
int
(UB.GetDistance2())
# Set critical allowed distance to object in front
if
distance1 <
=
50
:
PBR.MotorsOff()
elif
distance1 <
=
100
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
4ndr345
Sun, 01/24/2016 - 00:09
Permalink
Finally I found the problem
Finally I found the problem with the semi-automatic mode.
Instead <=50 I have 150 and <=100 I have 300.
Diddiborg slow down and stop directly before the obstacle.
Web UI show 50 mm to obstacle...
elif
movementMode
=
=
SEMI_AUTO_MODE:
# Semi-automatic movement mode, checks twice per second
# Ultrasonic distance readings semi auto mode
print
'Semi auto mode'
GPIO.output(
40
,
True
)
GPIO.output(
38
,
False
)
GPIO.output(
36
,
True
)
# Get the readings from ultra sensors
print
'Reading distance'
distance1
=
int
(UB.GetDistance1())
distance2
=
int
(UB.GetDistance2())
# Set critical allowed distance to object in front
if
distance1 <
=
150
:
PBR.MotorsOff()
elif
distance1 <
=
300
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
# Set critical allowed distance to object in front
if
distance2 <
=
150
:
PBR.MotorsOff()
elif
distance1 <
=
300
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
4ndr345
Sun, 01/24/2016 - 09:13
Permalink
Now I'm testing the Auto mode
Now I'm testing the Auto mode but having trouble getting the code free of buggs.
Need a little help to get ahead :-)
elif
movementMode
=
=
AUTO_MODE:
# Automatic movement mode, updates five times per second
GPIO.output(
40
,
False
)
GPIO.output(
38
,
False
)
GPIO.output(
36
,
False
)
# TODO: Fill in logic here
# Get the readings from ultra sensors
print
'Reading distance'
distance1
=
int
(UB.GetDistance1())
distance2
=
int
(UB.GetDistance2())
if
distance1 >
=
150
and
distance2 >
=
150
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(driveLeft
elif
distance1 <
=
149
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(
-
driveRight)
PBR.SetMotor2(driveLeft)
else
distance2 <
=
149
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
piborg
Sun, 01/24/2016 - 15:09
Permalink
Semi and Auto mode logic
The distances shown in the web UI in that code are divided by 10, so they display as cm.
The values in the code are in mm, so they need to be 10 times larger.
e.g.
if
distance1 <
150
:
will be for distances below 15 cm as shown on the web UI.
The code you have shown for the auto mode has a couple of coding mistakes that will cause it to not work:
elif
for each additional condition,else
is only used to mean "anything that is not covered by anyif
orelif
above".I guess you were trying to make him stop when too close.
You can simply the code a bit by only doing the
maxPower
calculation and the setting of the motors once.This is because each time you want to set the motors, the only difference is the speed you are setting.
I think something like this would do what you were intending:
elif
movementMode
=
=
AUTO_MODE:
# Automatic movement mode, updates five times per second
GPIO.output(
40
,
False
)
GPIO.output(
38
,
False
)
GPIO.output(
36
,
False
)
# TODO: Fill in logic here
# Get the readings from ultra sensors
print
'Reading distance'
distance1
=
int
(UB.GetDistance1())
distance2
=
int
(UB.GetDistance2())
if
distance1 >
=
150
and
distance2 >
=
150
:
driveRight
=
0.3
driveLeft
=
0.3
elif
distance1 <
=
149
:
driveRight
=
0.0
driveLeft
=
0.0
elif
distance2 <
=
149
:
driveRight
=
0.0
driveLeft
=
0.0
PBR.SetMotor1(driveRight
*
maxPower)
PBR.SetMotor2(
-
driveLeft
*
maxPower)
4ndr345
Sun, 01/24/2016 - 16:00
Permalink
Thanks for your help.
Thanks for your help.
I will trying to make him turning left or right when too close.
When I run the script I get an error .
>> line 260
driveRight = 0.3
^
IndentationError: expected an indented block <<
4ndr345
Sun, 01/24/2016 - 17:40
Permalink
Here is my slightly working
Here is my slightly working update for the "Auto Mode"
In "Auto Mode" the Borg have to turn right when obstacle is detected. But it seems there is to much time between the Sono readings. The Borg turns around 45° degrees and don't see that the obstacle is still there.
Hoppe you can help me with this.
elif
movementMode
=
=
AUTO_MODE:
# Automatic movement mode, updates five times per second
GPIO.output(
40
,
False
)
GPIO.output(
38
,
False
)
GPIO.output(
36
,
False
)
# TODO: Fill in logic here
# Get the readings from ultra sensors
print
'Reading distance auto mode'
distance1
=
int
(UB.GetDistance1())
distance2
=
int
(UB.GetDistance2())
if
distance1 >
=
350
or
distance2 >
=
350
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
elif
distance1 <
=
330
or
distance2 <
=
330
:
driveRight
=
0.5
*
maxPower
driveLeft
=
0.5
*
maxPower
PBR.SetMotor1(
-
driveRight)
PBR.SetMotor2(
-
driveLeft)
piborg
Mon, 01/25/2016 - 12:53
Permalink
Fast ultrasonic updates
I think the reason the code is slow to react to changes is due to the onboard filtering performed by the UltraBorg.
UltraBorg has two modes for reading ditances:
Cleans up the distance readings to give better accuracy and stability.
The filtered readings update slowly, they may take up to 2 seconds to change to a new value.
Used by calling the
UB.GetDistanceX()
functions.Provides the latest reading from the sensor without any filtering.
These raw readings update quickly, but they suffer from 'noise' and may be less accurate for measuring a fixed distance.
Used by calling the
UB.GetRawDistanceX()
functions.I would suggest replacing
UB.GetDistance1()
andUB.GetDistance2()
withUB.GetRawDistance1()
andUB.GetRawDistance2()
and see if the response is faster.4ndr345
Mon, 01/25/2016 - 19:54
Permalink
Thanks again!
UB.GetRawDistanceX() works perfect; reading and response are much better.
Now I have to tune the setup for the moves. I will also try with one more ultrasonic module rear.
#!/usr/bin/env python
# coding: Latin-1
# Creates a web-page interface for DiddyBorg Metal Edition
# Import library functions we need
import
PicoBorgRev
import
time
import
sys
import
threading
import
SocketServer
import
picamera
import
picamera.array
import
cv2
import
UltraBorg
import
datetime
import
RPi.GPIO as GPIO
# Settings Light
GPIO.setmode(GPIO.BOARD)
GPIO.setup(
40
, GPIO.OUT)
GPIO.setup(
38
, GPIO.OUT)
GPIO.setup(
36
, GPIO.OUT)
#GPIO.output(40, True)
#GPIO.output(38, True)
#GPIO.output(36, True)
# Settings for the web-page
webPort
=
80
# Port number for the web-page, 80 is what web-pages normally use
imageWidth
=
480
# Width of the captured image in pixels
imageHeight
=
360
# Height of the captured image in pixels
frameRate
=
20
# Number of images to capture per second
displayRate
=
4
# Number of images to request per second
photoDirectory
=
'/home/pi'
# Directory to save photos to
# Movement mode constants
MANUAL_MODE
=
0
# User controlled movement
SEMI_AUTO_MODE
=
1
# Semi-automatic movement
AUTO_MODE
=
2
# Fully automatic movement
# Global values
global
PBR
global
lastFrame
global
lockFrame
global
camera
global
processor
global
running
global
watchdog
global
movementMode
running
=
True
movementMode
=
MANUAL_MODE
# Setup the UltraBorg
global
UB
UB
=
UltraBorg.UltraBorg()
# Create a new UltraBorg object
UB.Init()
# Set the board up (checks the board is connected)
# 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
*
12
# Total battery voltage to the PicoBorg Reverse
voltageOut
=
12.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
()
# Automatic movement thread
class
AutoMovement(threading.Thread):
def
__init__(
self
):
super
(AutoMovement,
self
).__init__()
self
.terminated
=
False
self
.start()
def
run(
self
):
global
movementMode
# This method runs in a separate thread
while
not
self
.terminated:
# See which mode we are in
if
movementMode
=
=
MANUAL_MODE:
# User movement, wait a second before checking again
#print 'Manual mode'
GPIO.output(
40
,
True
)
GPIO.output(
38
,
True
)
GPIO.output(
36
,
True
)
time.sleep(
1.0
)
elif
movementMode
=
=
SEMI_AUTO_MODE:
# Semi-automatic movement mode, checks twice per second
# Ultrasonic distance readings semi auto mode
#print 'Semi auto mode'
GPIO.output(
40
,
True
)
GPIO.output(
38
,
False
)
GPIO.output(
36
,
True
)
# Get the readings from ultra sensors
#print 'Reading distance'
distance1
=
int
(UB.GetRawDistance1())
distance2
=
int
(UB.GetRawDistance2())
# Set critical allowed distance to object in front
if
distance1 <
=
200
:
PBR.MotorsOff()
elif
distance1 <
=
400
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
# Set critical allowed distance to object in front
if
distance2 <
=
200
:
PBR.MotorsOff()
elif
distance2 <
=
400
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
# Wait for 1/2 of a second before reading again
time.sleep(
0.1
)
elif
movementMode
=
=
AUTO_MODE:
# Automatic movement mode, updates five times per second
GPIO.output(
40
,
False
)
GPIO.output(
38
,
False
)
GPIO.output(
36
,
False
)
# Get the readings from ultra sensors
distance1
=
int
(UB.GetRawDistance1())
distance2
=
int
(UB.GetRawDistance2())
#if distance1 >= 400 or distance2 >= 400:
# driveRight = 0.4 * maxPower
# driveLeft = 0.4 * maxPower
# PBR.SetMotor1(driveRight)
# PBR.SetMotor2(-driveLeft)
if
distance1 <
=
350
:
driveRight
=
0.5
*
maxPower
driveLeft
=
0.5
*
maxPower
PBR.SetMotor1(
-
driveRight)
PBR.SetMotor2(
-
driveLeft)
elif
distance2 <
=
350
:
driveRight
=
0.5
*
maxPower
driveLeft
=
0.5
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(driveLeft)
else
:
driveRight
=
0.4
*
maxPower
driveLeft
=
0.4
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
# Wait for 1/5 of a second before reading again
time.sleep(
0.0001
)
else
:
# Unexpected, print an error and wait a second before trying again
print
'Unexpected movement mode %d'
%
(movementMode)
time.sleep(
0.01
)
# Class used to implement the web server
class
WebServer(SocketServer.BaseRequestHandler):
def
handle(
self
):
global
PBR
global
lastFrame
global
watchdog
global
movementMode
# Get the HTTP request data
reqData
=
self
.request.recv(
1024
).strip()
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(
'/distances-once'
):
# Ultrasonic distance readings
# Get the readings
distance1
=
int
(UB.GetRawDistance1())
distance2
=
int
(UB.GetRawDistance2())
distance3
=
int
(UB.GetRawDistance3())
# Build a table for the values
httpText
=
''
if
distance1
=
=
0
:
httpText
+
=
'None'
else
:
httpText
+
=
'%04d'
%
(distance1
/
10
)
if
distance3
=
=
0
:
httpText
+
=
'None'
else
:
httpText
+
=
'%04d'
%
(distance3
/
10
)
if
distance2
=
=
0
:
httpText
+
=
'None'
else
:
httpText
+
=
'%04d'
%
(distance2
/
10
)
httpText
+
=
''
self
.send(httpText)
elif
getPath.startswith(
'/semiAuto'
):
# Toggle Auto mode
if
movementMode
=
=
SEMI_AUTO_MODE:
# We are in semi-auto mode, turn it off
movementMode
=
MANUAL_MODE
httpText
=
''
httpText
+
=
'Speeds: 0 %, 0 %'
httpText
+
=
''
self
.send(httpText)
PBR.MotorsOff()
else
:
# We are not in semi-auto mode, turn it on
movementMode
=
SEMI_AUTO_MODE
httpText
=
''
httpText
+
=
'Semi Mode'
httpText
+
=
''
self
.send(httpText)
elif
getPath.startswith(
'/Auto'
):
# Toggle Auto mode
if
movementMode
=
=
AUTO_MODE:
# We are in auto mode, turn it off
movementMode
=
MANUAL_MODE
httpText
=
''
httpText
+
=
'Speeds: 0 %, 0 %'
httpText
+
=
''
self
.send(httpText)
PBR.MotorsOff()
else
:
# We are not in auto mode, turn it on
movementMode
=
AUTO_MODE
httpText
=
''
httpText
+
=
'Auto Mode'
httpText
+
=
''
self
.send(httpText)
elif
getPath.startswith(
'/cam.jpg'
):
# Camera snapshot
lockFrame.acquire()
sendFrame
=
lastFrame
lockFrame.release()
if
sendFrame !
=
None
:
self
.send(sendFrame.tostring())
elif
getPath.startswith(
'/off'
):
# Turn the drives off and switch to manual mode
movementMode
=
MANUAL_MODE
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
=
''
if
movementMode
=
=
MANUAL_MODE:
httpText
+
=
'Speeds: %.0f %%, %.0f %%'
%
(percentLeft, percentRight)
elif
movementMode
=
=
SEMI_AUTO_MODE:
httpText
+
=
'Semi: %.0f %%, %.0f %%'
%
(percentLeft, percentRight)
elif
movementMode
=
=
AUTO_MODE:
percentLeft
=
PBR.GetMotor2()
*
100.0
;
percentRight
=
PBR.GetMotor1()
*
100.0
;
httpText
+
=
'Auto: %.0f %%, %.0f %%'
%
(percentLeft, percentRight)
httpText
+
=
''
self
.send(httpText)
# Set the outputs as long as we are not in auto mode
if
movementMode !
=
AUTO_MODE:
driveLeft
*
=
maxPower
driveRight
*
=
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
elif
getPath.startswith(
'/photo'
):
# Save camera photo
lockFrame.acquire()
captureFrame
=
lastFrame
lockFrame.release()
httpText
=
''
if
captureFrame !
=
None
:
photoName
=
'%s/Photo %s.jpg'
%
(photoDirectory, datetime.datetime.utcnow())
try
:
photoFile
=
open
(photoName,
'wb'
)
photoFile.write(captureFrame)
photoFile.close()
httpText
+
=
'Photo saved to %s'
%
(photoName)
except
:
httpText
+
=
'Failed to take photo!'
else
:
httpText
+
=
'Failed to take photo!'
httpText
+
=
''
self
.send(httpText)
elif
getPath
=
=
'/'
:
# Main page, click buttons to move and to stop
httpText
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'<b>Spin Left</b>\n'
httpText
+
=
'<b>Forward</b>\n'
httpText
+
=
'<b>Spin Right</b>\n'
httpText
+
=
'\n'
httpText
+
=
'<b>Turn Left</b>\n'
httpText
+
=
'<b>Stop</b>\n'
httpText
+
=
'<b>Turn Right</b>\n'
httpText
+
=
'\n'
httpText
+
=
'<b>Semi Auto</b>\n'
httpText
+
=
'<b>Reverse</b>\n'
httpText
+
=
'<b>Auto Mode</b>\n'
httpText
+
=
'\n'
httpText
+
=
'<b>Save Photo</b>\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'Distances (cm)\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
self
.send(httpText)
elif
getPath
=
=
'/stream'
:
# Streaming frame, set a delayed refresh
displayDelay
=
int
(
1000
/
displayRate)
httpText
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
%
(displayDelay)
httpText
+
=
'<img src="/cam.jpg" name="rpicam" id="rpicam">\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
self
.send(httpText)
elif
getPath
=
=
'/distances'
:
# Repeated reading of the ultrasonic distances, set a delayed refresh
# We use AJAX to avoid screen refreshes caused by refreshing a frame
displayDelay
=
int
(
1000
/
displayRate)
httpText
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'Failed reading distances (not running?)";\n'
httpText
+
=
' }\n'
httpText
+
=
' }\n'
httpText
+
=
' }\n'
httpText
+
=
' xmlhttp.open("GET","distances-once",true);\n'
httpText
+
=
' xmlhttp.send();\n'
httpText
+
=
' setTimeout("readDistances()", %d);\n'
%
(displayDelay)
httpText
+
=
'}\n'
httpText
+
=
'//-->\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
%
(displayDelay)
httpText
+
=
'Waiting for first distance reading...\n'
httpText
+
=
'\n'
httpText
+
=
'\n'
self
.send(httpText)
else
:
# Unexpected page
self
.send(
'Path : "%s"'
%
(getPath))
def
send(
self
, content):
self
.request.sendall(
'HTTP/1.0 200 OK\n\n%s'
%
(content))
# Create the image buffer frame
lastFrame
=
None
lockFrame
=
threading.Lock()
# Startup sequence
print
'Setup camera'
camera
=
picamera.PiCamera()
camera.resolution
=
(imageWidth, imageHeight)
camera.framerate
=
frameRate
print
'Setup the stream processing thread'
processor
=
StreamProcessor()
print
'Wait ...'
time.sleep(
2
)
captureThread
=
ImageCapture()
print
'Setup the watchdog'
watchdog
=
Watchdog()
print
'Setup the automatic movement'
autoMovement
=
AutoMovement()
# Run the web server until we are told to close
httpServer
=
SocketServer.TCPServer((
"0.0.0.0"
, webPort), WebServer)
try
:
print
'Press CTRL+C to terminate the web-server'
while
running:
httpServer.handle_request()
except
KeyboardInterrupt:
# CTRL+C exit
print
'\nUser shutdown'
finally
:
# Turn the motors off under all scenarios
PBR.MotorsOff()
print
'Motors off'
# Tell each thread to stop, and wait for them to end
running
=
False
captureThread.join()
processor.terminated
=
True
watchdog.terminated
=
True
autoMovement.terminated
=
True
processor.join()
watchdog.join()
autoMovement.join()
del
camera
PBR.SetLed(
True
)
GPIO.output(
40
,
False
)
GPIO.output(
38
,
False
)
GPIO.output(
36
,
False
)
print
'Web-server terminated.'
4ndr345
Tue, 01/26/2016 - 12:05
Permalink
Hello again ...
I wonder if I can read the direction?
When I'm in the "Semi" mode and the Borg stops I need to push the Stop button before I can move backwards. When "Semi" is on I can't drive backwards.
Like ...
if
direction
"forwards"
and
distanceX <
=
350
MotorsOff()
Maybe end the "Semi mode"
if
distance2 <
=
200
:
PBR.MotorsOff()
end
=
=
SEMI_AUTO_MODE
elif
distance2 <
=
400
:
driveRight
=
0.3
*
maxPower
driveLeft
=
0.3
*
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
piborg
Tue, 01/26/2016 - 12:43
Permalink
Checking direction
What you could do is make another couple of global variables for the users set speeds.
You can then do some simple tests to see what direction the DiddyBorg is set to move in.
Here are the basic bits to get a "direction" value which you can compare to presets:
# Add some new constants to the movement mode constants
DIR_STOP
=
0
DIR_FORWARD
=
1
DIR_REVERSE
=
2
DIR_LEFT
=
3
DIR_RIGHT
=
4
DIR_SPIN_LEFT
=
5
DIR_SPIN_RIGHT
=
6
DIR_REV_LEFT
=
7
DIR_REV_RIGHT
=
8
# Add these to the global section
global
userLeft
global
userRight
userLeft
=
0.0
userRight
=
0.0
# Save the user set speeds in the "Motor power setting" section, above the AUTO_MODE check
userLeft
=
driveLeft
userRight
=
driveRight
# This block can be run before your distance tests to determine the direction
if
(userLeft
=
=
0.0
):
# Left stopped
if
(userRight
=
=
0.0
):
# Both stopped
direction
=
DIR_STOP
elif
(userRight >
0.0
):
# Turning left
direction
=
DIR_LEFT
else
:
# Reversing left
direction
=
DIR_REV_LEFT
elif
(userLeft >
0.0
):
# Left forward
if
(userRight
=
=
0.0
):
# Turning right
direction
=
DIR_RIGHT
elif
(userRight >
0.0
):
# Both forward
direction
=
DIR_FORWARD
else
:
# Spinning right
direction
=
DIR_SPIN_RIGHT
else
:
# Left reverse
if
(userRight
=
=
0.0
):
# Reversing right
direction
=
DIR_REV_RIGHT
elif
(userRight >
0.0
):
# Spinning left
direction
=
DIR_SPIN_LEFT
else
:
# Both reverse
direction
=
DIR_REVERSE
4ndr345
Thu, 01/28/2016 - 17:08
Permalink
Thanks ...
I will try :-)
4ndr345
Thu, 02/04/2016 - 17:35
Permalink
Hello again !
The Raspberry Pi doesn’t have a power switch, which means I have to remember to properly shut it down every time I want to turn it off. So the idea is to add a Shutdown Switch to the Borg. The script alone runs fine but having trouble getting the code free of buggs when I try to put into the diddy script.
#!/usr/bin/env python
# coding: Latin-1
import
time
import
os
import
RPi.GPIO as GPIO
#pins where the switch is connected
buttonPin1
=
33
GPIO.setmode(GPIO.BCM)
GPIO.setup(buttonPin1,GPIO.IN)
while
True
:
#assuming script1
if
(GPIO.
input
(buttonPin1)):
#this is the script that will be called (as root)
os.system(”sudo shutdown
-
h now)
piborg
Thu, 02/04/2016 - 17:58
Permalink
Power switch code
That is a good idea :)
It should be fairly easy to put that code into the diddyWeb.py script.
I think the simplest way would be to add these blocks of code:
At the bottom of the import list:
import
os
import
RPi.GPIO as GPIO
Before the "Timeout thread" code:
#pins where the switch is connected
global
buttonPin1
buttonPin1
=
33
GPIO.setmode(GPIO.BCM)
GPIO.setup(buttonPin1,GPIO.IN)
And add the highlighted lines to the section "Stream delegation loop" which is run each time an image is taken:
# Stream delegation loop
def
TriggerStream(
self
):
global
running
global
buttonPin1
while
running:
if
processor.event.is_set():
time.sleep(
0.01
)
else
:
yield
processor.stream
processor.event.
set
()
# Check for button press
if
(GPIO.
input
(buttonPin1)):
#this is the script that will be called (as root)
os.system(
"sudo shutdown-h now"
)
4ndr345
Thu, 02/04/2016 - 19:13
Permalink
Thanks :-)
Works fine, but I have to replace "sudo shutdown-h now" with "sudo init 0"
How to put in the code into the "metalJoy.py" script ?
piborg
Fri, 02/05/2016 - 11:40
Permalink
Adding code to metalJoy
Adding the button code to metalJoy.py is fairly simple:
# Loop indefinitely
while
running:
# Check for button press
if
(GPIO.
input
(buttonPin1)):
#this is the script that will be called (as root)
os.system(
"sudo shutdown-h now"
)
# Get the latest events from the system
hadEvent
=
False
events
=
pygame.event.get()
4ndr345
Fri, 02/05/2016 - 20:05
Permalink
...
unfortunately it didn't work :-(
File "metaljoy.py", line 133
os.system("sudo shutdown-h now")
^
IndentationError: expected an indented block
piborg
Sun, 02/07/2016 - 00:44
Permalink
Python and indentation
That line needs to be further to the right than the
if
line above it.Put simply Python is sensitive to the use of tabs or spaces at the start of the line.
The error you are seeing is telling you it expected a line to start to the right of a line above.
cleric boomer
Wed, 02/10/2016 - 13:24
Permalink
Question about the switch
Hi PiBorg.
I am planning on having a power (battery power) switch, and also a button to switch off the pi.
The on/off switch is easy.
But the shut down switch using the code above, what sort of switch would that need to be?
push to break (momentary) or
push to make (momentary)?
Thanks in advance
Chris
piborg
Wed, 02/10/2016 - 13:53
Permalink
Shutdown switch
You could use either, but I would suggest a push to make would be best.
You should wire it with a weak pull-down resistor like this:

Use a nice large value like 10 KΩ for R1, Vcc will be the 3V3 pin.
cleric boomer
Tue, 03/29/2016 - 17:30
Permalink
Please forgive my ignorance
OK, I know I am going to sound stupid here..
But here goes.
Following on from your diagram, and also considering that the 6 pins are taken by the reverse board.
so cable to pin 17 - 3.3v
other end to switch.
2 wires attached to other side of switch.
one to pin 40 (GPIO 21)
other to pin 39 (Ground) via a low value resistor.
Then code which says when GPIO goes high run sudo shutdown now
does that sound right?
Cheers
Chris
piborg
Tue, 03/29/2016 - 17:40
Permalink
That's it
That is the basic idea, yes.
The only thing is that you want a large value resistor.
A larger value means less wasted power when the switch is on.
Too low and you might exceed the 50 mA maximum for the 3.3v pin.
For the resistor I would use at least 1 KΩ, which would use 3.3 mA when turned on.
Ideally something like 10 KΩ would be better, that would use 0.33 mA when turned on.
cleric boomer
Tue, 03/29/2016 - 17:55
Permalink
Thanks
10k resister.
I have some of those
4ndr345
Fri, 04/01/2016 - 17:48
Permalink
Turn on and off lights
Now I'm playing around with some ideas.
I try to turn on light over the Web UI. Unfortunately i have now luck.
To put the buttons on the Web UI was easy, but so easy to activate :-(
I was trying ...
elif
getPath.startswith(’
/
Lighton'):
# Lights on
GPIO.output(
38
,
True
)
But it won't work.
It will be very nice if you could lock at this. Thanks
# Class used to implement the web server
class
WebServer(SocketServer.BaseRequestHandler):
def
handle(
self
):
global
PBR
global
lastFrame
global
watchdog
global
movementMode
# Get the HTTP request data
reqData
=
self
.request.recv(
1024
).strip()
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(
'/distances-once'
):
# Ultrasonic distance readings
# Get the readings
distance1
=
int
(UB.GetRawDistance1())
distance2
=
int
(UB.GetRawDistance2())
distance3
=
int
(UB.GetRawDistance3())
distance4
=
int
(UB.GetRawDistance4())
# Read and display time
localtime
=
time.asctime( time.localtime(time.time()) )
# Build a table for the values
httpText
=
'<html><body><center><table border="0" style="width:60%"><tr>'
if
distance1
=
=
0
:
httpText
+
=
'<td width="25%"><center>None</center></td>'
else
:
httpText
+
=
'<td width="25%%"><center>Front: %04d</center></td>'
%
(distance4
/
10
)
if
distance4
=
=
0
:
httpText
+
=
'<td width="25%"><center>None</center></td>'
else
:
httpText
+
=
'<td width="25%%"><center>Left: %04d</center></td>'
%
(distance1
/
10
)
if
distance3
=
=
0
:
httpText
+
=
'<td width="25%"><center>None</center></td>'
else
:
httpText
+
=
'<td width="25%%"><center>Right: %04d</center></td>'
%
(distance2
/
10
)
if
distance2
=
=
0
:
httpText
+
=
'<td width="25%"><center>None</center></td>'
else
:
httpText
+
=
'<td width="25%%"><center>Rear: %04d</center></td>'
%
(distance3
/
10
)
httpText
+
=
'</tr></table>'
httpText
+
=
'Temperature: {0:0.1f}°C - Humidity: {1:0.1f}% '
.
format
(temperature, humidity)
#httpText += 'Wifi Signal Strength %.0f %%' % (wifiPercent)
httpText
+
=
' - %s'
%
(localtime)
httpText
+
=
'</center></body></html>'
self
.send(httpText)
elif
getPath.startswith(
'/semiAuto'
):
# Toggle Auto mode
if
movementMode
=
=
SEMI_AUTO_MODE:
# We are in semi-auto mode, turn it off
movementMode
=
MANUAL_MODE
httpText
=
'<html><body><center>'
httpText
+
=
'Speeds: 0 %, 0 %'
httpText
+
=
'</center></body></html>'
self
.send(httpText)
PBR.MotorsOff()
else
:
# We are not in semi-auto mode, turn it on
movementMode
=
SEMI_AUTO_MODE
httpText
=
'<html><body><center>'
httpText
+
=
'Semi Mode'
httpText
+
=
'</center></body></html>'
self
.send(httpText)
elif
getPath.startswith(
'/Auto'
):
# Toggle Auto mode
if
movementMode
=
=
AUTO_MODE:
# We are in auto mode, turn it off
movementMode
=
MANUAL_MODE
httpText
=
'<html><body><center>'
httpText
+
=
'Speeds: 0 %, 0 %'
httpText
+
=
'</center></body></html>'
self
.send(httpText)
PBR.MotorsOff()
else
:
# We are not in auto mode, turn it on
movementMode
=
AUTO_MODE
httpText
=
'<html><body><center>'
httpText
+
=
'Auto Mode'
httpText
+
=
'</center></body></html>'
self
.send(httpText)
elif
getPath.startswith(
'/cam.jpg'
):
# Camera snapshot
lockFrame.acquire()
sendFrame
=
lastFrame
lockFrame.release()
if
sendFrame !
=
None
:
self
.send(sendFrame.tostring())
elif
getPath.startswith(’
/
Lighton'):
# Lights on
GPIO.output(
38
,
True
)
elif
getPath.startswith(’
/
Lightoff'):
# Lights off
GPIO.output(
38
,
False
)
elif
getPath.startswith(
'/off'
):
# Turn the drives off and switch to manual mode
movementMode
=
MANUAL_MODE
httpText
=
'<html><body><center>'
httpText
+
=
'Speeds: 0 %, 0 %'
httpText
+
=
'</center></body></html>'
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
=
'<html><body><center>'
if
movementMode
=
=
MANUAL_MODE:
httpText
+
=
'Speeds: %.0f %%, %.0f %%'
%
(percentLeft, percentRight)
elif
movementMode
=
=
SEMI_AUTO_MODE:
httpText
+
=
'Semi: %.0f %%, %.0f %%'
%
(percentLeft, percentRight)
elif
movementMode
=
=
AUTO_MODE:
percentLeft
=
PBR.GetMotor2()
*
100.0
;
percentRight
=
PBR.GetMotor1()
*
100.0
;
httpText
+
=
'Auto: %.0f %%, %.0f %%'
%
(percentLeft, percentRight)
httpText
+
=
'</center></body></html>'
self
.send(httpText)
# Set the outputs as long as we are not in auto mode
if
movementMode !
=
AUTO_MODE:
driveLeft
*
=
maxPower
driveRight
*
=
maxPower
PBR.SetMotor1(driveRight)
PBR.SetMotor2(
-
driveLeft)
elif
getPath.startswith(
'/photo'
):
# Save camera photo
lockFrame.acquire()
captureFrame
=
lastFrame
lockFrame.release()
httpText
=
'<html><body><center>'
if
captureFrame !
=
None
:
photoName
=
'%s/Photo %s.jpg'
%
(photoDirectory, datetime.datetime.utcnow())
try
:
photoFile
=
open
(photoName,
'wb'
)
photoFile.write(captureFrame)
photoFile.close()
httpText
+
=
'Photo saved to %s'
%
(photoName)
except
:
httpText
+
=
'Failed to take photo!'
else
:
httpText
+
=
'Failed to take photo!'
httpText
+
=
'</center></body></html>'
self
.send(httpText)
elif
getPath
=
=
'/'
:
# Main page, click buttons to move and to stop
httpText
=
'<html>\n'
httpText
+
=
'<head>\n'
httpText
+
=
'<script language="JavaScript"><!--\n'
httpText
+
=
'function Drive(left, right) {\n'
httpText
+
=
' var iframe = document.getElementById("setDrive");\n'
httpText
+
=
' var slider = document.getElementById("speed");\n'
httpText
+
=
' left *= speed.value / 100.0;'
httpText
+
=
' right *= speed.value / 100.0;'
httpText
+
=
' iframe.src = "/set/" + left + "/" + right;\n'
httpText
+
=
'}\n'
httpText
+
=
'function Lightoff() {\n'
httpText
+
=
' var iframe = document.getElementById("setDrive");\n'
httpText
+
=
' iframe.src = ”/Lightoff";\n'
httpText
+
=
'}\n'
httpText
+
=
'function Lighton() {\n'
httpText
+
=
' var iframe = document.getElementById("setDrive");\n'
httpText
+
=
' iframe.src
=
”
/
Lighton;\n’
httpText
+
=
'}\n'
httpText
+
=
'function Off() {\n'
httpText
+
=
' var iframe = document.getElementById("setDrive");\n'
httpText
+
=
' iframe.src = "/off";\n'
httpText
+
=
'}\n'
httpText
+
=
'function Photo() {\n'
httpText
+
=
' var iframe = document.getElementById("setDrive");\n'
httpText
+
=
' iframe.src = "/photo";\n'
httpText
+
=
'}\n'
httpText
+
=
'function semiAuto() {\n'
httpText
+
=
' var iframe = document.getElementById("setDrive");\n'
httpText
+
=
' iframe.src = "/semiAuto";\n'
httpText
+
=
'}\n'
httpText
+
=
'function Auto() {\n'
httpText
+
=
' var iframe = document.getElementById("setDrive");\n'
httpText
+
=
' iframe.src = "/Auto";\n'
httpText
+
=
'}\n'
httpText
+
=
'//--></script>\n'
httpText
+
=
'</head>\n'
httpText
+
=
'<body>\n'
httpText
+
=
'<iframe src="/stream" width="100%" height="500" frameborder="0"></iframe>\n'
httpText
+
=
'<iframe id="setDrive" src="/off" width="100%" height="50" frameborder="0"></iframe>\n'
httpText
+
=
'<center>\n'
httpText
+
=
'<button onclick="Drive(-1,1)" style="width:200px;height:40px;"><b>Spin Left</b></button>\n'
httpText
+
=
'<button onclick="Drive(1,1)" style="width:200px;height:40px;"><b>Forward</b></button>\n'
httpText
+
=
'<button onclick="Drive(1,-1)" style="width:200px;height:40px;"><b>Spin Right</b></button>\n'
httpText
+
=
'<br /><br />\n'
httpText
+
=
'<button onclick="Drive(0,1)" style="width:200px;height:40px;"><b>Turn Left</b></button>\n'
httpText
+
=
'<button onclick="Off()" style="width:200px;height:40px;"><b>Stop</b></button>\n'
httpText
+
=
'<button onclick="Drive(1,0)" style="width:200px;height:40px;"><b>Turn Right</b></button>\n'
httpText
+
=
'<br /><br />\n'
httpText
+
=
'<button onclick="semiAuto(1)" style="width:200px;height:40px;"><b>Semi Auto</b></button>\n'
httpText
+
=
'<button onclick="Drive(-1,-1)" style="width:200px;height:40px;"><b>Reverse</b></button>\n'
httpText
+
=
'<button onclick="Auto(1)" style="width:200px;height:40px;"><b>Auto Mode</b></button>\n'
httpText
+
=
'<br /><br />\n'
httpText
+
=
'<button onclick=”Lighton()” style="width:200px;height:40px;"><b>Light On</b></button>\n'
httpText
+
=
'<button onclick="Photo()" style="width:200px;height:40px;"><b>Save Photo</b></button>\n'
httpText
+
=
'<button onclick=”Lightoff()” style="width:200px;height:40px;"><b>Light Off</b></button>\n'
httpText
+
=
'<br /><br />\n'
httpText
+
=
'<input id="speed" type="range" min="0" max="100" value="40" style="width:600px" />\n'
httpText
+
=
'<br /><center> - Distances - </centre><br />\n'
httpText
+
=
'<iframe src="/distances" width="100%" height="200" frameborder="0"></iframe>\n'
httpText
+
=
'</center>\n'
httpText
+
=
'</body>\n'
httpText
+
=
'</html>\n'
self
.send(httpText)
piborg
Sat, 04/02/2016 - 09:58
Permalink
Turn on and off lights
This might be a problem cause by your text editor :)
Programming languages are very picky about the character symbols used in code.
In particular the quoting marks for Python and HTML need to be of the
"
or'
type.At the moment you have:
httpText += ' iframe.src = ”/Lightoff";\n'
and
httpText += ' iframe.src = ”/Lighton;\n’
for example which look like the text editor has swapped some of the quotes and double-quotes with the fancier style ones.
What you wanted to have was:
httpText += ' iframe.src = "/Lightoff";\n'
and
httpText += ' iframe.src = "/Lighton";\n'
The symbols you want to use are:
" '
The ones you want to watch out for are:
“ ‟ ‘ ‛
These are often used by editors such as Word to make you text look nicer.
There are a fair number of lines with these rouge symbols, and they can be quite hard to spot.
I would suggest either using the find tool on your editor if it has one, otherwise turning the font size up helps a lot.
In the section of code you posted there appear to be bad quote symbols on lines: 101, 105, 200, 204, 240, 242.
4ndr345
Sat, 04/02/2016 - 17:40
Permalink
Thanks so much :-)
Thanks so much :-)
These “ ‟ ‘ ‛ characters was killing all, now it works perfect.