Browse Source

websockets and html5 gamepad

split-pipe
Hendrik Langer 4 years ago
parent
commit
ca100fb31f
  1. 2
      raspberry/roberto.py
  2. 10
      raspberry/roberto/__init__.py
  3. 24
      raspberry/roberto/config.cfg
  4. 0
      raspberry/roberto/config.py
  5. 9
      raspberry/roberto/static/js/socket.io.js
  6. 4
      raspberry/roberto/views/frontend/routes.py
  7. 136
      raspberry/roberto/views/frontend/templates/gamepad.js
  8. 36
      raspberry/roberto/views/frontend/templates/index.html
  9. 8
      raspberry/roberto/views/websocket/__init__.py
  10. 31
      raspberry/roberto/views/websocket/routes.py

2
raspberry/roberto.py

@ -4,7 +4,7 @@ from roberto import create_app
# Call the Application Factory function to construct a Flask application instance # Call the Application Factory function to construct a Flask application instance
# using the standard configuration defined in /instance/flask.cfg # using the standard configuration defined in /instance/flask.cfg
app = create_app('config.py') app = create_app('config.cfg')
if __name__ == '__main__': if __name__ == '__main__':
app.run(host='0.0.0.0', debug=False, threaded=True) app.run(host='0.0.0.0', debug=False, threaded=True)

10
raspberry/roberto/__init__.py

@ -2,9 +2,12 @@ import os
from flask import Flask from flask import Flask
from flask_login import LoginManager from flask_login import LoginManager
from flask_socketio import SocketIO
####################### #######################
#### Configuration #### #### Configuration ####
#https://www.patricksoftwareblog.com/structuring-a-flask-project/
####################### #######################
# Create the instances of the Flask extensions (flask-sqlalchemy, flask-login, etc.) in # Create the instances of the Flask extensions (flask-sqlalchemy, flask-login, etc.) in
@ -12,6 +15,7 @@ from flask_login import LoginManager
# to the application at this point. # to the application at this point.
login = LoginManager() login = LoginManager()
login.login_view = "users.login" login.login_view = "users.login"
socketio = SocketIO()
from roberto.camera.camera_opencv import Camera from roberto.camera.camera_opencv import Camera
camera = Camera() camera = Camera()
@ -26,7 +30,7 @@ def create_app(config_filename=None):
DATABASE=os.path.join(app.instance_path, 'roberto.sqlite'), DATABASE=os.path.join(app.instance_path, 'roberto.sqlite'),
) )
if config_filename is None: if config_filename is None:
app.config.from_pyfile('config.py') app.config.from_pyfile('config.cfg')
else: else:
app.config.from_pyfile(config_filename) app.config.from_pyfile(config_filename)
initialize_extensions(app) initialize_extensions(app)
@ -47,12 +51,14 @@ def initialize_extensions(app):
from roberto.model import db from roberto.model import db
db.init_app(app) db.init_app(app)
#login.init_app(app) #login.init_app(app)
socketio.init_app(app)
pass pass
def register_blueprints(app): def register_blueprints(app):
# Since the application instance is now created, register each Blueprint # Since the application instance is now created, register each Blueprint
# with the Flask application instance (app) # with the Flask application instance (app)
from roberto.views.frontend import frontend_blueprint from roberto.views.frontend import frontend_blueprint
from roberto.views.websocket import websocket_blueprint
app.register_blueprint(frontend_blueprint) app.register_blueprint(frontend_blueprint)
app.register_blueprint(websocket_blueprint)

24
raspberry/roberto/config.cfg

@ -0,0 +1,24 @@
##########################################################
#
# This is a sample flask.cfg for developing a Flask application
#
##########################################################
import os
# Get the folder of the top-level directory of this project
BASEDIR = os.path.abspath(os.path.dirname(__file__))
# Update later by using a random number generator and moving
# the actual key outside of the source code under version control
#SECRET_KEY = 'bad_secret_key'
#WTF_CSRF_ENABLED = True
#DEBUG = True
# SQLAlchemy
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASEDIR, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
# Bcrypt algorithm hashing rounds
#BCRYPT_LOG_ROUNDS = 15

0
raspberry/roberto/config.py

9
raspberry/roberto/static/js/socket.io.js

File diff suppressed because one or more lines are too long

4
raspberry/roberto/views/frontend/routes.py

@ -26,3 +26,7 @@ def video_feed():
camera.__init__() camera.__init__()
return Response(gen(camera), return Response(gen(camera),
mimetype='multipart/x-mixed-replace; boundary=frame') mimetype='multipart/x-mixed-replace; boundary=frame')
@frontend_blueprint.route('/gamepad.js')
def gamepad_js():
return render_template('gamepad.js')

136
raspberry/roberto/views/frontend/templates/gamepad.js

