通过uv4l-webrtc datachannel解释发送到raspberry-pi的按键

时间:2017-07-28 04:28:21

标签: raspberry-pi webrtc uv4l

我道歉,如果这不合理,因为我还是一个使用覆盆子pi的新手,这是我第一次在StackOverflow上发帖。

我正在创建一个网络应用程序,让我可以在覆盆子pi中传输视频,同时还允许我发送密钥代码。发送的密钥代码最终会让我控制无人机上的伺服系统。在浏览互联网之后,我认为流式双向视频的最简单方法是使用uv4l,因此我将它与uv4l-webrtc一起安装在我的覆盆子pi上。我将一些GPIO引脚连接到飞行控制器,我正在使用pigpio向它发送PWM信号,然后我使用CleanFlight进行监控。

现在,如果我使用VNC远程访问pi,我可以使用python脚本操纵飞行控制器的滚动,俯仰等按键操作,但我希望最终能够做到这一点通过我的自定义网页,由uv4l服务器提供服务。我正在尝试使用WebRTC数据通道,但是我在理解识别通过数据通道发送的消息时需要做些什么时遇到了一些麻烦。我知道在启动视频通话时数据通道已打开,我已在此link中尝试测试,看看我是否确实可以将密钥代码发送到pi(我可以)。

我现在的问题是我不知道发送消息的位置或者我如何获取它们所以我可以将它们合并到我的python脚本中。我是否需要制作一个服务器来监听发送给pi的密钥代码?

tl; dr我在树莓派上有一个python脚本来控制飞行控制器上的伺服器使用按键和一个使用WebRTC流式传输视频的单独网页,但我不知道如何使用WebRTC数据通道将它们组合在一起。 / p>

感谢@adminkiam的解决方案。这是现在监听套接字的python脚本的一个版本。它本质上是this code by the person who made pigpio的变体:

import socket
import time
import pigpio

socket_path = '/tmp/uv4l.socket'

try:
    os.unlink(socket_path)
except OSError:
    if os.path.exists(socket_path):
        raise

s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)

ROLL_PIN     = 13
PITCH_PIN    = 14
YAW_PIN      = 15

MIN_PW = 1000
MID_PW = 1500
MAX_PW = 2000

NONE        = 0
LEFT_ARROW  = 1
RIGHT_ARROW = 2
UP_ARROW    = 3
DOWN_ARROW  = 4
LESS_BTN    = 5
GREATER_BTN = 6

print 'socket_path: %s' % socket_path
s.bind(socket_path)
s.listen(1)

def getch(keyCode):
    key = NONE
    if keyCode == 188:
        key = LESS_BTN
    elif keyCode == 190:
        key = GREATER_BTN
    elif keyCode == 37:
        key = LEFT_ARROW
    elif keyCode == 39:
        key = RIGHT_ARROW
    elif keyCode == 38:
        key = UP_ARROW
    elif keyCode == 40:
        key = DOWN_ARROW
    return key

def cleanup():
    pi.set_servo_pulsewidth(ROLL_PIN, 0)
    pi.set_servo_pulsewidth(PITCH_PIN, 0)
    pi.set_servo_pulsewidth(YAW_PIN, 0)
    pi.stop()

while True:
    print 'awaiting connection...'
    connection, client_address = s.accept()
    print 'client_address %s' % client_address
    try:
        print 'established connection with', client_address

        pi = pigpio.pi()

        rollPulsewidth     = MID_PW
        pitchPulsewidth    = MID_PW
        yawPulsewidth      = MID_PW

        pi.set_servo_pulsewidth(ROLL_PIN, rollPulsewidth)
        pi.set_servo_pulsewidth(PITCH_PIN, pitchPulsewidth)
        pi.set_servo_pulsewidth(YAW_PIN, yawPulsewidth)

        while True:
            data = connection.recv(16)
            print 'received message"%s"' % data

            time.sleep(0.01)
            key = getch(int(data))

            rollPW     = rollPulsewidth
            pitchPW    = pitchPulsewidth
            yawPW      = yawPulsewidth

            if key == UP_ARROW:
                pitchPW = pitchPW + 10
                if pitchPW > MAX_PW:
                    pitchPW = MAX_PW
            elif key == DOWN_ARROW:
                pitchPW = pitchPW - 10
                if pitchPW < MIN_PW:
                    pitchPW = MIN_PW
            elif key == LEFT_ARROW:
                rollPW = rollPW - 10
                if rollPW < MIN_PW:
                    rollPW = MIN_PW
            elif key == RIGHT_ARROW:
                rollPW = rollPW + 10
                if rollPW > MAX_PW:
                    rollPW = MAX_PW
            elif key == GREATER_BTN:
                yawPW = yawPW + 10
                if yawPW > MAX_PW:
                    yawPW = MAX_PW
            elif key == LESS_BTN:
                yawPW = yawPW - 10
                if yawPW < MIN_PW:
                    yawPW = MIN_PW

            if rollPW != rollPulsewidth:
                rollPulsewidth = rollPW
                pi.set_servo_pulsewidth(ROLL_PIN, rollPulsewidth)
            if pitchPW != pitchPulsewidth:
                pitchPulsewidth = pitchPW
                pi.set_servo_pulsewidth(PITCH_PIN, pitchPulsewidth)
            if yawPW != yawPulsewidth:
                yawPulsewidth = yawPW
                pi.set_servo_pulsewidth(YAW_PIN, yawPulsewidth)

            if data:
                print 'echo data to client'
                connection.sendall(data)
            else:
                print 'no more data from', client_address
                break

    finally:
        # Clean up the connection
        cleanup()
        connection.close()

