├── .gitattributes ├── .gitignore ├── LICENSE ├── README(CH).md ├── README.md ├── Tello-Swarm(.exe).zip ├── example_script ├── test_for_1.txt ├── test_for_2.txt ├── test_for_3.txt └── test_for_7.txt ├── formation_setup.py ├── get-pip.py ├── install ├── Linux │ └── linux_install.sh ├── Macos │ └── mac_install.sh └── Windows │ └── windows_install.bat ├── ip.txt ├── multi_tello_test.py ├── stats.py └── tello_manager.py /.gitattributes: -------------------------------------------------------------------------------- 1 | $ cat ./.gitattributes 2 | *.bat -text -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 TelloSDK 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README(CH).md: -------------------------------------------------------------------------------- 1 | ***Tello多机编队程序(Python)*** 2 | 3 | 0.该程序仅适用于tello EDU,即SDK2.0及以上版本。 4 | 5 | 1. 首先,你需要将你要跑的脚本指令写在脚本里面,脚本的格式为.txt,即普通的文本文档。 6 | 运行这套代码的方式为:打开命令行窗口,运行: python multi_tello_test.py 脚本文件名.txt。 7 | 脚本编写示例如下: 8 | ``` 9 | scan 1 10 | battery_check 20 11 | correct_ip 12 | 1=0TQDF6GBMB5SMF 13 | *>takeoff 14 | sync 10 15 | 1>land 16 | ``` 17 | 支持的脚本指令: 18 | - scan [要找的tello数量] 19 | 这个脚本指令会在网段中找连接的tello,直到找到对应数量的tello为止 20 | 注意:这必须是脚本的第一个脚本指令。 21 | 22 | - battery_check [最低电量值] 23 | 检查所有tello的电量。若有任意一台电量<提供的值,程序自动终止。 24 | 电量值区间为0~100,建议设置为20,即battery_check 20。 25 | 26 | - correct_ip 27 | 绑定tello的产品序列号和连上wifi后的ip地址,以便在后续的编队飞行中能够指定特定的飞机执行特定的脚本指令 28 | (你可以理解为:这条指令是为了记录所有已连接的tello在局域网中被分配的ip地址) 29 | 注意:这条脚本指令最好紧接在'scan'和'battery_check'之后,不要放在有'>'的脚本指令之后 30 | 31 | - [Tello的id]=[Tello的SN] 32 | 绑定tello的产品序列号(SN)与飞机id(飞机id即在带有'>'的脚本指令中,'>'左边的数字) 33 | 注意这条脚本指令要放在correct_ip之后 34 | '='左边是飞机的id,右边是飞机的产品序列号 35 | 产品序列号可以通过脚本ip.txt读取,即打开命令行窗口并运行:python multi_tello_test.py ip.txt 36 | 每次运行脚本ip.txt时,只能连接一架飞机,若运行成功,则会在命令行窗口上显示这架tello的SN,格式形如:0TQDF6GBMBTEWV 37 | 或者,也可以在tello的电池槽中的标签(即记录着tello wifi名称的标签)中读取tello的SN,第一行即为tello的SN。 38 | 39 | - [Tello的id]>[发给tello的SDK命令] 40 | 向指定id的tello发送SDK指令。每台Tello只有在完成上一个SDK指令后才会执行下一个SDK指令。因此这个脚本指令也可以理解为添加Tello待执行的SDK指令。 41 | Tello的id范围是由1开始,如果使用*,则代表对全部Tello发送同一个SDK指令 42 | SDK指令为Tello SDK 支持的指令。如"takeoff",更多的SDK指令请参照: 43 | https://dl-cdn.ryzerobotics.com/downloads/Tello/Tello_SDK_2.0_%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E.pdf 44 | 45 | - sync [等待时间] 46 | 对已发送的所有SDK指令进行同步 47 | 如果在sync这一脚本指令之前的所有SDK指令都已经完成(返回ok),则继续往下执行txt脚本 48 | 如果等待SDK指令的回复的时间超过给定的[等待时间],则意为着没有成功收到SDK指令的回复,或者之前的SDK指令仍未全部完成。 49 | 在这种情况下,sync会结束,即停止等待,脚本会继续向下执行。 50 | 51 | - // 52 | 添加注释 53 | 同一行,在'//'之后的文字将被程序忽略。 54 | 不支持in-line comment。 55 | E.g. 不要写 56 | ``` 57 | scan 2 // scan 2 drones 58 | ``` 59 | 而要写 60 | ``` 61 | // scan 2 drones 62 | scan 2 63 | ``` 64 | 65 | - delay [延时的时间,单位为秒] 66 | 延时设定的秒数,比如延时1秒,则为delay 1 67 | 例如:想让1和2相隔一秒起飞,则可编写: 68 | ``` 69 | 1>takeoff 70 | delay 1 71 | 2>takeoff 72 | ``` 73 | 注意:'delay'不能与'>'配合使用,即不能书写:1>delay 1 74 | 2. 环境配置及安装依赖项: 75 | 76 | - 使用一键安装脚本: 77 | 进入install文件夹,根据电脑的系统(windows,linux或macos)选择对应的脚本并运行(windows系统双击文件,linux或macos则打开命令行窗口并运行文件) 78 | 79 | - 手动安装: 80 | 先安装python2.7和pip。python上官网下载,pip可使用get-pip.py文件(文件夹已附带)来安装,如使用python get-pip.py指令。 81 | 运行 82 | Windows: 83 | ``` 84 | python -m pip install netifaces 85 | python -m pip install netaddr 86 | ``` 87 | Linux&&Macos: 88 | ``` 89 | sudo pip install netifaces 90 | sudo pip install netaddr 91 | sudo pip install matplotlib 92 | ``` 93 | 以安装依赖。 94 | 95 | 3. 设置Tello到station模式 96 | 1) 打开Tello 97 | 2) 连上Tello Wi-Fi. (e.g. Tello-AB89C4) 98 | 3) 在formation_setup.py里, 加入wifi名字以及wifi密码 99 | ``` 100 | set_ap(ssid, password) 101 | ``` 102 | 4) 保存并且运行 103 | ``` 104 | python formation_setup.py 105 | ``` 106 | 如看到 107 | ``` 108 | sending command command 109 | from ('192.168.10.1', 8889): ok 110 | sending command ap [your ssid] [your password] 111 | from ('192.168.10.1', 8889): OK,drone will reboot in 3s 112 | ``` 113 | 就ok了 114 | 115 | 4. 跑脚本 116 | ``` 117 | python multi_tello_test.py 文件名.txt 118 | ``` 119 | 命令行将打出每一个条指令及其回复。执行结束后会把命令都存在log文件夹下,以测试结束时间命名。 120 | 在example_script文件夹中,提供了若干个示例脚本,仅供参考。 121 | 122 | 5. 方便地读取飞机SN 123 | 连接飞机的wifi,运行 124 | ``` 125 | python multi_tello_test.py ip.txt 126 | ``` 127 | 可在命令行窗口打印得到产品序列号 128 | 129 | 6. 防止丢包的方式 130 | 如果担心某一条SDK指令或者tello的应答,在无线传输过程中发生丢包,可以使用在含有'>'的脚本指令中, 131 | 在SDK指令前加入'Re',加入该符号后,python对tello将对tello重复发送多条特殊格式的指令,tello将 132 | 只执行一次该指令,如果tello成功执行指令,将会重复返回多条特殊格式的'ok'回复。 133 | 示例: 134 | ``` 135 | 1>Re takeoff 136 | ``` 137 | 注意:1、'Re'和SDK指令中必须有一个空格! 138 | 2、'Re'只能加在SDK指令之前,而不能加在脚本指令之前,即只能用于含'>'的脚本指令中的SDK指令之前! 139 | 3、该防止丢包的方式只适用于tello EDU。 140 | 141 | 7. 可直接执行的多机编队程序(.exe) 142 | 如果你对python不熟悉,或者你直接跳过各种Python依赖项的安装,你可以解压Tello-Swarm.zip。里面包含两个.exe文件,分别是 143 | 用于编队飞行的主程序和用于设置tello进入station模式的副程序。具体使用细节参照压缩包中的UserGuide.txt文件。 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ***Tello Swarm Python Program*** 2 | 3 | 0. This program is only available for the Tello EDU, which is SDK 2.0 and above. 4 | 5 | 1. First, you need to write the script command set you want to run in the script. 6 | The file name of the script is suffixed with .txt, which is a normal text document. 7 | The way of the run the python code with '.txt' script is: Open the command line window,and run: python multi_tello_test.py script file name.txt。 8 | '.txt' Scripting examples are as follows: 9 | 10 | ``` 11 | scan 1 12 | battery_check 20 13 | correct_ip 14 | 1=0TQDF6GBMB5SMF 15 | *>takeoff 16 | sync 10 17 | 1>land 18 | 19 | ``` 20 | 21 | The supported script commands are as follows: 22 | 23 | - scan [The number of tellos] 24 | 25 | This script command will find the tellos to be connected in the network segment until the corresponding number of tellos are found. 26 | Attention:This must be the first script command of the script. 27 | 28 | - battery_check [Minimum power value] 29 | 30 | Check the power of all the tello. If any of the tello's power is less than the provided value, the program will automatically terminate. 31 | The power value range is 0~100.The recommended setting is 20, which is 'battery_check 20'. 32 | 33 | - correct_ip 34 | 35 | Bind the SNs of the tello and their assigned ip addresses.By this way you will be able to to be able to specify a specific tello to execute 36 | the commands.(You can understand it plainly: This script command is to remember the ip address assigned to each tello under the LAN.) 37 | Attention:This script command must be placed after 'scan' and 'battery_check', and not be placed after the script command with '>'. 38 | 39 | - [Tello's id]=[Tello's SN] 40 | 41 | Binding the serial number (SN) of tello with the tello's id (the tello's id is in the script command with '>',and is the number on the left of '>') 42 | Attention:This script command should be placed after 'correct_ip'. 43 | The left side of '=' is the id of tello, and the right side is the SN of tello. 44 | The SN can be read by the script ip.txt. 45 | E.g. open the command line window and run: 46 | 47 | ``` 48 | python multi_tello_test.py ip.txt 49 | 50 | ``` 51 | 52 | Each time you run the script ip.txt, you can only connect one tello. If the operation is successful, the SN of the tello will be displayed on the command line window. 53 | The SN format is like 0TQDF6GBMBTEWV. 54 | Alternatively, you can also read the SN of tello by its tag in the battery slot.(That is, the tag that records the name of the tello wifi). 55 | The first line is the SN of tello. 56 | 57 | - [Tello's id]>[SDK command] 58 | 59 | Sends an SDK command to the tello with the specified id. Each Tello will execute the next SDK command only after the last SDK command is completed. 60 | Therefore, this script command can also be understood as adding the SDK command to be executed by Tello. 61 | Tello's id range starts from 1. If * is used, it means sending the same SDK command to all Tello. 62 | The SDK command is command supported by Tello SDK. For example, "takeoff".For more SDK command,please refer to : 63 | https://dl-cdn.ryzerobotics.com/downloads/Tello/Tello%20SDK%202.0%20User%20Guide.pdf 64 | 65 | - sync [waiting time] 66 | 67 | Synchronize all SDK commands that have been sent. 68 | If all the SDK commands before the 'sync' are completed (return the response of 'ok'), continue to execute script commands of the .txt script. 69 | If the time to wait for the response of the SDK commands exceeds the given [waiting time], it means that the response of the 70 | SDK command has not been successfully received, or the previous SDK commands has not been completed. 71 | In this case, 'sync' will end, and stop waiting.The .txt script will continue to execute downwards. 72 | 73 | - // 74 | 75 | Add notes. 76 | On the same line, the text after '//' will be ignored by the program. 77 | In-line comment is not supported. 78 | E.g. Don't program: 79 | 80 | ``` 81 | scan 2 // scan 2 drones 82 | ``` 83 | 84 | Instead,you should program as follows: 85 | 86 | ``` 87 | // scan 2 drones 88 | scan 2 89 | ``` 90 | 91 | - delay [delay time(s)] 92 | 93 | The number of seconds set by the delay. 94 | E.g.If you want 1 and 2 to take off one second apart, you can write: 95 | 96 | ``` 97 | 1>takeoff 98 | delay 1 99 | 2>takeoff 100 | ``` 101 | 102 | Attention:'delay' cannot be used with '>'.For example,it cannot be written: 1>delay 1 103 | 104 | 2. Environment configuration and installation of related dependencies: 105 | 106 | - Use a one-click installation script: 107 | 108 | Enter the install folder, select the corresponding script according to the computer system (windows, linux or macos), 109 | and run (Windows:double-click the file, linux or macos:open the command line window and run the file). 110 | 111 | - Manual installation: 112 | 113 | Install python2.7 and pip first. Python download on the official website.Pip can be installed using the get-pip.py file 114 | (included with the folder). As shown: 115 | 116 | ``` 117 | python get-pip.py 118 | ``` 119 | 120 | Run command in the command line window: 121 | Windows: 122 | 123 | ``` 124 | python -m pip install netifaces 125 | python -m pip install netaddr 126 | ``` 127 | 128 | Linux&&Macos: 129 | 130 | ``` 131 | sudo pip install netifaces 132 | sudo pip install netaddr 133 | ``` 134 | 135 | to install realated dependencies. 136 | 137 | 3. Set up Tello to station mode 138 | 139 | 1) Turn on Tello 140 | 2) Connected to Tello's Wi-Fi. (e.g. Tello-AB89C4) 141 | 3) In the formation_setup.py, add the wifi name and wifi password 142 | 143 | ``` 144 | set_ap(ssid, password) 145 | ``` 146 | 147 | 4) Save the formation_setup.py and run in command line window: 148 | 149 | ``` 150 | python formation_setup.py 151 | ``` 152 | 153 | If you see some response text as shown: 154 | 155 | ``` 156 | sending command command 157 | from ('192.168.10.1', 8889): ok 158 | sending command ap [your ssid] [your password] 159 | from ('192.168.10.1', 8889): OK,drone will reboot in 3s 160 | ``` 161 | 162 | It means the ap mode setup is successful. 163 | 164 | 4. Run the python code with .txt script. 165 | 166 | ``` 167 | python multi_tello_test.py filename.txt 168 | ``` 169 | 170 | The command line window will print each command and its response. 171 | After the execution is finished, a log will be generated in the 'log' folder,named after the ending time. 172 | 173 | 5. Ways to prevent packet loss. 174 | 175 | If you are worried about the loss of a certain SDK command or tello response during wireless transmission, 176 | You can add 'Re' to the SDK command in the script command with '>'.After adding this symbol, Python will 177 | send special-format command to the tello repeatedly.And Tello will only execute the command once, if tello 178 | successfully executes the command,The 'ok' reply in a special format will be repeated sent back by tello. 179 | E.g. 180 | 181 | ``` 182 | 1>Re takeoff 183 | ``` 184 | 185 | Attention: 186 | 1) There must be a space in the 'Re' and SDK command! 187 | 2) 'Re' can only be added before the SDK command, but not before the script command, that is, 188 | it can only be used before the SDK command in the script command with '>'! 189 | 3) This way of preventing packet loss is only applicable to tello EDU. 190 | 191 | 6. Executable file version. 192 | 193 | If you are unfamiliar with Python, or if you skip the installation of various Python dependencies directly, 194 | you can extract Tello-Swarm.zip. It contains two .exe files, which are 195 | The main program for tello swarms and the sub program for setting the tello into the station mode. 196 | Refer to the UserGuide.txt file in the archive for more details. 197 | -------------------------------------------------------------------------------- /Tello-Swarm(.exe).zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TelloSDK/Multi-Tello-Formation/e3a1216afa1bd513f9ddeb10437fa6c27f2e6727/Tello-Swarm(.exe).zip -------------------------------------------------------------------------------- /example_script/test_for_1.txt: -------------------------------------------------------------------------------- 1 | scan 1 2 | battery_check 20 3 | correct_ip 4 | 1=0TQDF72EDB2BP8 5 | *>mon 6 | *>takeoff 7 | sync 15 8 | 1>go 50 0 80 60 m3 9 | sync 15 10 | 1>flip r 11 | sync 15 12 | *>land 13 | -------------------------------------------------------------------------------- /example_script/test_for_2.txt: -------------------------------------------------------------------------------- 1 | scan 2 2 | battery_check 20 3 | correct_ip 4 | 1=0TQDF72EDBL0ZP 5 | 2=0TQDF72EDB347P 6 | *>mon 7 | *>takeoff 8 | sync 15 9 | 1>jump 50 0 80 60 0 m1 m2 10 | 2>jump 50 0 80 60 0 m4 m3 11 | sync 15 12 | 1>jump 50 0 80 60 0 m2 m1 13 | 2>jump 50 0 80 60 0 m3 m4 14 | sync 15 15 | 1>jump 50 0 80 60 0 m1 m2 16 | 2>jump 50 0 80 60 0 m4 m3 17 | sync 15 18 | 1>jump 50 0 80 60 0 m2 m1 19 | 2>jump 50 0 80 60 0 m3 m4 20 | sync 15 21 | 1>jump 50 0 80 60 0 m1 m2 22 | 2>jump 50 0 80 60 0 m4 m3 23 | sync 15 24 | 1>jump 50 0 80 60 0 m2 m1 25 | 2>jump 50 0 80 60 0 m3 m4 26 | sync 15 27 | 1>jump 50 0 80 60 0 m1 m2 28 | 2>jump 50 0 80 60 0 m4 m3 29 | sync 15 30 | 1>jump 50 0 80 60 0 m2 m1 31 | 2>jump 50 0 80 60 0 m3 m4 32 | sync 15 33 | 1>jump 50 0 80 60 0 m1 m2 34 | 2>jump 50 0 80 60 0 m4 m3 35 | sync 15 36 | 1>jump 50 0 80 60 0 m2 m1 37 | 2>jump 50 0 80 60 0 m3 m4 38 | sync 15 39 | *>land 40 | -------------------------------------------------------------------------------- /example_script/test_for_3.txt: -------------------------------------------------------------------------------- 1 | scan 3 2 | battery_check 20 3 | correct_ip 4 | 1=0TQDF72EDB2WP0 5 | 2=0TQDF72EDB347P 6 | 3=0TQDF72EDB3665 7 | *>mon 8 | sync 15 9 | *>takeoff 10 | sync 15 11 | 1>jump 80 0 80 60 0 m1 m2 12 | 2>jump 80 0 80 60 0 m2 m3 13 | 3>jump 80 0 80 60 0 m3 m4 14 | sync 15 15 | 1>jump 80 0 80 60 0 m2 m3 16 | 2>jump 80 0 80 60 0 m3 m4 17 | 3>jump 80 0 80 60 0 m4 m1 18 | sync 15 19 | 1>jump 80 0 80 60 0 m3 m4 20 | 2>jump 80 0 80 60 0 m4 m1 21 | 3>jump 80 0 80 60 0 m1 m2 22 | sync 15 23 | 1>jump 80 0 80 60 0 m4 m1 24 | 2>jump 80 0 80 60 0 m1 m2 25 | 3>jump 80 0 80 60 0 m2 m3 26 | sync 15 27 | 1>jump 80 0 80 60 0 m1 m2 28 | 2>jump 80 0 80 60 0 m2 m3 29 | 3>jump 80 0 80 60 0 m3 m4 30 | sync 15 31 | 1>jump 80 0 80 60 0 m2 m3 32 | 2>jump 80 0 80 60 0 m3 m4 33 | 3>jump 80 0 80 60 0 m4 m1 34 | sync 15 35 | 1>jump 80 0 80 60 0 m3 m4 36 | 2>jump 80 0 80 60 0 m4 m1 37 | 3>jump 80 0 80 60 0 m1 m2 38 | sync 15 39 | 1>jump 80 0 80 60 0 m4 m1 40 | 2>jump 80 0 80 60 0 m1 m2 41 | 3>jump 80 0 80 60 0 m2 m3 42 | sync 15 43 | 1>jump 80 0 80 60 0 m1 m2 44 | 2>jump 80 0 80 60 0 m2 m3 45 | 3>jump 80 0 80 60 0 m3 m4 46 | sync 15 47 | 1>jump 80 0 80 60 0 m2 m3 48 | 2>jump 80 0 80 60 0 m3 m4 49 | 3>jump 80 0 80 60 0 m4 m1 50 | sync 15 51 | 1>jump 80 0 80 60 0 m3 m4 52 | 2>jump 80 0 80 60 0 m4 m1 53 | 3>jump 80 0 80 60 0 m1 m2 54 | sync 15 55 | 1>jump 80 0 80 60 0 m4 m1 56 | 2>jump 80 0 80 60 0 m1 m2 57 | 3>jump 80 0 80 60 0 m2 m3 58 | sync 15 59 | 1>jump 80 0 80 60 0 m1 m2 60 | 2>jump 80 0 80 60 0 m2 m3 61 | 3>jump 80 0 80 60 0 m3 m4 62 | sync 15 63 | 1>jump 80 0 80 60 0 m2 m3 64 | 2>jump 80 0 80 60 0 m3 m4 65 | 3>jump 80 0 80 60 0 m4 m1 66 | sync 15 67 | 1>jump 80 0 80 60 0 m3 m4 68 | 2>jump 80 0 80 60 0 m4 m1 69 | 3>jump 80 0 80 60 0 m1 m2 70 | sync 15 71 | 1>jump 80 0 80 60 0 m4 m1 72 | 2>jump 80 0 80 60 0 m1 m2 73 | 3>jump 80 0 80 60 0 m2 m3 74 | sync 15 75 | 1>jump 80 0 80 60 0 m1 m2 76 | 2>jump 80 0 80 60 0 m2 m3 77 | 3>jump 80 0 80 60 0 m3 m4 78 | sync 15 79 | 1>jump 80 0 80 60 0 m2 m3 80 | 2>jump 80 0 80 60 0 m3 m4 81 | 3>jump 80 0 80 60 0 m4 m1 82 | sync 15 83 | 1>jump 80 0 80 60 0 m3 m4 84 | 2>jump 80 0 80 60 0 m4 m1 85 | 3>jump 80 0 80 60 0 m1 m2 86 | sync 15 87 | 1>jump 80 0 80 60 0 m4 m1 88 | 2>jump 80 0 80 60 0 m1 m2 89 | 3>jump 80 0 80 60 0 m2 m3 90 | sync 15 91 | 1>jump 80 0 80 60 0 m1 m2 92 | 2>jump 80 0 80 60 0 m2 m3 93 | 3>jump 80 0 80 60 0 m3 m4 94 | sync 15 95 | 1>jump 80 0 80 60 0 m2 m3 96 | 2>jump 80 0 80 60 0 m3 m4 97 | 3>jump 80 0 80 60 0 m4 m1 98 | sync 15 99 | 1>jump 80 0 80 60 0 m3 m4 100 | 2>jump 80 0 80 60 0 m4 m1 101 | 3>jump 80 0 80 60 0 m1 m2 102 | sync 15 103 | 1>jump 80 0 80 60 0 m4 m1 104 | 2>jump 80 0 80 60 0 m1 m2 105 | 3>jump 80 0 80 60 0 m2 m3 106 | sync 15 107 | 1>jump 80 0 80 60 0 m1 m2 108 | 2>jump 80 0 80 60 0 m2 m3 109 | 3>jump 80 0 80 60 0 m3 m4 110 | sync 15 111 | 1>jump 80 0 80 60 0 m2 m3 112 | 2>jump 80 0 80 60 0 m3 m4 113 | 3>jump 80 0 80 60 0 m4 m1 114 | sync 15 115 | 1>jump 80 0 80 60 0 m3 m4 116 | 2>jump 80 0 80 60 0 m4 m1 117 | 3>jump 80 0 80 60 0 m1 m2 118 | sync 15 119 | 1>jump 80 0 80 60 0 m4 m1 120 | 2>jump 80 0 80 60 0 m1 m2 121 | 3>jump 80 0 80 60 0 m2 m3 122 | sync 15 123 | 1>jump 80 0 80 60 0 m1 m2 124 | 2>jump 80 0 80 60 0 m2 m3 125 | 3>jump 80 0 80 60 0 m3 m4 126 | sync 15 127 | 1>jump 80 0 80 60 0 m2 m3 128 | 2>jump 80 0 80 60 0 m3 m4 129 | 3>jump 80 0 80 60 0 m4 m1 130 | sync 15 131 | 1>jump 80 0 80 60 0 m3 m4 132 | 2>jump 80 0 80 60 0 m4 m1 133 | 3>jump 80 0 80 60 0 m1 m2 134 | sync 15 135 | 1>jump 80 0 80 60 0 m4 m1 136 | 2>jump 80 0 80 60 0 m1 m2 137 | 3>jump 80 0 80 60 0 m2 m3 138 | sync 15 139 | 1>jump 80 0 80 60 0 m1 m2 140 | 2>jump 80 0 80 60 0 m2 m3 141 | 3>jump 80 0 80 60 0 m3 m4 142 | sync 15 143 | 1>jump 80 0 80 60 0 m2 m3 144 | 2>jump 80 0 80 60 0 m3 m4 145 | 3>jump 80 0 80 60 0 m4 m1 146 | sync 15 147 | 1>jump 80 0 80 60 0 m3 m4 148 | 2>jump 80 0 80 60 0 m4 m1 149 | 3>jump 80 0 80 60 0 m1 m2 150 | sync 15 151 | 1>jump 80 0 80 60 0 m4 m1 152 | 2>jump 80 0 80 60 0 m1 m2 153 | 3>jump 80 0 80 60 0 m2 m3 154 | sync 15 155 | 1>jump 80 0 80 60 0 m1 m2 156 | 2>jump 80 0 80 60 0 m2 m3 157 | 3>jump 80 0 80 60 0 m3 m4 158 | sync 15 159 | 1>jump 80 0 80 60 0 m2 m3 160 | 2>jump 80 0 80 60 0 m3 m4 161 | 3>jump 80 0 80 60 0 m4 m1 162 | sync 15 163 | 1>jump 80 0 80 60 0 m3 m4 164 | 2>jump 80 0 80 60 0 m4 m1 165 | 3>jump 80 0 80 60 0 m1 m2 166 | sync 15 167 | 1>jump 80 0 80 60 0 m4 m1 168 | 2>jump 80 0 80 60 0 m1 m2 169 | 3>jump 80 0 80 60 0 m2 m3 170 | sync 15 171 | 1>jump 80 0 80 60 0 m1 m2 172 | 2>jump 80 0 80 60 0 m2 m3 173 | 3>jump 80 0 80 60 0 m3 m4 174 | sync 15 175 | 1>jump 80 0 80 60 0 m2 m3 176 | 2>jump 80 0 80 60 0 m3 m4 177 | 3>jump 80 0 80 60 0 m4 m1 178 | sync 15 179 | 1>jump 80 0 80 60 0 m3 m4 180 | 2>jump 80 0 80 60 0 m4 m1 181 | 3>jump 80 0 80 60 0 m1 m2 182 | sync 15 183 | 1>jump 80 0 80 60 0 m4 m1 184 | 2>jump 80 0 80 60 0 m1 m2 185 | 3>jump 80 0 80 60 0 m2 m3 186 | sync 15 187 | 1>jump 80 0 80 60 0 m1 m2 188 | 2>jump 80 0 80 60 0 m2 m3 189 | 3>jump 80 0 80 60 0 m3 m4 190 | sync 15 191 | 1>jump 80 0 80 60 0 m2 m3 192 | 2>jump 80 0 80 60 0 m3 m4 193 | 3>jump 80 0 80 60 0 m4 m1 194 | sync 15 195 | 1>jump 80 0 80 60 0 m3 m4 196 | 2>jump 80 0 80 60 0 m4 m1 197 | 3>jump 80 0 80 60 0 m1 m2 198 | sync 15 199 | 1>jump 80 0 80 60 0 m4 m1 200 | 2>jump 80 0 80 60 0 m1 m2 201 | 3>jump 80 0 80 60 0 m2 m3 202 | sync 15 203 | *>land 204 | -------------------------------------------------------------------------------- /example_script/test_for_7.txt: -------------------------------------------------------------------------------- 1 | scan 7 2 | battery_check 20 3 | correct_ip 4 | 1=0TQDF6GBMB5SMF 5 | 2=0TQDF6GBMBF410 6 | 3=0TQDF72EDB2WP0 7 | 4=0TQDF72EDB347P 8 | 5=0TQDF72EDB3665 9 | 6=0TQDF76EDB3UCY 10 | 7=0TQDF72EDBTVC2 11 | *>mon 12 | sync 15 13 | *>takeoff 14 | sync 15 15 | 1>jump 75 0 80 60 0 m1 m2 16 | 2>jump 75 0 80 60 0 m2 m3 17 | 3>jump 75 0 80 60 0 m3 m4 18 | 4>jump 75 0 80 60 0 m4 m1 19 | 5>jump 75 0 80 60 0 m1 m2 20 | 6>jump 75 0 80 60 0 m2 m3 21 | 7>jump 75 0 80 60 0 m3 m4 22 | sync 15 23 | 1>jump 75 0 80 60 0 m2 m3 24 | 2>jump 75 0 80 60 0 m3 m4 25 | 3>jump 75 0 80 60 0 m4 m1 26 | 4>jump 75 0 80 60 0 m1 m2 27 | 5>jump 75 0 80 60 0 m2 m3 28 | 6>jump 75 0 80 60 0 m3 m4 29 | 7>jump 75 0 80 60 0 m4 m1 30 | sync 15 31 | 1>jump 75 0 80 60 0 m3 m4 32 | 2>jump 75 0 80 60 0 m4 m1 33 | 3>jump 75 0 80 60 0 m1 m2 34 | 4>jump 75 0 80 60 0 m2 m3 35 | 5>jump 75 0 80 60 0 m3 m4 36 | 6>jump 75 0 80 60 0 m4 m1 37 | 7>jump 75 0 80 60 0 m1 m2 38 | sync 15 39 | 1>jump 75 0 80 60 0 m4 m1 40 | 2>jump 75 0 80 60 0 m1 m2 41 | 3>jump 75 0 80 60 0 m2 m3 42 | 4>jump 75 0 80 60 0 m3 m4 43 | 5>jump 75 0 80 60 0 m4 m1 44 | 6>jump 75 0 80 60 0 m1 m2 45 | 7>jump 75 0 80 60 0 m2 m3 46 | sync 15 47 | 1>jump 75 0 80 60 0 m1 m2 48 | 2>jump 75 0 80 60 0 m2 m3 49 | 3>jump 75 0 80 60 0 m3 m4 50 | 4>jump 75 0 80 60 0 m4 m1 51 | 5>jump 75 0 80 60 0 m1 m2 52 | 6>jump 75 0 80 60 0 m2 m3 53 | 7>jump 75 0 80 60 0 m3 m4 54 | sync 15 55 | 1>jump 75 0 80 60 0 m2 m3 56 | 2>jump 75 0 80 60 0 m3 m4 57 | 3>jump 75 0 80 60 0 m4 m1 58 | 4>jump 75 0 80 60 0 m1 m2 59 | 5>jump 75 0 80 60 0 m2 m3 60 | 6>jump 75 0 80 60 0 m3 m4 61 | 7>jump 75 0 80 60 0 m4 m1 62 | sync 15 63 | 1>jump 75 0 80 60 0 m3 m4 64 | 2>jump 75 0 80 60 0 m4 m1 65 | 3>jump 75 0 80 60 0 m1 m2 66 | 4>jump 75 0 80 60 0 m2 m3 67 | 5>jump 75 0 80 60 0 m3 m4 68 | 6>jump 75 0 80 60 0 m4 m1 69 | 7>jump 75 0 80 60 0 m1 m2 70 | sync 15 71 | 1>jump 75 0 80 60 0 m4 m1 72 | 2>jump 75 0 80 60 0 m1 m2 73 | 3>jump 75 0 80 60 0 m2 m3 74 | 4>jump 75 0 80 60 0 m3 m4 75 | 5>jump 75 0 80 60 0 m4 m1 76 | 6>jump 75 0 80 60 0 m1 m2 77 | 7>jump 75 0 80 60 0 m2 m3 78 | sync 15 79 | 1>jump 75 0 80 60 0 m1 m2 80 | 2>jump 75 0 80 60 0 m2 m3 81 | 3>jump 75 0 80 60 0 m3 m4 82 | 4>jump 75 0 80 60 0 m4 m1 83 | 5>jump 75 0 80 60 0 m1 m2 84 | 6>jump 75 0 80 60 0 m2 m3 85 | 7>jump 75 0 80 60 0 m3 m4 86 | sync 15 87 | 1>jump 75 0 80 60 0 m2 m3 88 | 2>jump 75 0 80 60 0 m3 m4 89 | 3>jump 75 0 80 60 0 m4 m1 90 | 4>jump 75 0 80 60 0 m1 m2 91 | 5>jump 75 0 80 60 0 m2 m3 92 | 6>jump 75 0 80 60 0 m3 m4 93 | 7>jump 75 0 80 60 0 m4 m1 94 | sync 15 95 | 1>jump 75 0 80 60 0 m3 m4 96 | 2>jump 75 0 80 60 0 m4 m1 97 | 3>jump 75 0 80 60 0 m1 m2 98 | 4>jump 75 0 80 60 0 m2 m3 99 | 5>jump 75 0 80 60 0 m3 m4 100 | 6>jump 75 0 80 60 0 m4 m1 101 | 7>jump 75 0 80 60 0 m1 m2 102 | sync 15 103 | 1>jump 75 0 80 60 0 m4 m1 104 | 2>jump 75 0 80 60 0 m1 m2 105 | 3>jump 75 0 80 60 0 m2 m3 106 | 4>jump 75 0 80 60 0 m3 m4 107 | 5>jump 75 0 80 60 0 m4 m1 108 | 6>jump 75 0 80 60 0 m1 m2 109 | 7>jump 75 0 80 60 0 m2 m3 110 | sync 15 111 | 1>jump 75 0 80 60 0 m1 m2 112 | 2>jump 75 0 80 60 0 m2 m3 113 | 3>jump 75 0 80 60 0 m3 m4 114 | 4>jump 75 0 80 60 0 m4 m1 115 | 5>jump 75 0 80 60 0 m1 m2 116 | 6>jump 75 0 80 60 0 m2 m3 117 | 7>jump 75 0 80 60 0 m3 m4 118 | sync 15 119 | 1>jump 75 0 80 60 0 m2 m3 120 | 2>jump 75 0 80 60 0 m3 m4 121 | 3>jump 75 0 80 60 0 m4 m1 122 | 4>jump 75 0 80 60 0 m1 m2 123 | 5>jump 75 0 80 60 0 m2 m3 124 | 6>jump 75 0 80 60 0 m3 m4 125 | 7>jump 75 0 80 60 0 m4 m1 126 | sync 15 127 | 1>jump 75 0 80 60 0 m3 m4 128 | 2>jump 75 0 80 60 0 m4 m1 129 | 3>jump 75 0 80 60 0 m1 m2 130 | 4>jump 75 0 80 60 0 m2 m3 131 | 5>jump 75 0 80 60 0 m3 m4 132 | 6>jump 75 0 80 60 0 m4 m1 133 | 7>jump 75 0 80 60 0 m1 m2 134 | sync 15 135 | 1>jump 75 0 80 60 0 m4 m1 136 | 2>jump 75 0 80 60 0 m1 m2 137 | 3>jump 75 0 80 60 0 m2 m3 138 | 4>jump 75 0 80 60 0 m3 m4 139 | 5>jump 75 0 80 60 0 m4 m1 140 | 6>jump 75 0 80 60 0 m1 m2 141 | 7>jump 75 0 80 60 0 m2 m3 142 | sync 15 143 | 1>jump 75 0 80 60 0 m1 m2 144 | 2>jump 75 0 80 60 0 m2 m3 145 | 3>jump 75 0 80 60 0 m3 m4 146 | 4>jump 75 0 80 60 0 m4 m1 147 | 5>jump 75 0 80 60 0 m1 m2 148 | 6>jump 75 0 80 60 0 m2 m3 149 | 7>jump 75 0 80 60 0 m3 m4 150 | sync 15 151 | 1>jump 75 0 80 60 0 m2 m3 152 | 2>jump 75 0 80 60 0 m3 m4 153 | 3>jump 75 0 80 60 0 m4 m1 154 | 4>jump 75 0 80 60 0 m1 m2 155 | 5>jump 75 0 80 60 0 m2 m3 156 | 6>jump 75 0 80 60 0 m3 m4 157 | 7>jump 75 0 80 60 0 m4 m1 158 | sync 15 159 | 1>jump 75 0 80 60 0 m3 m4 160 | 2>jump 75 0 80 60 0 m4 m1 161 | 3>jump 75 0 80 60 0 m1 m2 162 | 4>jump 75 0 80 60 0 m2 m3 163 | 5>jump 75 0 80 60 0 m3 m4 164 | 6>jump 75 0 80 60 0 m4 m1 165 | 7>jump 75 0 80 60 0 m1 m2 166 | sync 15 167 | 1>jump 75 0 80 60 0 m4 m1 168 | 2>jump 75 0 80 60 0 m1 m2 169 | 3>jump 75 0 80 60 0 m2 m3 170 | 4>jump 75 0 80 60 0 m3 m4 171 | 5>jump 75 0 80 60 0 m4 m1 172 | 6>jump 75 0 80 60 0 m1 m2 173 | 7>jump 75 0 80 60 0 m2 m3 174 | sync 15 175 | 1>jump 75 0 80 60 0 m1 m2 176 | 2>jump 75 0 80 60 0 m2 m3 177 | 3>jump 75 0 80 60 0 m3 m4 178 | 4>jump 75 0 80 60 0 m4 m1 179 | 5>jump 75 0 80 60 0 m1 m2 180 | 6>jump 75 0 80 60 0 m2 m3 181 | 7>jump 75 0 80 60 0 m3 m4 182 | sync 15 183 | 1>jump 75 0 80 60 0 m2 m3 184 | 2>jump 75 0 80 60 0 m3 m4 185 | 3>jump 75 0 80 60 0 m4 m1 186 | 4>jump 75 0 80 60 0 m1 m2 187 | 5>jump 75 0 80 60 0 m2 m3 188 | 6>jump 75 0 80 60 0 m3 m4 189 | 7>jump 75 0 80 60 0 m4 m1 190 | sync 15 191 | 1>jump 75 0 80 60 0 m3 m4 192 | 2>jump 75 0 80 60 0 m4 m1 193 | 3>jump 75 0 80 60 0 m1 m2 194 | 4>jump 75 0 80 60 0 m2 m3 195 | 5>jump 75 0 80 60 0 m3 m4 196 | 6>jump 75 0 80 60 0 m4 m1 197 | 7>jump 75 0 80 60 0 m1 m2 198 | sync 15 199 | 1>jump 75 0 80 60 0 m4 m1 200 | 2>jump 75 0 80 60 0 m1 m2 201 | 3>jump 75 0 80 60 0 m2 m3 202 | 4>jump 75 0 80 60 0 m3 m4 203 | 5>jump 75 0 80 60 0 m4 m1 204 | 6>jump 75 0 80 60 0 m1 m2 205 | 7>jump 75 0 80 60 0 m2 m3 206 | sync 15 207 | 1>jump 75 0 80 60 0 m1 m2 208 | 2>jump 75 0 80 60 0 m2 m3 209 | 3>jump 75 0 80 60 0 m3 m4 210 | 4>jump 75 0 80 60 0 m4 m1 211 | 5>jump 75 0 80 60 0 m1 m2 212 | 6>jump 75 0 80 60 0 m2 m3 213 | 7>jump 75 0 80 60 0 m3 m4 214 | sync 15 215 | 1>jump 75 0 80 60 0 m2 m3 216 | 2>jump 75 0 80 60 0 m3 m4 217 | 3>jump 75 0 80 60 0 m4 m1 218 | 4>jump 75 0 80 60 0 m1 m2 219 | 5>jump 75 0 80 60 0 m2 m3 220 | 6>jump 75 0 80 60 0 m3 m4 221 | 7>jump 75 0 80 60 0 m4 m1 222 | sync 15 223 | 1>jump 75 0 80 60 0 m3 m4 224 | 2>jump 75 0 80 60 0 m4 m1 225 | 3>jump 75 0 80 60 0 m1 m2 226 | 4>jump 75 0 80 60 0 m2 m3 227 | 5>jump 75 0 80 60 0 m3 m4 228 | 6>jump 75 0 80 60 0 m4 m1 229 | 7>jump 75 0 80 60 0 m1 m2 230 | sync 15 231 | 1>jump 75 0 80 60 0 m4 m1 232 | 2>jump 75 0 80 60 0 m1 m2 233 | 3>jump 75 0 80 60 0 m2 m3 234 | 4>jump 75 0 80 60 0 m3 m4 235 | 5>jump 75 0 80 60 0 m4 m1 236 | 6>jump 75 0 80 60 0 m1 m2 237 | 7>jump 75 0 80 60 0 m2 m3 238 | sync 15 239 | 1>jump 75 0 80 60 0 m1 m2 240 | 2>jump 75 0 80 60 0 m2 m3 241 | 3>jump 75 0 80 60 0 m3 m4 242 | 4>jump 75 0 80 60 0 m4 m1 243 | 5>jump 75 0 80 60 0 m1 m2 244 | 6>jump 75 0 80 60 0 m2 m3 245 | 7>jump 75 0 80 60 0 m3 m4 246 | sync 15 247 | 1>jump 75 0 80 60 0 m2 m3 248 | 2>jump 75 0 80 60 0 m3 m4 249 | 3>jump 75 0 80 60 0 m4 m1 250 | 4>jump 75 0 80 60 0 m1 m2 251 | 5>jump 75 0 80 60 0 m2 m3 252 | 6>jump 75 0 80 60 0 m3 m4 253 | 7>jump 75 0 80 60 0 m4 m1 254 | sync 15 255 | 1>jump 75 0 80 60 0 m3 m4 256 | 2>jump 75 0 80 60 0 m4 m1 257 | 3>jump 75 0 80 60 0 m1 m2 258 | 4>jump 75 0 80 60 0 m2 m3 259 | 5>jump 75 0 80 60 0 m3 m4 260 | 6>jump 75 0 80 60 0 m4 m1 261 | 7>jump 75 0 80 60 0 m1 m2 262 | sync 15 263 | 1>jump 75 0 80 60 0 m4 m1 264 | 2>jump 75 0 80 60 0 m1 m2 265 | 3>jump 75 0 80 60 0 m2 m3 266 | 4>jump 75 0 80 60 0 m3 m4 267 | 5>jump 75 0 80 60 0 m4 m1 268 | 6>jump 75 0 80 60 0 m1 m2 269 | 7>jump 75 0 80 60 0 m2 m3 270 | sync 15 271 | 1>jump 75 0 80 60 0 m1 m2 272 | 2>jump 75 0 80 60 0 m2 m3 273 | 3>jump 75 0 80 60 0 m3 m4 274 | 4>jump 75 0 80 60 0 m4 m1 275 | 5>jump 75 0 80 60 0 m1 m2 276 | 6>jump 75 0 80 60 0 m2 m3 277 | 7>jump 75 0 80 60 0 m3 m4 278 | sync 15 279 | 1>jump 75 0 80 60 0 m2 m3 280 | 2>jump 75 0 80 60 0 m3 m4 281 | 3>jump 75 0 80 60 0 m4 m1 282 | 4>jump 75 0 80 60 0 m1 m2 283 | 5>jump 75 0 80 60 0 m2 m3 284 | 6>jump 75 0 80 60 0 m3 m4 285 | 7>jump 75 0 80 60 0 m4 m1 286 | sync 15 287 | 1>jump 75 0 80 60 0 m3 m4 288 | 2>jump 75 0 80 60 0 m4 m1 289 | 3>jump 75 0 80 60 0 m1 m2 290 | 4>jump 75 0 80 60 0 m2 m3 291 | 5>jump 75 0 80 60 0 m3 m4 292 | 6>jump 75 0 80 60 0 m4 m1 293 | 7>jump 75 0 80 60 0 m1 m2 294 | sync 15 295 | 1>jump 75 0 80 60 0 m4 m1 296 | 2>jump 75 0 80 60 0 m1 m2 297 | 3>jump 75 0 80 60 0 m2 m3 298 | 4>jump 75 0 80 60 0 m3 m4 299 | 5>jump 75 0 80 60 0 m4 m1 300 | 6>jump 75 0 80 60 0 m1 m2 301 | 7>jump 75 0 80 60 0 m2 m3 302 | sync 15 303 | 1>jump 75 0 80 60 0 m1 m2 304 | 2>jump 75 0 80 60 0 m2 m3 305 | 3>jump 75 0 80 60 0 m3 m4 306 | 4>jump 75 0 80 60 0 m4 m1 307 | 5>jump 75 0 80 60 0 m1 m2 308 | 6>jump 75 0 80 60 0 m2 m3 309 | 7>jump 75 0 80 60 0 m3 m4 310 | sync 15 311 | 1>jump 75 0 80 60 0 m2 m3 312 | 2>jump 75 0 80 60 0 m3 m4 313 | 3>jump 75 0 80 60 0 m4 m1 314 | 4>jump 75 0 80 60 0 m1 m2 315 | 5>jump 75 0 80 60 0 m2 m3 316 | 6>jump 75 0 80 60 0 m3 m4 317 | 7>jump 75 0 80 60 0 m4 m1 318 | sync 15 319 | 1>jump 75 0 80 60 0 m3 m4 320 | 2>jump 75 0 80 60 0 m4 m1 321 | 3>jump 75 0 80 60 0 m1 m2 322 | 4>jump 75 0 80 60 0 m2 m3 323 | 5>jump 75 0 80 60 0 m3 m4 324 | 6>jump 75 0 80 60 0 m4 m1 325 | 7>jump 75 0 80 60 0 m1 m2 326 | sync 15 327 | 1>jump 75 0 80 60 0 m4 m1 328 | 2>jump 75 0 80 60 0 m1 m2 329 | 3>jump 75 0 80 60 0 m2 m3 330 | 4>jump 75 0 80 60 0 m3 m4 331 | 5>jump 75 0 80 60 0 m4 m1 332 | 6>jump 75 0 80 60 0 m1 m2 333 | 7>jump 75 0 80 60 0 m2 m3 334 | sync 15 335 | *>land 336 | -------------------------------------------------------------------------------- /formation_setup.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | def set_ap(ssid, password): 4 | ''' 5 | A Function to set tello in AP mode 6 | :param ssid: the ssid of the network (e.g. name of the Wi-Fi) 7 | :param password: the password of the network 8 | :return: 9 | ''' 10 | my_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket for sending cmd 11 | my_socket.bind(('', 8889)) 12 | cmd_str = 'command' 13 | print ('sending command %s' % cmd_str) 14 | my_socket.sendto(cmd_str.encode('utf-8'), ('192.168.10.1', 8889)) 15 | response, ip = my_socket.recvfrom(100) 16 | print('from %s: %s' % (ip, response)) 17 | 18 | cmd_str = 'ap %s %s' % (ssid, password) 19 | print ('sending command %s' % cmd_str) 20 | my_socket.sendto(cmd_str.encode('utf-8'), ('192.168.10.1', 8889)) 21 | response, ip = my_socket.recvfrom(100) 22 | print('from %s: %s' % (ip, response)) 23 | 24 | # example of setting Tello into command mode 25 | # only works if server is connected to Tello Wi-Fi 26 | set_ap('Tello_Nest', 'tellotello') 27 | 28 | 29 | -------------------------------------------------------------------------------- /install/Linux/linux_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo apt-get update -y 3 | 4 | # install python 2.7 5 | sudo apt-get install python2.7 python-pip -y 6 | sudo pip install --upgrade pip 7 | 8 | #switch to python2.7 9 | sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 150 10 | sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 100 11 | 12 | sudo apt-get update -y 13 | 14 | 15 | # install dependencies 16 | sudo pip install netifaces 17 | sudo pip install netaddr 18 | 19 | echo 'Installation Done!' 20 | -------------------------------------------------------------------------------- /install/Macos/mac_install.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/sh 3 | # install Homebrew 4 | 5 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 6 | 7 | brew update 8 | 9 | # install pip 10 | 11 | sudo easy_install pip 12 | 13 | curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py 14 | sudo python get-pip.py 15 | 16 | # install dependencies 17 | 18 | sudo pip install netifaces --ignore-installed 19 | 20 | sudo pip install netaddr --ignore-installed 21 | 22 | echo 'Installation Done!' 23 | -------------------------------------------------------------------------------- /install/Windows/windows_install.bat: -------------------------------------------------------------------------------- 1 | echo=1/*>nul&@cls 2 | @echo off 3 | setlocal enableDelayedExpansion 4 | ::runas administrator 5 | %1 start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit 6 | cls 7 | ::setlocal 8 | call :setdir 9 | call :configx86orx64 10 | set extract=extract 11 | set pythonLib="C:\Python27\Lib\site-packages\" 12 | set /a maxRetry=3 13 | set /a retryCount=0 14 | echo ------------------------------------------------------ 15 | 16 | ::-------------------down python2.7 and install------------------- 17 | echo ------------------------------------------------------ 18 | echo Downloading python2.7 19 | echo ------------------------------------------------------ 20 | ::此条注册表项用于开启ssl、tls多个版本的支持,用于解决python官网拒绝访问的问题 21 | REG ADD "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v SecureProtocols /t REG_DWORD /d 2728 /f >nul 22 | set /a retryCount=0 23 | 24 | if exist %pythonPackage% goto :downpythoninstall 25 | :downpython 26 | call :down %pythonDown% %pythonPackage% 27 | :downpythoninstall 28 | call :installmsiPackage %pythonPackage% 29 | ::添加python2.7环境变量 30 | ::由于wmic不会即时生效,所以进行set 31 | echo %PATH%|findstr "c:\python27" >nul 32 | if %errorlevel% neq 0 ( 33 | wmic ENVIRONMENT where "name='PATH' and username=''" set VariableValue="%PATH%;c:\python27" 34 | set "path=%path%;c:\python27;" 35 | ) 36 | echo %PATHEXT%|findstr ".PY;.PYM" >nul 37 | if %errorlevel% neq 0 ( 38 | wmic ENVIRONMENT where "name='PATHEXT' and username=''" set VariableValue="%PATHEXT%;.PY;.PYM" 39 | set "pathext=%pathext%;.PY;.PYM;" 40 | ) 41 | :downpythonend 42 | ::-------------------python pip的安装------------------- 43 | echo ------------------------------------------------------ 44 | echo Downloading pip 45 | echo ------------------------------------------------------ 46 | if exist %pipPackage% goto :downpipinstall 47 | :downpip 48 | call :down %pipDown% %pipPackage% 49 | :downpipinstall 50 | python %pipPackage% 51 | python -m pip install -U pip 52 | :downpipend 53 | python -m pip install --upgrade pip 54 | ::-------------------python-numpy python-matplotlib opencv-python的安装(pip方式)------------------- 55 | echo ------------------------------------------------------ 56 | echo Downloading netifaces 57 | echo ------------------------------------------------------ 58 | python -m pip install netifaces 59 | echo ------------------------------------------------------ 60 | echo Downloading netaddr 61 | echo ------------------------------------------------------ 62 | python -m pip install netaddr 63 | echo ------------------------------------------------------ 64 | :copydependencies 65 | pause 66 | goto :eof 67 | 68 | ::-----------------下面是目录切换定义区域------------------ 69 | ::在管理员模式执行时,默认路径变更,此处将目录切换回来 70 | :setdir 71 | set char=%~dp0% 72 | %char:~0,2% 73 | cd %~dp0% 74 | goto :eof 75 | 76 | ::-----------------下面是版本函数定义区域------------------ 77 | :configx86orx64 78 | IF %PROCESSOR_ARCHITECTURE% == AMD64 ( 79 | set versionFlag=win64 80 | ) else ( 81 | set versionFlag=win32 82 | ) 83 | 84 | echo Windows Version: %versionFlag% 85 | if %versionFlag%==win64 ( 86 | set pythonDown="https://www.python.org/ftp/python/2.7.15/python-2.7.15.amd64.msi" 87 | set pythonPackage=python-2.7.15.amd64.msi 88 | 89 | set pipDown="https://bootstrap.pypa.io/get-pip.py" 90 | set pipPackage=get-pip.py 91 | 92 | ) else ( 93 | set pythonDown="https://www.python.org/ftp/python/2.7.15/python-2.7.15.msi" 94 | set pythonPackage=python-2.7.15.msi 95 | 96 | set pipDown="https://bootstrap.pypa.io/get-pip.py" 97 | set pipPackage=get-pip.py 98 | ) 99 | 100 | goto :eof 101 | 102 | ::-----------------下面是下载函数定义区域------------------ 103 | :down 104 | echo Source: "%~1" 105 | echo Destination: "%~f2" 106 | echo Start downloading "%~2"... 107 | cscript -nologo -e:jscript "%~f0" "download" "%~1" "%~2" 108 | ::echo Download "%~2" OK! 109 | echo ------------------------------------------------------ 110 | goto :eof 111 | 112 | ::-----------------下面是解压函数定义区域------------------ 113 | :unpack 114 | echo Source: "%~f1" 115 | echo Destination: "%~f2" 116 | echo Start unpacking "%~1"... 117 | cscript -nologo -e:jscript "%~f0" "unpack" "%~1" "%~2" "%~dp0" 118 | echo Unpack "%~1" OK! 119 | echo ------------------------------------------------------ 120 | goto :eof 121 | ::-----------------下面是安装函数定义区域------------------ 122 | :installmsiPackage 123 | echo Source: "%~f1" 124 | echo Strat installing "%~f1"... 125 | msiexec /i "%~f1" /passive 126 | echo install "%~1" OK! 127 | echo ------------------------------------------------------ 128 | goto :eof 129 | */ 130 | 131 | function download(DownSource, DownDestination) 132 | { 133 | var DownPost 134 | ,DownGet; 135 | 136 | DownDestination=DownDestination.toLowerCase(); 137 | DownSource=DownSource.toLowerCase(); 138 | //DownPost = new ActiveXObject("Msxml2"+String.fromCharCode(0x2e)+"ServerXMLHTTP"); 139 | //DownPost = new ActiveXObject("Microsoft"+String.fromCharCode(0x2e)+"XMLHTTP"); 140 | //DownPost.setOption(2, 13056); 141 | var DownPost=null; 142 | try{ 143 | DownPost=new XMLHttpRequest(); 144 | }catch(e){ 145 | try{ 146 | DownPost=new ActiveXObject("Msxml2.XMLHTTP"); 147 | DownPost.setOption(2, 13056); 148 | }catch(ex){ 149 | try{ 150 | DownPost=new ActiveXObject("Microsoft.XMLHTTP"); 151 | }catch(e3){ 152 | DownPost=null; 153 | } 154 | } 155 | } 156 | DownPost.open("GET",DownSource,0); 157 | DownPost.send(); 158 | DownGet = new ActiveXObject("ADODB"+String.fromCharCode(0x2e)+"Stream"); 159 | DownGet.Mode = 3; 160 | DownGet.Type = 1; 161 | DownGet.Open(); 162 | DownGet.Write(DownPost.responseBody); 163 | DownGet.SaveToFile(DownDestination,2); 164 | } 165 | 166 | function unpack(PackedFileSource, UnpackFileDestination, ParentFolder) 167 | { 168 | var FileSysObject = new Object 169 | ,ShellObject = new ActiveXObject("Shell.Application") 170 | ,intOptions = 4 + 16 171 | ,DestinationObj 172 | ,SourceObj; 173 | 174 | if (!UnpackFileDestination) UnpackFileDestination = '.'; 175 | var FolderTest = ShellObject.NameSpace(ParentFolder + UnpackFileDestination); 176 | FileSysObject = ShellObject.NameSpace(ParentFolder); 177 | while (!FolderTest) 178 | { 179 | WSH.Echo ('Unpack Destination Folder Not Exist, Creating...'); 180 | FileSysObject.NewFolder(UnpackFileDestination); 181 | FolderTest = ShellObject.NameSpace(ParentFolder + UnpackFileDestination); 182 | if (FolderTest) 183 | WSH.Echo('Unpack Destination Folder Created.'); 184 | } 185 | DestinationObj = ShellObject.NameSpace(ParentFolder + UnpackFileDestination); 186 | SourceObj = ShellObject.NameSpace(ParentFolder + PackedFileSource); 187 | for (var i = 0; i < SourceObj.Items().Count; i++) 188 | { 189 | try { 190 | if (SourceObj) { 191 | WSH.Echo('Unpacking ' + SourceObj.Items().Item(i) + '... '); 192 | DestinationObj.CopyHere(SourceObj.Items().Item(i), intOptions); 193 | WSH.Echo('Unpack ' + SourceObj.Items().Item(i) + ' Done.'); 194 | } 195 | } 196 | catch(e) { 197 | WSH.Echo('Failed: ' + e); 198 | } 199 | } 200 | } 201 | 202 | switch (WScript.Arguments(0)){ 203 | case "download": 204 | download(WScript.Arguments(1), WScript.Arguments(2)); 205 | break; 206 | case "unpack": 207 | unpack(WScript.Arguments(1), WScript.Arguments(2), WScript.Arguments(3)); 208 | break; 209 | default: 210 | } 211 | 212 | -------------------------------------------------------------------------------- /ip.txt: -------------------------------------------------------------------------------- 1 | scan 1 2 | correct_ip 3 | delay 20 -------------------------------------------------------------------------------- /multi_tello_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import time 4 | from tello_manager import * 5 | import Queue 6 | import time 7 | import os 8 | import binascii 9 | reload(sys) 10 | sys.setdefaultencoding('utf-8') 11 | 12 | def create_execution_pools(num): 13 | pools = [] 14 | for x in range(num): 15 | execution_pool = Queue.Queue() 16 | pools.append(execution_pool) 17 | return pools 18 | 19 | 20 | def drone_handler(tello, queue): 21 | while True: 22 | while queue.empty(): 23 | pass 24 | command = queue.get() 25 | tello.send_command(command) 26 | 27 | 28 | def all_queue_empty(execution_pools): 29 | for queue in execution_pools: 30 | if not queue.empty(): 31 | return False 32 | return True 33 | 34 | def all_got_response(manager): 35 | for tello_log in manager.get_log().values(): 36 | if not tello_log[-1].got_response(): 37 | return False 38 | return True 39 | 40 | def save_log(manager): 41 | log = manager.get_log() 42 | 43 | if not os.path.exists('log'): 44 | try: 45 | os.makedirs('log') 46 | except Exception, e: 47 | pass 48 | 49 | out = open('log/' + start_time + '.txt', 'w') 50 | cnt = 1 51 | for stat_list in log.values(): 52 | out.write('------\nDrone: %s\n' % cnt) 53 | cnt += 1 54 | for stat in stat_list: 55 | #stat.print_stats() 56 | str = stat.return_stats() 57 | out.write(str) 58 | out.write('\n') 59 | 60 | def check_timeout(start_time, end_time, timeout): 61 | diff = end_time - start_time 62 | time.sleep(0.1) 63 | return diff > timeout 64 | 65 | 66 | manager = Tello_Manager() 67 | start_time = str(time.strftime("%a-%d-%b-%Y_%H-%M-%S-%Z", time.localtime(time.time()))) 68 | 69 | try: 70 | file_name = sys.argv[1] 71 | f = open(file_name, "r") 72 | commands = f.readlines() 73 | 74 | tello_list = [] 75 | execution_pools = [] 76 | sn_ip_dict = {} 77 | id_sn_dict = {} 78 | ip_fid_dict = {} 79 | 80 | for command in commands: 81 | if command != '' and command != '\n': 82 | command = command.rstrip() 83 | 84 | if '//' in command: 85 | # ignore comments 86 | continue 87 | elif 'scan' in command: 88 | num_of_tello = int(command.partition('scan')[2]) 89 | 90 | manager.find_avaliable_tello(num_of_tello) 91 | tello_list = manager.get_tello_list() 92 | execution_pools = create_execution_pools(num_of_tello) 93 | 94 | for x in range(len(tello_list)): 95 | t1 = Thread(target=drone_handler, args=(tello_list[x], execution_pools[x])) 96 | ip_fid_dict[tello_list[x].tello_ip] = x 97 | #str_cmd_index_dict_init_flag [x] = None 98 | t1.daemon = True 99 | t1.start() 100 | 101 | 102 | elif '>' in command: 103 | id_list = [] 104 | id = command.partition('>')[0] 105 | if id == '*': 106 | for x in range(len(tello_list)): 107 | id_list.append(x) 108 | else: 109 | # index starbattery_checkt from 1 110 | id_list.append(int(id)-1) 111 | action = str(command.partition('>')[2]) 112 | 113 | # push command to pools 114 | for tello_id in id_list: 115 | tmp_sn = id_sn_dict[tello_id] 116 | reflec_ip = sn_ip_dict[tmp_sn] 117 | fid = ip_fid_dict[reflec_ip] 118 | execution_pools[fid].put(action) 119 | 120 | elif 'battery_check' in command: 121 | 122 | threshold = int(command.partition('battery_check')[2]) 123 | for queue in execution_pools: 124 | queue.put('battery?') 125 | 126 | # wait till all commands are executed 127 | while not all_queue_empty(execution_pools): 128 | time.sleep(0.5) 129 | 130 | # wait for new log object append 131 | time.sleep(1) 132 | 133 | # wait till all responses are received 134 | while not all_got_response(manager): 135 | time.sleep(0.5) 136 | 137 | for tello_log in manager.get_log().values(): 138 | battery = int(tello_log[-1].response) 139 | print ('[Battery_Show]show drone battery: %d ip:%s\n' % (battery,tello_log[-1].drone_ip)) 140 | if battery < threshold: 141 | print('[Battery_Low]IP:%s Battery < Threshold. Exiting...\n'%tello_log[-1].drone_ip) 142 | save_log(manager) 143 | exit(0) 144 | print ('[Battery_Enough]Pass battery check\n') 145 | elif 'delay' in command: 146 | delay_time = float(command.partition('delay')[2]) 147 | print ('[Delay_Seconds]Start Delay for %f second\n' %delay_time) 148 | time.sleep(delay_time) 149 | elif 'correct_ip' in command: 150 | for queue in execution_pools: 151 | queue.put('sn?') 152 | while not all_queue_empty(execution_pools): 153 | time.sleep(0.5) 154 | 155 | time.sleep(1) 156 | 157 | while not all_got_response(manager): 158 | time.sleep(0.5) 159 | for tello_log in manager.get_log().values(): 160 | sn = str(tello_log[-1].response) 161 | tello_ip = str(tello_log[-1].drone_ip) 162 | sn_ip_dict[sn] = tello_ip 163 | 164 | elif '=' in command: 165 | drone_id = int(command.partition('=')[0]) 166 | drone_sn = command.partition('=')[2] 167 | id_sn_dict[drone_id-1] = drone_sn 168 | print ('[IP_SN_FID]:Tello_IP:%s------Tello_SN:%s------Tello_fid:%d\n'%(sn_ip_dict[drone_sn],drone_sn,drone_id)) 169 | #print id_sn_dict[drone_id] 170 | elif 'sync' in command: 171 | timeout = float(command.partition('sync')[2]) 172 | print '[Sync_And_Waiting]Sync for %s seconds \n' % timeout 173 | time.sleep(1) 174 | try: 175 | start = time.time() 176 | # wait till all commands are executed 177 | while not all_queue_empty(execution_pools): 178 | now = time.time() 179 | if check_timeout(start, now, timeout): 180 | raise RuntimeError 181 | 182 | print '[All_Commands_Send]All queue empty and all command send,continue\n' 183 | # wait till all responses are received 184 | while not all_got_response(manager): 185 | now = time.time() 186 | if check_timeout(start, now, timeout): 187 | raise RuntimeError 188 | print '[All_Responses_Get]All response got, continue\n' 189 | except RuntimeError: 190 | print '[Quit_Sync]Fail Sync:Timeout exceeded, continue...\n' 191 | 192 | 193 | # wait till all commands are executed 194 | while not all_queue_empty(execution_pools): 195 | time.sleep(0.5) 196 | 197 | time.sleep(1) 198 | 199 | # wait till all responses are received 200 | while not all_got_response(manager): 201 | time.sleep(0.5) 202 | 203 | save_log(manager) 204 | 205 | except KeyboardInterrupt: 206 | print '[Quit_ALL]Multi_Tello_Task got exception. Sending land to all drones...\n' 207 | for ip in manager.tello_ip_list: 208 | manager.socket.sendto('land'.encode('utf-8'), (ip, 8889)) 209 | 210 | save_log(manager) 211 | 212 | -------------------------------------------------------------------------------- /stats.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | class Stats: 4 | def __init__(self, command, id): 5 | self.command = command 6 | self.response = None 7 | self.id = id 8 | 9 | self.start_time = datetime.now() 10 | self.end_time = None 11 | self.duration = None 12 | self.drone_ip = None 13 | def add_response(self, response,ip): 14 | if self.response == None: 15 | self.response = response 16 | self.end_time = datetime.now() 17 | self.duration = self.get_duration() 18 | self.drone_ip = ip 19 | # self.print_stats() 20 | 21 | def get_duration(self): 22 | diff = self.end_time - self.start_time 23 | return diff.total_seconds() 24 | 25 | def print_stats(self): 26 | print '\nid: %s' % self.id 27 | print 'command: %s' % self.command 28 | print 'response: %s' % self.response 29 | print 'start time: %s' % self.start_time 30 | print 'end_time: %s' % self.end_time 31 | print 'duration: %s\n' % self.duration 32 | 33 | def got_response(self): 34 | if self.response is None: 35 | return False 36 | else: 37 | return True 38 | 39 | def return_stats(self): 40 | str = '' 41 | str += '\nid: %s\n' % self.id 42 | str += 'command: %s\n' % self.command 43 | str += 'response: %s\n' % self.response 44 | str += 'start time: %s\n' % self.start_time 45 | str += 'end_time: %s\n' % self.end_time 46 | str += 'duration: %s\n' % self.duration 47 | return str -------------------------------------------------------------------------------- /tello_manager.py: -------------------------------------------------------------------------------- 1 | import threading 2 | from threading import Thread 3 | import socket 4 | import time 5 | import netifaces 6 | import netaddr 7 | from netaddr import IPNetwork 8 | from collections import defaultdict 9 | from stats import Stats 10 | import binascii 11 | class Tello: 12 | """ 13 | A wrapper class to interact with Tello 14 | Communication with Tello is handled by Tello_Manager 15 | """ 16 | def __init__(self, tello_ip, Tello_Manager): 17 | self.tello_ip = tello_ip 18 | self.Tello_Manager = Tello_Manager 19 | def send_command(self, command): 20 | return self.Tello_Manager.send_command(command, self.tello_ip) 21 | 22 | class Tello_Manager: 23 | def __init__(self): 24 | self.local_ip = '' 25 | self.local_port = 8889 26 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # socket for sending cmd 27 | self.socket.bind((self.local_ip, self.local_port)) 28 | 29 | # thread for receiving cmd ack 30 | self.receive_thread = threading.Thread(target=self._receive_thread) 31 | self.receive_thread.daemon = True 32 | self.receive_thread.start() 33 | 34 | self.tello_ip_list = [] 35 | self.tello_list = [] 36 | self.log = defaultdict(list) 37 | 38 | self.COMMAND_TIME_OUT = 9.0 39 | 40 | self.last_response_index = {} 41 | self.str_cmd_index = {} 42 | 43 | def find_avaliable_tello(self, num): 44 | """ 45 | Find avaliable tello in server's subnets 46 | :param num: Number of Tello this method is expected to find 47 | :return: None 48 | """ 49 | print '[Start_Searching]Searching for %s available Tello...\n' % num 50 | 51 | subnets, address = self.get_subnets() 52 | possible_addr = [] 53 | 54 | for subnet, netmask in subnets: 55 | for ip in IPNetwork('%s/%s' % (subnet, netmask)): 56 | # skip local and broadcast 57 | if str(ip).split('.')[3] == '0' or str(ip).split('.')[3] == '255': 58 | continue 59 | possible_addr.append(str(ip)) 60 | 61 | while len(self.tello_ip_list) < num: 62 | print '[Still_Searching]Trying to find Tello in subnets...\n' 63 | 64 | # delete already fond Tello 65 | for tello_ip in self.tello_ip_list: 66 | if tello_ip in possible_addr: 67 | possible_addr.remove(tello_ip) 68 | # skip server itself 69 | for ip in possible_addr: 70 | if ip in address: 71 | continue 72 | 73 | # record this command 74 | self.log[ip].append(Stats('command', len(self.log[ip]))) 75 | self.socket.sendto(b'command', (ip, 8889)) 76 | time.sleep(5) 77 | 78 | # filter out non-tello addresses in log 79 | temp = defaultdict(list) 80 | for ip in self.tello_ip_list: 81 | temp[ip] = self.log[ip] 82 | self.log = temp 83 | 84 | 85 | 86 | def get_subnets(self): 87 | """ 88 | Look through the server's internet connection and 89 | returns subnet addresses and server ip 90 | :return: list[str]: subnets 91 | list[str]: addr_list 92 | """ 93 | subnets = [] 94 | ifaces = netifaces.interfaces() 95 | addr_list = [] 96 | for myiface in ifaces: 97 | addrs = netifaces.ifaddresses(myiface) 98 | 99 | if socket.AF_INET not in addrs: 100 | continue 101 | # Get ipv4 stuff 102 | ipinfo = addrs[socket.AF_INET][0] 103 | address = ipinfo['addr'] 104 | netmask = ipinfo['netmask'] 105 | 106 | # limit range of search. This will work for router subnets 107 | if netmask != '255.255.255.0': 108 | continue 109 | 110 | # Create ip object and get 111 | cidr = netaddr.IPNetwork('%s/%s' % (address, netmask)) 112 | network = cidr.network 113 | subnets.append((network, netmask)) 114 | addr_list.append(address) 115 | return subnets, addr_list 116 | 117 | def get_tello_list(self): 118 | return self.tello_list 119 | 120 | def send_command(self, command, ip): 121 | """ 122 | Send a command to the ip address. Will be blocked until 123 | the last command receives an 'OK'. 124 | If the command fails (either b/c time out or error), 125 | will try to resend the command 126 | :param command: (str) the command to send 127 | :param ip: (str) the ip of Tello 128 | :return: The latest command response 129 | """ 130 | #global cmd 131 | command_sof_1 = ord(command[0]) 132 | command_sof_2 = ord(command[1]) 133 | if command_sof_1 == 0x52 and command_sof_2 == 0x65: 134 | multi_cmd_send_flag = True 135 | else : 136 | multi_cmd_send_flag = False 137 | 138 | if multi_cmd_send_flag == True: 139 | self.str_cmd_index[ip] = self.str_cmd_index[ip] + 1 140 | for num in range(1,5): 141 | str_cmd_index_h = self.str_cmd_index[ip]/128 + 1 142 | str_cmd_index_l = self.str_cmd_index[ip]%128 143 | if str_cmd_index_l == 0: 144 | str_cmd_index_l = str_cmd_index_l + 2 145 | cmd_sof =[0x52,0x65,str_cmd_index_h,str_cmd_index_l,0x01,num + 1,0x20] 146 | cmd_sof_str = str(bytearray(cmd_sof)) 147 | cmd = cmd_sof_str + command[3:] 148 | self.socket.sendto(cmd.encode('utf-8'), (ip, 8889)) 149 | 150 | print '[Multi_Command]----Multi_Send----IP:%s----Command: %s\n' % (ip, command[3:]) 151 | real_command = command[3:] 152 | else: 153 | self.socket.sendto(command.encode('utf-8'), (ip, 8889)) 154 | print '[Single_Command]----Single_Send----IP:%s----Command: %s\n' % (ip, command) 155 | real_command = command 156 | 157 | self.log[ip].append(Stats(real_command, len(self.log[ip]))) 158 | start = time.time() 159 | while not self.log[ip][-1].got_response(): 160 | now = time.time() 161 | diff = now - start 162 | if diff > self.COMMAND_TIME_OUT: 163 | print '[Not_Get_Response]Max timeout exceeded...command: %s \n' % real_command 164 | return 165 | 166 | def _receive_thread(self): 167 | """Listen to responses from the Tello. 168 | 169 | Runs as a thread, sets self.response to whatever the Tello last returned. 170 | 171 | """ 172 | while True: 173 | try: 174 | self.response, ip = self.socket.recvfrom(1024) 175 | ip = ''.join(str(ip[0])) 176 | if self.response.upper() == 'OK' and ip not in self.tello_ip_list: 177 | print '[Found_Tello]Found Tello.The Tello ip is:%s\n' % ip 178 | self.tello_ip_list.append(ip) 179 | self.last_response_index[ip] = 100 180 | self.tello_list.append(Tello(ip, self)) 181 | self.str_cmd_index[ip] = 1 182 | response_sof_part1 = ord(self.response[0]) 183 | response_sof_part2 = ord(self.response[1]) 184 | if response_sof_part1 == 0x52 and response_sof_part2 == 0x65: 185 | response_index = ord(self.response[3]) 186 | 187 | if response_index != self.last_response_index[ip]: 188 | #print '--------------------------response_index:%x %x'%(response_index,self.last_response_index) 189 | print'[Multi_Response] ----Multi_Receive----IP:%s----Response: %s ----\n' % (ip, self.response[7:]) 190 | self.log[ip][-1].add_response(self.response[7:],ip) 191 | self.last_response_index[ip] = response_index 192 | else: 193 | print'[Single_Response]----Single_Receive----IP:%s----Response: %s ----\n' % (ip, self.response) 194 | self.log[ip][-1].add_response(self.response,ip) 195 | #print'[Response_WithIP]----Receive----IP:%s----Response:%s----\n' % (ip, self.response) 196 | 197 | except socket.error, exc: 198 | print "[Exception_Error]Caught exception socket.error : %s\n" % exc 199 | 200 | def get_log(self): 201 | return self.log 202 | 203 | 204 | --------------------------------------------------------------------------------