@ -0,0 +1,136 @@
/*
* Gamepad API Test
* Written in 2013 by Ted Mielczarek <ted@mielczarek.org>
*
* To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
namespace = '/gamepad';
var socket = io(namespace);
socket.on('connect', function() {
socket.emit('my_event', {data: 'I\'m connected!'});
});
var haveEvents = 'GamepadEvent' in window;
var haveWebkitEvents = 'WebKitGamepadEvent' in window;
var controllers = {};
var axesState = {};
var rAF = window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.requestAnimationFrame;
function connecthandler(e) {
addgamepad(e.gamepad);
}
function addgamepad(gamepad) {
controllers[gamepad.index] = gamepad; var d = document.createElement("div");
d.setAttribute("id", "controller" + gamepad.index);
var t = document.createElement("h1");
t.appendChild(document.createTextNode("gamepad: " + gamepad.id));
d.appendChild(t);
var b = document.createElement("div");
b.className = "buttons";
for (var i=0; i<gamepad.buttons.length; i++) {
var e = document.createElement("span");
e.className = "button";
//e.id = "b" + i;
e.innerHTML = i;
b.appendChild(e);
}
d.appendChild(b);
var a = document.createElement("div");
a.className = "axes";
for (i=0; i<gamepad.axes.length; i++) {
e = document.createElement("meter");
e.className = "axis";
//e.id = "a" + i;
e.setAttribute("min", "-1");
e.setAttribute("max", "1");
e.setAttribute("value", "0");
e.innerHTML = i;
a.appendChild(e);
}
d.appendChild(a);
document.getElementById("start").style.display = "none";
document.body.appendChild(d);
rAF(updateStatus);
}
function disconnecthandler(e) {
removegamepad(e.gamepad);
}
function removegamepad(gamepad) {
var d = document.getElementById("controller" + gamepad.index);
document.body.removeChild(d);
delete controllers[gamepad.index];
}
function updateStatus() {
scangamepads();
for (j in controllers) {
var controller = controllers[j];
var d = document.getElementById("controller" + j);
var buttons = d.getElementsByClassName("button");
for (var i=0; i<controller.buttons.length; i++) {
var b = buttons[i];
var val = controller.buttons[i];
var pressed = val == 1.0;
var touched = false;
if (typeof(val) == "object") {
pressed = val.pressed;
if ('touched' in val) {
touched = val.touched;
}
val = val.value;
}
var pct = Math.round(val * 100) + "%";
b.style.backgroundSize = pct + " " + pct;
b.className = "button";
if (pressed) {
b.className += " pressed";
}
if (touched) {
b.className += " touched";
}
}
var axesChanged = false;
var axes = d.getElementsByClassName("axis");
for (var i=0; i<controller.axes.length; i++) {
var a = axes[i];
a.innerHTML = i + ": " + controller.axes[i].toFixed(4);
a.setAttribute("value", controller.axes[i]);
if (axesState[i] != controller.axes[i]) {
axesState[i] = controller.axes[i];
axesChanged = true;
}
}
if (axesChanged) {
socket.emit('axes', {0: controller.axes[0], 1: controller.axes[1], 3: controller.axes[3], 4: controller.axes[4]});
}
}
rAF(updateStatus);
}
function scangamepads() {
var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
for (var i = 0; i < gamepads.length; i++) {
if (gamepads[i] && (gamepads[i].index in controllers)) {
controllers[gamepads[i].index] = gamepads[i];
}
}
}
if (haveEvents) {
window.addEventListener("gamepadconnected", connecthandler);
window.addEventListener("gamepaddisconnected", disconnecthandler);
} else if (haveWebkitEvents) {
window.addEventListener("webkitgamepadconnected", connecthandler);
window.addEventListener("webkitgamepaddisconnected", disconnecthandler);
} else {
setInterval(scangamepads, 500);
}

36
raspberry/roberto/views/frontend/templates/index.html

@ -10,8 +10,44 @@
<body> <body>
<h1>Video Streaming Demonstration</h1> <h1>Video Streaming Demonstration</h1>
<img src="{{ url_for('frontend.video_feed') }}"> <img src="{{ url_for('frontend.video_feed') }}">
<h2>Send:</h2>
<form id="emit" method="POST" action='#'>
<input type="text" name="emit_data" id="emit_data" placeholder="Message">
<input type="submit" value="Echo">
</form>
<form id="broadcast" method="POST" action='#'>
<input type="text" name="broadcast_data" id="broadcast_data" placeholder="Message">
<input type="submit" value="Broadcast">
</form>
<h2>Receive:</h2>
<div id="log"></div>
<h2 id="start">Press a button on your controller to start</h2>
<!-- jQuery and Bootstrap --> <!-- jQuery and Bootstrap -->
<script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script> <script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script> <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/socket.io.js') }}"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
socket.on('my response', function(msg) {
$('#log').append('<p>Received: ' + msg.data + '</p>');
});
$('form#emit').submit(function(event) {
socket.emit('my event', {data: $('#emit_data').val()});
return false;
});
$('form#broadcast').submit(function(event) {
socket.emit('my broadcast event', {data: $('#broadcast_data').val()});
return false;
});
});
</script>
<script src="{{ url_for('frontend.gamepad_js') }}"></script>
</body> </body>
</html> </html>

8
raspberry/roberto/views/websocket/__init__.py

@ -0,0 +1,8 @@
"""
The frontend blueprint handles the webui.
"""
from flask import Blueprint
websocket_blueprint = Blueprint('websocket', __name__, template_folder='templates')
from . import routes

31
raspberry/roberto/views/websocket/routes.py

@ -0,0 +1,31 @@
from . import websocket_blueprint
from flask import current_app, render_template
from flask_socketio import SocketIO, emit
from roberto import socketio
################
#### routes ####
################
@socketio.on('my_event', namespace='/test')
def test_message(message):
emit('my response', {'data': message['data']})
@socketio.on('my broadcast event', namespace='/test')
def test_message(message):
emit('my response', {'data': message['data']}, broadcast=True)
@socketio.on('connect', namespace='/test')
def test_connect():
emit('my response', {'data': 'Connected'})
@socketio.on('disconnect', namespace='/test')
def test_disconnect():
print('Client disconnected')
@socketio.on('axes', namespace='/gamepad')
def gamepad_axes(message):
print('GAMEPAD axes')
print(message)
Loading…
Cancel
Save