Add files via upload
|
@ -0,0 +1,416 @@
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <event2/event.h>
|
||||||
|
#include <event2/util.h>
|
||||||
|
#include <event2/buffer.h>
|
||||||
|
#include <event2/bufferevent.h>
|
||||||
|
|
||||||
|
#define MAX_MTU 9000
|
||||||
|
|
||||||
|
bool verbose = false;
|
||||||
|
|
||||||
|
const char *default_master = "/dev/ttyAMA0";
|
||||||
|
const int default_baudrate = 115200;
|
||||||
|
const char *defualt_out_addr = "127.0.0.1:14600";
|
||||||
|
const char *default_in_addr = "127.0.0.1:14601";
|
||||||
|
|
||||||
|
uint8_t ch_count = 0;
|
||||||
|
uint16_t ch[14];
|
||||||
|
|
||||||
|
struct bufferevent *serial_bev;
|
||||||
|
struct sockaddr_in sin_out = {
|
||||||
|
.sin_family = AF_INET,
|
||||||
|
};
|
||||||
|
int out_sock;
|
||||||
|
|
||||||
|
static void print_usage()
|
||||||
|
{
|
||||||
|
printf("Usage: mavfwd [OPTIONS]\n"
|
||||||
|
"Where:\n"
|
||||||
|
" --master Local MAVLink master port (%s by default)\n"
|
||||||
|
" --baudrate Serial port baudrate (%d by default)\n"
|
||||||
|
" --out Remote output port (%s by default)\n"
|
||||||
|
" --in Remote input port (%s by default)\n"
|
||||||
|
" --channels RC override channels to parse after first 4 and call /root/channels.sh $ch $val, default 0\n"
|
||||||
|
" --verbose display each packet, default not\n"
|
||||||
|
" --help Display this help\n",
|
||||||
|
default_master, default_baudrate, defualt_out_addr,
|
||||||
|
default_in_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static speed_t speed_by_value(int baudrate)
|
||||||
|
{
|
||||||
|
switch (baudrate) {
|
||||||
|
case 9600:
|
||||||
|
return B9600;
|
||||||
|
case 19200:
|
||||||
|
return B19200;
|
||||||
|
case 38400:
|
||||||
|
return B38400;
|
||||||
|
case 57600:
|
||||||
|
return B57600;
|
||||||
|
case 115200:
|
||||||
|
return B115200;
|
||||||
|
case 230400:
|
||||||
|
return B230400;
|
||||||
|
case 460800:
|
||||||
|
return B460800;
|
||||||
|
case 500000:
|
||||||
|
return B500000;
|
||||||
|
case 921600:
|
||||||
|
return B921600;
|
||||||
|
case 1500000:
|
||||||
|
return B1500000;
|
||||||
|
default:
|
||||||
|
printf("Not implemented baudrate %d\n", baudrate);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_host_port(const char *s, struct in_addr *out_addr,
|
||||||
|
in_port_t *out_port)
|
||||||
|
{
|
||||||
|
char host_and_port[32] = { 0 };
|
||||||
|
strncpy(host_and_port, s, sizeof(host_and_port) - 1);
|
||||||
|
|
||||||
|
char *colon = strchr(host_and_port, ':');
|
||||||
|
if (NULL == colon) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*colon = '\0';
|
||||||
|
const char *host = host_and_port, *port_ptr = colon + 1;
|
||||||
|
|
||||||
|
const bool is_valid_addr = inet_aton(host, out_addr) != 0;
|
||||||
|
if (!is_valid_addr) {
|
||||||
|
printf("Cannot parse host `%s'.\n", host);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int port;
|
||||||
|
if (sscanf(port_ptr, "%d", &port) != 1) {
|
||||||
|
printf("Cannot parse port `%s'.\n", port_ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out_port = htons(port);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signal_cb(evutil_socket_t fd, short event, void *arg)
|
||||||
|
{
|
||||||
|
struct event_base *base = arg;
|
||||||
|
(void)event;
|
||||||
|
|
||||||
|
printf("%s signal received\n", strsignal(fd));
|
||||||
|
event_base_loopbreak(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_mavlink_packet(unsigned char *data, const char *direction)
|
||||||
|
{
|
||||||
|
uint8_t seq;
|
||||||
|
uint8_t sys_id;
|
||||||
|
uint8_t comp_id;
|
||||||
|
uint32_t msg_id;
|
||||||
|
|
||||||
|
if(data[0] == 0xFE) { //mavlink 1
|
||||||
|
seq = data[2];
|
||||||
|
sys_id = data[3];
|
||||||
|
comp_id = data[4];
|
||||||
|
msg_id = data[5];
|
||||||
|
} else { //mavlink 2
|
||||||
|
seq = data[4];
|
||||||
|
sys_id = data[5];
|
||||||
|
comp_id = data[6];
|
||||||
|
msg_id = data[7];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) printf("%s %#02x sender %d/%d\t%d\t%d\n", direction, data[0], sys_id, comp_id, seq, msg_id);
|
||||||
|
|
||||||
|
uint16_t val;
|
||||||
|
|
||||||
|
//RC_CHANNELS ( #65 ) hook
|
||||||
|
if(msg_id == 65 && ch_count > 0) {
|
||||||
|
uint8_t offset = 18; //15 = 1ch;
|
||||||
|
for(uint8_t i=0; i < ch_count; i++) {
|
||||||
|
val = data[offset] | (data[offset+1] << 8);
|
||||||
|
if(ch[i] != val) {
|
||||||
|
ch[i] = val;
|
||||||
|
char buff[44];
|
||||||
|
sprintf(buff, "/root/channels.sh %d %d &", i+5, val);
|
||||||
|
system(buff);
|
||||||
|
if (verbose) printf("called /root/channels.sh %d %d\n", i+5, val);
|
||||||
|
}
|
||||||
|
offset = offset + 2;
|
||||||
|
} //for
|
||||||
|
} //msg_id
|
||||||
|
}
|
||||||
|
|
||||||
|
/* https://discuss.ardupilot.org/uploads/short-url/vS0JJd3BQfN9uF4DkY7bAeb6Svd.pdf
|
||||||
|
* 0. Message header, always 0xFE
|
||||||
|
* 1. Message length
|
||||||
|
* 2. Sequence number -- rolls around from 255 to 0 (0x4e, previous was 0x4d)
|
||||||
|
* 3. System ID - what system is sending this message
|
||||||
|
* 4. Component ID- what component of the system is sending the message
|
||||||
|
* 5. Message ID (e.g. 0 = heartbeat and many more! Don’t be shy, you can add too..)
|
||||||
|
*/
|
||||||
|
static bool get_mavlink_packet(unsigned char *in_buffer, int buf_len,
|
||||||
|
int *packet_len)
|
||||||
|
{
|
||||||
|
if (buf_len < 6 /* header */) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
assert(in_buffer[0] == 0xFE || in_buffer[0] == 0xFD); //mavlink 1 or 2
|
||||||
|
|
||||||
|
uint8_t msg_len = in_buffer[1];
|
||||||
|
if (in_buffer[0] == 0xFE)
|
||||||
|
*packet_len = 6 /* header */ + msg_len + 2 /* crc */; //mavlink 1
|
||||||
|
else
|
||||||
|
*packet_len = 10 /* header */ + msg_len + 2 /* crc */; //mavlink 2
|
||||||
|
if (buf_len < *packet_len)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dump_mavlink_packet(in_buffer, ">>");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns num bytes before first occurrence of 0xFE or full data length
|
||||||
|
static size_t until_first_fe(unsigned char *data, size_t len)
|
||||||
|
{
|
||||||
|
for (size_t i = 1; i < len; i++) {
|
||||||
|
if (data[i] == 0xFE || data[i] == 0xFD) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_read_cb(struct bufferevent *bev, void *arg)
|
||||||
|
{
|
||||||
|
struct evbuffer *input = bufferevent_get_input(bev);
|
||||||
|
int packet_len, in_len;
|
||||||
|
struct event_base *base = arg;
|
||||||
|
|
||||||
|
while ((in_len = evbuffer_get_length(input))) {
|
||||||
|
unsigned char *data = evbuffer_pullup(input, in_len);
|
||||||
|
if (data == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find first 0xFE and skip everything before it
|
||||||
|
if (*data != 0xFE && *data != 0xFD) {
|
||||||
|
int bad_len = until_first_fe(data, in_len);
|
||||||
|
if (verbose) printf(">> Skipping %d bytes of unknown data\n",
|
||||||
|
bad_len);
|
||||||
|
evbuffer_drain(input, bad_len);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!get_mavlink_packet(data, in_len, &packet_len))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: check CRC correctness and skip bad packets
|
||||||
|
|
||||||
|
if (sendto(out_sock, data, packet_len, 0,
|
||||||
|
(struct sockaddr *)&sin_out,
|
||||||
|
sizeof(sin_out)) == -1) {
|
||||||
|
perror("sendto()");
|
||||||
|
event_base_loopbreak(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
evbuffer_drain(input, packet_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_event_cb(struct bufferevent *bev, short events, void *arg)
|
||||||
|
{
|
||||||
|
(void)bev;
|
||||||
|
struct event_base *base = arg;
|
||||||
|
|
||||||
|
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
|
||||||
|
printf("Serial connection closed\n");
|
||||||
|
event_base_loopbreak(base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void in_read(evutil_socket_t sock, short event, void *arg)
|
||||||
|
{
|
||||||
|
(void)event;
|
||||||
|
unsigned char buf[MAX_MTU];
|
||||||
|
struct event_base *base = arg;
|
||||||
|
ssize_t nread;
|
||||||
|
|
||||||
|
nread = recvfrom(sock, &buf, sizeof(buf) - 1, 0, NULL, NULL);
|
||||||
|
if (nread == -1) {
|
||||||
|
perror("recvfrom()");
|
||||||
|
event_base_loopbreak(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(nread > 6);
|
||||||
|
|
||||||
|
dump_mavlink_packet(buf, "<<");
|
||||||
|
|
||||||
|
bufferevent_write(serial_bev, buf, nread);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_data(const char *port_name, int baudrate,
|
||||||
|
const char *out_addr, const char *in_addr)
|
||||||
|
{
|
||||||
|
struct event_base *base = NULL;
|
||||||
|
struct event *sig_int = NULL, *in_ev = NULL;
|
||||||
|
int ret = EXIT_SUCCESS;
|
||||||
|
|
||||||
|
int serial_fd = open(port_name, O_RDWR | O_NOCTTY);
|
||||||
|
if (serial_fd < 0) {
|
||||||
|
printf("Error while openning port %s: %s\n", port_name,
|
||||||
|
strerror(errno));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
};
|
||||||
|
evutil_make_socket_nonblocking(serial_fd);
|
||||||
|
|
||||||
|
struct termios options;
|
||||||
|
tcgetattr(serial_fd, &options);
|
||||||
|
cfsetspeed(&options, speed_by_value(baudrate));
|
||||||
|
|
||||||
|
options.c_cflag &= ~CSIZE; // Mask the character size bits
|
||||||
|
options.c_cflag |= CS8; // 8 bit data
|
||||||
|
options.c_cflag &= ~PARENB; // set parity to no
|
||||||
|
options.c_cflag &= ~PARODD; // set parity to no
|
||||||
|
options.c_cflag &= ~CSTOPB; // set one stop bit
|
||||||
|
|
||||||
|
options.c_cflag |= (CLOCAL | CREAD);
|
||||||
|
|
||||||
|
options.c_oflag &= ~OPOST;
|
||||||
|
|
||||||
|
options.c_lflag &= 0;
|
||||||
|
options.c_iflag &= 0; // disable software flow controll
|
||||||
|
options.c_oflag &= 0;
|
||||||
|
|
||||||
|
cfmakeraw(&options);
|
||||||
|
tcsetattr(serial_fd, TCSANOW, &options);
|
||||||
|
|
||||||
|
out_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
int in_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
struct sockaddr_in sin_in = {
|
||||||
|
.sin_family = AF_INET,
|
||||||
|
};
|
||||||
|
if (!parse_host_port(in_addr, (struct in_addr *)&sin_in.sin_addr.s_addr,
|
||||||
|
&sin_in.sin_port))
|
||||||
|
goto err;
|
||||||
|
if (!parse_host_port(out_addr,
|
||||||
|
(struct in_addr *)&sin_out.sin_addr.s_addr,
|
||||||
|
&sin_out.sin_port))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if (bind(in_sock, (struct sockaddr *)&sin_in, sizeof(sin_in))) {
|
||||||
|
perror("bind()");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
printf("Listening on %s...\n", in_addr);
|
||||||
|
|
||||||
|
base = event_base_new();
|
||||||
|
|
||||||
|
sig_int = evsignal_new(base, SIGINT, signal_cb, base);
|
||||||
|
event_add(sig_int, NULL);
|
||||||
|
// it's recommended by libevent authors to ignore SIGPIPE
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
serial_bev = bufferevent_socket_new(base, serial_fd, 0);
|
||||||
|
bufferevent_setcb(serial_bev, serial_read_cb, NULL, serial_event_cb,
|
||||||
|
base);
|
||||||
|
bufferevent_enable(serial_bev, EV_READ);
|
||||||
|
|
||||||
|
in_ev = event_new(base, in_sock, EV_READ | EV_PERSIST, in_read, NULL);
|
||||||
|
event_add(in_ev, NULL);
|
||||||
|
|
||||||
|
event_base_dispatch(base);
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (serial_fd >= 0)
|
||||||
|
close(serial_fd);
|
||||||
|
|
||||||
|
if (serial_bev)
|
||||||
|
bufferevent_free(serial_bev);
|
||||||
|
|
||||||
|
if (in_ev) {
|
||||||
|
event_del(in_ev);
|
||||||
|
event_free(in_ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sig_int)
|
||||||
|
event_free(sig_int);
|
||||||
|
|
||||||
|
if (base)
|
||||||
|
event_base_free(base);
|
||||||
|
|
||||||
|
libevent_global_shutdown();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
const struct option long_options[] = {
|
||||||
|
{ "master", required_argument, NULL, 'm' },
|
||||||
|
{ "baudrate", required_argument, NULL, 'b' },
|
||||||
|
{ "out", required_argument, NULL, 'o' },
|
||||||
|
{ "in", required_argument, NULL, 'i' },
|
||||||
|
{ "channels", required_argument, NULL, 'c' },
|
||||||
|
{ "verbose", no_argument, NULL, 'v' },
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ NULL, 0, NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *port_name = default_master;
|
||||||
|
int baudrate = default_baudrate;
|
||||||
|
const char *out_addr = defualt_out_addr;
|
||||||
|
const char *in_addr = default_in_addr;
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
int long_index = 0;
|
||||||
|
while ((opt = getopt_long_only(argc, argv, "", long_options,
|
||||||
|
&long_index)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'm':
|
||||||
|
port_name = optarg;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
baudrate = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
out_addr = optarg;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
in_addr = optarg;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
ch_count = atoi(optarg);
|
||||||
|
if(ch_count == 0) printf("rc_channels_override monitoring disabled\n");
|
||||||
|
else printf("rc_channels_override monitoring %d channels after first 4\n", ch_count);
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose = true;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
default:
|
||||||
|
print_usage();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle_data(port_name, baudrate, out_addr, in_addr);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
### Зачем нужен mavfwd
|
||||||
|
`mavfwd` в первую очередь необходим для связи телеметрийного потока wifibroadcast,
|
||||||
|
разделенного на входящий и исходящий на разных udp-портах, с uart камеры, который
|
||||||
|
подключен к uart полетного контроллера UAV, настроенного на обмен телеметрией.
|
||||||
|
Поддерживается mavlink 1 и 2 версий. Подробности о параметрах доступны по `mavfwd --help`.
|
||||||
|
|
||||||
|
Во вторую очередь, mavfwd способен мониторить передаваемые в mavlink-пакете [RC_CHANNELS #65](https://mavlink.io/en/messages/common.html#RC_CHANNELS)
|
||||||
|
значения каналов с 4-го и выше, указанное в параметре --channels числом. По изменению значений каналов вызывается bash-скрипт /root/channels.sh,
|
||||||
|
передавая ему параметрами номер канала и его значение. Это нужно, чтобы организовать какое-то управление хост-системой (камерой), например ее перезагрузку
|
||||||
|
или настройку каких-то параметров стримера. В приложенном примере производятся:
|
||||||
|
* переключение разрешений 1080p / 720p;
|
||||||
|
* включение и отключение ircut камеры;
|
||||||
|
* пороговое изменение яркости, три режима, для подбора нужного под текущие условия освещённости (яркий день, обычный день, ночь).
|
|
@ -0,0 +1,13 @@
|
||||||
|
### Why you need mavfwd
|
||||||
|
`mavfwd` is primarily necessary for the connection of the telemetry stream wifibroadcast,
|
||||||
|
divided into incoming and outgoing on different udp ports, from a uart camera, which
|
||||||
|
connected to the uart flight controller UAV, configured to exchange telemetry.
|
||||||
|
maxlink 1 and 2 versions are supported. Details of the parameters are available for `mavfwd --help`.
|
||||||
|
|
||||||
|
In the second place, mavfwd is able to monitor transmitted in the mavlink-pack [RC_CHANNELS #65](htts://mavlink.io/en/messages/common.html#RC_CHANNELS)
|
||||||
|
the channel value with 4 and above, specified in the --channels number parameter. By changing the values of the channels, the bash-script /root/channels.sh is called,
|
||||||
|
passing the channel number and its value. This is necessary to organize some kind of control of the host system (camera), for example, its reboot
|
||||||
|
or setting up some streamer parameters. The attached example makes:
|
||||||
|
* 180p / 720p resolution switching;
|
||||||
|
* Turning on and off the ircut camera;
|
||||||
|
* threshold change of brightness, three modes, for selecting the necessary under current conditions of illumination (strong day, ordinary day, night).
|
After Width: | Height: | Size: 153 KiB |
|
@ -0,0 +1,27 @@
|
||||||
|
text,type,typeId,Description
|
||||||
|
,Rectangle,creately.basic.rectangle,
|
||||||
|
GROUND (NVR),Text,creately.basic.text,
|
||||||
|
WIFI,WiFi,creately.material-icons.wifi_twotone_2,
|
||||||
|
rtl8812au,Rectangle,creately.basic.rectangle,
|
||||||
|
wifi adapter driver in monitor mode,Rectangle,creately.basic.rectangle,
|
||||||
|
wfb-rx,Rectangle,creately.basic.rectangle,
|
||||||
|
LAN / WLAN or usb network,Ellipse,creately.basic.ellipse,
|
||||||
|
,Rectangle,creately.basic.rectangle,
|
||||||
|
PC or Pocket,Text,creately.basic.text,
|
||||||
|
GS telemetry program,Computer,creately.material-icons.computer_twotone_2,
|
||||||
|
udp:14551,Text,creately.basic.text,
|
||||||
|
udp:14550,Text,creately.basic.text,
|
||||||
|
,Rectangle,creately.basic.rectangle,
|
||||||
|
CAM,Text,creately.basic.text,
|
||||||
|
mavfwd,Rectangle,creately.basic.rectangle,
|
||||||
|
wfb-tx,Rectangle,creately.basic.rectangle,
|
||||||
|
wfb-rx,Rectangle,creately.basic.rectangle,
|
||||||
|
udp:14550,Text,creately.basic.text,
|
||||||
|
udp:14551,Text,creately.basic.text,
|
||||||
|
FC > cam uart,Rectangle,creately.basic.rectangle,
|
||||||
|
wifi adapter driver in monitor mode,Rectangle,creately.basic.rectangle,
|
||||||
|
rtl8812au,Rectangle,creately.basic.rectangle,
|
||||||
|
WIFI,WiFi,creately.material-icons.wifi_twotone_2,
|
||||||
|
wfb-rx,Rectangle,creately.basic.rectangle,
|
||||||
|
mavlink-routerd,Rectangle,creately.basic.rectangle,
|
||||||
|
udp:14550,Text,creately.basic.text,
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
text,type,typeId,Description
|
||||||
|
,Rectangle,creately.basic.rectangle,
|
||||||
|
sensor,Rectangle,creately.basic.rectangle,
|
||||||
|
CAM,Text,creately.basic.text,
|
||||||
|
majestic or vencoder,Rectangle,creately.basic.rectangle,
|
||||||
|
wfb-tx,Rectangle,creately.basic.rectangle,
|
||||||
|
wifi adapter driver in monitor mode,Rectangle,creately.basic.rectangle,
|
||||||
|
isp,Text,creately.basic.text,
|
||||||
|
rtp:5600,Text,creately.basic.text,
|
||||||
|
rtl8812au,Rectangle,creately.basic.rectangle,
|
||||||
|
WIFI,WiFi,creately.material-icons.wifi_twotone_2,
|
||||||
|
,Rectangle,creately.basic.rectangle,
|
||||||
|
GROUND (NVR),Text,creately.basic.text,
|
||||||
|
WIFI,WiFi,creately.material-icons.wifi_twotone_2,
|
||||||
|
rtl8812au,Rectangle,creately.basic.rectangle,
|
||||||
|
wifi adapter driver in monitor mode,Rectangle,creately.basic.rectangle,
|
||||||
|
wfb-rx,Rectangle,creately.basic.rectangle,
|
||||||
|
LAN / WLAN or usb network,Ellipse,creately.basic.ellipse,
|
||||||
|
,Rectangle,creately.basic.rectangle,
|
||||||
|
PC or Pocket,Text,creately.basic.text,
|
||||||
|
videoplayer or GS program,Computer,creately.material-icons.computer_twotone_2,
|
||||||
|
,To Right Arrow,creately.arrows.torightarrow,
|
||||||
|
,To Right Arrow,creately.arrows.torightarrow,
|
||||||
|
rtp:5600,Text,creately.basic.text,
|
||||||
|
rtp:5600,Text,creately.basic.text,
|
||||||
|
wfb-rx,Rectangle,creately.basic.rectangle,
|
||||||
|
vdecoder,Rectangle,creately.basic.rectangle,
|
||||||
|
HDMI,Rectangle,creately.basic.rectangle,
|
||||||
|
udp:5000,Text,creately.basic.text,
|
|
After Width: | Height: | Size: 755 KiB |
After Width: | Height: | Size: 1.7 MiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 333 KiB |
After Width: | Height: | Size: 451 KiB |
After Width: | Height: | Size: 323 KiB |
After Width: | Height: | Size: 490 KiB |
After Width: | Height: | Size: 1.5 MiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 135 KiB |
After Width: | Height: | Size: 47 KiB |