1 个答案:

答案 0 :(得分:1)

当在UV4L和其他WebRTC对等体(即浏览器,Janus Gateway等)之间创建WebRTC数据通道时,UV4L会创建一个全双工Unix域套接字(默认情况下为/tmp/uv4l.socket)您可以在Raspberry Pi上接收/发送消息。您的python脚本应该只是打开,监听并读取套接字以获取来自例如的传入消息。 Web应用程序和/或将消息写入同一个套接字,以便Web应用程序接收它们。在C ++中执行此操作的example位于您在问题中指出的教程的链接:

/*
    Copyright (c) 2016 info@linux-projects.org
    All rights reserved.

    Redistribution and use in source and binary forms are permitted
    provided that the above copyright notice and this paragraph are
    duplicated in all such forms and that any documentation,
    advertising materials, and other materials related to such
    distribution and use acknowledge that the software was developed
    by the linux-projects.org. The name of the
    linux-projects.org may not be used to endorse or promote products derived
    from this software without specific prior written permission.
    THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/

/*
 * This is a simple echo server.
 * It creates to a unix domain socket of type SOCK_SEQPACKET specified by
 * command line, listens to it waiting for incoming messages from clients
 * (e.g. UV4L) and replies the received messages back to the senders.
 *
 * Example:
 *     $ ./datachannel_server /tmp/uv4l.socket
 *
 * To compile this program you need boost v1.60 or greater, for example:
 * g++ -Wall -I/path/to/boost/include/ -std=c++11 datachannel_server.cpp -L/path/to/boost/lib -l:libboost_coroutine.a -l:libboost_context.a -l:libboost_system.a -l:libboost_thread.a -pthread -o datachannel_server
 */

#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio.hpp>
#include <memory>
#include <cstdio>
#include <array>
#include <functional>
#include <iostream>

#if !defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
#error Local sockets not available on this platform.
#endif

constexpr std::size_t MAX_PACKET_SIZE = 1024 * 16;

namespace seqpacket {

    struct seqpacket_protocol {

        int type() const {
            return SOCK_SEQPACKET;
        }

        int protocol() const {
            return 0;
        }

        int family() const {
            return AF_UNIX;
        }

        using endpoint = boost::asio::local::basic_endpoint<seqpacket_protocol>;
        using socket = boost::asio::generic::seq_packet_protocol::socket;
        using acceptor = boost::asio::basic_socket_acceptor<seqpacket_protocol>;

#if !defined(BOOST_ASIO_NO_IOSTREAM)
        /// The UNIX domain iostream type.
        using iostream = boost::asio::basic_socket_iostream<seqpacket_protocol>;
#endif
    };
}

using seqpacket::seqpacket_protocol;

struct session : public std::enable_shared_from_this<session> {
    explicit session(seqpacket_protocol::socket socket) : socket_(std::move(socket)) {}

    ~session() {
        //std::cerr << "session closed\n";
    }

    void echo(boost::asio::yield_context yield) {
        auto self = shared_from_this();
        try {
            for (;;) {
                seqpacket_protocol::socket::message_flags in_flags = MSG_WAITALL, out_flags = MSG_WAITALL;

                // Wait for the message from the client
                auto bytes_transferred = socket_.async_receive(boost::asio::buffer(data_), in_flags, yield);

                // Write the same message back to the client
                socket_.async_send(boost::asio::buffer(data_, bytes_transferred), out_flags, yield);
            }
        } catch (const std::exception& e) {
            std::cerr << e.what() << '\n';
            socket_.close();
        }
    }

    void go() {
        boost::asio::spawn(socket_.get_io_service(), std::bind(&session::echo, this, std::placeholders::_1));
    }

private:
    seqpacket_protocol::socket socket_;
    std::array<char, MAX_PACKET_SIZE> data_;
};

int main(int argc, char* argv[]) {
    try {
        if (argc != 2) {
            std::cerr << "Usage: datachannel_server <file> (e.g. /tmp/uv4l.socket)\n";
            std::cerr << "*** WARNING: existing file is removed ***\n";
            return EXIT_FAILURE;
        }

        boost::asio::io_service io_service;

        std::remove(argv[1]);

        boost::asio::spawn(io_service, [&](boost::asio::yield_context yield) {
                    seqpacket_protocol::acceptor acceptor_(io_service, seqpacket_protocol::endpoint(argv[1]));
                    for (;;) {
                        boost::system::error_code ec;
                        seqpacket_protocol::socket socket_(io_service);
                        acceptor_.async_accept(socket_, yield[ec]);
                        if (!ec)
                            std::make_shared<session>(std::move(socket_))->go();
                    }
                });

        io_service.run();

    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
        return EXIT_FAILURE;
    }
}