Last active
December 14, 2015 09:28
-
-
Save reclosedev/5064616 to your computer and use it in GitHub Desktop.
Droid audio car
http://python.su/forum/topic/20832/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# Based on http://www.marsohod.org/index.php/projects/plata1/205-carctrlandr | |
import os | |
import BaseHTTPServer | |
from threading import Thread | |
import urlparse | |
import time | |
import string | |
try: | |
import android | |
droid = android.Android() | |
except ImportError: | |
def mediaPlay(path): | |
print "PLAY", path | |
import mock | |
droid = mock.Mock() | |
droid.mediaPlay = mediaPlay | |
HOST_NAME = '' | |
PORT_NUMBER = 9090 | |
COMMAND_SEND_INTERVAL = 1.0 # seconds | |
COMMAND_TIMEOUT = COMMAND_SEND_INTERVAL * 2 | |
PAGE_TEMPLATE = string.Template(''' | |
<html> | |
<head> | |
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> | |
<style type="text/css"> | |
body { | |
margin: 0; | |
} | |
.btn { | |
width: 7em; | |
height: 2em; | |
} | |
div#leftcolumn, div#rightcolumn { | |
float: left; | |
width: 50%; | |
height: 100%; | |
} | |
.center { | |
display:block; | |
margin-left: auto; | |
margin-right: auto; | |
} | |
.dull {color: #a9a9a9} | |
</style> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> | |
<script src="http://jeromeetienne.github.com/virtualjoystick.js/virtualjoystick.js"></script> | |
<script> | |
$(document).ready(function(){ | |
function CommandSender(cmdInterval){ | |
this.cmdInterval = cmdInterval; | |
this.lastCmd = ""; | |
this.lastState = ""; | |
this.lastTime = new Date(); | |
} | |
CommandSender.prototype.run = function(){ | |
var $this = this; | |
setInterval(function(){ | |
$this.updateState($this.lastState, $this.lastCmd); | |
}, this.cmdInterval); | |
} | |
CommandSender.prototype.updateState = function(state, cmd){ | |
if((new Date() - this.lastTime) < this.cmdInterval && | |
cmd == this.lastCmd && state == this.lastState) | |
return; | |
if (state == "begin" && cmd){ | |
this.sendCommand(state, cmd); | |
} | |
else if (state != this.lastState) { | |
this.sendCommand("end", cmd); | |
} | |
this.lastState = state; | |
this.lastCmd = cmd; | |
this.lastTime = new Date(); | |
} | |
CommandSender.prototype.sendCommand = function(state, cmd){ | |
$.post('command/' + state + ':' + cmd, function(resp){ | |
console.log(resp); | |
}); | |
} | |
var cmdInterval = ${COMMAND_SEND_INTERVAL}; | |
(function buttonsHandler(){ | |
var sender = new CommandSender(cmdInterval); | |
$(".btn").mousedown(function(){ | |
sender.updateState("begin", $(this).data("command")); | |
}).mouseup(function(){ | |
sender.updateState("end", $(this).data("command")); | |
}).mouseleave(function(){ | |
sender.updateState("end", ""); | |
}); | |
sender.run(); | |
})(); | |
(function keyBoardHandler(){ | |
var sender = new CommandSender(cmdInterval); | |
var arrow = {37: 'left', 38: 'up', 39: 'right', 40:'down'}; | |
$(document).keydown(function (e) { | |
var keyCode = e.keyCode || e.which; | |
var cmd = arrow[keyCode]; | |
if (cmd) | |
sender.updateState("begin", cmd); | |
}).keyup(function (e) { | |
sender.updateState("end", ""); | |
}); | |
sender.run(); | |
})(); | |
$("#joystickframe").load(function(){ | |
var container = $('#joystickframe').contents().find('#joystick')[0]; | |
var joystick = new VirtualJoystick({ | |
container : container, | |
mouseSupport: true | |
}); | |
var sender = new CommandSender(cmdInterval); | |
setInterval(function(){ | |
var cmd = "" | |
var state = "begin"; | |
if(joystick.up()){ | |
cmd = "up"; | |
} else if (joystick.down()) { | |
cmd = "down"; | |
} else if (joystick.left()) { | |
cmd = "left"; | |
} else if (joystick.right()) { | |
cmd = "right"; | |
} else { | |
state = "end"; | |
} | |
sender.updateState(state, cmd); | |
}, 1/30 * 1000); | |
}); | |
}); | |
</script> | |
</head> | |
<body> | |
<div id="leftcolumn"> | |
<iframe src="http://${IP_ADDRESS}:8080/videofeed" class="center" scrolling="no" width="640" height="480" >No iframes?</iframe> | |
<table border=0 class="center"> | |
<tr> | |
<td></td> | |
<td><button class="btn" data-command="up">Up</button></td> | |
<td></td> | |
</tr> | |
<tr> | |
<td><button class="btn" data-command="left">Left</button></td> | |
<td></td> | |
<td><button class="btn" data-command="right">Right</button></td> | |
</tr> | |
<tr> | |
<td></td> | |
<td><button class="btn" data-command="down">Down</button></td> | |
<td></td> | |
</tr> | |
</table> | |
<p class="dull">Press and hold buttons or use keyboard arrows.</p> | |
</div> | |
<div id="rightcolumn"> | |
<iframe id="joystickframe" src="joystick.html" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen="" width="100%" height="100%" frameborder="0"> | |
</iframe> | |
</div> | |
</body> | |
</html> | |
''') | |
PAGE_JOYSTICK_TEMPLATE = ''' | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> | |
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> | |
<style type="text/css"> | |
body { | |
overflow: hidden; | |
padding: 0; | |
margin: 0; | |
} | |
div#joystick { | |
background: black; | |
color: white; | |
width: 100%; | |
height: 100%; | |
overflow: hidden; | |
padding: 0; | |
margin: 0; | |
-webkit-user-select: none; | |
-moz-user-select: none; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="joystick" style="">Joystick area. Press and drag (touch or mouse)</div> | |
</body> | |
''' | |
class CarControllerThread(Thread): | |
path_to_audio = '/mnt/sdcard/droid_car/' | |
command_to_audio = { | |
'up': 'up.wav', | |
'down': 'down.wav', | |
'left': 'left.wav', | |
'right': 'right.wav', | |
} | |
def __init__(self, timeout=1): | |
super(CarControllerThread, self).__init__() | |
self.timeout = timeout | |
self.pool_timeout = timeout / 3.0 | |
self.current_state = "" | |
self.current_command = "" | |
self.last_cmd_timestamp = time.time() | |
def run(self): | |
while True: | |
elapsed = time.time() - self.last_cmd_timestamp | |
if self.current_state == "begin" and elapsed > self.timeout: | |
self.update_state("end", "") | |
time.sleep(self.pool_timeout) | |
def update_state(self, state, command): | |
self.last_cmd_timestamp = time.time() | |
if state == self.current_state and command == self.current_command: | |
return | |
self.current_state = state | |
self.current_command = command | |
if state == "end": | |
print "PAUSE" | |
droid.mediaPlaySetLooping(False) | |
droid.mediaPlayPause() | |
elif state == "begin": | |
print "PLAY", command | |
audio = self.command_to_audio.get(command) | |
if audio: | |
droid.mediaPlay(os.path.join(self.path_to_audio, audio)) | |
droid.mediaPlaySetLooping(True) | |
else: | |
print "wrong command:", command | |
class DroidHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |
def do_HEAD(self): | |
self.send_response(200) | |
self.send_header("Content-type", "text/html; charset=utf-8") | |
self.end_headers() | |
def do_GET(self): | |
self.send_response(200) | |
self.send_header("Content-type", "text/html; charset=utf-8") | |
self.end_headers() | |
url = urlparse.urlsplit(self.path) | |
if url.path == '/joystick.html': | |
self.wfile.write(PAGE_JOYSTICK_TEMPLATE) | |
else: | |
ip, port = self.headers.get('Host').split(":", 2) | |
self.wfile.write( | |
PAGE_TEMPLATE.safe_substitute( | |
IP_ADDRESS=ip, | |
COMMAND_SEND_INTERVAL=int(COMMAND_SEND_INTERVAL * 1000), | |
) | |
) | |
def do_POST(self): | |
self.send_response(200) | |
self.send_header("Content-type", "text/plain; charset=utf-8") | |
self.end_headers() | |
if self.path.startswith("/command/"): | |
try: | |
#/command/state:up | |
state, cmd = self.path.split("/")[-1].split(":") | |
except ValueError: | |
print "ERROR", self.path | |
self.wfile.write("fail") | |
return | |
controller.update_state(state, cmd) | |
self.wfile.write("ok") | |
elif self.path.startswith("/ping"): | |
self.wfile.write("pong") | |
controller = CarControllerThread(COMMAND_TIMEOUT) | |
controller.start() | |
try: | |
droid.wakeLockAcquireBright() | |
droid.webcamStart(0, 10, 9091) | |
droid.webcamAdjustQuality(0, 10) | |
except Exception as e: | |
print "Webcam error", e | |
my_srv = BaseHTTPServer.HTTPServer((HOST_NAME, PORT_NUMBER), DroidHandler) | |
print 'web server running on port %s' % PORT_NUMBER | |
my_srv.serve_forever() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment