diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..a86e18d3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "tools/multicomm/QGC/MAVLINK Broker/c_library_v2"] + path = tools/multicomm/QGC/MAVLINK Broker/c_library_v2 + url = git@github.com:mavlink/c_library_v2.git +[submodule "tools/multicomm/MAVLINK Broker/c_library_v2"] + path = tools/multicomm/MAVLINK Broker/c_library_v2 + url = git@github.com:mavlink/c_library_v2.git diff --git a/tools/multicomm/MAVLINK Broker/Makefile b/tools/multicomm/MAVLINK Broker/Makefile new file mode 100644 index 00000000..ac8f7e75 --- /dev/null +++ b/tools/multicomm/MAVLINK Broker/Makefile @@ -0,0 +1,15 @@ +CXX = g++ +SRCS = mavlink_relay_server.cpp +OBJS = $(SRCS:.cpp = .o) +TARGET = mavlink_relay_server +INC = -I./c_library_v2 + +all : $(TARGET) + $(CXX) -o $(TARGET) $(OBJS) $(INC) + +$(TARGET) : + $(CXX) -c $(SRCS) $(INC) + +clean : + rm -f $(TARGET) + rm -f *.o \ No newline at end of file diff --git a/tools/multicomm/MAVLINK Broker/README.md b/tools/multicomm/MAVLINK Broker/README.md new file mode 100644 index 00000000..e5ec0755 --- /dev/null +++ b/tools/multicomm/MAVLINK Broker/README.md @@ -0,0 +1,90 @@ +# Access QGroundControl with marvlink broker +
+ +
+ +## Overview +* Run mavlink broker on GCS host +* Setup mavlink service on UAVs + * Run up mavlink-router to rout mavlink message to GSC host from UAVs + + or + + * Run up mavros to forward mavlink to GSC host + +* Run QGC to connect local port which broker forward to + +## Run up Broker +First, go to this folder. And then, + + make + ./mavlink_relay_server {TCP_PORT=20250} {UDP_PORT=20200} + +TCP and UDP is used for connecting GCSs and vehicles relatively. + +**argument examples:** + +TCP_PORT = 20250 (default), UDP_PORT = 20200 (default) + + ./mavlink_relay_server + +TCP_PORT = 20000, UDP_PORT = 20200 (default) + + ./mavlink_relay_server 20000 + +TCP_PORT = 20000, UDP_PORT = 19000 + + ./mavlink_relay_server 20000 19000 + + +### Run up marvlink router on UVAs + +Please see the output of `mavlink-routerd --help` for the full list of command +line options. The most important facts are: + +- The TCP server is enabled by default +- TCP and UDP endpoints can be added multiple times +- UDP endpoints added with the `-e` option are started in `normal` mode + (sending data to the specified address and port) +- The last parameter (without a key) can either be one UART device or an UDP + connection. This UDP endpoint will be started in `server` mode (waiting for + an incoming connection)! +To route mavlink packets from UART `ttyUSB0` to 2 other UDP endpoints, use the +following command: + + $ mavlink-routerd -e {GCS_IP}:14550 -e 127.0.0.1:14550 /dev/ttyUSB0:115200 + +The `115200` after the colon in `/dev/ttyUSB0:115200` sets the UART baudrate. +See more options with `mavlink-routerd --help`. + +### Run up mavros router on UVAs + +And mavros is used on UAVs that install LTE dongle for send packet to server. + + roslaunch mavros px4.launch fcu_url:="udp://:14540@{your server's IP}:{your server's UDP port}" + roslaunch mavros px4.launch fcu_url:="/dev/ttyUSB0:115200" gcs_url:="{your server's IP}:{your server's UDP port}" + + + + +## Run QGC to connect port which broker forward to + +You can set UDP connection at QGC as follow. + +1. exec QGC +2. Application SEttings +3. Comm Links +4. Add +5. Type to TCP +6. Set Host Address (your server's fixed IP address), TCP Port (port number you set above) +7. Click OK +8. Select slot and click Connect + +
+ +
+ + + + + diff --git a/tools/multicomm/MAVLINK Broker/assets/BROCKER.png b/tools/multicomm/MAVLINK Broker/assets/BROCKER.png new file mode 100644 index 00000000..246f2cd6 Binary files /dev/null and b/tools/multicomm/MAVLINK Broker/assets/BROCKER.png differ diff --git a/tools/multicomm/MAVLINK Broker/assets/broker.JPG b/tools/multicomm/MAVLINK Broker/assets/broker.JPG new file mode 100644 index 00000000..28d2eed9 Binary files /dev/null and b/tools/multicomm/MAVLINK Broker/assets/broker.JPG differ diff --git a/tools/multicomm/MAVLINK Broker/c_library_v2 b/tools/multicomm/MAVLINK Broker/c_library_v2 new file mode 160000 index 00000000..5637057a --- /dev/null +++ b/tools/multicomm/MAVLINK Broker/c_library_v2 @@ -0,0 +1 @@ +Subproject commit 5637057af5ab8ec5667e3f8e5f8c73d3bc60eed8 diff --git a/tools/multicomm/MAVLINK Broker/mavlink_relay_server.cpp b/tools/multicomm/MAVLINK Broker/mavlink_relay_server.cpp new file mode 100644 index 00000000..f8199143 --- /dev/null +++ b/tools/multicomm/MAVLINK Broker/mavlink_relay_server.cpp @@ -0,0 +1,299 @@ +/* + * When number of GCS and vehicle is N, M respectively, + * this server relay packet to eachother. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define DEFAULT_TCP_PORT 20250 +#define DEFAULT_UDP_PORT 20200 + +void error_handling(const char *message); +uint8_t extract_target_id(uint8_t* buf, ssize_t buf_size); +void vehicle2gcs(int sock, uint8_t* buf, ssize_t buf_size); +void gcs2vehicle(int sock, uint8_t* buf, ssize_t buf_size); + +int udp_sock; +sockaddr_in udp_clnt_adr; +socklen_t udp_clnt_adr_sz; + +uint16_t tcp_port; /* connect with GCSs */ +uint16_t udp_port; /* connect with vehicles */ + +/* save mavlink's system id of vehicle with udp socket address */ +std::map uav_info; + +/* + * List of GCS connected with this server. + * Packet of vehicles are routed to these GCSs. + */ +std::vector gcs_socket_fd_list; + +int main(int argc, char *argv[]) +{ + if(argc > 3){ + printf("usage : \n"); + printf("mavlink_relay_server {TCP_PORT=20250} {UDP_PORT=20200}\n"); + return 0; + } + else if(argc == 3){ + tcp_port = atoi(argv[1]); + udp_port = atoi(argv[2]); + } + else if(argc == 2){ + tcp_port = atoi(argv[1]); + udp_port = DEFAULT_UDP_PORT; + } + else{ + tcp_port = DEFAULT_TCP_PORT; + udp_port = DEFAULT_UDP_PORT; + } + + printf(" TCP port (for GCS): %u\n", tcp_port); + printf(" UDP port (for vehicle) : %u\n", udp_port); + + int fd_max = 0; + + /* TCP socket wating packets from GCSs. */ + int tcp_sock; + sockaddr_in tcp_adr; + tcp_sock = socket(PF_INET, SOCK_STREAM, 0); + if(tcp_sock == -1) + error_handling("TCP socket creation error"); + memset(&tcp_adr, 0, sizeof(tcp_adr)); + tcp_adr.sin_family=AF_INET; + tcp_adr.sin_addr.s_addr=htonl(INADDR_ANY); + tcp_adr.sin_port=htons(tcp_port); + if(bind(tcp_sock, (struct sockaddr*) &tcp_adr, sizeof(tcp_adr))==-1) + error_handling("bind() error"); + if(listen(tcp_sock, 3)==-1) + error_handling("listen() error"); + + fd_set reads; + FD_ZERO(&reads); + FD_SET(tcp_sock, &reads); + fd_max = tcp_sock; + + /* UDP socket wating packets from vehicles */ + sockaddr_in udp_adr; + udp_sock = socket(PF_INET, SOCK_DGRAM, 0); + if(udp_sock==-1) + error_handling("UDP socket creation error"); + + memset(&udp_adr, 0, sizeof(udp_adr)); + udp_adr.sin_family=AF_INET; + udp_adr.sin_addr.s_addr=htonl(INADDR_ANY); + udp_adr.sin_port=htons(udp_port); + if(bind(udp_sock, (struct sockaddr*)&udp_adr, sizeof(udp_adr))==-1) + error_handling("bind() error"); + + FD_SET(udp_sock, &reads); + fd_max = udp_sock; + + + /* TCP and UDP client initialize */ + int tcp_clnt_sock; + sockaddr_in tcp_clnt_adr; + socklen_t tcp_clnt_adr_sz = sizeof(tcp_clnt_adr); + + int udp_clnt_sock; + udp_clnt_adr_sz = sizeof(udp_clnt_adr); + + uint8_t buf[MAVLINK_MAX_PACKET_LEN]; + + /* variable for iteration */ + fd_set cpy_reads; + timeval timeout; + int fd_num; + + while(true) + { + cpy_reads = reads; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if((fd_num=select(fd_max+1, &cpy_reads, 0, 0, &timeout))==-1) + break; + + if(fd_num==0) + continue; + + /* scan socket connected to this server */ + for(int i=0; i::iterator iter; + iter = std::find(gcs_socket_fd_list.begin(), gcs_socket_fd_list.end(), i); + gcs_socket_fd_list.erase(iter); + FD_CLR(i, &reads); + close(i); + printf("GCS is terminated : %d \n", i); + } + /* routing packet to vehicle */ + else{ + gcs2vehicle(i, buf, n); + } + } + } + } + } + close(tcp_sock); + close(udp_sock); + return 0; +} + +/* + * print error status to user. + * \param message error name + */ +void error_handling(const char *message) +{ + fputs(message, stderr); + fputc('\n', stderr); + exit(1); +} + +/* + * extract target id from buffer received through socket. + * \param buf buffer received through socket + * \param buf_size size of buffer + * \return target id of this packet + */ +uint8_t extract_target_id(uint8_t* buf, ssize_t buf_size) +{ + mavlink_message_t msg; + mavlink_status_t status; + for (int i = 0; i < buf_size; i++) { + if (mavlink_parse_char(MAVLINK_COMM_0, buf[i], &msg, &status)) { + // handle_new_message(&msg); + break; + } + } + uint8_t* ptr = (uint8_t*)&msg.payload64; + const mavlink_msg_entry_t* payload_ofs; + payload_ofs = mavlink_get_msg_entry(msg.msgid); + if(payload_ofs->flags != MAV_MSG_ENTRY_FLAG_HAVE_TARGET_SYSTEM) + { + return 0; + } + // printf("payload offset : %d\n", payload_ofs->target_system_ofs); + uint8_t target_id = *(ptr+payload_ofs->target_system_ofs); + // printf("target id : %d\n", target_id); + return target_id; +} + + +/* + * \param sock fd of socket + * \param buf buffer received from vehicle + * \param buf_size size of buffer + */ +void vehicle2gcs(int sock, uint8_t* buf, ssize_t buf_size) +{ + mavlink_message_t msg; + mavlink_status_t status; + /* parse buf to mavlink message */ + for(int i = 0; i < buf_size; i++) { + if(mavlink_parse_char(MAVLINK_COMM_0, buf[i], &msg, &status)){ + // handle_new_message(&msg); + break; + } + } + + /* + * save socket address of this vehicle + * (this data are used for routing GCS's packet to vehicle) + */ + uav_info[msg.sysid] = udp_clnt_adr; + + /* + * Route vehicle's packet to all of GCS connected this server. + */ + for(auto iter = gcs_socket_fd_list.begin(); iter < gcs_socket_fd_list.end(); iter++){ + if(write(*iter, buf, buf_size) == -1){ + fputc(*iter, stderr); + fputs(" : tcp write fail\n", stderr); + continue; + } + } +} + +/* + * \param sock fd of socket + * \param buf buffer received from GCS + * \param buf_size size of buffer + */ +void gcs2vehicle(int sock, uint8_t* buf, ssize_t buf_size) +{ + socklen_t udp_clnt_adr_sz = sizeof(sockaddr_in); + + /* extract target id from buffer */ + uint8_t target_id = extract_target_id(buf, buf_size); + + /* brodcast the packet when target id is 0 */ + if(target_id == 0){ + for(auto uav : uav_info) + { + if(sendto(udp_sock, buf, buf_size, 0, + (struct sockaddr*)&uav.second, udp_clnt_adr_sz) == -1){ + fputs("udp sendto fail\n", stderr); + return; + } + } + } + /* unicasting packet */ + else{ + if(sendto(udp_sock, buf, buf_size, 0, + (struct sockaddr*)&uav_info[target_id], udp_clnt_adr_sz) == -1){ + fputs("udp sendto fail\n", stderr); + return; + } + // printf("%d %d, %d\n", target_id, uav_info[target_id].sin_addr.s_addr, uav_info[target_id].sin_port); + } +} \ No newline at end of file diff --git a/tools/multicomm/MAVROS/README.md b/tools/multicomm/MAVROS/README.md new file mode 100644 index 00000000..aad12514 --- /dev/null +++ b/tools/multicomm/MAVROS/README.md @@ -0,0 +1,150 @@ +# Running MAVROS across multiple machines + +
+ +
+ + +## Overview +* Setup VPN and then connect GSC and all UAV on. + + Note: Maybe a kernel space VPN server like OpenVPN and Wireguard are free choises. https://www.snbforums.com/threads/disabling-openvpn-encryption-auth-completely.57177/. Or charged VPN service https://www.cyberciti.biz/faq/ubuntu-20-04-set-up-wireguard-vpn-server/, if need more performance. + +* All UAV must be configured to use the same GSC as master, via ROS_MASTER_URI. + +* There must be complete, bi-directional connectivity between all pairs of machines, on all ports (see [ROS/NetworkSetup](http://wiki.ros.org/ROS/NetworkSetup)). + +* Each machine must advertise itself by a name that all other machines can resolve (see [ROS/NetworkSetup](http://wiki.ros.org/ROS/NetworkSetup)). + +* (Optional) Add custom messages on MAVROS, please refer https://docs.px4.io/master/en/ros/mavros_custom_messages.html + +## Setup + +### VPN +Here is the sample for OpenVPN as Client. + +```shell +# Install OpenVPN Client https://openvpn.net/cloud-docs/openvpn-3-client-for-linux/ +sudo apt install apt-transport-https +sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub +sudo apt-key add openvpn-repo-pkg-key.pub +sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-$DISTRO.list # bionic +sudo apt update +sudo apt install openvpn3 + +# Connect OpenVPN Server + +openvpn3 session-start --config ${CONFIGURATION_FILE} + +``` + +### Name resolution +When a ROS node advertises a topic, it provides a hostname:port combination (a URI) that other nodes will contact when they want to subscribe to that topic. +It is important that the hostname that a node provides can be used by all other nodes to contact it. The ROS client libraries use the name that the machine reports to be its hostname. +This is the name that is returned by the command hostname. + +If a machine reports a hostname that is not addressable by other machines, then you need to set either the ROS_IP or ROS_HOSTNAME environment variables. + +#### Example +Continuing the example of gcs and uav1, say we want to bring in a third machine. The new machine, named uav2, uses a VPN address, say 10.0.0.3, and other machines cannot resolve the hostname uav2 into an IP address (this should not happen on a properly configured DHCP-managed network, but it is a common problem). + +In this situation, neither gcs nor uav1 are able to ping uav2 by name, and so they would not be able to contact nodes that advertise themselves as running on uav2. The fix is to set ROS_IP in the environment before starting a node on uav2: +```shell +ssh 10.0.0.3 # We can't ssh to uav2 by name +export ROS_IP=10.0.0.3 # Correct the fact that uav2's VPN address can't be resolved +export ROS_MASTER_URI=http://10.0.0.1:11311 + +``` +A similar problem can occur if a machine's name is resolvable, but the machine doesn't know its own name. Say uav2 can be properly resolved into 10.0.0.3, but running hostname on uav2 returns localhost. Then you should set ROS_HOSTNAME: +```shell +ssh uav2 # We can ssh to uav2 by name +export ROS_IP=10.0.0.3 # Correct the fact that uav2's VPN address can't be resolved +export ROS_HOSTNAME=uav2 # Correct the fact that uav2 doesn't know its name +export ROS_MASTER_URI=http://10.0.0.1:11311 +add prefix in mavros +``` +
+ +
+ +### Configuring /etc/hosts +Another option is to add entries to your /etc/hosts file so that the machines can find each other. The hosts file tells each machine how to convert specific names into an IP address. + +Following is a sample /etc/hosts file: +```shell + IPAddress Hostname + 127.0.0.1 localhost + 10.0.0.1 gcs + 10.0.0.2 uav1 + 10.0.0.3 uav2 +``` + +### (Optional)Add prefix of topic for multiple nodes in mavros + +Because all UAVs need to communicate with GCS, we need to run mavros up for each UVAs. But we should set a unique mavros group name for each UAVs. +Open the px4.launch, and then add ... + + $ roscd mavros/launch + +```xml + + + + + + + + + + + + + + + + + + + +``` + +
+ +
+Let's run mavros up! + +On GCP MASTER Node: + + $ roscore +
+ +
+ +On UAV Nodes: + + $ roslaunch mavros px4.launch fcu_url:=/dev/ttyUSB0:115200 mavros_ns:=uav1 tgt_system:=1 + $ # Or, controled by QGC at the same time. + $ roslaunch mavros px4.launch fcu_url:=/dev/ttyUSB0:115200 gcs_url:=udp://:14556@{QGC_IP}:14550 + +
+ + +
+ +Check with *rostopic list* on MASTER Node: + +
+ + +
+ +### Appendix + + * Monitoring STATUS FOR MASTER CONNECTION: https://answers.ros.org/question/202369/how-to-handle-disconnectionreconnection-to-the-ros-master/ + + + + + + + diff --git a/tools/multicomm/MAVROS/assets/MAVROS.png b/tools/multicomm/MAVROS/assets/MAVROS.png new file mode 100644 index 00000000..b15152e5 Binary files /dev/null and b/tools/multicomm/MAVROS/assets/MAVROS.png differ diff --git a/tools/multicomm/MAVROS/assets/px4_config.png b/tools/multicomm/MAVROS/assets/px4_config.png new file mode 100644 index 00000000..4b71a5cb Binary files /dev/null and b/tools/multicomm/MAVROS/assets/px4_config.png differ diff --git a/tools/multicomm/MAVROS/assets/ros_env.png b/tools/multicomm/MAVROS/assets/ros_env.png new file mode 100644 index 00000000..e1b8166c Binary files /dev/null and b/tools/multicomm/MAVROS/assets/ros_env.png differ diff --git a/tools/multicomm/MAVROS/assets/run_master.png b/tools/multicomm/MAVROS/assets/run_master.png new file mode 100644 index 00000000..bbc0a5fc Binary files /dev/null and b/tools/multicomm/MAVROS/assets/run_master.png differ diff --git a/tools/multicomm/MAVROS/assets/run_mavros.png b/tools/multicomm/MAVROS/assets/run_mavros.png new file mode 100644 index 00000000..fdadb507 Binary files /dev/null and b/tools/multicomm/MAVROS/assets/run_mavros.png differ diff --git a/tools/multicomm/MAVROS/assets/run_uav.png b/tools/multicomm/MAVROS/assets/run_uav.png new file mode 100644 index 00000000..18a66292 Binary files /dev/null and b/tools/multicomm/MAVROS/assets/run_uav.png differ diff --git a/tools/multicomm/MAVROS/assets/topic_graph.png b/tools/multicomm/MAVROS/assets/topic_graph.png new file mode 100644 index 00000000..3c7e1f7a Binary files /dev/null and b/tools/multicomm/MAVROS/assets/topic_graph.png differ diff --git a/tools/multicomm/MAVROS/assets/topic_list.png b/tools/multicomm/MAVROS/assets/topic_list.png new file mode 100644 index 00000000..ba9e0111 Binary files /dev/null and b/tools/multicomm/MAVROS/assets/topic_list.png differ diff --git a/tools/multicomm/QGC/README.md b/tools/multicomm/QGC/README.md new file mode 100644 index 00000000..c471c0cb --- /dev/null +++ b/tools/multicomm/QGC/README.md @@ -0,0 +1,80 @@ +# Access QGroundControl with marvlink + +## Overview +* Run up mavlink-router to rout mavlink message to GSC host from UAVs +* Run QGC on GCS host to connect local port which UAVs send to +
+ +
+## Install mavlink router +[mavlink-router official repo](https://github.com/mavlink-router/mavlink-router#cli-parameters) + +### Compilation and Installation + +In order to compile you need the following packages: + +- GCC or Clang compiler +- meson >= 0.57 and ninja-build +- C and C++ standard libraries + +### Fetch dependencies + +We currently depend on the mavlink C library. The corresponding submodule +should be fetched: + + $ git submodule update --init --recursive + +We need some additional packages as build dependencies. Packages for some +distros: + +Debian/Ubuntu: + + $ sudo apt install git meson ninja-build pkg-config gcc g++ systemd +Note that meson package must be version 0.55 or later. If your package manager +does not have this version, a more recent version can be downloaded via pip: + + $ sudo pip3 install meson + +If using this method, meson must _not_ be installed with ``--user``. + +### Build + +The build system follows the usual configure/build/install cycle. +Configuration is needed to be done only once. + +A typical configuration for a x86-64 system is shown below: + + $ meson setup build . + +### CLI Parameters + +Please see the output of `mavlink-routerd --help` for the full list of command +line options. The most important facts are: + +- The TCP server is enabled by default +- TCP and UDP endpoints can be added multiple times +- UDP endpoints added with the `-e` option are started in `normal` mode + (sending data to the specified address and port) +- The last parameter (without a key) can either be one UART device or an UDP + connection. This UDP endpoint will be started in `server` mode (waiting for + an incoming connection)! +To route mavlink packets from UART `ttyUSB0` to 2 other UDP endpoints, use the +following command: + + $ mavlink-routerd -e {GCS_IP}:14550 -e 127.0.0.1:14550 /dev/ttyUSB0:115200 + +The `115200` after the colon in `/dev/ttyUSB0:1500000` sets the UART baudrate. +See more options with `mavlink-routerd --help`. + +## Run QGC to connect local port + + +
+ + +
+ + + + + diff --git a/tools/multicomm/QGC/assets/QGC.png b/tools/multicomm/QGC/assets/QGC.png new file mode 100644 index 00000000..20a4a414 Binary files /dev/null and b/tools/multicomm/QGC/assets/QGC.png differ diff --git a/tools/multicomm/QGC/assets/qgc_listen.JPG b/tools/multicomm/QGC/assets/qgc_listen.JPG new file mode 100644 index 00000000..04bb3e22 Binary files /dev/null and b/tools/multicomm/QGC/assets/qgc_listen.JPG differ diff --git a/tools/multicomm/QGC/assets/qgc_run.JPG b/tools/multicomm/QGC/assets/qgc_run.JPG new file mode 100644 index 00000000..eee416ca Binary files /dev/null and b/tools/multicomm/QGC/assets/qgc_run.JPG differ diff --git a/tools/multicomm/README.md b/tools/multicomm/README.md new file mode 100644 index 00000000..de7b465d --- /dev/null +++ b/tools/multicomm/README.md @@ -0,0 +1,30 @@ +# Hookup 4G/LTE Dongle + +## Guide + +An example using D-Link DWM-222. + +Fist, plugging into usb port on the dongle. And then reboot the host. +If the driver is set, you can see Mobile Setting on Network Panel. + +
+ + + + + + + + + + +
+ +If the driver is not set. It needs to install "usb-modeswitch" then reboot. + + $ sudo apt-get update -y + $ sudo apt-get install -y usb-modeswitch + $ sudo modprobe qmi_wwan + $ sudo modprobe option + $ reboot + diff --git "a/tools/multicomm/Sim\345\215\241\345\244\232\346\251\237\347\266\262\350\267\257\351\200\243\347\267\232.pptx" "b/tools/multicomm/Sim\345\215\241\345\244\232\346\251\237\347\266\262\350\267\257\351\200\243\347\267\232.pptx" new file mode 100644 index 00000000..dbab0fc4 Binary files /dev/null and "b/tools/multicomm/Sim\345\215\241\345\244\232\346\251\237\347\266\262\350\267\257\351\200\243\347\267\232.pptx" differ diff --git a/tools/multicomm/assets/LTE_dongle.png b/tools/multicomm/assets/LTE_dongle.png new file mode 100644 index 00000000..bde56eb0 Binary files /dev/null and b/tools/multicomm/assets/LTE_dongle.png differ diff --git a/tools/multicomm/assets/network_panel-0.png b/tools/multicomm/assets/network_panel-0.png new file mode 100644 index 00000000..178be57d Binary files /dev/null and b/tools/multicomm/assets/network_panel-0.png differ diff --git a/tools/multicomm/assets/network_panel-1.png b/tools/multicomm/assets/network_panel-1.png new file mode 100644 index 00000000..38c0b484 Binary files /dev/null and b/tools/multicomm/assets/network_panel-1.png differ diff --git a/tools/multicomm/assets/network_panel-2.png b/tools/multicomm/assets/network_panel-2.png new file mode 100644 index 00000000..ccb423d6 Binary files /dev/null and b/tools/multicomm/assets/network_panel-2.png differ diff --git a/tools/multicomm/assets/network_panel-3.png b/tools/multicomm/assets/network_panel-3.png new file mode 100644 index 00000000..81751f99 Binary files /dev/null and b/tools/multicomm/assets/network_panel-3.png differ diff --git a/tools/multicomm/assets/network_panel-4.png b/tools/multicomm/assets/network_panel-4.png new file mode 100644 index 00000000..663f248c Binary files /dev/null and b/tools/multicomm/assets/network_panel-4.png differ diff --git a/tools/multicomm/assets/network_panel-5.png b/tools/multicomm/assets/network_panel-5.png new file mode 100644 index 00000000..a974b93d Binary files /dev/null and b/tools/multicomm/assets/network_panel-5.png differ diff --git a/tools/multicomm/assets/network_panel-6.png b/tools/multicomm/assets/network_panel-6.png new file mode 100644 index 00000000..d91d35f2 Binary files /dev/null and b/tools/multicomm/assets/network_panel-6.png differ diff --git a/tools/multicomm/assets/network_panel-7.png b/tools/multicomm/assets/network_panel-7.png new file mode 100644 index 00000000..0cb431e9 Binary files /dev/null and b/tools/multicomm/assets/network_panel-7.png differ diff --git a/tools/multicomm/assets/network_panel-8.png b/tools/multicomm/assets/network_panel-8.png new file mode 100644 index 00000000..f2242e1d Binary files /dev/null and b/tools/multicomm/assets/network_panel-8.png differ