Всем привет! В прошлой статье я рассказал как работает веб интерфейс управления роботом, эдакий передатчик команд. В этой статье было бы логично рассказать как работает "приемник этих самых команд".
Если Вы следите за трелло, то знаете, что в планах было реализовать управление роботом по сокетам и с помощью REST API, но от последнего я отказался.
Собственно, начнем.
Стек технологий на самом деле легко предсказать: нам нужно принимать команды и применять их каким-то образом. Вспоминаем, что мы собираем робота на RPi, значит нам пригодится RPi.GPIO, ну а принимать команды мы будем с помощью python-socketio:
%lang(python)% import eventlet import socketio import mech
mech это самописный модуль, его импорты таковы:
%lang(python)% import RPi.GPIO as GPIO
Сначала нужно проинициализировать сокет сервер:
%lang(python)% sio = socketio.Server(cors_allowed_origins='*') app = socketio.WSGIApp(sio, static_files={ '/': {'content_type': 'text/html', 'filename': 'index.html'} })
Мы инициализируем его как WSGI приложение, однако пользоваться этим мы не будем)))
Не будем ставить на малину веб сервер, будем просто кидать запросы на ip:port.
Обращаю внимание, что мы разрешаем делать запросы откуда угодно. Это нужно потому что запрос с ПК оператора робота на малину считается cross-origin (что логично), и без такой настройки он просто не пройдет.
Далее инициализируем наш mech модуль:
%lang(python)% mech.init()
Вот код иницализации:
%lang(python)% GPIO.setmode(GPIO.BCM) # настраиваем логический маппинг пинов (их 2) GPIO.setwarnings(False) # говорим, что нам неинтересно знать, что там уже используется for pin in pins_on: GPIO.setup(pin, GPIO.OUT) # настраиваем все пины на выход left_right_motors_off() # на всякий случай ставим их в логический 0
pins_on это массив GPIO, которые мы используем.
Рассмотрим низкоуровневые функции работы с GPIO:
%lang(python)% def left_right_motors_off(): for pin in pins_on: GPIO.output(pin, False) def left_motor_on(ward): level = True if ward == 'forward' else False GPIO.output(21, level) GPIO.output(22, not level) def right_motor_on(ward): level = True if ward == 'forward' else False GPIO.output(23, level) GPIO.output(24, not level)
Мы просто в зависимости от переданного аргумента (forward/backward) устанавливаем пины в нужные логические уровни. Моторы подключены через драйвер TB6612FNG:
Таким образом, мы можем менять направление вращения ротора моторов.
Условно есть 2 пина A и B. Если A = 1, B = 0, то вращение, например, по часовой стрелке, иначе - против.
Пины PWM этого драйвера мы не используем, поэтому просто подадим на них лог. 1.
На пин STBY подаем лог. 1 (Это пин включения драйвера, пускай всегда будет включен)
Остались пины питания и входы-выходы драйверы, но тут все вроде бы логично.
Теперь посмотрим чуть выше:
%lang(python)% def go(direction): print('go', direction) right_motor_on(direction) left_motor_on(direction) def stop(): print('stop') left_right_motors_off() def turn(direction): print('turn', direction) if direction == 'left': left_motor_on('forward') right_motor_on('backward') else: left_motor_on('backward') right_motor_on('forward')
В зависимости от направления включаем моторы в нужном порядке в нужные стороны вращения.
Все довольно просто.
Но как же все это работает в совокупности?
А вот как:
%lang(python)% @sio.event def connect(sid, environ): print('connect ', sid) @sio.event def disconnect(sid): print('disconnect ', sid) @sio.on('go') def go(sid, data): mech.go(data) @sio.on('turn') def turn(sid, data): mech.turn(data) @sio.on('stop') def stop(sid, data): mech.stop()
Мы регистрируем слушателей событий. И в момент, когда эти события приходят с веб-интерфейса, вызываем нужные функции. Все!
Остается разве что запустить сервер:
%lang(python)% if __name__ == '__main__': eventlet.wsgi.server(eventlet.listen(('', 8765)), app)
Практика подходит к концу. Остается немного времени. На днях я соберу весь материал в кучу и мы посмотрим, что нам удалось собрать. Следите за обновлениями!
Спасибо за внимание.