MJRoBot (Marcelo Rovai)
Published © GPL3+

Raspberry Pi Cam Pan-Tilt Controlled Over Local Internet

Pan-Tilt Camera position controlled over local internet, using Flask and Python.

BeginnerFull instructions provided8 hours7,880
Raspberry Pi Cam Pan-Tilt Controlled Over Local Internet

Things used in this project

Story

Read more

Schematics

Electrical Diagram

Code

Code snippet #9

Plain text
from flask import Flask, render_template, Response

# Raspberry Pi camera module (requires picamera package, developed by Miguel Grinberg)
from camera_pi import Camera

app = Flask(__name__)

@app.route('/')
def index():
    """Video streaming home page."""
    return render_template('index.html')

def gen(camera):
    """Video streaming generator function."""
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

@app.route('/video_feed')
def video_feed():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(gen(Camera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port =80, debug=True, threaded=True)

Code snippet #10

Plain text
<html>
  <head>
    <title>MJRoBot Lab Live Streaming</title>
    <link rel="stylesheet" href='../static/style.css'/>
  </head>
  <body>
    <h1>MJRoBot Lab Live Streaming</h1>
    <h3><img src="{{ url_for('video_feed') }}" width="90%"></h3>
    <hr>
    <p> @2018 Developed by MJRoBot.org</p>
  </body>
</html>

Code snippet #12

Plain text
├── Documents
       └── camWebServer
               ├── camera_pi.py
               ├── appCam.py
               ├── templates
               |     └── index.html
               └── static
                     └── style.css

Code snippet #15

Plain text
from time import sleep
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

def setServoAngle(servo, angle):
	pwm = GPIO.PWM(servo, 50)
	pwm.start(8)
	dutyCycle = angle / 18. + 3.
	pwm.ChangeDutyCycle(dutyCycle)
	sleep(0.3)
	pwm.stop()

if __name__ == '__main__':
	import sys
	servo = int(sys.argv[1])
	GPIO.setup(servo, GPIO.OUT)
	setServoAngle(servo, int(sys.argv[2]))
	GPIO.cleanup()

Code snippet #19

Plain text
├── Documents
       └── PanTiltControl1
               ├── camera_pi.py
	       ├── angleServoCtrl.py
               ├── appCamPanTilt1.py
               ├── templates
               |     └── index.html
               └── static
                     └── style.css

Code snippet #20

Plain text
<html>
  <head>
    <title>MJRoBot Lab Live Streaming</title>
    <link rel="stylesheet" href='../static/style.css'/>
  </head>
  <body>
    <h1>MJRoBot Lab Live Streaming</h1>
    <h3><img src="{{ url_for('video_feed') }}" width="80%"></h3>
	<hr>
	<h4> PAN:  
		<a href="/pan/30"class="button">30</a>
		<a href="/pan/45"class="button">45</a>
		<a href="/pan/60"class="button">60</a>
		<a href="/pan/75"class="button">75</a>
		<a href="/pan/90"class="button">90</a>
		<a href="/pan/105"class="button">105</a>
		<a href="/pan/120"class="button">120</a>
		<a href="/pan/135"class="button">135</a>
		<a href="/pan/150"class="button">150</a>
		==> Angle: [ {{ panServoAngle }} ]
	</h4>
		<h4> TILT: 
		<a href="/tilt/30"class="button">30</a>
		<a href="/tilt/45"class="button">45</a>		
		<a href="/tilt/60"class="button">60</a>
		<a href="/tilt/75"class="button">75</a>		
		<a href="/tilt/90"class="button">90</a>
		<a href="/tilt/105"class="button">105</a>		
		<a href="/tilt/120"class="button">120</a>
		<a href="/tilt/135"class="button">135</a>		
		<a href="/tilt/150"class="button">150</a>
		==> Angle: [ {{ tiltServoAngle }} ]
	</h4>
	<hr>
	<p> @2018 Developed by MJRoBot.org</p>
  </body>
</html>

Code snippet #22

Plain text
@app.route("/<servo>/<angle>")
def move(servo, angle):
	global panServoAngle
	global tiltServoAngle
	if servo == 'pan':
		panServoAngle = int(angle)
		os.system("python3 angleServoCtrl.py " + str(panPin) + " " + str(panServoAngle))
	if servo == 'tilt':
		tiltServoAngle = int(angle)
		os.system("python3 angleServoCtrl.py " + str(tiltPin) + " " + str(tiltServoAngle))
	
	templateData = {
      'panServoAngle'	: panServoAngle,
      'tiltServoAngle'	: tiltServoAngle
	}
	return render_template('index.html', **templateData)

Code snippet #26

Plain text
├── Documents
       └── PanTiltControl2
               ├── camera_pi.py
	       ├── angleServoCtrl.py
               ├── appCamPanTilt2.py
               ├── templates
               |     └── index.html
               └── static
                     └── style.css

Code snippet #27

Plain text
<html>
  <head>
    <title>MJRoBot Lab Live Streaming</title>
    <link rel="stylesheet" href='../static/style.css'/>
  </head>
  <body>
    <h1>MJRoBot Lab Live Streaming</h1>
    <h3><img src="{{ url_for('video_feed') }}" width="80%"></h3>
	<hr>
	<h4> PAN Angle: <a href="/pan/-"class="button">-</a> [ {{ panServoAngle }} ] <a href="/pan/+"class="button">+</a> </h4>
	<h4> TILT Angle: <a href="/tilt/-"class="button">-</a> [ {{ tiltServoAngle }} ] <a href="/tilt/+"class="button">+</a> </h4> 
	<hr>
	<p> @2018 Developed by MJRoBot.org</p>
  </body>
</html>

Code snippet #29

Plain text
@app.route("/<servo>/<angle>")
def move(servo, angle):
	global panServoAngle
	global tiltServoAngle
	if servo == 'pan':
		if angle == '+':
			panServoAngle = panServoAngle + 10
		else:
			panServoAngle = panServoAngle - 10
		os.system("python3 angleServoCtrl.py " + str(panPin) + " " + str(panServoAngle))
	if servo == 'tilt':
		if angle == '+':
			tiltServoAngle = tiltServoAngle + 10
		else:
			tiltServoAngle = tiltServoAngle - 10
		os.system("python3 angleServoCtrl.py " + str(tiltPin) + " " + str(tiltServoAngle))
	
	templateData = {
      'panServoAngle'	: panServoAngle,
      'tiltServoAngle'	: tiltServoAngle
	}
	return render_template('index.html', **templateData)

Code snippet #30

Plain text
if angle == '+':
	panServoAngle = panServoAngle + 10
else:
	panServoAngle = panServoAngle - 10
os.system("python3 angleServoCtrl.py " + str(panPin) + " " + str(panServoAngle)) 

Code snippet #33

Plain text
├── Documents
       └── PanTiltControl3
               ├── camera_pi.py
	       ├── angleServoCtrl.py
               ├── appCamPanTilt3.py
               ├── templates
               |     └── index.html
               └── static
                     └── style.css

Code snippet #34

Plain text
<html>
  <head>
    <title>MJRoBot Lab Live Streaming</title>
    <link rel="stylesheet" href='../static/style.css'/>
  </head>
  <body>
    <h1>MJRoBot Lab Live Streaming</h1>
    <h3><img src="{{ url_for('video_feed') }}" width="80%"></h3>
    <p> Enter Pan Tilt Servo Angle:
	<form method="POST">
		PAN:  <input type="text" name="panServoAngle" value= {{panServoAngle}} size="3">
		TILT: <input type="text" name="tiltServoAngle" value= {{tiltServoAngle}} size="3">
		<input type="submit">
	</form>
   </p>
   <hr>
	<p> @2018 Developed by MJRoBot.org</p>
  </body>
</html>

Code snippet #35

Plain text
@app.route('/', methods=['POST'])
def my_form_post():
	global panServoAngle
	global tiltServoAngle

	panNewAngle = int(request.form['panServoAngle'])
	if (panNewAngle != panServoAngle):
		panServoAngle = panNewAngle
		os.system("python3 angleServoCtrl.py " + str(panPin) + " " + str(panServoAngle))

	tiltNewAngle = int(request.form['tiltServoAngle'])
	if (tiltNewAngle != tiltServoAngle):
		tiltServoAngle = tiltNewAngle
		os.system("python3 angleServoCtrl.py " + str(tiltPin) + " " + str(tiltServoAngle))

	templateData = {
      'panServoAngle'	: panServoAngle,
      'tiltServoAngle'	: tiltServoAngle
	}
	return render_template('index.html', **templateData)

Github file

https://github.com/Mjrovai/Video-Streaming-with-Flask/blob/master/camWebServer/camera_pi.py

Github file

https://github.com/Mjrovai/OpenCV-Object-Face-Tracking/blob/master/angleServoCtrl.py

Github file

https://github.com/Mjrovai/WebCam-Pan-Tilt-Control-via-Flask/blob/master/PanTiltControl1/camera_pi.py

Credits

MJRoBot (Marcelo Rovai)

MJRoBot (Marcelo Rovai)

60 projects • 913 followers
Professor, Engineer, MBA, Master in Data Science. Writes about Electronics with a focus on Physical Computing, IoT, ML, TinyML and Robotics.
Thanks to Miguel Grinberg.

Comments