В моих недавних экпериментах (здесь начало, еще тут и тут) я зажигал светодиоды платы Марсоход по команде из телефона c ОС Android. Плата соединялась через аудио разъем телефона и определяла звуковой тон, играемый на телефоне, который и служил командой. На телефоне запускалась простая управляющая программа в виде скрипта на Python (SL4A). Теперь я хочу поместить плату Марсоход и телефон на гусенечное шасси, и удаленно включать и выключать коллекторные моторчики и, таким образом, управлять машинкой через WiFi. Более того, было бы интересно удаленно получить изображение с камеры телефона и сделать полное управление машинкой из браузера.
Итак, вот еще пара фотографий машинки:
Вот фронтальный вид:
Дальше нужно сделать проект для ПЛИС Альтера платы Марсоход. Проект должен определать частоту подаваемого аудио сигнала и интерпретировать эти сигналы как команды. Собственно такой проект у меня уже есть готовый (там же и детальное описание куда и как подавать аудио сигнал на ПЛИС). Только в том проекте просто зажигались светодиоды. Сейчас нужно еще добавить управление моторчиками. Собственно это я и сделал. По схеме плата Марсоход имеет шесть мощных контактов F0, F1, F2, F3, F4, F5 как раз для прямого подключения игрушечных моторчиков - на каждом таком контакте по 10 параллельных выходов ПЛИС (тока моторчикам хватит). Один моторчик подключается к двум контактам, например F0 и F1. Если на F0 единица, а на F1 ноль то вращается в одну сторону. Если на F1 единица, а на F0 ноль, то вращается в другую сторону. Оба контакта в нуле - стоит. Вся логика описана в проекте на Verilog HDL с помощью конструкции case-encase. Ничего сложного там нет.
Весь проект для среды Altera Quartus II можно взять здесь:
После того, как проект будет откомпилирован в Altera Quartus II и прошит в плату можно собирать всю машинку.
Дальше самое сложное для меня - написание скрипта для Python для телефона. Опыта у меня не очень много, но читая описания и примеры на сайте SL4A (Script Layer For Android) мне удалось сделать задуманное.
Скрипт на питоне, работающий на телефоне, делает следующее:
- Реализует WebServer, работающий на телефоне с Android (собственно это я уже делал здесь). TCP порт 9090.
- WebServer отдает три разных HTML страницы (их текст закодирован прямо в теле скрипта Python). Первая главная html страница описывает 2 фрейма, которые делят экран браузера на 2 окна, две другие html страницы - собственно для двух html фреймов.
- В верхнем фрейме будет окно трансляции с камеры телефона.
- В нижнем фрейме будет форма с кнопками вроде "start", "stop", "left", "right". По нажатию на кнопки html формы нижнего фрейма будет отсылаться HTTP-GET запрос на телефон.
- WebServer принимает и обрабатывает HTTP-GET запросы и согласно запросу воспроизводит короткий звуковой ролик с синусоидой нужной тональности. Плата Марсоход распознает частоту звука и интерпретирует ее как команду ехать, повернуть влево или вправо, остановиться и т.д.
- Еще важная функция скрипта - инициировать вещание с камеры телефона. Вещание ведется на TCP порту 9091.
- Деление окна браузера на два фрейма нужно, чтобы вещание не прекращалось пока идет HTTP-GET запрос на управление машинкой. Фреймы независимы друг от друга.
Собственно тект скрипта вот такой:
"""HTTP server"""
import android
import BaseHTTPServer
import socket
import urlparse
HOST_NAME = ''
PORT_NUMBER = 9090
droid = android.Android()
PAGE_TEMPLATE = '''
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>DroidBot Remote Control</title>
</head>
<FRAMESET ROWS="50%,50%">
<FRAME SRC="frame_a.html">
<FRAME SRC="frame_b.html">
</FRAMESET>
</html>
'''
PAGE_TEMPLATE_A = '''
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>DroidBot Remote Control</title>
</head>
<body>
<h1>Marsohod Remote Control</h1>
<iframe width="640" height="480" src ="http://%s:9091">No iframes?</iframe>"
</body>
</html>
'''
PAGE_TEMPLATE_B = '''
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>DroidBot Remote Control</title>
<style type="text/css">
#action {
background:yellow;
border:0px solid #555;
color:#555;
width:0px;
height:0px;
padding:0px;
}
</style>
<script>
function AddText(text)
{
document.myform.action.value=text;
}
</script>
</head>
<body>
<form name="myform" method="get">
<textarea id="action" name="action">start</textarea>
<input id="button1" type="submit" value="Start" OnClick='javascript:AddText ("start")' />
<input id="button2" type="submit" value="Stop" OnClick='javascript:AddText ("stop")' />
<input id="button3" type="submit" value="Back" OnClick='javascript:AddText ("back")' />
<input id="button4" type="submit" value="Left" OnClick='javascript:AddText ("left")' />
<input id="button5" type="submit" value="Right" OnClick='javascript:AddText ("right")' />
</form>
</body>
</html>
'''
def play( id ):
if (id=='start'):
droid.mediaPlay('/mnt/sdcard/media/audio/s1000.wav')
elif (id=='back'):
droid.mediaPlay('/mnt/sdcard/media/audio/s1200.wav')
elif (id=='left'):
droid.mediaPlay('/mnt/sdcard/media/audio/s1400.wav')
elif (id=='right'):
droid.mediaPlay('/mnt/sdcard/media/audio/s1600.wav')
elif (id=='stop'):
droid.mediaPlay('/mnt/sdcard/media/audio/s1800.wav')
class DroidHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_HEAD(s):
s.send_response(200)
s.send_header("Content-type", "text/html; charset=utf-8")
s.end_headers()
def do_GET(s):
s.send_response(200)
my_full_addr = s.headers.get('Host')
my_addr = my_full_addr.split(":",2)
my_ip_addr = my_addr[0]
url = urlparse.urlsplit(s.path)
print url.path
if url.path == '/frame_a.html':
s.send_header("Content-type", "text/html; charset=utf-8")
s.end_headers()
html = PAGE_TEMPLATE_A % my_ip_addr
s.wfile.write(html)
return
elif url.path == '/frame_b.html':
s.send_header("Content-type", "text/html; charset=utf-8")
s.end_headers()
query = url.query
args = urlparse.parse_qsl(query)
action = ''
for arg in args:
if arg[0] == 'action':
action = arg[1].strip().replace('\r', '')
print(action)
play(action)
break
html = PAGE_TEMPLATE_B
s.wfile.write(html)
return
s.send_header("Content-type", "text/html; charset=utf-8")
s.end_headers()
html = PAGE_TEMPLATE
s.wfile.write(html)
print 'web server running on port %s' % PORT_NUMBER
droid.wakeLockAcquireBright()
droid.webcamStart(0,10,9091)
droid.webcamAdjustQuality(0,10)
my_srv = BaseHTTPServer.HTTPServer((HOST_NAME, PORT_NUMBER), DroidHandler)
my_srv.serve_forever()
Вот этот скрипт нужно перенести на телефон и запускать из программы SL4A.
Не забудьте перед запуском скрипта посмотреть текущий IP адрес телефона. У меня дома WiFi точка и телефон похоже всегда берет один адрес 192.168.1.200. У вас конечно может быть другой.
Еще фишка - почему-то Firefox не хочет нормально смотреть трансляцию с телефона. А вот с Google Chrome все нормально. Трансляция видео не очень живая, но я думаю это из-за того, что мой телефон довольно слаб. Нужно попробовать что-то помегагерцнее...
В строке браузера набираете адрес телефона и номер порта 9090 через двоеточие: вот снимок экрана
Ну и наконец демонстрация работы, фото и видео:
Вот еще фотка:
Правда на улице освещение слишком яркое, снимать видео с блестящего экрана ноутбука почти невозможно.
В помещении видно чуть получше:
Вот такой замечательный проект мне удалось реализовать.
Подробнее...