【问题标题】:Interpreting keypresses sent to raspberry-pi through uv4l-webrtc datachannel解释通过 uv4l-webrtc 数据通道发送到 raspberry-pi 的按键
【发布时间】:2017-07-28 04:28:20
【问题描述】:

如果这没有意义,我深表歉意,因为我还是一个使用树莓派的新手,这是我第一次在 StackOverflow 上发帖。

我正在制作一个网络应用程序,它可以让我在树莓派之间传输视频,同时还可以让我发送密钥代码。发送的键码最终会让我控制无人机上的伺服系统。在搜索了互联网之后,我发现流式传输 2 路视频的最简单方法是使用 uv4l,因此我将它与 uv4l-webrtc 一起安装在我的树莓派上。我将一些 GPIO 引脚连接到飞行控制器,并使用 pigpio 向其发送 PWM 信号,然后使用 CleanFlight 对其进行监控。

现在,如果我使用VNC 远程访问 pi,我可以使用 python 脚本通过按键操作飞行控制器的滚动、俯仰等,但我希望最终能够通过我的由 uv4l-server 提供的自定义网页。我正在尝试使用 WebRTC 数据通道,但我无法理解如何识别通过数据通道发送的消息。我知道发起视频通话时会打开数据通道,并且我已经在link 中尝试过测试,看看我是否确实可以将密钥代码发送到 pi(我可以)。

我现在的问题是我不知道这些发送的消息去哪里了,也不知道如何获取它们,以便将它们合并到我的 python 脚本中。我需要创建一个服务器来监听发送到 pi 的键码吗?

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

感谢@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()

【问题讨论】:

    标签: raspberry-pi webrtc uv4l


    【解决方案1】:

    当在 UV4L 和其他 WebRTC 对等点(即浏览器、Janus 网关等)之间创建 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;
        }
    }
    

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
    • 这不是“仅链接”的答案。这是基本部分的答案 - 除非你有更好的答案 - 带有官方示例的链接,在 OP 问题中也引用了该链接。
    • 不过,您应该在此处添加代码摘录来说明您的答案。
    • 谢谢@adminkiam!这正是我所需要的。我在测试数据通道时看到了该代码,但我忽略了它,因为我不想处理 c++ 。我只需要将一些部分翻译成 python,它现在就可以工作了。事实证明我应该更加关注它,我可以节省一些时间:P
    • @trixr4kdz 很高兴知道你让它工作了......如果你可以在 python 中发布相关代码,这对其他人会有所帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-16
    • 1970-01-01
    相关资源
    最近更新 更多