解释通过uv4l-webrtc数据通道发送到覆盆子pi的按键

问题描述:

我很抱歉,如果这没有意义,因为我仍然是使用覆盆子pi的新手,这是我第一次在*上发布。解释通过uv4l-webrtc数据通道发送到覆盆子pi的按键

我正在制作一个网络应用程序,可以让我从一个树莓派流视频,同时让我发送键码。发送的键码最终可以让我控制无人机上的舵机。淘汰互联网之后,我发现流式传输视频的最简单方法是使用uv4l,因此我将它与uv4l-webrtc一起安装在我的覆盆子pi上。我将一些GPIO引脚连接到飞行控制器,并使用pigpio向它发送PWM信号,然后使用CleanFlight进行监控。

现在,我可以使用python脚本来操纵飞行控制器的滚动,俯仰等操作,如果我使用VNC远程访问pi,但我希望最终能够通过我的自定义网页由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() 

当UV4L和其他的WebRTC等(即浏览器,剑锋网关等)之间建立WebRTC数据信道,UV4L创建一个全双工Unix领域套接字(默认为/tmp/uv4l.socket),您可以在Raspberry Pi上接收/发送消息。您的python脚本应该打开,监听并读取来自例如传入消息的套接字。 Web应用程序和/或将消息写入到Web应用程序的相同套接字来接收它们。一个example在C++中这样做是链接到你在你的问题中指出的教程下:

/* 
    Copyright (c) 2016 [email protected] 
    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; 
    } 
} 
+0

虽然这种联系可以回答这个问题,最好是在这里有答案的主要部件,并提供连结以供参考。如果链接页面更改,则仅链接答案可能会失效。 –

+0

这不是一个“仅链接”的答案。这是关键部分的答案 - 除非你有更好的答案 - 与官方例子的链接,这在OP问题中也被引用。 – adminkiam

+0

不过,您应该在这里添加代码摘录来说明您的答案。 –