├── docs ├── screenshots │ ├── NodeRED-Editor.png │ ├── NodeRED-Tello-Controls-flow.png │ ├── NodeRED-Tello-Missions-flow.png │ ├── NodeRED-Tello-Telemetry-flow.png │ ├── NodeRED-Tello-DeveloperSelfie.png │ ├── NodeRED-Tello-Telemetry-gauges.png │ ├── NodeRED-Tello-Controls-Dash-flow.png │ ├── NodeRED-Tello-Controls-Dashboard.png │ ├── NodeRED-Tello-Pictures-Solution.png │ ├── Watson-Visual-Recognition-APIkey.png │ ├── NodeRED-Tello-Commands-Starter-flow.png │ ├── NodeRED-Tello-Missions-Starter-flow.png │ ├── NodeRED-Tello-Commands-Solution-flow.png │ ├── NodeRED-Tello-Dashboard-Solution-flow.png │ ├── NodeRED-Tello-Dashboard-Starter-flow.png │ ├── NodeRED-Tello-Pictures-Solution-flow.png │ ├── NodeRED-Tello-Telemetry-Solution-flow.png │ ├── NodeRED-Tello-Telemetry-Starter-flow.png │ ├── NodeRED-Tello-VisualRecognition-APIkey.png │ ├── NodeRED-Tello-Controls-Dashboard-Starter.png │ ├── NodeRED-Tello-VisualRecognition-Solution.png │ ├── NodeRED-Tello-Controls-Dashboard-Solution.png │ ├── NodeRED-Tello-Telemetry-Dashboard-Solution.png │ ├── NodeRED-Tello-VisualRecognition-Solution-flow.png │ ├── WatsonStudioVisualRecognitionCustomClassifier.png │ ├── NodeRED-Tello-CustomClassifierVisualRecognition-Dashboard.png │ └── NodeRED-Tello-CustomClassifierVisualRecognition-Solution-flow.png ├── PART7.md ├── PART1.md ├── PART6.md ├── PART2.md ├── PART3.md ├── PART4.md ├── PART8.md ├── PART9.md └── PART5.md ├── .gitignore ├── README.md ├── TOBEREMOVED ├── removedFromREADME.md ├── nodered-tello-missions.json └── nodered-tello-controls.json ├── flows ├── starter │ ├── part3_starter.json │ └── part4_starter.json └── solutions │ ├── part7_solution.json │ ├── part3_solution.json │ └── part8_solution.json └── LICENSE /docs/screenshots/NodeRED-Editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Editor.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Controls-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Controls-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Missions-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Missions-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Telemetry-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Telemetry-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-DeveloperSelfie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-DeveloperSelfie.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Telemetry-gauges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Telemetry-gauges.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Controls-Dash-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Controls-Dash-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Controls-Dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Controls-Dashboard.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Pictures-Solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Pictures-Solution.png -------------------------------------------------------------------------------- /docs/screenshots/Watson-Visual-Recognition-APIkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/Watson-Visual-Recognition-APIkey.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Commands-Starter-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Commands-Starter-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Missions-Starter-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Missions-Starter-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Commands-Solution-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Commands-Solution-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Dashboard-Solution-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Dashboard-Solution-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Dashboard-Starter-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Dashboard-Starter-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Pictures-Solution-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Pictures-Solution-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Telemetry-Solution-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Telemetry-Solution-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Telemetry-Starter-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Telemetry-Starter-flow.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-VisualRecognition-APIkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-VisualRecognition-APIkey.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Controls-Dashboard-Starter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Controls-Dashboard-Starter.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-VisualRecognition-Solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-VisualRecognition-Solution.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Controls-Dashboard-Solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Controls-Dashboard-Solution.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-Telemetry-Dashboard-Solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-Telemetry-Dashboard-Solution.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-VisualRecognition-Solution-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-VisualRecognition-Solution-flow.png -------------------------------------------------------------------------------- /docs/screenshots/WatsonStudioVisualRecognitionCustomClassifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/WatsonStudioVisualRecognitionCustomClassifier.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-CustomClassifierVisualRecognition-Dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-CustomClassifierVisualRecognition-Dashboard.png -------------------------------------------------------------------------------- /docs/screenshots/NodeRED-Tello-CustomClassifierVisualRecognition-Solution-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnwalicki/Node-RED-Tello-Control/main/docs/screenshots/NodeRED-Tello-CustomClassifierVisualRecognition-Solution-flow.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ###################################### 2 | # Visual Studio Code generated files # 3 | ###################################### 4 | *.code-workspace 5 | .vscode 6 | 7 | ###################################### 8 | # OS generated files # 9 | ###################################### 10 | .DS_Store 11 | .DS_Store? 12 | ._* 13 | .Spotlight-V100 14 | .Trashes 15 | ehthumbs.db 16 | Thumbs.db -------------------------------------------------------------------------------- /docs/PART7.md: -------------------------------------------------------------------------------- 1 | # Take pictures 2 | 3 | The Tello has a [low level protocol](https://tellopilots.com/wiki/protocol/) that allows you to take pictures. This flow sends the take picture command and receives / reconstructs the picture data from the Tello. 4 | 5 | ### Solution 6 | 7 | There is a [solution flow](/flows/solutions/part7_solution.json) available if you need help or want to check your solution. 8 | 9 | ![Tello Pictures Dashboard Solution flow](/docs/screenshots/NodeRED-Tello-Pictures-Solution-flow.png?raw=true "Tello Pictures Dashboard Solution flow") 10 | 11 | Launch the Node-RED Dashboard by turning to the Dashboard tab in the right menu and then click on the launch button. 12 | 13 | ![Tello Pictures Dashboard Solution flow](/docs/screenshots/NodeRED-Tello-Pictures-Solution.png?raw=true "Tello Pictures Dashboard Solution") 14 | --- 15 | 16 | [Home](/README.md) | [Node-RED](/docs/PART1.md) | [Setup](/docs/PART2.md) | [Commands](/docs/PART3.md) | [Dashboard](/docs/PART4.md) | [Telemetry](/docs/PART5.md) | [Mission](/docs/PART6.md) | **Pictures** | [Visual Recognition](/docs/PART8.md) | [Custom Classifier](/docs/PART9.md) 17 | 18 | --- 19 | -------------------------------------------------------------------------------- /docs/PART1.md: -------------------------------------------------------------------------------- 1 | # Learn about Node-RED 2 | 3 | **Node-RED** is a open source project and flow-based programming environment from the 4 | [JS Foundation](https://js.foundation/community/projects). It provides a palette of nodes that allow users 5 | to very quickly wire up IoT applications that can combine streams of both physical and digital events. 6 | The [Node.JS](https://nodejs.org/) runtime is easy to install on both devices and the cloud, and provides a framework for extending its capabilities. 7 | 8 | [Node-RED](https://nodered.org) applications, or flows, are created using a browser based editor served from the Node-RED runtime. There is a palette of nodes installed out of the box, but there are a wealth of additional [nodes](https://flows.nodered.org/?type=node&num_pages=1) available to be installed to add additional capabilities to Node-RED. 9 | 10 | Node-RED flows are created by dragging nodes from the pallet then connecting them in the editor. 11 | 12 | ![Flow editor](screenshots/NodeRED-Editor.png) 13 | 14 | To get started you need to setup your Node-RED environment, so move to the [next section](PART2.md) and complete the setup. 15 | 16 | --- 17 | 18 | [Home](/README.md) | **Node-RED** | [Setup](/docs/PART2.md) | [Commands](/docs/PART3.md) | [Dashboard](/docs/PART4.md) | [Telemetry](/docs/PART5.md) | [Mission](/docs/PART6.md) | [Pictures](/docs/PART7.md) | [Visual Recognition](/docs/PART8.md) | [Custom Classifier](/docs/PART9.md) 19 | 20 | --- 21 | -------------------------------------------------------------------------------- /docs/PART6.md: -------------------------------------------------------------------------------- 1 | # Create missions for your drone 2 | 3 | You can create subflows in Node-RED. A subflow acts like a function in other programming languages. You can capture an existing flow, or part of a flow, into a subflow or create a subflow from scratch. The subflows appear in the palette and look like any other nodes in the palette. You can then drag the subflow nodes onto the palette to run the flow within the subflow. 4 | 5 | <- video here -> 6 | 7 | ## Task 8 | 9 | Import the [part6 starter flow](/flows/starter/part6_starter.json), which provides you will all the subflow nodes as shown in the video. You should create a patrol mission for your drone, which comprises of the following tasks: 10 | 11 | - Take off 12 | - Descend 1.5 meters 13 | - Rotate 360º clockwise 14 | - Complete a square by moving forward 50cm, then rotating 90º and repeating 4 times 15 | - Land 16 | 17 | Be sure to add sufficient delays into your solution to allow the drone to complete all the moves. Add a new group to the dashboard and then create a button to allow you to start the mission from the dashboard. 18 | 19 | You can import a [sample solution](/flows/solutions/part6_solution.json) if you need help or want to compare your solution with a sample solution. 20 | 21 | ![Tello Mission Starter flow](/docs/screenshots/NodeRED-Tello-Missions-Starter-flow.png?raw=true "Tello Missions Starter flow") 22 | 23 | --- 24 | 25 | [Home](/README.md) | [Node-RED](/docs/PART1.md) | [Setup](/docs/PART2.md) | [Commands](/docs/PART3.md) | [Dashboard](/docs/PART4.md) | [Telemetry](/docs/PART5.md) | **Mission** | [Pictures](/docs/PART7.md) | [Visual Recognition](/docs/PART8.md) | [Custom Classifier](/docs/PART9.md) 26 | 27 | --- 28 | -------------------------------------------------------------------------------- /docs/PART2.md: -------------------------------------------------------------------------------- 1 | # Install Node-RED 2 | 3 | Before programming the Tello using Node-RED, several pre-requiste software packages need to be installed on your laptop or workstation. 4 | 5 | To control the drone you need to connect your laptop or workstation to the drone WiFi access point, so Node-RED needs to be running locally, rather than in the cloud. 6 | 7 | - [Node.js Installation Instructions](https://nodejs.org/en/download/) 8 | - [Node-RED Installation Instructions](https://nodered.org/docs/getting-started/installation) 9 | 10 | Once Node-RED is installed, several additional Node-RED nodes will be necessary. Install the following nodes using the [Node-RED Manage Palette](https://github.com/binnes/esp8266Workshop/blob/master/en/part3/NODERED.md#step-3---how-to-install-additional-node-red-nodes) or via the npm command line: 11 | 12 | - [node-red-dashboard](https://flows.nodered.org/node/node-red-dashboard) - A set of dashboard nodes for Node-RED 13 | - [node-red-node-watson](https://flows.nodered.org/node/node-red-node-watson) - A collection of Node-RED nodes for IBM Watson services 14 | - [node-red-node-base64](https://flows.nodered.org/node/node-red-node-base64) - A Node-RED node to encode and decode base64 formatted messages. Required to display pictures taken by the Tello camera. 15 | 16 | Learn how to [import flows](https://github.com/binnes/esp8266Workshop/blob/master/en/part3/NODERED.md#step-4---how-to-import-a-prebuilt-flow-from-github) 17 | into your Node-RED editor. 18 | 19 | --- 20 | 21 | [Home](/README.md) | [Node-RED](/docs/PART1.md) | **Setup** | [Commands](/docs/PART3.md) | [Dashboard](/docs/PART4.md) | [Telemetry](/docs/PART5.md) | [Mission](/docs/PART6.md) | [Pictures](/docs/PART7.md) | [Visual Recognition](/docs/PART8.md) | [Custom Classifier](/docs/PART9.md) 22 | 23 | --- 24 | -------------------------------------------------------------------------------- /docs/PART3.md: -------------------------------------------------------------------------------- 1 | # Send SDK commands to the Tello drone 2 | 3 | Now that Node-RED is setup and configured we will start to send commands to the drone. If you have a new Tello Drone, you must use the Tello mobile app at least once to activate the drone before using the SDK programming toolkit. The SDK documentation, available from [the Ryze downloads page](https://www.ryzerobotics.com/tello-edu/downloads) details all the available commands. 4 | 5 | The video below shows how commands are sent using the UDP node and also shows how the starter flow was created. 6 | 7 | <- Add video here -> 8 | 9 | ## Task 10 | 11 | Begin with the [Part3 starter flow](/flows/starter/part3_starter.json) and add an inject and change nodes to enable the following commands: 12 | 13 | - emergency 14 | - up 50cm 15 | - down 50cm 16 | - right 50cm 17 | - left 50cm 18 | - forward 50cm 19 | - back 50cm 20 | - rotate clockwise 90º 21 | - rotate counter-clockwise 90º 22 | - rotate clockwise 360º 23 | - flip forward 24 | 25 | When you need to pass a parameter for a command, ensure you use the property **msg.payload.tellovalue** so the **format output message** node will generate the correct command to send to the drone. 26 | 27 | ![Tello Send SDK Commands Starter flow](/docs/screenshots/NodeRED-Tello-Commands-Starter-flow.png?raw=true "Tello Send SDK Commands Starter flow") 28 | 29 | ### Solution 30 | 31 | There is a [solution flow](/flows/solutions/part3_solution.json) available if you need help or want to check your solution. 32 | 33 | ![Tello Send SDK Commands Solution flow](/docs/screenshots/NodeRED-Tello-Commands-Solution-flow.png?raw=true "Tello Send SDK Commands Solution flow") 34 | 35 | --- 36 | 37 | [Home](/README.md) | [Node-RED](/docs/PART1.md) | [Setup](/docs/PART2.md) | **Commands** | [Dashboard](/docs/PART4.md) | [Telemetry](/docs/PART5.md) | [Mission](/docs/PART6.md) | [Pictures](/docs/PART7.md) | [Visual Recognition](/docs/PART8.md) | [Custom Classifier](/docs/PART9.md) 38 | 39 | --- 40 | -------------------------------------------------------------------------------- /docs/PART4.md: -------------------------------------------------------------------------------- 1 | # Build a Node-RED dashboard 2 | 3 | In Part 3 you constructed a Node-RED flow to send commands to the drone. In this Part 4 exercise, we will create a Node-RED Dashboard with buttons that send the SDK commands. Recall in Part 1, you installed the node-red-dashboard nodes and you should find the Node-RED Dashboard nodes in your palette. 4 | 5 | The video below shows how button nodes can be configured to send the SDK commands to the drone. 6 | 7 | <- Add video here -> 8 | 9 | ## Task 10 | 11 | Begin with the [Part4 starter flow](/flows/starter/part4_starter.json) and add button nodes to the following commands: 12 | 13 | - emergency 14 | - up 50cm 15 | - down 50cm 16 | - right 50cm 17 | - left 50cm 18 | - forward 50cm 19 | - back 50cm 20 | - rotate clockwise 90º 21 | - rotate counter-clockwise 90º 22 | - rotate clockwise 360º 23 | - flip forward 24 | 25 | ![Tello Control Dashboard Starter flow](/docs/screenshots/NodeRED-Tello-Dashboard-Starter-flow.png?raw=true "Tello Control Dashboard Starter flow") 26 | 27 | Launch the Node-RED Dashboard by turning to the Dashboard tab in the right menu and then click on the launch button. 28 | 29 | ![Tello Controls Dashboard Buttons](/docs/screenshots/NodeRED-Tello-Controls-Dashboard-Starter.png?raw=true "Tello Controls Dashboard Starter") 30 | 31 | 32 | ### Solution 33 | 34 | There is a [solution flow](/flows/solutions/part4_solution.json) available if you need help or want to check your solution. 35 | 36 | ![Tello Control Dashboard Solution flow](/docs/screenshots/NodeRED-Tello-Dashboard-Solution-flow.png?raw=true "Tello Control Dashboard Solution flow") 37 | 38 | ![Tello Controls Dashboard Buttons](/docs/screenshots/NodeRED-Tello-Controls-Dashboard-Solution.png?raw=true "Tello Controls Dashboard Solution") 39 | --- 40 | 41 | [Home](/README.md) | [Node-RED](/docs/PART1.md) | [Setup](/docs/PART2.md) | [Commands](/docs/PART3.md) | **Dashboard** | [Telemetry](/docs/PART5.md) | [Mission](/docs/PART6.md) | [Pictures](/docs/PART7.md) | [Visual Recognition](/docs/PART8.md) | [Custom Classifier](/docs/PART9.md) 42 | 43 | --- 44 | -------------------------------------------------------------------------------- /docs/PART8.md: -------------------------------------------------------------------------------- 1 | # Use Visual Recognition on drone pictures 2 | 3 | ### Alert - **[Watson Visual Recognition service](https://cloud.ibm.com/docs/visual-recognition?topic=visual-recognition-index) has been discontinued** 4 | The steps in this section are no longer available. 5 | 6 | The Tello has a low level protocol that allows you to take pictures. This flow sends the take picture command and receives / reconstructs the picture data from the Tello. Then it calls the Watson Visual Recognition service to classify what is in the image. 7 | 8 | ## Task 9 | ### Connect to the IBM Cloud 10 | Use an ethernet cable to hardwire your laptop to your network so that it can reach the Internet. Connect over WiFi to the Tello drone. 11 | 12 | ### Create a Watson Visual Recognition service instance 13 | - Log into [IBM Cloud](http://cloud.ibm.com) 14 | - Create a [Watson Visual Recognition service](https://cloud.ibm.com/catalog/services/visual-recognition) 15 | - Returned to the [IBM Cloud Resources Dashboard](https://cloud.ibm.com/resources) 16 | - Click on your Watson Visual Recognition instance 17 | - Copy the Watson Visual Recognition API key to your clipboard. 18 | 19 | ![Watson Visual Recognition API Key](/docs/screenshots/Watson-Visual-Recognition-APIkey.png?raw=true "Watson Visual Recognition API Key") 20 | 21 | ### Configure your Node-RED Flow to use Watson visual recognition 22 | - Return to your Node-RED browser window 23 | - Recall that in [Setup / Part 2](/docs/PART2.md) you installed the **node-red-node-watson** nodes which provide a collection of Node-RED nodes for IBM Watson services 24 | - Import the [solution flow](/flows/solutions/part8_solution.json) 25 | - Double-click on the **visual recognition** node 26 | - Paste the API key from the clipboard into the Visual Recognition node 27 | - Click on the Done button 28 | - See screenshot: 29 | 30 | ![Tello Watson Visual Recognition API Key](/docs/screenshots/NodeRED-Tello-VisualRecognition-APIkey.png?raw=true "Tello Watson Visual Recognition API Key") 31 | 32 | ### Solution 33 | 34 | There is a [solution flow](/flows/solutions/part8_solution.json) available if you need help or want to check your solution. 35 | 36 | ![Tello Pictures Visual Recognition Dashboard Solution flow](/docs/screenshots/NodeRED-Tello-VisualRecognition-Solution-flow.png?raw=true "Tello Pictures Visual Recognition Dashboard Solution flow") 37 | 38 | Launch the Node-RED Dashboard by turning to the Dashboard tab in the right menu and then click on the launch button. 39 | 40 | ![Tello Pictures Visual Recognition Dashboard Solution flow](/docs/screenshots/NodeRED-Tello-VisualRecognition-Solution.png?raw=true "Tello Pictures Visual Recognition Dashboard Solution") 41 | 42 | --- 43 | 44 | [Home](/README.md) | [Node-RED](/docs/PART1.md) | [Setup](/docs/PART2.md) | [Commands](/docs/PART3.md) | [Dashboard](/docs/PART4.md) | [Telemetry](/docs/PART5.md) | [Mission](/docs/PART6.md) | [Pictures](/docs/PART7.md) | **Visual Recognition** | [Custom Classifier](/docs/PART9.md) 45 | 46 | --- 47 | -------------------------------------------------------------------------------- /docs/PART9.md: -------------------------------------------------------------------------------- 1 | # Fly the Tello drone with custom Visual Recognition of drone pictures 2 | 3 | ### Alert - **[Watson Visual Recognition service](https://cloud.ibm.com/docs/visual-recognition?topic=visual-recognition-index) has been discontinued** 4 | The steps in this section are no longer available. 5 | 6 | Now that the Tello is taking pictures and classifying images using the default classifier, in this final challenge the Tello will fly, take pictures, classify images using a custom classifier. 7 | 8 | ## Task 9 | 10 | ### Train a Watson Visual Recogition Custom Classifier 11 | 12 | Follow the instructions in this [tutorial](https://developer.ibm.com/tutorials/detect-wildfire-damaged-homes-using-drone-images-watson-visual-recognition/) to train a Watson Visual Recognition custom classifier. You might take drone images of balls in your yard, your dog playing in your yard, leaves in your gutters, bird nests in trees. Anything your Tello Drone might see. 13 | 14 | ### Watson Visual Recogition Custom Classifier Model ID 15 | 16 | - Train a Watson Visual Recogition Custom Classifier using images of your choice. 17 | - After training has completed, turn to the Project Asset Visual Recognition model **Overview** tab. 18 | - Copy the **Model ID** to your clipboard. 19 | 20 | 21 | ![Watson Visual Recognition Custom Classifier Model ID](/docs/screenshots/WatsonStudioVisualRecognitionCustomClassifier.png?raw=true "Watson Visual Recognition Custom Classifier") 22 | 23 | ### Configure your Node-RED Flow to fly your Tello while taking pictures and classifying images 24 | 25 | - Return to your Node-RED browser window 26 | - Import the [solution flow](/flows/solutions/part9_solution.json) 27 | - Turn on your drone and connect to the drone Wifi 28 | - Manually press the **TELLO_LOW_LEVEL_CONNECT** inject node. 29 | - Turn to the Node-RED Dashboard - Tello Camera Dashboard. 30 | - Paste the Model ID into the dashboard 31 | - Press the button to start taking pictures every 10 seconds. 32 | - Take-Off 33 | - See screenshot: 34 | 35 | ![Tello Custom Classifier Visual Recognition Dashboard Solution](/docs/screenshots/NodeRED-Tello-CustomClassifierVisualRecognition-Dashboard.png?raw=true "Tello Custom Classifier Visual Recognition Dashboard Solution") 36 | 37 | ### Solution 38 | 39 | There is a [solution flow](/flows/solutions/part9_solution.json) available if you need help or want to check your solution. 40 | 41 | ![Tello Custom Classifier Visual Recognition Dashboard Solution flow](/docs/screenshots/NodeRED-Tello-CustomClassifierVisualRecognition-Solution-flow.png?raw=true "Tello Custom Classifier Visual Recognition Dashboard Solution flow") 42 | 43 | Launch the Node-RED Dashboard by turning to the Dashboard tab in the right menu and then click on the launch button. 44 | 45 | ![Tello Custom Classifier Visual Recognition Dashboard Solution Developer Selfie](/docs/screenshots/NodeRED-Tello-DeveloperSelfie.png?raw=true "Tello Custom Classifier Visual Recognition Dashboard Solution") 46 | 47 | --- 48 | 49 | [Home](/README.md) | [Node-RED](/docs/PART1.md) | [Setup](/docs/PART2.md) | [Commands](/docs/PART3.md) | [Dashboard](/docs/PART4.md) | [Telemetry](/docs/PART5.md) | [Mission](/docs/PART6.md) | [Pictures](/docs/PART7.md) | [Visual Recognition](/docs/PART8.md) | **Custom Classifier** 50 | 51 | --- 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node-RED-Tello-Control 2 | 3 | Node-RED flows to control the Ryze / DJI Tello Drone 4 | 5 | This repository contains Node-RED flow examples that demonstrate how to control 6 | the [DJI Drone](https://www.ryzerobotics.com/tello) using the [Node-RED visual editor](http://nodered.org). 7 | 8 | ## Introduction 9 | 10 | Flying the Tello drone with the Android/iOS mobile apps is amazing fun but the Tello has another fantastic feature. 11 | The Tello includes a programming interface that developers can use to send commands to control the drone. Programming 12 | APIs can also receive telemetry and video stream data from the Tello. Ryze and DJI provide 13 | [SDK programming documentation](https://www.ryzerobotics.com/tello/downloads) to help developers learn how to program 14 | the drone. Tello programming has grown so popular that they have released a [Tello EDU](https://www.ryzerobotics.com/tello-edu) 15 | version that has additional [SDK documentation](https://www.ryzerobotics.com/tello-edu/downloads). 16 | An entire open source community has formed around programming the Tello. If you are a developer, a good place to start is the 17 | [TelloPilots](https://tellopilots.com/) forums. There are dozens of [github repositories](https://github.com/topics/tello) 18 | that have programming examples written in [Python](https://github.com/damiafuentes/DJITelloPy), 19 | [Go](https://github.com/SMerrony/tello), [Swift](https://github.com/tranchis/TelloSwift) and 20 | [Node.js](https://github.com/SovGVD/nodetello). This **Node-RED implementation** was inspired by the 21 | [Tello Scratch node.js example](https://dl-cdn.ryzerobotics.com/downloads/tello/0222/Tello+Scratch+Readme.pdf). 22 | 23 | ## How this git repository is organised 24 | 25 | To help you get familiar with Node-RED and how to control the Tello drone this git repository is split into a number of sections, where you have tasks to complete in each section. If you just want to have fun with the drone and prefer not to do the tasks then jump into the flows folder and use the solutions. 26 | 27 | ## Tasks 28 | 29 | 1. [Learn about Node-RED](/docs/PART1.md) 30 | 2. [Install Node-RED](/docs/PART2.md) 31 | 3. [Send SDK commands to the drone](/docs/PART3.md) 32 | 4. [Build a Node-RED dashboard](/docs/PART4.md) 33 | 5. [Receive telemetry data from the drone](/docs/PART5.md) 34 | 6. [Create missions for your drone](/docs/PART6.md) 35 | 7. [Take pictures](/docs/PART7.md) 36 | 37 | As a reminder, **Safety First!** Take caution when flying your drone. Fly your drone indoors at your own risk. 38 | Also, be respectful of your neighbors and public property when flying your drone outdoors. When recording video 39 | and taking pictures, be mindful of other people's privacy. Obey all FAA regulatory restrictions posted about UAV 40 | flight prohibitions. 41 | 42 | ### Authors 43 | 44 | - [John Walicki](https://github.com/johnwalicki/) 45 | - [Markus Van Kempen](https://github.com/markusvankempen) 46 | - [Brian Innes](https://github.com/binnes) 47 | 48 | ___ 49 | 50 | Enjoy! Give us [feedback](https://github.com/johnwalicki/Node-RED-Tello-Control/issues) if you have suggestions on how to improve this tutorial. 51 | 52 | ## License 53 | 54 | This tutorial is licensed under the Apache Software License, Version 2. Separate third party code objects invoked within this code pattern are licensed by their respective providers pursuant to their own separate licenses. Contributions are subject to the [Developer Certificate of Origin, Version 1.1 (DCO)](https://developercertificate.org/) and the [Apache Software License, Version 2](http://www.apache.org/licenses/LICENSE-2.0.txt). 55 | 56 | [Apache Software License (ASL) FAQ](http://www.apache.org/foundation/license-faq.html#WhatDoesItMEAN) 57 | -------------------------------------------------------------------------------- /docs/PART5.md: -------------------------------------------------------------------------------- 1 | # Receive telemetry data from the drone 2 | In Part 4 you constructed a Node-RED Dashboard flow to send commands to the drone. In this Part 5 exercise, we will create a Node-RED Dashboard with gauges and charts to display telemetry data from the drone. 3 | 4 | ## Receiving Tello Telemetry Data 5 | 6 | Open port 8890/udp in your laptop firewall to receive Tello telemetry data. 7 | If you want to turn on the video stream, you will need to open port 11111/udp. 8 | Send commands and receive responses from the drone on port 8889/udp. 9 | 10 | ### Linux 11 | ``` 12 | $ sudo firewall-cmd --permanent --add-port=8890/udp 13 | $ sudo firewall-cmd --permanent --add-port=8889/udp 14 | $ sudo firewall-cmd --permanent --add-port=11111/udp 15 | ``` 16 | Then reload the firewall to effect the changes: 17 | ``` 18 | $ sudo firewall-cmd --reload 19 | ``` 20 | If you want to list open ports, run this command: 21 | ``` 22 | $ sudo firewall-cmd --permanent --list-ports 23 | ``` 24 | 25 | ### Mac OSX 26 | On Mac, use System Preferences to open the ports. 27 | - Open System Preferences > Security & Privacy > Firewall > Firewall Options 28 | - Click the + / Add button 29 | - Choose 'node' application from the Applications folder and click Add. 30 | - Ensure that the option next to the application is set to Allow incoming connections. 31 | - Click OK. 32 | 33 | ## Telemetry Data 34 | The drone will send telemetry data to port 8890. The **Parse Status into an object** change node uses a JSONata expression to parse the telemetry data into an JSON Object. 35 | 36 | - **pitch** : the degree of the attitude pitch. 37 | - **roll** : the degree of the attitude roll. 38 | - **yaw** : the degree of the attitude yaw. 39 | - **vgx** : the speed of the “x” axis. 40 | - **vgy** : the speed of the “y” axis. 41 | - **vgz** : the speed of the “z” axis. 42 | - **templ** : the lowest temperature in degree Celsius. 43 | - **temph** : the highest temperature in degree Celsius 44 | - **tof** : the time of flight distance in cm. 45 | - **h** : the height in cm. 46 | - **bat** : the percentage of the current battery level. 47 | - **baro** : the barometer measurement in cm. 48 | - **time** : the amount of time the motor has been used. 49 | - **agx** : the acceleration of the “x” axis. 50 | - **agy** : the acceleration of the “y” axis. 51 | - **agz** : the acceleration of the “z” axis. 52 | 53 | JSON Object example output: 54 | ``` 55 | {"pitch":7,"roll":33,"yaw":-29,"vgx":0,"vgy":0,"vgz":0,"templ":61,"temph":62,"tof":10,"h":0,"bat":43,"baro":39.96,"time":22,"agx":307,"agy":-324,"agz":-930} 56 | ``` 57 | 58 | ## Task 59 | 60 | Begin with the [Part5 starter flow](/flows/starter/part5_starter.json) and add gauge nodes to display telemetry: 61 | 62 | - Temp 63 | - Barometer 64 | - Accel X 65 | - Accel Y 66 | - Accel Z 67 | - Speed X 68 | - Speed Y 69 | - Speed Z 70 | 71 | Add a Chart node to display 72 | - Drone Height 73 | 74 | Add a Text node to display 75 | - Flight Time 76 | 77 | ![Tello Telemetry Dashboard Starter flow](/docs/screenshots/NodeRED-Tello-Telemetry-Starter-flow.png?raw=true "Tello Telemetry Starter flow") 78 | 79 | ### Solution 80 | 81 | There is a [solution flow](/flows/solutions/part5_solution.json) available if you need help or want to check your solution. 82 | 83 | ![Tello Telemetry Dashboard Solution flow](/docs/screenshots/NodeRED-Tello-Telemetry-Solution-flow.png?raw=true "Tello Telemetry Dashboard Solution flow") 84 | 85 | Launch the Node-RED Dashboard by turning to the Dashboard tab in the right menu and then click on the launch button. 86 | 87 | ![Tello Telemetry Dashboard Gauges](/docs/screenshots/NodeRED-Tello-Telemetry-Dashboard-Solution.png?raw=true "Tello Telemetry Dashboard Solution") 88 | --- 89 | 90 | [Home](/README.md) | [Node-RED](/docs/PART1.md) | [Setup](/docs/PART2.md) | [Commands](/docs/PART3.md) | [Dashboard](/docs/PART4.md) | **Telemetry** | [Mission](/docs/PART6.md) | [Pictures](/docs/PART7.md) | [Visual Recognition](/docs/PART8.md) | [Custom Classifier](/docs/PART9.md) 91 | 92 | --- 93 | -------------------------------------------------------------------------------- /TOBEREMOVED/removedFromREADME.md: -------------------------------------------------------------------------------- 1 | # TO BE REMOVED OR REFACTORED INTO DOCS FOLDER CONTENT 2 | 3 | This content has been removed from the readme.md file. Rather than deleting it, it has been temporarily moved here until it can be reused. 4 | 5 | ## Node-RED flows in this repository 6 | 7 | ### Send Commands to a Tello Drone 8 | 9 | - This [flow](/flows/nodered-tello-controls.json) demonstrates how to send individual commands to the Tello Drone. 10 | - Each **inject** node, will send a single command to the Tello. Units are in centimeters or degrees. 11 | - A list of commands can be found in the [Tello SDK documentation](https://dl-cdn.ryzerobotics.com/downloads/Tello/Tello%20SDK%202.0%20User%20Guide.pdf) 12 | 13 | ![Tello Control Flow](/docs/screenshots/NodeRED-Tello-Controls-flow.png?raw=true "Tello Control flow") 14 | 15 | ___ 16 | 17 | ### Execute a Pre-Programmed Flight Plan 18 | 19 | - This [flow](/flows/nodered-tello-missions.json) sends a pre-programmed flight plan to the Tello Drone 20 | - Edit the Template nodes to construct your own pre-programmed Tello path 21 | - Double click on the Template nodes to enter the list of Tello commands 22 | - Recall that the SDK pdf has a list of valid commands 23 | - Each line should contain the following instructions: 24 | *command*,*distance (cm)|rotation degrees*,*optionally the delay time (milliseconds) until next command is sent* 25 | eg. 26 | ```text 27 | command,0,250 28 | takeoff,0 29 | up,25,5000 30 | cw,90 31 | land,0 32 | ``` 33 | - The command *command* initializes the drone to receive instructions. 34 | - A *0* for the distance just tells the flow that no units are necessary and is only required if you want to set a delay. 35 | - Wire the Tello command set that you want to execute into the **Initialize Mission** and **Split** nodes. 36 | - Start the mission by pressing the **inject** node labeled **Run Mission** 37 | - If the Tello goes out of control, click on the **Abort Mayday Abort** inject node. It will send a *emergency* command to the Tello to immediately stop the propellers. 38 | 39 | ![Tello Missions Flow](/docs/screenshots/NodeRED-Tello-Missions-flow.png?raw=true "Tello Missions flow") 40 | 41 | ___ 42 | 43 | ### Node-RED Tello Controls Dashboard Example 44 | 45 | - This [flow](/flows/nodered-tello-controls-dashboard.json) builds a Node-RED dashboard of commands that control the drone 46 | - The Inject nodes from the first flow have been replaced with Node-RED dashboard **Button** nodes 47 | - Node-RED dashboard **Button** nodes can be customized with colors and Font Awesome icons 48 | - Each Node-RED dashboard group contains a column of related Tello commands 49 | - Each **Flight Path** button sends the Tello command and 50cm distance - which is a reasonable compromise for indoor testing but could be extended for outdoor flight 50 | - The **Rotation** buttons send the specified degrees rotation in the clockwise or counter-clockwise directions of the button icon 51 | - The **Flips!** buttons flip the drone in the direction specified 52 | 53 | ![Tello Controls Dashboard Buttons](/docs/screenshots/NodeRED-Tello-Controls-Dashboard.png?raw=true "Tello Telemetry Dashboard buttons") 54 | ![Tello Controls Dashboard Flow](/docs/screenshots/NodeRED-Tello-Controls-Dash-flow.png?raw=true "Tello Controls Dashboard flow") 55 | 56 | ___ 57 | 58 | ### Node-RED Tello Telemetry Dashboard Example - Experimental 59 | 60 | - This [flow](/flows/nodered-tello-telemetry.json) reads the telemetry data from the drone and displays some of the data in a Node-RED Dashboard. 61 | - The complete command set of telemetry data has not been fulled documented by Ryze. 62 | - This flow is experimental and does not fully function. Patches are welcome. 63 | 64 | ![Tello Telemetry Dashboard Gauges](/docs/screenshots/NodeRED-Tello-Telemetry-gauges.png?raw=true "Tello Telemetry Dashboard gauges") 65 | ![Tello Telemetry Dashboard Flow](/docs/screenshots/NodeRED-Tello-Telemetry-flow.png?raw=true "Tello Telemetry Dashboard flow") -------------------------------------------------------------------------------- /flows/starter/part3_starter.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "46d53d66.69f3cc", 4 | "type": "tab", 5 | "label": "Tello SDK commands", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "1cb2b9b9.e77ace", 11 | "type": "udp out", 12 | "z": "46d53d66.69f3cc", 13 | "name": "", 14 | "addr": "192.168.10.1", 15 | "iface": "", 16 | "port": "8889", 17 | "ipv": "udp4", 18 | "outport": "52955", 19 | "base64": false, 20 | "multicast": "false", 21 | "x": 590, 22 | "y": 200, 23 | "wires": [] 24 | }, 25 | { 26 | "id": "88548e31.2e5e88", 27 | "type": "debug", 28 | "z": "46d53d66.69f3cc", 29 | "name": "", 30 | "active": true, 31 | "console": "false", 32 | "complete": "false", 33 | "x": 550, 34 | "y": 160, 35 | "wires": [] 36 | }, 37 | { 38 | "id": "90bc3fbe.50d47", 39 | "type": "comment", 40 | "z": "46d53d66.69f3cc", 41 | "name": "1 - Connect to TELLO-NNNNNN WiFi", 42 | "info": "", 43 | "x": 200, 44 | "y": 100, 45 | "wires": [] 46 | }, 47 | { 48 | "id": "f17f9da4.da9ce", 49 | "type": "link in", 50 | "z": "46d53d66.69f3cc", 51 | "name": "TelloControls-In", 52 | "links": [ 53 | "3c8f9e99.f65c2a" 54 | ], 55 | "x": 95, 56 | "y": 180, 57 | "wires": [ 58 | [ 59 | "21ca8590.71ad42" 60 | ] 61 | ] 62 | }, 63 | { 64 | "id": "bf6321d4.c76c98", 65 | "type": "udp in", 66 | "z": "46d53d66.69f3cc", 67 | "name": "", 68 | "iface": "", 69 | "port": "52955", 70 | "ipv": "udp4", 71 | "multicast": "false", 72 | "group": "", 73 | "datatype": "utf8", 74 | "x": 140, 75 | "y": 360, 76 | "wires": [ 77 | [ 78 | "7b66039c.d109b4" 79 | ] 80 | ] 81 | }, 82 | { 83 | "id": "7b66039c.d109b4", 84 | "type": "debug", 85 | "z": "46d53d66.69f3cc", 86 | "name": "", 87 | "active": false, 88 | "tosidebar": true, 89 | "console": false, 90 | "tostatus": false, 91 | "complete": "false", 92 | "x": 550, 93 | "y": 360, 94 | "wires": [] 95 | }, 96 | { 97 | "id": "c85199e8.9aa978", 98 | "type": "comment", 99 | "z": "46d53d66.69f3cc", 100 | "name": "Receive Return Strings", 101 | "info": "The UDP input node listens for return values", 102 | "x": 160, 103 | "y": 320, 104 | "wires": [] 105 | }, 106 | { 107 | "id": "5c9acd48.c78bfc", 108 | "type": "comment", 109 | "z": "46d53d66.69f3cc", 110 | "name": "Send Commands", 111 | "info": "This section of the flow formats the Tello command\ninto a single string to be sent to the drone.\n\nFormat is [command] [optional values]\n\nFor a list of Tello SDK commands, visit\nhttps://dl-cdn.ryzerobotics.com/downloads/Tello/Tello%20SDK%202.0%20User%20Guide.pdf", 112 | "x": 160, 113 | "y": 140, 114 | "wires": [] 115 | }, 116 | { 117 | "id": "a906e629.f60c", 118 | "type": "comment", 119 | "z": "46d53d66.69f3cc", 120 | "name": "================================ Send Commands to drone ==========================", 121 | "info": "", 122 | "x": 370, 123 | "y": 60, 124 | "wires": [] 125 | }, 126 | { 127 | "id": "e6c41a97.6994e", 128 | "type": "comment", 129 | "z": "46d53d66.69f3cc", 130 | "name": "================================ Handle incoming messages =========================", 131 | "info": "", 132 | "x": 370, 133 | "y": 280, 134 | "wires": [] 135 | }, 136 | { 137 | "id": "2a71860e.072c02", 138 | "type": "inject", 139 | "z": "46d53d66.69f3cc", 140 | "name": "Land", 141 | "topic": "", 142 | "payload": "true", 143 | "payloadType": "bool", 144 | "repeat": "", 145 | "crontab": "", 146 | "once": false, 147 | "onceDelay": "", 148 | "x": 130, 149 | "y": 480, 150 | "wires": [ 151 | [ 152 | "178a383e.79a6b8" 153 | ] 154 | ] 155 | }, 156 | { 157 | "id": "9a167994.476a88", 158 | "type": "inject", 159 | "z": "46d53d66.69f3cc", 160 | "name": "TakeOff", 161 | "topic": "", 162 | "payload": "true", 163 | "payloadType": "bool", 164 | "repeat": "", 165 | "crontab": "", 166 | "once": false, 167 | "onceDelay": "", 168 | "x": 130, 169 | "y": 520, 170 | "wires": [ 171 | [ 172 | "788037a.55fa048" 173 | ] 174 | ] 175 | }, 176 | { 177 | "id": "61fcc550.043e44", 178 | "type": "delay", 179 | "z": "46d53d66.69f3cc", 180 | "name": "", 181 | "pauseType": "delay", 182 | "timeout": "250", 183 | "timeoutUnits": "milliseconds", 184 | "rate": "1", 185 | "nbRateUnits": "1", 186 | "rateUnits": "second", 187 | "randomFirst": "1", 188 | "randomLast": "5", 189 | "randomUnits": "seconds", 190 | "drop": false, 191 | "x": 150, 192 | "y": 560, 193 | "wires": [ 194 | [ 195 | "f0e47800.b60cf8" 196 | ] 197 | ] 198 | }, 199 | { 200 | "id": "f0e47800.b60cf8", 201 | "type": "change", 202 | "z": "46d53d66.69f3cc", 203 | "name": "TakeOff", 204 | "rules": [ 205 | { 206 | "t": "delete", 207 | "p": "payload", 208 | "pt": "msg" 209 | }, 210 | { 211 | "t": "set", 212 | "p": "payload.tellocmd", 213 | "pt": "msg", 214 | "to": "takeoff", 215 | "tot": "str" 216 | } 217 | ], 218 | "action": "", 219 | "property": "", 220 | "from": "", 221 | "to": "", 222 | "reg": false, 223 | "x": 420, 224 | "y": 560, 225 | "wires": [ 226 | [ 227 | "3c8f9e99.f65c2a" 228 | ] 229 | ] 230 | }, 231 | { 232 | "id": "788037a.55fa048", 233 | "type": "change", 234 | "z": "46d53d66.69f3cc", 235 | "name": "Init", 236 | "rules": [ 237 | { 238 | "t": "delete", 239 | "p": "payload", 240 | "pt": "msg" 241 | }, 242 | { 243 | "t": "set", 244 | "p": "payload.tellocmd", 245 | "pt": "msg", 246 | "to": "command", 247 | "tot": "str" 248 | } 249 | ], 250 | "action": "", 251 | "property": "", 252 | "from": "", 253 | "to": "", 254 | "reg": false, 255 | "x": 410, 256 | "y": 520, 257 | "wires": [ 258 | [ 259 | "61fcc550.043e44", 260 | "3c8f9e99.f65c2a" 261 | ] 262 | ] 263 | }, 264 | { 265 | "id": "178a383e.79a6b8", 266 | "type": "change", 267 | "z": "46d53d66.69f3cc", 268 | "name": "Land", 269 | "rules": [ 270 | { 271 | "t": "delete", 272 | "p": "payload", 273 | "pt": "msg" 274 | }, 275 | { 276 | "t": "set", 277 | "p": "payload.tellocmd", 278 | "pt": "msg", 279 | "to": "land", 280 | "tot": "str" 281 | } 282 | ], 283 | "action": "", 284 | "property": "", 285 | "from": "", 286 | "to": "", 287 | "reg": false, 288 | "x": 410, 289 | "y": 480, 290 | "wires": [ 291 | [ 292 | "3c8f9e99.f65c2a" 293 | ] 294 | ] 295 | }, 296 | { 297 | "id": "3c8f9e99.f65c2a", 298 | "type": "link out", 299 | "z": "46d53d66.69f3cc", 300 | "name": "TelloControls-Out", 301 | "links": [ 302 | "f17f9da4.da9ce" 303 | ], 304 | "x": 695, 305 | "y": 520, 306 | "wires": [] 307 | }, 308 | { 309 | "id": "79c3138.3dac3ec", 310 | "type": "comment", 311 | "z": "46d53d66.69f3cc", 312 | "name": "================================ Send Tello SDK Commands =========================", 313 | "info": "For a list of Tello SDK commands, visit\nhttps://dl-cdn.ryzerobotics.com/downloads/Tello/Tello%20SDK%202.0%20User%20Guide.pdf", 314 | "x": 370, 315 | "y": 440, 316 | "wires": [] 317 | }, 318 | { 319 | "id": "21ca8590.71ad42", 320 | "type": "change", 321 | "z": "46d53d66.69f3cc", 322 | "name": "format outbound message", 323 | "rules": [ 324 | { 325 | "t": "set", 326 | "p": "payload", 327 | "pt": "msg", 328 | "to": "$exists(payload.tellovalue) ? payload.tellocmd & ' ' & payload.tellovalue : payload.tellocmd", 329 | "tot": "jsonata" 330 | } 331 | ], 332 | "action": "", 333 | "property": "", 334 | "from": "", 335 | "to": "", 336 | "reg": false, 337 | "x": 280, 338 | "y": 180, 339 | "wires": [ 340 | [ 341 | "1cb2b9b9.e77ace", 342 | "88548e31.2e5e88" 343 | ] 344 | ] 345 | } 346 | ] 347 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /TOBEREMOVED/nodered-tello-missions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "392633aa.83a6c4", 4 | "type": "tab", 5 | "label": "Tello Missions", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "5e0f28ec.d3106", 11 | "type": "inject", 12 | "z": "392633aa.83a6c4", 13 | "name": "Run Mission", 14 | "topic": "", 15 | "payload": "true", 16 | "payloadType": "bool", 17 | "repeat": "", 18 | "crontab": "", 19 | "once": false, 20 | "onceDelay": 0.1, 21 | "x": 110, 22 | "y": 180, 23 | "wires": [ 24 | [ 25 | "d43186ee.210938", 26 | "f99853e5.f181a", 27 | "b3911c2a.600c18", 28 | "ab56cb43.745fa" 29 | ] 30 | ] 31 | }, 32 | { 33 | "id": "d43186ee.210938", 34 | "type": "template", 35 | "z": "392633aa.83a6c4", 36 | "name": "Custom Path", 37 | "field": "payload", 38 | "fieldType": "msg", 39 | "format": "handlebars", 40 | "syntax": "mustache", 41 | "template": "command,0,1000\ntakeoff,0\ndown,50\ncw,360\nforward,20\nback,20\nccw,360\nright,20\nleft,20\nland,0", 42 | "x": 310, 43 | "y": 240, 44 | "wires": [ 45 | [] 46 | ] 47 | }, 48 | { 49 | "id": "f99853e5.f181a", 50 | "type": "template", 51 | "z": "392633aa.83a6c4", 52 | "name": "Box", 53 | "field": "payload", 54 | "fieldType": "msg", 55 | "format": "handlebars", 56 | "syntax": "mustache", 57 | "template": "command,0,250\ntakeoff,0\nup,50,1000\ndown,50\nforward,100,1000\ncw,90\nforward,100,1000\ncw,90\nforward,100,1000\ncw,90\nforward,100,1000\ncw,90\nland,0", 58 | "x": 290, 59 | "y": 120, 60 | "wires": [ 61 | [] 62 | ] 63 | }, 64 | { 65 | "id": "a0006604.b05fa8", 66 | "type": "comment", 67 | "z": "392633aa.83a6c4", 68 | "name": "3 - Run a Pre-Programmed Mission Path", 69 | "info": "Enter the commands in the \"Path\" template nodes\nas comma separated commands\n\n#1 Always start with\ncommand,0,250\ntakeoff,0\n\n#2 Flight path\ncommand [up,down,right,left,forward, back],distance(cm),time(ms)\n\n#3 Rotation\nc", 70 | "x": 630, 71 | "y": 140, 72 | "wires": [] 73 | }, 74 | { 75 | "id": "b8337bdd.902fc8", 76 | "type": "csv", 77 | "z": "392633aa.83a6c4", 78 | "name": "Split", 79 | "sep": ",", 80 | "hdrin": false, 81 | "hdrout": "", 82 | "multi": "one", 83 | "ret": "\\n", 84 | "temp": "tellocmd,distance,time", 85 | "skip": 0, 86 | "x": 650, 87 | "y": 180, 88 | "wires": [ 89 | [ 90 | "fabeb631.96d04" 91 | ] 92 | ] 93 | }, 94 | { 95 | "id": "81c7d53.0a45128", 96 | "type": "comment", 97 | "z": "392633aa.83a6c4", 98 | "name": "1 - Connect to TELLO-NNNNNN WiFi", 99 | "info": "", 100 | "x": 300, 101 | "y": 40, 102 | "wires": [] 103 | }, 104 | { 105 | "id": "b3911c2a.600c18", 106 | "type": "template", 107 | "z": "392633aa.83a6c4", 108 | "name": "Simple", 109 | "field": "payload", 110 | "fieldType": "msg", 111 | "format": "handlebars", 112 | "syntax": "mustache", 113 | "template": "command,0,250\ntakeoff,0\nup,25,5000\nflip,f\ndown,50,2000\nforward,50,4000\nback,50,4000\nleft,50,4000\nright,50,4000\nccw,360\nland,0", 114 | "x": 290, 115 | "y": 160, 116 | "wires": [ 117 | [] 118 | ] 119 | }, 120 | { 121 | "id": "ab56cb43.745fa", 122 | "type": "template", 123 | "z": "392633aa.83a6c4", 124 | "name": "Flip", 125 | "field": "payload", 126 | "fieldType": "msg", 127 | "format": "handlebars", 128 | "syntax": "mustache", 129 | "template": "command,0,250\ntakeoff,0\nup,20,4000\nflip,r,2000\ndown 20,4000\nland,0", 130 | "x": 290, 131 | "y": 200, 132 | "wires": [ 133 | [ 134 | "10ddf60b.3b81f2" 135 | ] 136 | ] 137 | }, 138 | { 139 | "id": "10d5237f.8bdf95", 140 | "type": "comment", 141 | "z": "392633aa.83a6c4", 142 | "name": "2 - Construct a programmed path", 143 | "info": "", 144 | "x": 290, 145 | "y": 80, 146 | "wires": [] 147 | }, 148 | { 149 | "id": "22032260.f6c12e", 150 | "type": "udp out", 151 | "z": "392633aa.83a6c4", 152 | "name": "", 153 | "addr": "192.168.10.1", 154 | "iface": "", 155 | "port": "8889", 156 | "ipv": "udp4", 157 | "outport": "", 158 | "base64": false, 159 | "multicast": "false", 160 | "x": 1220, 161 | "y": 380, 162 | "wires": [] 163 | }, 164 | { 165 | "id": "eeb16f8.9ec989", 166 | "type": "inject", 167 | "z": "392633aa.83a6c4", 168 | "name": "Abort Mayday Abort", 169 | "topic": "", 170 | "payload": "land", 171 | "payloadType": "str", 172 | "repeat": "", 173 | "crontab": "", 174 | "once": false, 175 | "onceDelay": "", 176 | "x": 130, 177 | "y": 380, 178 | "wires": [ 179 | [ 180 | "7b799182.eec8d8" 181 | ] 182 | ] 183 | }, 184 | { 185 | "id": "7b799182.eec8d8", 186 | "type": "change", 187 | "z": "392633aa.83a6c4", 188 | "name": "Emergency landing!!", 189 | "rules": [ 190 | { 191 | "t": "delete", 192 | "p": "payload", 193 | "pt": "msg" 194 | }, 195 | { 196 | "t": "set", 197 | "p": "payload.tellocmd", 198 | "pt": "msg", 199 | "to": "land", 200 | "tot": "str" 201 | }, 202 | { 203 | "t": "set", 204 | "p": "payload.distance", 205 | "pt": "msg", 206 | "to": "0", 207 | "tot": "str" 208 | } 209 | ], 210 | "action": "", 211 | "property": "", 212 | "from": "", 213 | "to": "", 214 | "reg": false, 215 | "x": 500, 216 | "y": 380, 217 | "wires": [ 218 | [ 219 | "e92845d4.4b7558" 220 | ] 221 | ] 222 | }, 223 | { 224 | "id": "e92845d4.4b7558", 225 | "type": "function", 226 | "z": "392633aa.83a6c4", 227 | "name": "Send Tello Command", 228 | "func": "var telloaction ;\n\nif( msg.payload.distance != \"0\") {\n telloaction = new Buffer( msg.payload.tellocmd + ' '+ msg.payload.distance );\n} else {\n telloaction = new Buffer( msg.payload.tellocmd );\n}\n\nmsg.payload = telloaction;\t\nreturn msg;\n", 229 | "outputs": 1, 230 | "noerr": 0, 231 | "x": 740, 232 | "y": 380, 233 | "wires": [ 234 | [ 235 | "22032260.f6c12e" 236 | ] 237 | ] 238 | }, 239 | { 240 | "id": "64a592a9.2b56f4", 241 | "type": "debug", 242 | "z": "392633aa.83a6c4", 243 | "name": "", 244 | "active": true, 245 | "tosidebar": true, 246 | "console": false, 247 | "tostatus": false, 248 | "complete": "false", 249 | "x": 1190, 250 | "y": 340, 251 | "wires": [] 252 | }, 253 | { 254 | "id": "81bf35ef.aae38", 255 | "type": "change", 256 | "z": "392633aa.83a6c4", 257 | "name": "increment MissionCmd Counter", 258 | "rules": [ 259 | { 260 | "t": "set", 261 | "p": "MissionCmd", 262 | "pt": "flow", 263 | "to": "($flowContext(\"MissionCmd\")+1)", 264 | "tot": "jsonata" 265 | } 266 | ], 267 | "action": "", 268 | "property": "", 269 | "from": "", 270 | "to": "", 271 | "reg": false, 272 | "x": 1250, 273 | "y": 300, 274 | "wires": [ 275 | [ 276 | "14627ff3.529858" 277 | ] 278 | ] 279 | }, 280 | { 281 | "id": "a4a27eaf.e1f818", 282 | "type": "delay", 283 | "z": "392633aa.83a6c4", 284 | "name": "", 285 | "pauseType": "delayv", 286 | "timeout": "5", 287 | "timeoutUnits": "seconds", 288 | "rate": "1", 289 | "nbRateUnits": "1", 290 | "rateUnits": "second", 291 | "randomFirst": "1", 292 | "randomLast": "5", 293 | "randomUnits": "seconds", 294 | "drop": false, 295 | "x": 1020, 296 | "y": 340, 297 | "wires": [ 298 | [ 299 | "81bf35ef.aae38", 300 | "64a592a9.2b56f4", 301 | "22032260.f6c12e" 302 | ] 303 | ] 304 | }, 305 | { 306 | "id": "14627ff3.529858", 307 | "type": "function", 308 | "z": "392633aa.83a6c4", 309 | "name": "Send Individual Tello Commands", 310 | "func": "var Mission = flow.get(\"Mission\");\nvar i = flow.get(\"MissionCmd\");\nvar telloaction ;\nmsg.payload = Mission[i];\n\nif( i < Mission.length ) {\n if( Mission[i].distance != \"0\") {\n telloaction = new Buffer( Mission[i].tellocmd + ' '+ Mission[i].distance );\n } else {\n telloaction = new Buffer( Mission[i].tellocmd );\n }\n\n if( typeof(Mission[i].time) !== 'undefined') {\n msg.delay = Mission[i].time;\n } else {\n msg.delay=5000;\n }\n msg.payload = telloaction;\n return msg;\n} \n\nreturn null;\n", 311 | "outputs": 1, 312 | "noerr": 0, 313 | "x": 1190, 314 | "y": 180, 315 | "wires": [ 316 | [ 317 | "a4a27eaf.e1f818" 318 | ] 319 | ] 320 | }, 321 | { 322 | "id": "10ddf60b.3b81f2", 323 | "type": "change", 324 | "z": "392633aa.83a6c4", 325 | "name": "Initialize Mission", 326 | "rules": [ 327 | { 328 | "t": "delete", 329 | "p": "Mission", 330 | "pt": "flow" 331 | }, 332 | { 333 | "t": "set", 334 | "p": "MissionCmd", 335 | "pt": "flow", 336 | "to": "0", 337 | "tot": "num" 338 | }, 339 | { 340 | "t": "delete", 341 | "p": "John", 342 | "pt": "flow" 343 | } 344 | ], 345 | "action": "", 346 | "property": "", 347 | "from": "", 348 | "to": "", 349 | "reg": false, 350 | "x": 480, 351 | "y": 180, 352 | "wires": [ 353 | [ 354 | "b8337bdd.902fc8" 355 | ] 356 | ] 357 | }, 358 | { 359 | "id": "fabeb631.96d04", 360 | "type": "function", 361 | "z": "392633aa.83a6c4", 362 | "name": "Store Tello Mission Commands", 363 | "func": "// Load the Mission commands into an flow array\nvar Mission = flow.get(\"Mission\") || [];\nMission.push(msg.payload);\nflow.set(\"Mission\", Mission);\n\n// Load all the Mission Commands and exit\nif( msg.parts.index != (msg.parts.count - 1) ) {\n return null ; \n}\n// Continue with the flow once the last Mission command has been stored\nreturn msg;", 364 | "outputs": 1, 365 | "noerr": 0, 366 | "x": 870, 367 | "y": 180, 368 | "wires": [ 369 | [ 370 | "14627ff3.529858", 371 | "b85b1d4f.65fe6" 372 | ] 373 | ] 374 | }, 375 | { 376 | "id": "b85b1d4f.65fe6", 377 | "type": "change", 378 | "z": "392633aa.83a6c4", 379 | "name": "", 380 | "rules": [ 381 | { 382 | "t": "set", 383 | "p": "payload", 384 | "pt": "msg", 385 | "to": "Mission", 386 | "tot": "flow" 387 | } 388 | ], 389 | "action": "", 390 | "property": "", 391 | "from": "", 392 | "to": "", 393 | "reg": false, 394 | "x": 1140, 395 | "y": 120, 396 | "wires": [ 397 | [ 398 | "3b79d66d.143f4a" 399 | ] 400 | ] 401 | }, 402 | { 403 | "id": "3b79d66d.143f4a", 404 | "type": "debug", 405 | "z": "392633aa.83a6c4", 406 | "name": "", 407 | "active": true, 408 | "tosidebar": true, 409 | "console": false, 410 | "tostatus": false, 411 | "complete": "false", 412 | "x": 1330, 413 | "y": 120, 414 | "wires": [] 415 | } 416 | ] 417 | -------------------------------------------------------------------------------- /flows/solutions/part7_solution.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "865602f6.dc2338", 4 | "type": "tab", 5 | "label": "Tello Picture Dash", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "5b0a7d4e.258d2c", 11 | "type": "udp out", 12 | "z": "865602f6.dc2338", 13 | "name": "", 14 | "addr": "192.168.10.1", 15 | "iface": "", 16 | "port": "8889", 17 | "ipv": "udp4", 18 | "outport": "52955", 19 | "base64": false, 20 | "multicast": "false", 21 | "x": 930, 22 | "y": 100, 23 | "wires": [] 24 | }, 25 | { 26 | "id": "d8f0f1c9.9d5b6", 27 | "type": "function", 28 | "z": "865602f6.dc2338", 29 | "name": "Tello Send Cmd", 30 | "func": "var TBL_CRC16 = [\n\t\t\t0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,\n\t\t\t0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,\n\t\t\t0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,\n\t\t\t0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,\n\t\t\t0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,\n\t\t\t0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,\n\t\t\t0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,\n\t\t\t0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,\n\t\t\t0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,\n\t\t\t0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,\n\t\t\t0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,\n\t\t\t0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,\n\t\t\t0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,\n\t\t\t0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,\n\t\t\t0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,\n\t\t\t0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78\n\t\t];\n\t\t\nvar TBL_CRC8 = [\n\t\t\t0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41,\n\t\t\t0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, 0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc,\n\t\t\t0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, 0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62,\n\t\t\t0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, 0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff,\n\t\t\t0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, 0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07,\n\t\t\t0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, 0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a,\n\t\t\t0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, 0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24,\n\t\t\t0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9,\n\t\t\t0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f, 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd,\n\t\t\t0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50,\n\t\t\t0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, 0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee,\n\t\t\t0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1, 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73,\n\t\t\t0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, 0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b,\n\t\t\t0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16,\n\t\t\t0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8,\n\t\t\t0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, 0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35,\n\t\t]\n\n\t\t\nfunction _calcCRC16 (buf, size) {\n\t\tvar i = 0 ;\n\t\tvar seed = 0x3692 ;\n\t\twhile (size > 0) {\n\t\t\tseed = TBL_CRC16[(seed ^ buf[i]) & 0xff] ^ (seed >> 8)\n\t\t\ti++ ;\n\t\t\tsize-- ;\n\t\t} \n\t\treturn seed ;\n\t}\n\nfunction _calcCRC8 (buf, size) {\n\t\tvar i = 0 ;\n\t\tvar seed = 0x77 ;\n\t\twhile (size > 0) {\n\t\t\tseed = TBL_CRC8[(seed ^ buf[i]) & 0xff]\n\t\t\ti++ ;\n\t\t\tsize-- ;\n\t\t}\n\t\treturn seed ;\n\t}\n\n\t\nfunction buildPacket(pacType, cmdID, seq, data) {\n\t\tvar size = 11 + (data?data.length:0);\n\t\tvar bb = Buffer.alloc(size)\n\t\tvar packetTypeInfo = 0x40 | ((pacType & 0x07) <<3);\n\t\tbb.writeUInt8(0xCC,0)\n\t\tbb.writeUInt8((size << 3) & 0xFF ,1)\n\t\tbb.writeUInt8((size >> 5) & 0xFF ,2)\n\t\n \tvar crc8 = _calcCRC8([...bb], 3)\n\t\tbb.writeUInt8(crc8,3);\n\t\tbb.writeUInt8(packetTypeInfo,4);\n\t\tbb.writeUInt16LE(cmdID,5);\n\t\tbb.writeUInt16LE(seq,7);\n\t\t//console.log(\"DBG\", data, len);\n\t\tif (data) {\n\t\t\tfor (var i=0; i= 11) {\n\t\tvar mark = bb.readUInt8(0);\n\t\tif (mark == 0xCC) {\t// start of packet\n\t\t size = bb.readUInt16LE(1) >> 3;\n\t\t var crc8 = bb.readUInt8(3);\n\t\t pacType = bb.readUInt8(4);\n\t\t cmdID = bb.readUInt16LE(5);\n\t\t seqID = bb.readUInt16LE(7);\n\t dataSize = size - 11;\n\t var data = false;\n\t if (dataSize > 0) {\n\t data = bb.slice(9, size-2);\n\t }\n\t var crc16 = bb.readUInt16LE(size - 2);\n\t \n\t ret.cmd = cmdID;\n\t ret.seq = seqID;\n\t ret.pacType= pacType;\n\t ret.dataSize = dataSize;\n\t ret.data = data;\n\t\t} else {\n\t\t ret = bb.toString();\n\t\t}\n\t} else {\n\t ret = bb.toString();\n\t}\n\treturn ret;\n}\n\t\nmsg.payload = parsePacket(msg.payload);\nif (msg.payload.cmd) {\n var tstatus = global.get(\"tellomsg\") | {};\n tstatus.cmdID = msg.payload.cmd;\n tstatus.seqID = msg.payload.seq;\n tstatus.pacType= msg.payload.pacType;\n global.set(\"tellomsg\",tstatus);\n}\nreturn msg;", 113 | "outputs": 1, 114 | "noerr": 0, 115 | "x": 320, 116 | "y": 280, 117 | "wires": [ 118 | [ 119 | "8df549.78793ab8" 120 | ] 121 | ] 122 | }, 123 | { 124 | "id": "9203aa1b.6b2c6", 125 | "type": "debug", 126 | "z": "865602f6.dc2338", 127 | "name": "Otherwise", 128 | "active": false, 129 | "tosidebar": true, 130 | "console": false, 131 | "tostatus": false, 132 | "complete": "true", 133 | "x": 700, 134 | "y": 380, 135 | "wires": [] 136 | }, 137 | { 138 | "id": "a1f903c9.9ca2d", 139 | "type": "comment", 140 | "z": "865602f6.dc2338", 141 | "name": "Receive Return Strings", 142 | "info": "", 143 | "x": 160, 144 | "y": 240, 145 | "wires": [] 146 | }, 147 | { 148 | "id": "43ff55ff.8f10e4", 149 | "type": "function", 150 | "z": "865602f6.dc2338", 151 | "name": "Send Con_Req", 152 | "func": "videoPort = 6038;\nmsg = {};\nmsg.payload = new Buffer(\"conn_req:lh\");\nmsg.payload[9] = videoPort & 0xFF;\nmsg.payload[10] = (videoPort >> 8) & 0xFF;\nglobal.set(\"seqID\", -1);\nreturn msg;", 153 | "outputs": 1, 154 | "noerr": 0, 155 | "x": 700, 156 | "y": 60, 157 | "wires": [ 158 | [ 159 | "5b0a7d4e.258d2c" 160 | ] 161 | ] 162 | }, 163 | { 164 | "id": "4603c589.6ed634", 165 | "type": "inject", 166 | "z": "865602f6.dc2338", 167 | "name": "TELLO_LOW_LEVEL_CONNECT", 168 | "topic": "", 169 | "payload": "true", 170 | "payloadType": "bool", 171 | "repeat": "", 172 | "crontab": "", 173 | "once": false, 174 | "onceDelay": "", 175 | "x": 200, 176 | "y": 60, 177 | "wires": [ 178 | [ 179 | "43ff55ff.8f10e4" 180 | ] 181 | ] 182 | }, 183 | { 184 | "id": "8df549.78793ab8", 185 | "type": "switch", 186 | "z": "865602f6.dc2338", 187 | "name": "", 188 | "property": "payload.cmd", 189 | "propertyType": "msg", 190 | "rules": [ 191 | { 192 | "t": "eq", 193 | "v": "86", 194 | "vt": "num" 195 | }, 196 | { 197 | "t": "eq", 198 | "v": "26", 199 | "vt": "num" 200 | }, 201 | { 202 | "t": "eq", 203 | "v": "53", 204 | "vt": "num" 205 | }, 206 | { 207 | "t": "eq", 208 | "v": "4176", 209 | "vt": "num" 210 | }, 211 | { 212 | "t": "eq", 213 | "v": "98", 214 | "vt": "num" 215 | }, 216 | { 217 | "t": "eq", 218 | "v": "99", 219 | "vt": "num" 220 | }, 221 | { 222 | "t": "eq", 223 | "v": "48", 224 | "vt": "num" 225 | }, 226 | { 227 | "t": "else" 228 | } 229 | ], 230 | "checkall": "true", 231 | "repair": false, 232 | "outputs": 8, 233 | "x": 510, 234 | "y": 280, 235 | "wires": [ 236 | [], 237 | [], 238 | [], 239 | [], 240 | [ 241 | "3f41ea5b.baa436" 242 | ], 243 | [ 244 | "2b644e1c.14938a" 245 | ], 246 | [ 247 | "ef29b4f9.1ae9" 248 | ], 249 | [ 250 | "9203aa1b.6b2c6" 251 | ] 252 | ], 253 | "outputLabels": [ 254 | "FlightStatus", 255 | "WifiStrength", 256 | "LightStrength", 257 | "LogHeader", 258 | "FileSize", 259 | "FileData", 260 | "DoTakePic", 261 | "otherwise" 262 | ] 263 | }, 264 | { 265 | "id": "ef29b4f9.1ae9", 266 | "type": "debug", 267 | "z": "865602f6.dc2338", 268 | "name": "DoTakePic", 269 | "active": false, 270 | "tosidebar": true, 271 | "console": false, 272 | "tostatus": false, 273 | "complete": "true", 274 | "x": 710, 275 | "y": 340, 276 | "wires": [] 277 | }, 278 | { 279 | "id": "aa8ecfdc.aa0128", 280 | "type": "link in", 281 | "z": "865602f6.dc2338", 282 | "name": "TELLO-SND_LOW_LEVEL_CMD", 283 | "links": [ 284 | "d9406e9d.313378" 285 | ], 286 | "x": 555, 287 | "y": 140, 288 | "wires": [ 289 | [ 290 | "d8f0f1c9.9d5b6" 291 | ] 292 | ] 293 | }, 294 | { 295 | "id": "3f41ea5b.baa436", 296 | "type": "function", 297 | "z": "865602f6.dc2338", 298 | "name": "unpack FileSize message", 299 | "func": "// Ignore if not from drone\nif (0x80 & msg.payload.pacType) {\n msg.payload.fileType = msg.payload.data.readInt8(0);\n msg.payload.fileSize = msg.payload.data.readInt32LE(1);\n msg.payload.fileID = msg.payload.data.readInt16LE(5);\n flow.set(\"file\" + msg.payload.fileID, {\"size\" : msg.payload.fileSize, \n \"receivedBytes\" : 0,\n \"imageBytes\" : Buffer.alloc(msg.payload.fileSize),\n \"pieces\" : {}\n });\n return msg;\n}", 300 | "outputs": 1, 301 | "noerr": 0, 302 | "x": 750, 303 | "y": 260, 304 | "wires": [ 305 | [ 306 | "c6da806c.43e838" 307 | ] 308 | ] 309 | }, 310 | { 311 | "id": "c6da806c.43e838", 312 | "type": "change", 313 | "z": "865602f6.dc2338", 314 | "name": "Ack FileSizeMsg", 315 | "rules": [ 316 | { 317 | "t": "set", 318 | "p": "payload", 319 | "pt": "msg", 320 | "to": "{\"TelloCmdId\":98,\"data\":[0],\"packType\":2}", 321 | "tot": "json" 322 | } 323 | ], 324 | "action": "", 325 | "property": "", 326 | "from": "", 327 | "to": "", 328 | "reg": false, 329 | "x": 980, 330 | "y": 260, 331 | "wires": [ 332 | [ 333 | "d9406e9d.313378" 334 | ] 335 | ] 336 | }, 337 | { 338 | "id": "d9406e9d.313378", 339 | "type": "link out", 340 | "z": "865602f6.dc2338", 341 | "name": "", 342 | "links": [ 343 | "aa8ecfdc.aa0128" 344 | ], 345 | "x": 1135, 346 | "y": 280, 347 | "wires": [] 348 | }, 349 | { 350 | "id": "2b644e1c.14938a", 351 | "type": "function", 352 | "z": "865602f6.dc2338", 353 | "name": "Unpack FileData message", 354 | "func": "var fileID = msg.payload.data.readInt16LE(0);\nvar filePiece = msg.payload.data.readInt32LE(2);\nvar fileChunk = msg.payload.data.readInt32LE(6);\nvar chunkLen = msg.payload.data.readInt16LE(10);\nvar file = flow.get(\"file\" + fileID);\nif ((!file) || (\"undefined\" === typeof file.receivedBytes)) {\n msg.payload = {};\n} else {\n msg.payload.data.copy(file.imageBytes, 1024*fileChunk, 12);\n msg.payload = {};\n msg.payload.unpackedHeader = {\"ID\" : fileID, \n \"piece\" : filePiece,\n \"chunk\" : fileChunk,\n \"len\" : chunkLen\n };\n if (file.pieces[filePiece]) {\n if (!file.pieces[filePiece].includes(fileChunk)) {\n file.pieces[filePiece].push(fileChunk);\n file.receivedBytes += chunkLen;\n if (8 == file.pieces[filePiece].length) {\n msg.payload.TelloCmdId = 99;\n msg.payload.packType = 2;\n msg.payload.data = Buffer.alloc(7);\n msg.payload.data.writeInt8(0, 0);\n msg.payload.data.writeInt16LE(fileID, 1);\n msg.payload.data.writeInt32LE(filePiece, 3);\n }\n }\n } else {\n msg.payload = {};\n msg.payload.unpackedHeader = {\"ID\" : fileID, \n \"piece\" : filePiece,\n \"chunk\" : fileChunk,\n \"len\" : chunkLen\n };\n file.pieces[filePiece] = [fileChunk];\n file.receivedBytes += chunkLen;\n }\n msg.payload.unpackedHeader.receivedBytes = file.receivedBytes;\n \n if (file.receivedBytes == file.size) {\n flow.set ([\"file\" + fileID], null);\n msg.payload.TelloCmdId = 99;\n msg.payload.packType = 2;\n msg.payload.data = Buffer.alloc(7);\n msg.payload.data.writeInt8(1, 0);\n msg.payload.data.writeInt16LE(fileID, 1);\n msg.payload.data.writeInt32LE(filePiece, 3);\n \n msg1 = {\"payload\" : {}};\n msg1.payload.TelloCmdId = 100;\n msg1.payload.packType = 1;\n return [[msg, msg1], {\"payload\":file.imageBytes}];\n } else if (msg.payload.TelloCmdId) {\n flow.set(\"file\" + fileID, file);\n return [msg, null];\n } else {\n flow.set(\"file\" + fileID, file);\n return [null, null];\n }\n}", 355 | "outputs": 2, 356 | "noerr": 0, 357 | "x": 760, 358 | "y": 300, 359 | "wires": [ 360 | [ 361 | "d9406e9d.313378" 362 | ], 363 | [ 364 | "7deed9e0.4b1ec" 365 | ] 366 | ], 367 | "outputLabels": [ 368 | "completedPieceAck", 369 | "Received image" 370 | ] 371 | }, 372 | { 373 | "id": "a40bb8cc.dae26", 374 | "type": "ui_template", 375 | "z": "865602f6.dc2338", 376 | "group": "230dc716.b089d", 377 | "name": "Photo", 378 | "order": 2, 379 | "width": "12", 380 | "height": "9", 381 | "format": "
\n\"Watson", 382 | "storeOutMessages": false, 383 | "fwdInMessages": false, 384 | "templateScope": "local", 385 | "x": 710, 386 | "y": 500, 387 | "wires": [ 388 | [] 389 | ] 390 | }, 391 | { 392 | "id": "6376d619.fd6d28", 393 | "type": "change", 394 | "z": "865602f6.dc2338", 395 | "name": "", 396 | "rules": [ 397 | { 398 | "t": "set", 399 | "p": "template", 400 | "pt": "msg", 401 | "to": "
\"Watson", 402 | "tot": "str" 403 | } 404 | ], 405 | "action": "", 406 | "property": "", 407 | "from": "", 408 | "to": "", 409 | "reg": false, 410 | "x": 510, 411 | "y": 500, 412 | "wires": [ 413 | [ 414 | "a40bb8cc.dae26" 415 | ] 416 | ] 417 | }, 418 | { 419 | "id": "22e77fcb.046f6", 420 | "type": "base64", 421 | "z": "865602f6.dc2338", 422 | "name": "Encode", 423 | "x": 260, 424 | "y": 500, 425 | "wires": [ 426 | [ 427 | "6376d619.fd6d28" 428 | ] 429 | ] 430 | }, 431 | { 432 | "id": "eda2f263.8283c8", 433 | "type": "ui_button", 434 | "z": "865602f6.dc2338", 435 | "name": "", 436 | "group": "230dc716.b089d", 437 | "order": 1, 438 | "width": 0, 439 | "height": 0, 440 | "passthru": false, 441 | "label": "Take Picture", 442 | "color": "", 443 | "bgcolor": "", 444 | "icon": "", 445 | "payload": "", 446 | "payloadType": "str", 447 | "topic": "", 448 | "x": 210, 449 | "y": 140, 450 | "wires": [ 451 | [ 452 | "cb99267f.854da8" 453 | ] 454 | ] 455 | }, 456 | { 457 | "id": "7deed9e0.4b1ec", 458 | "type": "link out", 459 | "z": "865602f6.dc2338", 460 | "name": "Picture Arrived", 461 | "links": [ 462 | "d64a7534.24e1e" 463 | ], 464 | "x": 1135, 465 | "y": 320, 466 | "wires": [] 467 | }, 468 | { 469 | "id": "d64a7534.24e1e", 470 | "type": "link in", 471 | "z": "865602f6.dc2338", 472 | "name": "Picture Processing", 473 | "links": [ 474 | "7deed9e0.4b1ec" 475 | ], 476 | "x": 75, 477 | "y": 520, 478 | "wires": [ 479 | [ 480 | "22e77fcb.046f6" 481 | ] 482 | ] 483 | }, 484 | { 485 | "id": "230dc716.b089d", 486 | "type": "ui_group", 487 | "z": "", 488 | "name": "Drone Image", 489 | "tab": "192b6a33.f665ce", 490 | "order": 1, 491 | "disp": true, 492 | "width": "12", 493 | "collapse": false 494 | }, 495 | { 496 | "id": "192b6a33.f665ce", 497 | "type": "ui_tab", 498 | "z": "", 499 | "name": "Tello Camera", 500 | "icon": "fa-tachometer", 501 | "order": 5 502 | } 503 | ] 504 | -------------------------------------------------------------------------------- /flows/solutions/part3_solution.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "46d53d66.69f3cc", 4 | "type": "tab", 5 | "label": "Tello SDK commands", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "1cb2b9b9.e77ace", 11 | "type": "udp out", 12 | "z": "46d53d66.69f3cc", 13 | "name": "", 14 | "addr": "192.168.10.1", 15 | "iface": "", 16 | "port": "8889", 17 | "ipv": "udp4", 18 | "outport": "52955", 19 | "base64": false, 20 | "multicast": "false", 21 | "x": 590, 22 | "y": 200, 23 | "wires": [] 24 | }, 25 | { 26 | "id": "88548e31.2e5e88", 27 | "type": "debug", 28 | "z": "46d53d66.69f3cc", 29 | "name": "", 30 | "active": true, 31 | "console": "false", 32 | "complete": "false", 33 | "x": 550, 34 | "y": 160, 35 | "wires": [] 36 | }, 37 | { 38 | "id": "90bc3fbe.50d47", 39 | "type": "comment", 40 | "z": "46d53d66.69f3cc", 41 | "name": "1 - Connect to TELLO-NNNNNN WiFi", 42 | "info": "", 43 | "x": 200, 44 | "y": 100, 45 | "wires": [] 46 | }, 47 | { 48 | "id": "f17f9da4.da9ce", 49 | "type": "link in", 50 | "z": "46d53d66.69f3cc", 51 | "name": "TelloControls-In", 52 | "links": [ 53 | "3c8f9e99.f65c2a" 54 | ], 55 | "x": 95, 56 | "y": 180, 57 | "wires": [ 58 | [ 59 | "21ca8590.71ad42" 60 | ] 61 | ] 62 | }, 63 | { 64 | "id": "bf6321d4.c76c98", 65 | "type": "udp in", 66 | "z": "46d53d66.69f3cc", 67 | "name": "", 68 | "iface": "", 69 | "port": "52955", 70 | "ipv": "udp4", 71 | "multicast": "false", 72 | "group": "", 73 | "datatype": "utf8", 74 | "x": 140, 75 | "y": 360, 76 | "wires": [ 77 | [ 78 | "7b66039c.d109b4" 79 | ] 80 | ] 81 | }, 82 | { 83 | "id": "7b66039c.d109b4", 84 | "type": "debug", 85 | "z": "46d53d66.69f3cc", 86 | "name": "", 87 | "active": false, 88 | "tosidebar": true, 89 | "console": false, 90 | "tostatus": false, 91 | "complete": "false", 92 | "x": 550, 93 | "y": 360, 94 | "wires": [] 95 | }, 96 | { 97 | "id": "c85199e8.9aa978", 98 | "type": "comment", 99 | "z": "46d53d66.69f3cc", 100 | "name": "Receive Return Strings", 101 | "info": "The UDP input node listens for return values", 102 | "x": 160, 103 | "y": 320, 104 | "wires": [] 105 | }, 106 | { 107 | "id": "5c9acd48.c78bfc", 108 | "type": "comment", 109 | "z": "46d53d66.69f3cc", 110 | "name": "Send Commands", 111 | "info": "This section of the flow formats the Tello command\ninto a single string to be sent to the drone.\n\nFormat is [command] [optional values]\n\nFor a list of Tello SDK commands, visit\nhttps://dl-cdn.ryzerobotics.com/downloads/Tello/Tello%20SDK%202.0%20User%20Guide.pdf", 112 | "x": 160, 113 | "y": 140, 114 | "wires": [] 115 | }, 116 | { 117 | "id": "a906e629.f60c", 118 | "type": "comment", 119 | "z": "46d53d66.69f3cc", 120 | "name": "================================ Send Commands to drone ==========================", 121 | "info": "", 122 | "x": 370, 123 | "y": 60, 124 | "wires": [] 125 | }, 126 | { 127 | "id": "e6c41a97.6994e", 128 | "type": "comment", 129 | "z": "46d53d66.69f3cc", 130 | "name": "================================ Handle incoming messages =========================", 131 | "info": "", 132 | "x": 370, 133 | "y": 280, 134 | "wires": [] 135 | }, 136 | { 137 | "id": "2a71860e.072c02", 138 | "type": "inject", 139 | "z": "46d53d66.69f3cc", 140 | "name": "Land", 141 | "topic": "", 142 | "payload": "true", 143 | "payloadType": "bool", 144 | "repeat": "", 145 | "crontab": "", 146 | "once": false, 147 | "onceDelay": "", 148 | "x": 130, 149 | "y": 480, 150 | "wires": [ 151 | [ 152 | "178a383e.79a6b8" 153 | ] 154 | ] 155 | }, 156 | { 157 | "id": "9a167994.476a88", 158 | "type": "inject", 159 | "z": "46d53d66.69f3cc", 160 | "name": "TakeOff", 161 | "topic": "", 162 | "payload": "true", 163 | "payloadType": "bool", 164 | "repeat": "", 165 | "crontab": "", 166 | "once": false, 167 | "onceDelay": "", 168 | "x": 130, 169 | "y": 520, 170 | "wires": [ 171 | [ 172 | "788037a.55fa048" 173 | ] 174 | ] 175 | }, 176 | { 177 | "id": "61fcc550.043e44", 178 | "type": "delay", 179 | "z": "46d53d66.69f3cc", 180 | "name": "", 181 | "pauseType": "delay", 182 | "timeout": "250", 183 | "timeoutUnits": "milliseconds", 184 | "rate": "1", 185 | "nbRateUnits": "1", 186 | "rateUnits": "second", 187 | "randomFirst": "1", 188 | "randomLast": "5", 189 | "randomUnits": "seconds", 190 | "drop": false, 191 | "x": 150, 192 | "y": 560, 193 | "wires": [ 194 | [ 195 | "f0e47800.b60cf8" 196 | ] 197 | ] 198 | }, 199 | { 200 | "id": "f0e47800.b60cf8", 201 | "type": "change", 202 | "z": "46d53d66.69f3cc", 203 | "name": "TakeOff", 204 | "rules": [ 205 | { 206 | "t": "delete", 207 | "p": "payload", 208 | "pt": "msg" 209 | }, 210 | { 211 | "t": "set", 212 | "p": "payload.tellocmd", 213 | "pt": "msg", 214 | "to": "takeoff", 215 | "tot": "str" 216 | } 217 | ], 218 | "action": "", 219 | "property": "", 220 | "from": "", 221 | "to": "", 222 | "reg": false, 223 | "x": 420, 224 | "y": 560, 225 | "wires": [ 226 | [ 227 | "3c8f9e99.f65c2a" 228 | ] 229 | ] 230 | }, 231 | { 232 | "id": "788037a.55fa048", 233 | "type": "change", 234 | "z": "46d53d66.69f3cc", 235 | "name": "Init", 236 | "rules": [ 237 | { 238 | "t": "delete", 239 | "p": "payload", 240 | "pt": "msg" 241 | }, 242 | { 243 | "t": "set", 244 | "p": "payload.tellocmd", 245 | "pt": "msg", 246 | "to": "command", 247 | "tot": "str" 248 | } 249 | ], 250 | "action": "", 251 | "property": "", 252 | "from": "", 253 | "to": "", 254 | "reg": false, 255 | "x": 410, 256 | "y": 520, 257 | "wires": [ 258 | [ 259 | "61fcc550.043e44", 260 | "3c8f9e99.f65c2a" 261 | ] 262 | ] 263 | }, 264 | { 265 | "id": "178a383e.79a6b8", 266 | "type": "change", 267 | "z": "46d53d66.69f3cc", 268 | "name": "Land", 269 | "rules": [ 270 | { 271 | "t": "delete", 272 | "p": "payload", 273 | "pt": "msg" 274 | }, 275 | { 276 | "t": "set", 277 | "p": "payload.tellocmd", 278 | "pt": "msg", 279 | "to": "land", 280 | "tot": "str" 281 | } 282 | ], 283 | "action": "", 284 | "property": "", 285 | "from": "", 286 | "to": "", 287 | "reg": false, 288 | "x": 410, 289 | "y": 480, 290 | "wires": [ 291 | [ 292 | "3c8f9e99.f65c2a" 293 | ] 294 | ] 295 | }, 296 | { 297 | "id": "3c8f9e99.f65c2a", 298 | "type": "link out", 299 | "z": "46d53d66.69f3cc", 300 | "name": "TelloControls-Out", 301 | "links": [ 302 | "f17f9da4.da9ce" 303 | ], 304 | "x": 655, 305 | "y": 760, 306 | "wires": [] 307 | }, 308 | { 309 | "id": "79c3138.3dac3ec", 310 | "type": "comment", 311 | "z": "46d53d66.69f3cc", 312 | "name": "================================ Send Tello SDK Commands =========================", 313 | "info": "For a list of Tello SDK commands, visit\nhttps://dl-cdn.ryzerobotics.com/downloads/Tello/Tello%20SDK%202.0%20User%20Guide.pdf", 314 | "x": 370, 315 | "y": 440, 316 | "wires": [] 317 | }, 318 | { 319 | "id": "21ca8590.71ad42", 320 | "type": "change", 321 | "z": "46d53d66.69f3cc", 322 | "name": "format outbound message", 323 | "rules": [ 324 | { 325 | "t": "set", 326 | "p": "payload", 327 | "pt": "msg", 328 | "to": "$exists(payload.tellovalue) ? payload.tellocmd & ' ' & payload.tellovalue : payload.tellocmd", 329 | "tot": "jsonata" 330 | } 331 | ], 332 | "action": "", 333 | "property": "", 334 | "from": "", 335 | "to": "", 336 | "reg": false, 337 | "x": 280, 338 | "y": 180, 339 | "wires": [ 340 | [ 341 | "1cb2b9b9.e77ace", 342 | "88548e31.2e5e88" 343 | ] 344 | ] 345 | }, 346 | { 347 | "id": "96042f96.a6f48", 348 | "type": "change", 349 | "z": "46d53d66.69f3cc", 350 | "name": "Up 50cm", 351 | "rules": [ 352 | { 353 | "t": "delete", 354 | "p": "payload", 355 | "pt": "msg" 356 | }, 357 | { 358 | "t": "set", 359 | "p": "payload.tellocmd", 360 | "pt": "msg", 361 | "to": "up", 362 | "tot": "str" 363 | }, 364 | { 365 | "t": "set", 366 | "p": "payload.tellovalue", 367 | "pt": "msg", 368 | "to": "50", 369 | "tot": "str" 370 | } 371 | ], 372 | "action": "", 373 | "property": "", 374 | "from": "", 375 | "to": "", 376 | "reg": false, 377 | "x": 420, 378 | "y": 640, 379 | "wires": [ 380 | [ 381 | "3c8f9e99.f65c2a" 382 | ] 383 | ] 384 | }, 385 | { 386 | "id": "825841d5.c45ea8", 387 | "type": "change", 388 | "z": "46d53d66.69f3cc", 389 | "name": "Right 50cm", 390 | "rules": [ 391 | { 392 | "t": "delete", 393 | "p": "payload", 394 | "pt": "msg" 395 | }, 396 | { 397 | "t": "set", 398 | "p": "payload.tellocmd", 399 | "pt": "msg", 400 | "to": "right", 401 | "tot": "str" 402 | }, 403 | { 404 | "t": "set", 405 | "p": "payload.tellovalue", 406 | "pt": "msg", 407 | "to": "50", 408 | "tot": "str" 409 | } 410 | ], 411 | "action": "", 412 | "property": "", 413 | "from": "", 414 | "to": "", 415 | "reg": false, 416 | "x": 430, 417 | "y": 720, 418 | "wires": [ 419 | [ 420 | "3c8f9e99.f65c2a" 421 | ] 422 | ] 423 | }, 424 | { 425 | "id": "2149370a.de2958", 426 | "type": "change", 427 | "z": "46d53d66.69f3cc", 428 | "name": "Forward 50cm", 429 | "rules": [ 430 | { 431 | "t": "delete", 432 | "p": "payload", 433 | "pt": "msg" 434 | }, 435 | { 436 | "t": "set", 437 | "p": "payload.tellocmd", 438 | "pt": "msg", 439 | "to": "forward", 440 | "tot": "str" 441 | }, 442 | { 443 | "t": "set", 444 | "p": "payload.tellovalue", 445 | "pt": "msg", 446 | "to": "50", 447 | "tot": "str" 448 | } 449 | ], 450 | "action": "", 451 | "property": "", 452 | "from": "", 453 | "to": "", 454 | "reg": false, 455 | "x": 440, 456 | "y": 800, 457 | "wires": [ 458 | [ 459 | "3c8f9e99.f65c2a" 460 | ] 461 | ] 462 | }, 463 | { 464 | "id": "72ce977b.85749", 465 | "type": "change", 466 | "z": "46d53d66.69f3cc", 467 | "name": "Down 50cm", 468 | "rules": [ 469 | { 470 | "t": "delete", 471 | "p": "payload", 472 | "pt": "msg" 473 | }, 474 | { 475 | "t": "set", 476 | "p": "payload.tellocmd", 477 | "pt": "msg", 478 | "to": "down", 479 | "tot": "str" 480 | }, 481 | { 482 | "t": "set", 483 | "p": "payload.tellovalue", 484 | "pt": "msg", 485 | "to": "50", 486 | "tot": "str" 487 | } 488 | ], 489 | "action": "", 490 | "property": "", 491 | "from": "", 492 | "to": "", 493 | "reg": false, 494 | "x": 430, 495 | "y": 680, 496 | "wires": [ 497 | [ 498 | "3c8f9e99.f65c2a" 499 | ] 500 | ] 501 | }, 502 | { 503 | "id": "eba9bdb7.5803e8", 504 | "type": "change", 505 | "z": "46d53d66.69f3cc", 506 | "name": "Left 50cm", 507 | "rules": [ 508 | { 509 | "t": "delete", 510 | "p": "payload", 511 | "pt": "msg" 512 | }, 513 | { 514 | "t": "set", 515 | "p": "payload.tellocmd", 516 | "pt": "msg", 517 | "to": "left", 518 | "tot": "str" 519 | }, 520 | { 521 | "t": "set", 522 | "p": "payload.tellovalue", 523 | "pt": "msg", 524 | "to": "50", 525 | "tot": "str" 526 | } 527 | ], 528 | "action": "", 529 | "property": "", 530 | "from": "", 531 | "to": "", 532 | "reg": false, 533 | "x": 420, 534 | "y": 760, 535 | "wires": [ 536 | [ 537 | "3c8f9e99.f65c2a" 538 | ] 539 | ] 540 | }, 541 | { 542 | "id": "374f5e6c.a1891a", 543 | "type": "change", 544 | "z": "46d53d66.69f3cc", 545 | "name": "Back 50cm", 546 | "rules": [ 547 | { 548 | "t": "delete", 549 | "p": "payload", 550 | "pt": "msg" 551 | }, 552 | { 553 | "t": "set", 554 | "p": "payload.tellocmd", 555 | "pt": "msg", 556 | "to": "back", 557 | "tot": "str" 558 | }, 559 | { 560 | "t": "set", 561 | "p": "payload.tellovalue", 562 | "pt": "msg", 563 | "to": "50", 564 | "tot": "str" 565 | } 566 | ], 567 | "action": "", 568 | "property": "", 569 | "from": "", 570 | "to": "", 571 | "reg": false, 572 | "x": 430, 573 | "y": 840, 574 | "wires": [ 575 | [ 576 | "3c8f9e99.f65c2a" 577 | ] 578 | ] 579 | }, 580 | { 581 | "id": "ae6be809.182f48", 582 | "type": "change", 583 | "z": "46d53d66.69f3cc", 584 | "name": "cw 90º", 585 | "rules": [ 586 | { 587 | "t": "delete", 588 | "p": "payload", 589 | "pt": "msg" 590 | }, 591 | { 592 | "t": "set", 593 | "p": "payload.tellocmd", 594 | "pt": "msg", 595 | "to": "cw", 596 | "tot": "str" 597 | }, 598 | { 599 | "t": "set", 600 | "p": "payload.tellovalue", 601 | "pt": "msg", 602 | "to": "90", 603 | "tot": "str" 604 | } 605 | ], 606 | "action": "", 607 | "property": "", 608 | "from": "", 609 | "to": "", 610 | "reg": false, 611 | "x": 410, 612 | "y": 880, 613 | "wires": [ 614 | [ 615 | "3c8f9e99.f65c2a" 616 | ] 617 | ] 618 | }, 619 | { 620 | "id": "43c61f88.2babb8", 621 | "type": "change", 622 | "z": "46d53d66.69f3cc", 623 | "name": "ccw 90º", 624 | "rules": [ 625 | { 626 | "t": "delete", 627 | "p": "payload", 628 | "pt": "msg" 629 | }, 630 | { 631 | "t": "set", 632 | "p": "payload.tellocmd", 633 | "pt": "msg", 634 | "to": "ccw", 635 | "tot": "str" 636 | }, 637 | { 638 | "t": "set", 639 | "p": "payload.tellovalue", 640 | "pt": "msg", 641 | "to": "90", 642 | "tot": "str" 643 | } 644 | ], 645 | "action": "", 646 | "property": "", 647 | "from": "", 648 | "to": "", 649 | "reg": false, 650 | "x": 420, 651 | "y": 920, 652 | "wires": [ 653 | [ 654 | "3c8f9e99.f65c2a" 655 | ] 656 | ] 657 | }, 658 | { 659 | "id": "72fd932.25eb46c", 660 | "type": "change", 661 | "z": "46d53d66.69f3cc", 662 | "name": "Flip f", 663 | "rules": [ 664 | { 665 | "t": "delete", 666 | "p": "payload", 667 | "pt": "msg" 668 | }, 669 | { 670 | "t": "set", 671 | "p": "payload.tellocmd", 672 | "pt": "msg", 673 | "to": "flip", 674 | "tot": "str" 675 | }, 676 | { 677 | "t": "set", 678 | "p": "payload.tellovalue", 679 | "pt": "msg", 680 | "to": "f", 681 | "tot": "str" 682 | } 683 | ], 684 | "action": "", 685 | "property": "", 686 | "from": "", 687 | "to": "", 688 | "reg": false, 689 | "x": 410, 690 | "y": 1000, 691 | "wires": [ 692 | [ 693 | "3c8f9e99.f65c2a" 694 | ] 695 | ] 696 | }, 697 | { 698 | "id": "f0d9c55e.cc55f8", 699 | "type": "inject", 700 | "z": "46d53d66.69f3cc", 701 | "name": "Up 50cm", 702 | "topic": "", 703 | "payload": "true", 704 | "payloadType": "bool", 705 | "repeat": "", 706 | "crontab": "", 707 | "once": false, 708 | "onceDelay": "", 709 | "x": 140, 710 | "y": 640, 711 | "wires": [ 712 | [ 713 | "96042f96.a6f48" 714 | ] 715 | ] 716 | }, 717 | { 718 | "id": "912f2ab6.7b7518", 719 | "type": "inject", 720 | "z": "46d53d66.69f3cc", 721 | "name": "Down 50cm", 722 | "topic": "", 723 | "payload": "true", 724 | "payloadType": "bool", 725 | "repeat": "", 726 | "crontab": "", 727 | "once": false, 728 | "onceDelay": "", 729 | "x": 150, 730 | "y": 680, 731 | "wires": [ 732 | [ 733 | "72ce977b.85749" 734 | ] 735 | ] 736 | }, 737 | { 738 | "id": "1064ba13.7a2ac6", 739 | "type": "inject", 740 | "z": "46d53d66.69f3cc", 741 | "name": "Right 50cm", 742 | "topic": "", 743 | "payload": "true", 744 | "payloadType": "bool", 745 | "repeat": "", 746 | "crontab": "", 747 | "once": false, 748 | "onceDelay": "", 749 | "x": 150, 750 | "y": 720, 751 | "wires": [ 752 | [ 753 | "825841d5.c45ea8" 754 | ] 755 | ] 756 | }, 757 | { 758 | "id": "26437f8d.5a22", 759 | "type": "inject", 760 | "z": "46d53d66.69f3cc", 761 | "name": "Left 50cm", 762 | "topic": "", 763 | "payload": "true", 764 | "payloadType": "bool", 765 | "repeat": "", 766 | "crontab": "", 767 | "once": false, 768 | "onceDelay": "", 769 | "x": 140, 770 | "y": 760, 771 | "wires": [ 772 | [ 773 | "eba9bdb7.5803e8" 774 | ] 775 | ] 776 | }, 777 | { 778 | "id": "ce46713a.d4b6d8", 779 | "type": "inject", 780 | "z": "46d53d66.69f3cc", 781 | "name": "Forward 50cm", 782 | "topic": "", 783 | "payload": "true", 784 | "payloadType": "bool", 785 | "repeat": "", 786 | "crontab": "", 787 | "once": false, 788 | "onceDelay": "", 789 | "x": 150, 790 | "y": 800, 791 | "wires": [ 792 | [ 793 | "2149370a.de2958" 794 | ] 795 | ] 796 | }, 797 | { 798 | "id": "6dc87f5a.d1565", 799 | "type": "inject", 800 | "z": "46d53d66.69f3cc", 801 | "name": "Back 50cm", 802 | "topic": "", 803 | "payload": "true", 804 | "payloadType": "bool", 805 | "repeat": "", 806 | "crontab": "", 807 | "once": false, 808 | "onceDelay": "", 809 | "x": 140, 810 | "y": 840, 811 | "wires": [ 812 | [ 813 | "374f5e6c.a1891a" 814 | ] 815 | ] 816 | }, 817 | { 818 | "id": "1cb0a27f.85d796", 819 | "type": "inject", 820 | "z": "46d53d66.69f3cc", 821 | "name": "cw 90º", 822 | "topic": "", 823 | "payload": "true", 824 | "payloadType": "bool", 825 | "repeat": "", 826 | "crontab": "", 827 | "once": false, 828 | "onceDelay": "", 829 | "x": 130, 830 | "y": 880, 831 | "wires": [ 832 | [ 833 | "ae6be809.182f48" 834 | ] 835 | ] 836 | }, 837 | { 838 | "id": "5cec1624.6a77b", 839 | "type": "inject", 840 | "z": "46d53d66.69f3cc", 841 | "name": "Full 360º", 842 | "topic": "", 843 | "payload": "true", 844 | "payloadType": "bool", 845 | "repeat": "", 846 | "crontab": "", 847 | "once": false, 848 | "onceDelay": "", 849 | "x": 140, 850 | "y": 960, 851 | "wires": [ 852 | [ 853 | "59c99d5.20e8d64" 854 | ] 855 | ] 856 | }, 857 | { 858 | "id": "1f598d3.f6f3373", 859 | "type": "inject", 860 | "z": "46d53d66.69f3cc", 861 | "name": "Flip forward", 862 | "topic": "", 863 | "payload": "true", 864 | "payloadType": "bool", 865 | "repeat": "", 866 | "crontab": "", 867 | "once": false, 868 | "onceDelay": "", 869 | "x": 150, 870 | "y": 1000, 871 | "wires": [ 872 | [ 873 | "72fd932.25eb46c" 874 | ] 875 | ] 876 | }, 877 | { 878 | "id": "926b7f26.edb71", 879 | "type": "inject", 880 | "z": "46d53d66.69f3cc", 881 | "name": "ccw 90º", 882 | "topic": "", 883 | "payload": "true", 884 | "payloadType": "bool", 885 | "repeat": "", 886 | "crontab": "", 887 | "once": false, 888 | "onceDelay": "", 889 | "x": 130, 890 | "y": 920, 891 | "wires": [ 892 | [ 893 | "43c61f88.2babb8" 894 | ] 895 | ] 896 | }, 897 | { 898 | "id": "59c99d5.20e8d64", 899 | "type": "change", 900 | "z": "46d53d66.69f3cc", 901 | "name": "cw 360º", 902 | "rules": [ 903 | { 904 | "t": "delete", 905 | "p": "payload", 906 | "pt": "msg" 907 | }, 908 | { 909 | "t": "set", 910 | "p": "payload.tellocmd", 911 | "pt": "msg", 912 | "to": "cw", 913 | "tot": "str" 914 | }, 915 | { 916 | "t": "set", 917 | "p": "payload.tellovalue", 918 | "pt": "msg", 919 | "to": "360", 920 | "tot": "str" 921 | } 922 | ], 923 | "action": "", 924 | "property": "", 925 | "from": "", 926 | "to": "", 927 | "reg": false, 928 | "x": 420, 929 | "y": 960, 930 | "wires": [ 931 | [ 932 | "3c8f9e99.f65c2a" 933 | ] 934 | ] 935 | }, 936 | { 937 | "id": "d9df0550.c8e928", 938 | "type": "change", 939 | "z": "46d53d66.69f3cc", 940 | "name": "Emergency", 941 | "rules": [ 942 | { 943 | "t": "delete", 944 | "p": "payload", 945 | "pt": "msg" 946 | }, 947 | { 948 | "t": "set", 949 | "p": "payload.tellocmd", 950 | "pt": "msg", 951 | "to": "emergency", 952 | "tot": "str" 953 | } 954 | ], 955 | "action": "", 956 | "property": "", 957 | "from": "", 958 | "to": "", 959 | "reg": false, 960 | "x": 430, 961 | "y": 600, 962 | "wires": [ 963 | [ 964 | "3c8f9e99.f65c2a" 965 | ] 966 | ] 967 | }, 968 | { 969 | "id": "bbc17caa.7b0b68", 970 | "type": "inject", 971 | "z": "46d53d66.69f3cc", 972 | "name": "Emergency", 973 | "topic": "", 974 | "payload": "true", 975 | "payloadType": "bool", 976 | "repeat": "", 977 | "crontab": "", 978 | "once": false, 979 | "onceDelay": "", 980 | "x": 150, 981 | "y": 600, 982 | "wires": [ 983 | [ 984 | "d9df0550.c8e928" 985 | ] 986 | ] 987 | } 988 | ] 989 | -------------------------------------------------------------------------------- /flows/starter/part4_starter.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "93117b9d.ff2d88", 4 | "type": "tab", 5 | "label": "Tello Dashboard", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "2f023812.2bd6e", 11 | "type": "udp out", 12 | "z": "93117b9d.ff2d88", 13 | "name": "", 14 | "addr": "192.168.10.1", 15 | "iface": "", 16 | "port": "8889", 17 | "ipv": "udp4", 18 | "outport": "52955", 19 | "base64": false, 20 | "multicast": "false", 21 | "x": 590, 22 | "y": 180, 23 | "wires": [] 24 | }, 25 | { 26 | "id": "6f4d98a2.d07de8", 27 | "type": "debug", 28 | "z": "93117b9d.ff2d88", 29 | "name": "", 30 | "active": true, 31 | "console": "false", 32 | "complete": "false", 33 | "x": 550, 34 | "y": 140, 35 | "wires": [] 36 | }, 37 | { 38 | "id": "3bf004a0.61da3c", 39 | "type": "comment", 40 | "z": "93117b9d.ff2d88", 41 | "name": "1 - Connect to TELLO-NNNNNN WiFi", 42 | "info": "", 43 | "x": 200, 44 | "y": 80, 45 | "wires": [] 46 | }, 47 | { 48 | "id": "fe23826b.8ceb28", 49 | "type": "link in", 50 | "z": "93117b9d.ff2d88", 51 | "name": "TelloControls-In", 52 | "links": [ 53 | "799058c7.aecc28" 54 | ], 55 | "x": 95, 56 | "y": 160, 57 | "wires": [ 58 | [ 59 | "d51deb36.00702" 60 | ] 61 | ] 62 | }, 63 | { 64 | "id": "7107ad43.8fbe84", 65 | "type": "udp in", 66 | "z": "93117b9d.ff2d88", 67 | "name": "", 68 | "iface": "", 69 | "port": "52955", 70 | "ipv": "udp4", 71 | "multicast": "false", 72 | "group": "", 73 | "datatype": "utf8", 74 | "x": 120, 75 | "y": 320, 76 | "wires": [ 77 | [ 78 | "5e8333ec.d4773c" 79 | ] 80 | ] 81 | }, 82 | { 83 | "id": "5e8333ec.d4773c", 84 | "type": "debug", 85 | "z": "93117b9d.ff2d88", 86 | "name": "", 87 | "active": false, 88 | "tosidebar": true, 89 | "console": false, 90 | "tostatus": false, 91 | "complete": "false", 92 | "x": 550, 93 | "y": 320, 94 | "wires": [] 95 | }, 96 | { 97 | "id": "91896f1d.ebec3", 98 | "type": "comment", 99 | "z": "93117b9d.ff2d88", 100 | "name": "Receive Return Strings", 101 | "info": "The UDP input node listens for return values", 102 | "x": 160, 103 | "y": 280, 104 | "wires": [] 105 | }, 106 | { 107 | "id": "39433bd3.bc3f9c", 108 | "type": "comment", 109 | "z": "93117b9d.ff2d88", 110 | "name": "================================ Send Commands to drone ==========================", 111 | "info": "", 112 | "x": 370, 113 | "y": 40, 114 | "wires": [] 115 | }, 116 | { 117 | "id": "3c178793.166e58", 118 | "type": "comment", 119 | "z": "93117b9d.ff2d88", 120 | "name": "================================ Handle incoming messages =========================", 121 | "info": "", 122 | "x": 370, 123 | "y": 240, 124 | "wires": [] 125 | }, 126 | { 127 | "id": "ee4d8d4d.293538", 128 | "type": "inject", 129 | "z": "93117b9d.ff2d88", 130 | "name": "Land", 131 | "topic": "", 132 | "payload": "true", 133 | "payloadType": "bool", 134 | "repeat": "", 135 | "crontab": "", 136 | "once": false, 137 | "onceDelay": "", 138 | "x": 110, 139 | "y": 440, 140 | "wires": [ 141 | [ 142 | "c74aeaeb.484ad" 143 | ] 144 | ] 145 | }, 146 | { 147 | "id": "ff2aee20.7531e8", 148 | "type": "inject", 149 | "z": "93117b9d.ff2d88", 150 | "name": "TakeOff", 151 | "topic": "", 152 | "payload": "true", 153 | "payloadType": "bool", 154 | "repeat": "", 155 | "crontab": "", 156 | "once": false, 157 | "onceDelay": "", 158 | "x": 110, 159 | "y": 480, 160 | "wires": [ 161 | [ 162 | "5e69e6db.c6b498" 163 | ] 164 | ] 165 | }, 166 | { 167 | "id": "7a1560f1.0164d8", 168 | "type": "delay", 169 | "z": "93117b9d.ff2d88", 170 | "name": "", 171 | "pauseType": "delay", 172 | "timeout": "250", 173 | "timeoutUnits": "milliseconds", 174 | "rate": "1", 175 | "nbRateUnits": "1", 176 | "rateUnits": "second", 177 | "randomFirst": "1", 178 | "randomLast": "5", 179 | "randomUnits": "seconds", 180 | "drop": false, 181 | "x": 130, 182 | "y": 520, 183 | "wires": [ 184 | [ 185 | "4e1e9a8f.0b4efc" 186 | ] 187 | ] 188 | }, 189 | { 190 | "id": "4e1e9a8f.0b4efc", 191 | "type": "change", 192 | "z": "93117b9d.ff2d88", 193 | "name": "TakeOff", 194 | "rules": [ 195 | { 196 | "t": "delete", 197 | "p": "payload", 198 | "pt": "msg" 199 | }, 200 | { 201 | "t": "set", 202 | "p": "payload.tellocmd", 203 | "pt": "msg", 204 | "to": "takeoff", 205 | "tot": "str" 206 | } 207 | ], 208 | "action": "", 209 | "property": "", 210 | "from": "", 211 | "to": "", 212 | "reg": false, 213 | "x": 520, 214 | "y": 520, 215 | "wires": [ 216 | [ 217 | "799058c7.aecc28" 218 | ] 219 | ] 220 | }, 221 | { 222 | "id": "5e69e6db.c6b498", 223 | "type": "change", 224 | "z": "93117b9d.ff2d88", 225 | "name": "Init", 226 | "rules": [ 227 | { 228 | "t": "delete", 229 | "p": "payload", 230 | "pt": "msg" 231 | }, 232 | { 233 | "t": "set", 234 | "p": "payload.tellocmd", 235 | "pt": "msg", 236 | "to": "command", 237 | "tot": "str" 238 | } 239 | ], 240 | "action": "", 241 | "property": "", 242 | "from": "", 243 | "to": "", 244 | "reg": false, 245 | "x": 510, 246 | "y": 480, 247 | "wires": [ 248 | [ 249 | "7a1560f1.0164d8", 250 | "799058c7.aecc28" 251 | ] 252 | ] 253 | }, 254 | { 255 | "id": "c74aeaeb.484ad", 256 | "type": "change", 257 | "z": "93117b9d.ff2d88", 258 | "name": "Land", 259 | "rules": [ 260 | { 261 | "t": "delete", 262 | "p": "payload", 263 | "pt": "msg" 264 | }, 265 | { 266 | "t": "set", 267 | "p": "payload.tellocmd", 268 | "pt": "msg", 269 | "to": "land", 270 | "tot": "str" 271 | } 272 | ], 273 | "action": "", 274 | "property": "", 275 | "from": "", 276 | "to": "", 277 | "reg": false, 278 | "x": 510, 279 | "y": 440, 280 | "wires": [ 281 | [ 282 | "799058c7.aecc28" 283 | ] 284 | ] 285 | }, 286 | { 287 | "id": "799058c7.aecc28", 288 | "type": "link out", 289 | "z": "93117b9d.ff2d88", 290 | "name": "TelloControls-Out", 291 | "links": [ 292 | "fe23826b.8ceb28" 293 | ], 294 | "x": 715, 295 | "y": 720, 296 | "wires": [] 297 | }, 298 | { 299 | "id": "d51deb36.00702", 300 | "type": "change", 301 | "z": "93117b9d.ff2d88", 302 | "name": "format outbound message", 303 | "rules": [ 304 | { 305 | "t": "set", 306 | "p": "payload", 307 | "pt": "msg", 308 | "to": "$exists(payload.tellovalue) ? payload.tellocmd & ' ' & payload.tellovalue : payload.tellocmd", 309 | "tot": "jsonata" 310 | } 311 | ], 312 | "action": "", 313 | "property": "", 314 | "from": "", 315 | "to": "", 316 | "reg": false, 317 | "x": 280, 318 | "y": 160, 319 | "wires": [ 320 | [ 321 | "2f023812.2bd6e", 322 | "6f4d98a2.d07de8" 323 | ] 324 | ] 325 | }, 326 | { 327 | "id": "8689758.5cda788", 328 | "type": "change", 329 | "z": "93117b9d.ff2d88", 330 | "name": "Up 50cm", 331 | "rules": [ 332 | { 333 | "t": "delete", 334 | "p": "payload", 335 | "pt": "msg" 336 | }, 337 | { 338 | "t": "set", 339 | "p": "payload.tellocmd", 340 | "pt": "msg", 341 | "to": "up", 342 | "tot": "str" 343 | }, 344 | { 345 | "t": "set", 346 | "p": "payload.tellovalue", 347 | "pt": "msg", 348 | "to": "50", 349 | "tot": "str" 350 | } 351 | ], 352 | "action": "", 353 | "property": "", 354 | "from": "", 355 | "to": "", 356 | "reg": false, 357 | "x": 520, 358 | "y": 600, 359 | "wires": [ 360 | [ 361 | "799058c7.aecc28" 362 | ] 363 | ] 364 | }, 365 | { 366 | "id": "97d13df8.0978", 367 | "type": "change", 368 | "z": "93117b9d.ff2d88", 369 | "name": "Right 50cm", 370 | "rules": [ 371 | { 372 | "t": "delete", 373 | "p": "payload", 374 | "pt": "msg" 375 | }, 376 | { 377 | "t": "set", 378 | "p": "payload.tellocmd", 379 | "pt": "msg", 380 | "to": "right", 381 | "tot": "str" 382 | }, 383 | { 384 | "t": "set", 385 | "p": "payload.tellovalue", 386 | "pt": "msg", 387 | "to": "50", 388 | "tot": "str" 389 | } 390 | ], 391 | "action": "", 392 | "property": "", 393 | "from": "", 394 | "to": "", 395 | "reg": false, 396 | "x": 530, 397 | "y": 680, 398 | "wires": [ 399 | [ 400 | "799058c7.aecc28" 401 | ] 402 | ] 403 | }, 404 | { 405 | "id": "652ddcc7.dbb8ac", 406 | "type": "change", 407 | "z": "93117b9d.ff2d88", 408 | "name": "Forward 50cm", 409 | "rules": [ 410 | { 411 | "t": "delete", 412 | "p": "payload", 413 | "pt": "msg" 414 | }, 415 | { 416 | "t": "set", 417 | "p": "payload.tellocmd", 418 | "pt": "msg", 419 | "to": "forward", 420 | "tot": "str" 421 | }, 422 | { 423 | "t": "set", 424 | "p": "payload.tellovalue", 425 | "pt": "msg", 426 | "to": "50", 427 | "tot": "str" 428 | } 429 | ], 430 | "action": "", 431 | "property": "", 432 | "from": "", 433 | "to": "", 434 | "reg": false, 435 | "x": 540, 436 | "y": 760, 437 | "wires": [ 438 | [ 439 | "799058c7.aecc28" 440 | ] 441 | ] 442 | }, 443 | { 444 | "id": "ca17a1ce.70741", 445 | "type": "change", 446 | "z": "93117b9d.ff2d88", 447 | "name": "Down 50cm", 448 | "rules": [ 449 | { 450 | "t": "delete", 451 | "p": "payload", 452 | "pt": "msg" 453 | }, 454 | { 455 | "t": "set", 456 | "p": "payload.tellocmd", 457 | "pt": "msg", 458 | "to": "down", 459 | "tot": "str" 460 | }, 461 | { 462 | "t": "set", 463 | "p": "payload.tellovalue", 464 | "pt": "msg", 465 | "to": "50", 466 | "tot": "str" 467 | } 468 | ], 469 | "action": "", 470 | "property": "", 471 | "from": "", 472 | "to": "", 473 | "reg": false, 474 | "x": 530, 475 | "y": 640, 476 | "wires": [ 477 | [ 478 | "799058c7.aecc28" 479 | ] 480 | ] 481 | }, 482 | { 483 | "id": "736008f1.250ab8", 484 | "type": "change", 485 | "z": "93117b9d.ff2d88", 486 | "name": "Left 50cm", 487 | "rules": [ 488 | { 489 | "t": "delete", 490 | "p": "payload", 491 | "pt": "msg" 492 | }, 493 | { 494 | "t": "set", 495 | "p": "payload.tellocmd", 496 | "pt": "msg", 497 | "to": "left", 498 | "tot": "str" 499 | }, 500 | { 501 | "t": "set", 502 | "p": "payload.tellovalue", 503 | "pt": "msg", 504 | "to": "50", 505 | "tot": "str" 506 | } 507 | ], 508 | "action": "", 509 | "property": "", 510 | "from": "", 511 | "to": "", 512 | "reg": false, 513 | "x": 520, 514 | "y": 720, 515 | "wires": [ 516 | [ 517 | "799058c7.aecc28" 518 | ] 519 | ] 520 | }, 521 | { 522 | "id": "e22d11ad.46de2", 523 | "type": "change", 524 | "z": "93117b9d.ff2d88", 525 | "name": "Back 50cm", 526 | "rules": [ 527 | { 528 | "t": "delete", 529 | "p": "payload", 530 | "pt": "msg" 531 | }, 532 | { 533 | "t": "set", 534 | "p": "payload.tellocmd", 535 | "pt": "msg", 536 | "to": "back", 537 | "tot": "str" 538 | }, 539 | { 540 | "t": "set", 541 | "p": "payload.tellovalue", 542 | "pt": "msg", 543 | "to": "50", 544 | "tot": "str" 545 | } 546 | ], 547 | "action": "", 548 | "property": "", 549 | "from": "", 550 | "to": "", 551 | "reg": false, 552 | "x": 530, 553 | "y": 800, 554 | "wires": [ 555 | [ 556 | "799058c7.aecc28" 557 | ] 558 | ] 559 | }, 560 | { 561 | "id": "f7721bed.f59298", 562 | "type": "change", 563 | "z": "93117b9d.ff2d88", 564 | "name": "cw 90º", 565 | "rules": [ 566 | { 567 | "t": "delete", 568 | "p": "payload", 569 | "pt": "msg" 570 | }, 571 | { 572 | "t": "set", 573 | "p": "payload.tellocmd", 574 | "pt": "msg", 575 | "to": "cw", 576 | "tot": "str" 577 | }, 578 | { 579 | "t": "set", 580 | "p": "payload.tellovalue", 581 | "pt": "msg", 582 | "to": "90", 583 | "tot": "str" 584 | } 585 | ], 586 | "action": "", 587 | "property": "", 588 | "from": "", 589 | "to": "", 590 | "reg": false, 591 | "x": 510, 592 | "y": 840, 593 | "wires": [ 594 | [ 595 | "799058c7.aecc28" 596 | ] 597 | ] 598 | }, 599 | { 600 | "id": "f37996f1.9be3e", 601 | "type": "change", 602 | "z": "93117b9d.ff2d88", 603 | "name": "ccw 90º", 604 | "rules": [ 605 | { 606 | "t": "delete", 607 | "p": "payload", 608 | "pt": "msg" 609 | }, 610 | { 611 | "t": "set", 612 | "p": "payload.tellocmd", 613 | "pt": "msg", 614 | "to": "ccw", 615 | "tot": "str" 616 | }, 617 | { 618 | "t": "set", 619 | "p": "payload.tellovalue", 620 | "pt": "msg", 621 | "to": "90", 622 | "tot": "str" 623 | } 624 | ], 625 | "action": "", 626 | "property": "", 627 | "from": "", 628 | "to": "", 629 | "reg": false, 630 | "x": 520, 631 | "y": 880, 632 | "wires": [ 633 | [ 634 | "799058c7.aecc28" 635 | ] 636 | ] 637 | }, 638 | { 639 | "id": "67ed70.f20a729", 640 | "type": "change", 641 | "z": "93117b9d.ff2d88", 642 | "name": "Flip f", 643 | "rules": [ 644 | { 645 | "t": "delete", 646 | "p": "payload", 647 | "pt": "msg" 648 | }, 649 | { 650 | "t": "set", 651 | "p": "payload.tellocmd", 652 | "pt": "msg", 653 | "to": "flip", 654 | "tot": "str" 655 | }, 656 | { 657 | "t": "set", 658 | "p": "payload.tellovalue", 659 | "pt": "msg", 660 | "to": "f", 661 | "tot": "str" 662 | } 663 | ], 664 | "action": "", 665 | "property": "", 666 | "from": "", 667 | "to": "", 668 | "reg": false, 669 | "x": 510, 670 | "y": 960, 671 | "wires": [ 672 | [ 673 | "799058c7.aecc28" 674 | ] 675 | ] 676 | }, 677 | { 678 | "id": "91f3bae9.3af4f8", 679 | "type": "inject", 680 | "z": "93117b9d.ff2d88", 681 | "name": "Up 50cm", 682 | "topic": "", 683 | "payload": "true", 684 | "payloadType": "bool", 685 | "repeat": "", 686 | "crontab": "", 687 | "once": false, 688 | "onceDelay": "", 689 | "x": 120, 690 | "y": 600, 691 | "wires": [ 692 | [ 693 | "8689758.5cda788" 694 | ] 695 | ] 696 | }, 697 | { 698 | "id": "c83e9cd5.249a28", 699 | "type": "inject", 700 | "z": "93117b9d.ff2d88", 701 | "name": "Down 50cm", 702 | "topic": "", 703 | "payload": "true", 704 | "payloadType": "bool", 705 | "repeat": "", 706 | "crontab": "", 707 | "once": false, 708 | "onceDelay": "", 709 | "x": 130, 710 | "y": 640, 711 | "wires": [ 712 | [ 713 | "ca17a1ce.70741" 714 | ] 715 | ] 716 | }, 717 | { 718 | "id": "bbceaac6.bc97f", 719 | "type": "inject", 720 | "z": "93117b9d.ff2d88", 721 | "name": "Right 50cm", 722 | "topic": "", 723 | "payload": "true", 724 | "payloadType": "bool", 725 | "repeat": "", 726 | "crontab": "", 727 | "once": false, 728 | "onceDelay": "", 729 | "x": 130, 730 | "y": 680, 731 | "wires": [ 732 | [ 733 | "97d13df8.0978" 734 | ] 735 | ] 736 | }, 737 | { 738 | "id": "7fa7d0c6.5c8128", 739 | "type": "inject", 740 | "z": "93117b9d.ff2d88", 741 | "name": "Left 50cm", 742 | "topic": "", 743 | "payload": "true", 744 | "payloadType": "bool", 745 | "repeat": "", 746 | "crontab": "", 747 | "once": false, 748 | "onceDelay": "", 749 | "x": 120, 750 | "y": 720, 751 | "wires": [ 752 | [ 753 | "736008f1.250ab8" 754 | ] 755 | ] 756 | }, 757 | { 758 | "id": "552e6e58.115498", 759 | "type": "inject", 760 | "z": "93117b9d.ff2d88", 761 | "name": "Forward 50cm", 762 | "topic": "", 763 | "payload": "true", 764 | "payloadType": "bool", 765 | "repeat": "", 766 | "crontab": "", 767 | "once": false, 768 | "onceDelay": "", 769 | "x": 130, 770 | "y": 760, 771 | "wires": [ 772 | [ 773 | "652ddcc7.dbb8ac" 774 | ] 775 | ] 776 | }, 777 | { 778 | "id": "39d4f2ee.3854be", 779 | "type": "inject", 780 | "z": "93117b9d.ff2d88", 781 | "name": "Back 50cm", 782 | "topic": "", 783 | "payload": "true", 784 | "payloadType": "bool", 785 | "repeat": "", 786 | "crontab": "", 787 | "once": false, 788 | "onceDelay": "", 789 | "x": 120, 790 | "y": 800, 791 | "wires": [ 792 | [ 793 | "e22d11ad.46de2" 794 | ] 795 | ] 796 | }, 797 | { 798 | "id": "31476b10.e36b6c", 799 | "type": "inject", 800 | "z": "93117b9d.ff2d88", 801 | "name": "cw 90º", 802 | "topic": "", 803 | "payload": "true", 804 | "payloadType": "bool", 805 | "repeat": "", 806 | "crontab": "", 807 | "once": false, 808 | "onceDelay": "", 809 | "x": 110, 810 | "y": 840, 811 | "wires": [ 812 | [ 813 | "f7721bed.f59298" 814 | ] 815 | ] 816 | }, 817 | { 818 | "id": "6baf7c51.2a124c", 819 | "type": "inject", 820 | "z": "93117b9d.ff2d88", 821 | "name": "Full 360º", 822 | "topic": "", 823 | "payload": "true", 824 | "payloadType": "bool", 825 | "repeat": "", 826 | "crontab": "", 827 | "once": false, 828 | "onceDelay": "", 829 | "x": 120, 830 | "y": 920, 831 | "wires": [ 832 | [ 833 | "146547c7.dfa85" 834 | ] 835 | ] 836 | }, 837 | { 838 | "id": "8f691e92.1ad3f", 839 | "type": "inject", 840 | "z": "93117b9d.ff2d88", 841 | "name": "Flip forward", 842 | "topic": "", 843 | "payload": "true", 844 | "payloadType": "bool", 845 | "repeat": "", 846 | "crontab": "", 847 | "once": false, 848 | "onceDelay": "", 849 | "x": 130, 850 | "y": 960, 851 | "wires": [ 852 | [ 853 | "67ed70.f20a729" 854 | ] 855 | ] 856 | }, 857 | { 858 | "id": "594c12a4.735bd4", 859 | "type": "inject", 860 | "z": "93117b9d.ff2d88", 861 | "name": "ccw 90º", 862 | "topic": "", 863 | "payload": "true", 864 | "payloadType": "bool", 865 | "repeat": "", 866 | "crontab": "", 867 | "once": false, 868 | "onceDelay": "", 869 | "x": 110, 870 | "y": 880, 871 | "wires": [ 872 | [ 873 | "f37996f1.9be3e" 874 | ] 875 | ] 876 | }, 877 | { 878 | "id": "146547c7.dfa85", 879 | "type": "change", 880 | "z": "93117b9d.ff2d88", 881 | "name": "cw 360º", 882 | "rules": [ 883 | { 884 | "t": "delete", 885 | "p": "payload", 886 | "pt": "msg" 887 | }, 888 | { 889 | "t": "set", 890 | "p": "payload.tellocmd", 891 | "pt": "msg", 892 | "to": "cw", 893 | "tot": "str" 894 | }, 895 | { 896 | "t": "set", 897 | "p": "payload.tellovalue", 898 | "pt": "msg", 899 | "to": "360", 900 | "tot": "str" 901 | } 902 | ], 903 | "action": "", 904 | "property": "", 905 | "from": "", 906 | "to": "", 907 | "reg": false, 908 | "x": 520, 909 | "y": 920, 910 | "wires": [ 911 | [ 912 | "799058c7.aecc28" 913 | ] 914 | ] 915 | }, 916 | { 917 | "id": "cff9d8d.c558fa8", 918 | "type": "change", 919 | "z": "93117b9d.ff2d88", 920 | "name": "Emergency", 921 | "rules": [ 922 | { 923 | "t": "delete", 924 | "p": "payload", 925 | "pt": "msg" 926 | }, 927 | { 928 | "t": "set", 929 | "p": "payload.tellocmd", 930 | "pt": "msg", 931 | "to": "emergency", 932 | "tot": "str" 933 | } 934 | ], 935 | "action": "", 936 | "property": "", 937 | "from": "", 938 | "to": "", 939 | "reg": false, 940 | "x": 530, 941 | "y": 560, 942 | "wires": [ 943 | [ 944 | "799058c7.aecc28" 945 | ] 946 | ] 947 | }, 948 | { 949 | "id": "dcca42be.f9f568", 950 | "type": "inject", 951 | "z": "93117b9d.ff2d88", 952 | "name": "Emergency", 953 | "topic": "", 954 | "payload": "true", 955 | "payloadType": "bool", 956 | "repeat": "", 957 | "crontab": "", 958 | "once": false, 959 | "onceDelay": "", 960 | "x": 130, 961 | "y": 560, 962 | "wires": [ 963 | [ 964 | "cff9d8d.c558fa8" 965 | ] 966 | ] 967 | }, 968 | { 969 | "id": "15fdf85a.a98c2", 970 | "type": "ui_button", 971 | "z": "93117b9d.ff2d88", 972 | "name": "", 973 | "group": "3fb6ada5.67360a", 974 | "order": 3, 975 | "width": 0, 976 | "height": 0, 977 | "passthru": false, 978 | "label": "Land", 979 | "color": "", 980 | "bgcolor": "", 981 | "icon": "", 982 | "payload": "land", 983 | "payloadType": "str", 984 | "topic": "", 985 | "x": 290, 986 | "y": 420, 987 | "wires": [ 988 | [ 989 | "c74aeaeb.484ad" 990 | ] 991 | ] 992 | }, 993 | { 994 | "id": "d8f17b1f.9d8638", 995 | "type": "ui_button", 996 | "z": "93117b9d.ff2d88", 997 | "name": "", 998 | "group": "3fb6ada5.67360a", 999 | "order": 2, 1000 | "width": 0, 1001 | "height": 0, 1002 | "passthru": false, 1003 | "label": "Takeoff", 1004 | "color": "", 1005 | "bgcolor": "", 1006 | "icon": "", 1007 | "payload": "", 1008 | "payloadType": "str", 1009 | "topic": "", 1010 | "x": 300, 1011 | "y": 460, 1012 | "wires": [ 1013 | [ 1014 | "5e69e6db.c6b498" 1015 | ] 1016 | ] 1017 | }, 1018 | { 1019 | "id": "6d096aae.053e0c", 1020 | "type": "comment", 1021 | "z": "93117b9d.ff2d88", 1022 | "name": "Send Commands", 1023 | "info": "This section of the flow formats the Tello command\ninto a single string to be sent to the drone.\n\nFormat is [command] [optional values]\n\nFor a list of Tello SDK commands, visit\nhttps://dl-cdn.ryzerobotics.com/downloads/Tello/Tello%20SDK%202.0%20User%20Guide.pdf", 1024 | "x": 160, 1025 | "y": 120, 1026 | "wires": [] 1027 | }, 1028 | { 1029 | "id": "44bb768d.49ccd", 1030 | "type": "comment", 1031 | "z": "93117b9d.ff2d88", 1032 | "name": "================================ Send Tello SDK Commands =========================", 1033 | "info": "For a list of Tello SDK commands, visit\nhttps://dl-cdn.ryzerobotics.com/downloads/Tello/Tello%20SDK%202.0%20User%20Guide.pdf", 1034 | "x": 370, 1035 | "y": 380, 1036 | "wires": [] 1037 | }, 1038 | { 1039 | "id": "3fb6ada5.67360a", 1040 | "type": "ui_group", 1041 | "z": "", 1042 | "name": "Control", 1043 | "tab": "b7c1800f.7c22b", 1044 | "order": 4, 1045 | "disp": true, 1046 | "width": "4", 1047 | "collapse": false 1048 | }, 1049 | { 1050 | "id": "b7c1800f.7c22b", 1051 | "type": "ui_tab", 1052 | "z": "", 1053 | "name": "Tello Control Dashboard", 1054 | "icon": "fa-tachometer", 1055 | "order": 1 1056 | } 1057 | ] 1058 | -------------------------------------------------------------------------------- /flows/solutions/part8_solution.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "865602f6.dc2338", 4 | "type": "tab", 5 | "label": "Tello Picture Dash", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "5b0a7d4e.258d2c", 11 | "type": "udp out", 12 | "z": "865602f6.dc2338", 13 | "name": "", 14 | "addr": "192.168.10.1", 15 | "iface": "", 16 | "port": "8889", 17 | "ipv": "udp4", 18 | "outport": "52955", 19 | "base64": false, 20 | "multicast": "false", 21 | "x": 930, 22 | "y": 100, 23 | "wires": [] 24 | }, 25 | { 26 | "id": "d8f0f1c9.9d5b6", 27 | "type": "function", 28 | "z": "865602f6.dc2338", 29 | "name": "Tello Send Cmd", 30 | "func": "var TBL_CRC16 = [\n\t\t\t0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,\n\t\t\t0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,\n\t\t\t0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,\n\t\t\t0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,\n\t\t\t0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,\n\t\t\t0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,\n\t\t\t0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,\n\t\t\t0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,\n\t\t\t0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,\n\t\t\t0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,\n\t\t\t0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,\n\t\t\t0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,\n\t\t\t0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,\n\t\t\t0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,\n\t\t\t0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,\n\t\t\t0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78\n\t\t];\n\t\t\nvar TBL_CRC8 = [\n\t\t\t0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41,\n\t\t\t0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, 0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc,\n\t\t\t0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, 0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62,\n\t\t\t0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, 0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff,\n\t\t\t0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, 0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07,\n\t\t\t0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, 0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a,\n\t\t\t0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, 0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24,\n\t\t\t0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9,\n\t\t\t0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f, 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd,\n\t\t\t0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92, 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50,\n\t\t\t0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c, 0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee,\n\t\t\t0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1, 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73,\n\t\t\t0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49, 0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b,\n\t\t\t0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16,\n\t\t\t0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8,\n\t\t\t0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, 0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35,\n\t\t]\n\n\t\t\nfunction _calcCRC16 (buf, size) {\n\t\tvar i = 0 ;\n\t\tvar seed = 0x3692 ;\n\t\twhile (size > 0) {\n\t\t\tseed = TBL_CRC16[(seed ^ buf[i]) & 0xff] ^ (seed >> 8)\n\t\t\ti++ ;\n\t\t\tsize-- ;\n\t\t} \n\t\treturn seed ;\n\t}\n\nfunction _calcCRC8 (buf, size) {\n\t\tvar i = 0 ;\n\t\tvar seed = 0x77 ;\n\t\twhile (size > 0) {\n\t\t\tseed = TBL_CRC8[(seed ^ buf[i]) & 0xff]\n\t\t\ti++ ;\n\t\t\tsize-- ;\n\t\t}\n\t\treturn seed ;\n\t}\n\n\t\nfunction buildPacket(pacType, cmdID, seq, data) {\n\t\tvar size = 11 + (data?data.length:0);\n\t\tvar bb = Buffer.alloc(size)\n\t\tvar packetTypeInfo = 0x40 | ((pacType & 0x07) <<3);\n\t\tbb.writeUInt8(0xCC,0)\n\t\tbb.writeUInt8((size << 3) & 0xFF ,1)\n\t\tbb.writeUInt8((size >> 5) & 0xFF ,2)\n\t\n \tvar crc8 = _calcCRC8([...bb], 3)\n\t\tbb.writeUInt8(crc8,3);\n\t\tbb.writeUInt8(packetTypeInfo,4);\n\t\tbb.writeUInt16LE(cmdID,5);\n\t\tbb.writeUInt16LE(seq,7);\n\t\t//console.log(\"DBG\", data, len);\n\t\tif (data) {\n\t\t\tfor (var i=0; i= 11) {\n\t\tvar mark = bb.readUInt8(0);\n\t\tif (mark == 0xCC) {\t// start of packet\n\t\t size = bb.readUInt16LE(1) >> 3;\n\t\t var crc8 = bb.readUInt8(3);\n\t\t pacType = bb.readUInt8(4);\n\t\t cmdID = bb.readUInt16LE(5);\n\t\t seqID = bb.readUInt16LE(7);\n\t dataSize = size - 11;\n\t var data = false;\n\t if (dataSize > 0) {\n\t data = bb.slice(9, size-2);\n\t }\n\t var crc16 = bb.readUInt16LE(size - 2);\n\t \n\t ret.cmd = cmdID;\n\t ret.seq = seqID;\n\t ret.pacType= pacType;\n\t ret.dataSize = dataSize;\n\t ret.data = data;\n\t\t} else {\n\t\t ret = bb.toString();\n\t\t}\n\t} else {\n\t ret = bb.toString();\n\t}\n\treturn ret;\n}\n\t\nmsg.payload = parsePacket(msg.payload);\nif (msg.payload.cmd) {\n var tstatus = global.get(\"tellomsg\") | {};\n tstatus.cmdID = msg.payload.cmd;\n tstatus.seqID = msg.payload.seq;\n tstatus.pacType= msg.payload.pacType;\n global.set(\"tellomsg\",tstatus);\n}\nreturn msg;", 113 | "outputs": 1, 114 | "noerr": 0, 115 | "x": 320, 116 | "y": 280, 117 | "wires": [ 118 | [ 119 | "8df549.78793ab8" 120 | ] 121 | ] 122 | }, 123 | { 124 | "id": "9203aa1b.6b2c6", 125 | "type": "debug", 126 | "z": "865602f6.dc2338", 127 | "name": "Otherwise", 128 | "active": false, 129 | "tosidebar": true, 130 | "console": false, 131 | "tostatus": false, 132 | "complete": "true", 133 | "x": 700, 134 | "y": 380, 135 | "wires": [] 136 | }, 137 | { 138 | "id": "a1f903c9.9ca2d", 139 | "type": "comment", 140 | "z": "865602f6.dc2338", 141 | "name": "Receive Return Strings", 142 | "info": "", 143 | "x": 160, 144 | "y": 240, 145 | "wires": [] 146 | }, 147 | { 148 | "id": "43ff55ff.8f10e4", 149 | "type": "function", 150 | "z": "865602f6.dc2338", 151 | "name": "Send Con_Req", 152 | "func": "videoPort = 6038;\nmsg = {};\nmsg.payload = new Buffer(\"conn_req:lh\");\nmsg.payload[9] = videoPort & 0xFF;\nmsg.payload[10] = (videoPort >> 8) & 0xFF;\nglobal.set(\"seqID\", -1);\nreturn msg;", 153 | "outputs": 1, 154 | "noerr": 0, 155 | "x": 700, 156 | "y": 60, 157 | "wires": [ 158 | [ 159 | "5b0a7d4e.258d2c" 160 | ] 161 | ] 162 | }, 163 | { 164 | "id": "4603c589.6ed634", 165 | "type": "inject", 166 | "z": "865602f6.dc2338", 167 | "name": "TELLO_LOW_LEVEL_CONNECT", 168 | "topic": "", 169 | "payload": "true", 170 | "payloadType": "bool", 171 | "repeat": "", 172 | "crontab": "", 173 | "once": false, 174 | "onceDelay": "", 175 | "x": 200, 176 | "y": 60, 177 | "wires": [ 178 | [ 179 | "43ff55ff.8f10e4" 180 | ] 181 | ] 182 | }, 183 | { 184 | "id": "8df549.78793ab8", 185 | "type": "switch", 186 | "z": "865602f6.dc2338", 187 | "name": "", 188 | "property": "payload.cmd", 189 | "propertyType": "msg", 190 | "rules": [ 191 | { 192 | "t": "eq", 193 | "v": "86", 194 | "vt": "num" 195 | }, 196 | { 197 | "t": "eq", 198 | "v": "26", 199 | "vt": "num" 200 | }, 201 | { 202 | "t": "eq", 203 | "v": "53", 204 | "vt": "num" 205 | }, 206 | { 207 | "t": "eq", 208 | "v": "4176", 209 | "vt": "num" 210 | }, 211 | { 212 | "t": "eq", 213 | "v": "98", 214 | "vt": "num" 215 | }, 216 | { 217 | "t": "eq", 218 | "v": "99", 219 | "vt": "num" 220 | }, 221 | { 222 | "t": "eq", 223 | "v": "48", 224 | "vt": "num" 225 | }, 226 | { 227 | "t": "else" 228 | } 229 | ], 230 | "checkall": "true", 231 | "repair": false, 232 | "outputs": 8, 233 | "x": 510, 234 | "y": 280, 235 | "wires": [ 236 | [], 237 | [], 238 | [], 239 | [], 240 | [ 241 | "3f41ea5b.baa436" 242 | ], 243 | [ 244 | "2b644e1c.14938a" 245 | ], 246 | [ 247 | "ef29b4f9.1ae9" 248 | ], 249 | [ 250 | "9203aa1b.6b2c6" 251 | ] 252 | ], 253 | "outputLabels": [ 254 | "FlightStatus", 255 | "WifiStrength", 256 | "LightStrength", 257 | "LogHeader", 258 | "FileSize", 259 | "FileData", 260 | "DoTakePic", 261 | "otherwise" 262 | ] 263 | }, 264 | { 265 | "id": "ef29b4f9.1ae9", 266 | "type": "debug", 267 | "z": "865602f6.dc2338", 268 | "name": "DoTakePic", 269 | "active": false, 270 | "tosidebar": true, 271 | "console": false, 272 | "tostatus": false, 273 | "complete": "true", 274 | "x": 710, 275 | "y": 340, 276 | "wires": [] 277 | }, 278 | { 279 | "id": "aa8ecfdc.aa0128", 280 | "type": "link in", 281 | "z": "865602f6.dc2338", 282 | "name": "TELLO-SND_LOW_LEVEL_CMD", 283 | "links": [ 284 | "d9406e9d.313378" 285 | ], 286 | "x": 555, 287 | "y": 140, 288 | "wires": [ 289 | [ 290 | "d8f0f1c9.9d5b6" 291 | ] 292 | ] 293 | }, 294 | { 295 | "id": "3f41ea5b.baa436", 296 | "type": "function", 297 | "z": "865602f6.dc2338", 298 | "name": "unpack FileSize message", 299 | "func": "// Ignore if not from drone\nif (0x80 & msg.payload.pacType) {\n msg.payload.fileType = msg.payload.data.readInt8(0);\n msg.payload.fileSize = msg.payload.data.readInt32LE(1);\n msg.payload.fileID = msg.payload.data.readInt16LE(5);\n flow.set(\"file\" + msg.payload.fileID, {\"size\" : msg.payload.fileSize, \n \"receivedBytes\" : 0,\n \"imageBytes\" : Buffer.alloc(msg.payload.fileSize),\n \"pieces\" : {}\n });\n return msg;\n}", 300 | "outputs": 1, 301 | "noerr": 0, 302 | "x": 750, 303 | "y": 260, 304 | "wires": [ 305 | [ 306 | "c6da806c.43e838" 307 | ] 308 | ] 309 | }, 310 | { 311 | "id": "c6da806c.43e838", 312 | "type": "change", 313 | "z": "865602f6.dc2338", 314 | "name": "Ack FileSizeMsg", 315 | "rules": [ 316 | { 317 | "t": "set", 318 | "p": "payload", 319 | "pt": "msg", 320 | "to": "{\"TelloCmdId\":98,\"data\":[0],\"packType\":2}", 321 | "tot": "json" 322 | } 323 | ], 324 | "action": "", 325 | "property": "", 326 | "from": "", 327 | "to": "", 328 | "reg": false, 329 | "x": 980, 330 | "y": 260, 331 | "wires": [ 332 | [ 333 | "d9406e9d.313378" 334 | ] 335 | ] 336 | }, 337 | { 338 | "id": "d9406e9d.313378", 339 | "type": "link out", 340 | "z": "865602f6.dc2338", 341 | "name": "", 342 | "links": [ 343 | "aa8ecfdc.aa0128" 344 | ], 345 | "x": 1135, 346 | "y": 280, 347 | "wires": [] 348 | }, 349 | { 350 | "id": "2b644e1c.14938a", 351 | "type": "function", 352 | "z": "865602f6.dc2338", 353 | "name": "Unpack FileData message", 354 | "func": "var fileID = msg.payload.data.readInt16LE(0);\nvar filePiece = msg.payload.data.readInt32LE(2);\nvar fileChunk = msg.payload.data.readInt32LE(6);\nvar chunkLen = msg.payload.data.readInt16LE(10);\nvar file = flow.get(\"file\" + fileID);\nif ((!file) || (\"undefined\" === typeof file.receivedBytes)) {\n msg.payload = {};\n} else {\n msg.payload.data.copy(file.imageBytes, 1024*fileChunk, 12);\n msg.payload = {};\n msg.payload.unpackedHeader = {\"ID\" : fileID, \n \"piece\" : filePiece,\n \"chunk\" : fileChunk,\n \"len\" : chunkLen\n };\n if (file.pieces[filePiece]) {\n if (!file.pieces[filePiece].includes(fileChunk)) {\n file.pieces[filePiece].push(fileChunk);\n file.receivedBytes += chunkLen;\n if (8 == file.pieces[filePiece].length) {\n msg.payload.TelloCmdId = 99;\n msg.payload.packType = 2;\n msg.payload.data = Buffer.alloc(7);\n msg.payload.data.writeInt8(0, 0);\n msg.payload.data.writeInt16LE(fileID, 1);\n msg.payload.data.writeInt32LE(filePiece, 3);\n }\n }\n } else {\n msg.payload = {};\n msg.payload.unpackedHeader = {\"ID\" : fileID, \n \"piece\" : filePiece,\n \"chunk\" : fileChunk,\n \"len\" : chunkLen\n };\n file.pieces[filePiece] = [fileChunk];\n file.receivedBytes += chunkLen;\n }\n msg.payload.unpackedHeader.receivedBytes = file.receivedBytes;\n \n if (file.receivedBytes == file.size) {\n flow.set ([\"file\" + fileID], null);\n msg.payload.TelloCmdId = 99;\n msg.payload.packType = 2;\n msg.payload.data = Buffer.alloc(7);\n msg.payload.data.writeInt8(1, 0);\n msg.payload.data.writeInt16LE(fileID, 1);\n msg.payload.data.writeInt32LE(filePiece, 3);\n \n msg1 = {\"payload\" : {}};\n msg1.payload.TelloCmdId = 100;\n msg1.payload.packType = 1;\n return [[msg, msg1], {\"payload\":file.imageBytes}];\n } else if (msg.payload.TelloCmdId) {\n flow.set(\"file\" + fileID, file);\n return [msg, null];\n } else {\n flow.set(\"file\" + fileID, file);\n return [null, null];\n }\n}", 355 | "outputs": 2, 356 | "noerr": 0, 357 | "x": 760, 358 | "y": 300, 359 | "wires": [ 360 | [ 361 | "d9406e9d.313378" 362 | ], 363 | [ 364 | "7deed9e0.4b1ec" 365 | ] 366 | ], 367 | "outputLabels": [ 368 | "completedPieceAck", 369 | "Received image" 370 | ] 371 | }, 372 | { 373 | "id": "a40bb8cc.dae26", 374 | "type": "ui_template", 375 | "z": "865602f6.dc2338", 376 | "group": "230dc716.b089d", 377 | "name": "Photo", 378 | "order": 2, 379 | "width": "12", 380 | "height": "9", 381 | "format": "
\n\"Watson", 382 | "storeOutMessages": false, 383 | "fwdInMessages": false, 384 | "templateScope": "local", 385 | "x": 710, 386 | "y": 500, 387 | "wires": [ 388 | [] 389 | ] 390 | }, 391 | { 392 | "id": "6376d619.fd6d28", 393 | "type": "change", 394 | "z": "865602f6.dc2338", 395 | "name": "", 396 | "rules": [ 397 | { 398 | "t": "set", 399 | "p": "template", 400 | "pt": "msg", 401 | "to": "
\"Watson", 402 | "tot": "str" 403 | } 404 | ], 405 | "action": "", 406 | "property": "", 407 | "from": "", 408 | "to": "", 409 | "reg": false, 410 | "x": 510, 411 | "y": 500, 412 | "wires": [ 413 | [ 414 | "a40bb8cc.dae26" 415 | ] 416 | ] 417 | }, 418 | { 419 | "id": "22e77fcb.046f6", 420 | "type": "base64", 421 | "z": "865602f6.dc2338", 422 | "name": "Encode", 423 | "x": 260, 424 | "y": 500, 425 | "wires": [ 426 | [ 427 | "6376d619.fd6d28" 428 | ] 429 | ] 430 | }, 431 | { 432 | "id": "eda2f263.8283c8", 433 | "type": "ui_button", 434 | "z": "865602f6.dc2338", 435 | "name": "", 436 | "group": "230dc716.b089d", 437 | "order": 1, 438 | "width": 0, 439 | "height": 0, 440 | "passthru": false, 441 | "label": "Take Picture", 442 | "color": "", 443 | "bgcolor": "", 444 | "icon": "", 445 | "payload": "", 446 | "payloadType": "str", 447 | "topic": "", 448 | "x": 210, 449 | "y": 140, 450 | "wires": [ 451 | [ 452 | "cb99267f.854da8" 453 | ] 454 | ] 455 | }, 456 | { 457 | "id": "7deed9e0.4b1ec", 458 | "type": "link out", 459 | "z": "865602f6.dc2338", 460 | "name": "Picture Arrived", 461 | "links": [ 462 | "d64a7534.24e1e" 463 | ], 464 | "x": 1135, 465 | "y": 320, 466 | "wires": [] 467 | }, 468 | { 469 | "id": "d64a7534.24e1e", 470 | "type": "link in", 471 | "z": "865602f6.dc2338", 472 | "name": "Picture Processing", 473 | "links": [ 474 | "7deed9e0.4b1ec" 475 | ], 476 | "x": 75, 477 | "y": 520, 478 | "wires": [ 479 | [ 480 | "22e77fcb.046f6", 481 | "260d5e26.d8e4c2" 482 | ] 483 | ] 484 | }, 485 | { 486 | "id": "260d5e26.d8e4c2", 487 | "type": "visual-recognition-v3", 488 | "z": "865602f6.dc2338", 489 | "name": "", 490 | "vr-service-endpoint": "https://gateway.watsonplatform.net/visual-recognition/api", 491 | "image-feature": "classifyImage", 492 | "lang": "en", 493 | "x": 290, 494 | "y": 560, 495 | "wires": [ 496 | [ 497 | "b3708723.4334d", 498 | "9b3be2d6.9aee6" 499 | ] 500 | ] 501 | }, 502 | { 503 | "id": "b3708723.4334d", 504 | "type": "function", 505 | "z": "865602f6.dc2338", 506 | "name": "Process Results", 507 | "func": "if (typeof msg.result == 'undefined') {\n return null;\n}\n\nif (typeof msg.result.error != 'undefined') {\n //The Lite Plan allows users to make 7,500 API calls for free\n // Daily limit is (up to 250 calls per day) \n // {\"status\":\"ERROR\",\"statusInfo\":\"Key is over transaction limit\"}\n msg.template = msg.result.error.message;\n return msg;\n}\n\n// Text Extraction\nif (typeof msg.result.images[0].text != 'undefined') {\n var image_text = msg.result.images[0].text;\n msg.payload = image_text;\n msg.template = image_text;\n if( image_text.length >0 ) {\n msg.template= \"Watson found the words: \"+image_text;\n }\n return msg;\n}\n\nvar bestcolor = -1;\nvar colorscore = 0;\nvar c_id = 0;\nvar say = \"\";\nvar item;\n\nfor ( c_id=0; c_id < (msg.result.images[0].classifiers.length); c_id++ ){\n // find the best color, if any\n for( i =0; i<(msg.result.images[0].classifiers[c_id].classes.length); i++ ){\n var object = msg.result.images[0].classifiers[c_id].classes[i].class;\n if ( object.includes(\"color\") ) {\n if( msg.result.images[0].classifiers[c_id].classes[i].score > colorscore){\n bestcolor = i;\n colorscore = msg.result.images[0].classifiers[c_id].classes[i].score;\n }\n }\n }\n \n var bestItem = 0;\n var itemScore = 0;\n for( i =0; i<(msg.result.images[0].classifiers[c_id].classes.length); i++ ){\n var object = msg.result.images[0].classifiers[c_id].classes[i].class;\n if ( !object.includes(\"color\") ) {\n if( msg.result.images[0].classifiers[c_id].classes[i].score > itemScore){\n// bestItem = i;\n bestItem = 0;\n itemScore = msg.result.images[0].classifiers[c_id].classes[i].score;\n }\n }\n }\n \n if( bestcolor != \"-1\") {\n // found a color\n item = msg.result.images[0].classifiers[c_id].classes[bestcolor].class + \" \" + msg.result.images[0].classifiers[c_id].classes[bestItem].class;\n bestcolor = -1;\n } else {\n item = msg.result.images[0].classifiers[c_id].classes[bestItem].class;\n }\n// say = say + \" Watson's \" + msg.result.images[0].classifiers[c_id].name + \" classifier thinks this picture contains a \" + item +\".\";\n say = say + \" Watson thinks this picture contains a \" + item +\".\";\n}\nmsg.payload = say;\n\nvar picInfo = msg.result.images[0].classifiers[0].classes;\nvar arrayLength = picInfo.length;\n\nmsg.template=\"\";\n\nmsg.template=msg.template+\"

\"+say+\"

\";\nfor (var i = 0; i < arrayLength; i++) {\n msg.template = msg.template + \"\";\n}\nmsg.template = msg.template + \"
ClassConfidence
\" + picInfo[i].class + \"\" + picInfo[i].score + \"
\";\n\nreturn msg;", 508 | "outputs": 1, 509 | "noerr": 0, 510 | "x": 500, 511 | "y": 560, 512 | "wires": [ 513 | [ 514 | "21d0b160.4f3b36", 515 | "5834c0fd.031ec" 516 | ] 517 | ] 518 | }, 519 | { 520 | "id": "9b3be2d6.9aee6", 521 | "type": "debug", 522 | "z": "865602f6.dc2338", 523 | "name": "", 524 | "active": true, 525 | "console": "false", 526 | "complete": "result", 527 | "x": 490, 528 | "y": 600, 529 | "wires": [] 530 | }, 531 | { 532 | "id": "5834c0fd.031ec", 533 | "type": "ui_template", 534 | "z": "865602f6.dc2338", 535 | "group": "90432f75.726208", 536 | "name": "Results Table", 537 | "order": 1, 538 | "width": "9", 539 | "height": "10", 540 | "format": "", 541 | "storeOutMessages": true, 542 | "fwdInMessages": true, 543 | "templateScope": "local", 544 | "x": 740, 545 | "y": 560, 546 | "wires": [ 547 | [] 548 | ] 549 | }, 550 | { 551 | "id": "21d0b160.4f3b36", 552 | "type": "debug", 553 | "z": "865602f6.dc2338", 554 | "name": "What did Watson find?", 555 | "active": true, 556 | "console": "false", 557 | "complete": "payload", 558 | "x": 760, 559 | "y": 600, 560 | "wires": [] 561 | }, 562 | { 563 | "id": "230dc716.b089d", 564 | "type": "ui_group", 565 | "z": "", 566 | "name": "Drone Image", 567 | "tab": "192b6a33.f665ce", 568 | "order": 1, 569 | "disp": true, 570 | "width": "12", 571 | "collapse": false 572 | }, 573 | { 574 | "id": "90432f75.726208", 575 | "type": "ui_group", 576 | "z": "", 577 | "name": "Results", 578 | "tab": "192b6a33.f665ce", 579 | "order": 2, 580 | "disp": true, 581 | "width": "9" 582 | }, 583 | { 584 | "id": "192b6a33.f665ce", 585 | "type": "ui_tab", 586 | "z": "", 587 | "name": "Tello Camera", 588 | "icon": "fa-tachometer", 589 | "order": 5 590 | } 591 | ] 592 | -------------------------------------------------------------------------------- /TOBEREMOVED/nodered-tello-controls.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "a072a174.a07318", 4 | "type": "tab", 5 | "label": "Tello Controls", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "eb68f2c7.63cf5", 11 | "type": "change", 12 | "z": "a072a174.a07318", 13 | "name": "Init", 14 | "rules": [ 15 | { 16 | "t": "delete", 17 | "p": "payload", 18 | "pt": "msg" 19 | }, 20 | { 21 | "t": "set", 22 | "p": "payload.tellocmd", 23 | "pt": "msg", 24 | "to": "command", 25 | "tot": "str" 26 | }, 27 | { 28 | "t": "set", 29 | "p": "payload.tellovalue", 30 | "pt": "msg", 31 | "to": "0", 32 | "tot": "str" 33 | } 34 | ], 35 | "action": "", 36 | "property": "", 37 | "from": "", 38 | "to": "", 39 | "reg": false, 40 | "x": 370, 41 | "y": 260, 42 | "wires": [ 43 | [ 44 | "661694ec.caab84", 45 | "34e43013.bc4b38", 46 | "d2e59cb6.21fb98" 47 | ] 48 | ] 49 | }, 50 | { 51 | "id": "f6a2364d.bac908", 52 | "type": "change", 53 | "z": "a072a174.a07318", 54 | "name": "TakeOff", 55 | "rules": [ 56 | { 57 | "t": "delete", 58 | "p": "payload", 59 | "pt": "msg" 60 | }, 61 | { 62 | "t": "set", 63 | "p": "payload.tellocmd", 64 | "pt": "msg", 65 | "to": "takeoff", 66 | "tot": "str" 67 | }, 68 | { 69 | "t": "set", 70 | "p": "payload.tellovalue", 71 | "pt": "msg", 72 | "to": "0", 73 | "tot": "str" 74 | } 75 | ], 76 | "action": "", 77 | "property": "", 78 | "from": "", 79 | "to": "", 80 | "reg": false, 81 | "x": 380, 82 | "y": 300, 83 | "wires": [ 84 | [ 85 | "661694ec.caab84", 86 | "d2e59cb6.21fb98" 87 | ] 88 | ] 89 | }, 90 | { 91 | "id": "108620de.169def", 92 | "type": "change", 93 | "z": "a072a174.a07318", 94 | "name": "Up 50cm", 95 | "rules": [ 96 | { 97 | "t": "delete", 98 | "p": "payload", 99 | "pt": "msg" 100 | }, 101 | { 102 | "t": "set", 103 | "p": "payload.tellocmd", 104 | "pt": "msg", 105 | "to": "up", 106 | "tot": "str" 107 | }, 108 | { 109 | "t": "set", 110 | "p": "payload.tellovalue", 111 | "pt": "msg", 112 | "to": "50", 113 | "tot": "str" 114 | } 115 | ], 116 | "action": "", 117 | "property": "", 118 | "from": "", 119 | "to": "", 120 | "reg": false, 121 | "x": 380, 122 | "y": 340, 123 | "wires": [ 124 | [ 125 | "d2e59cb6.21fb98" 126 | ] 127 | ] 128 | }, 129 | { 130 | "id": "e9ae399.91724c8", 131 | "type": "change", 132 | "z": "a072a174.a07318", 133 | "name": "Right 50cm", 134 | "rules": [ 135 | { 136 | "t": "delete", 137 | "p": "payload", 138 | "pt": "msg" 139 | }, 140 | { 141 | "t": "set", 142 | "p": "payload.tellocmd", 143 | "pt": "msg", 144 | "to": "right", 145 | "tot": "str" 146 | }, 147 | { 148 | "t": "set", 149 | "p": "payload.tellovalue", 150 | "pt": "msg", 151 | "to": "50", 152 | "tot": "str" 153 | } 154 | ], 155 | "action": "", 156 | "property": "", 157 | "from": "", 158 | "to": "", 159 | "reg": false, 160 | "x": 390, 161 | "y": 420, 162 | "wires": [ 163 | [ 164 | "d2e59cb6.21fb98" 165 | ] 166 | ] 167 | }, 168 | { 169 | "id": "af25dfd9.09f25", 170 | "type": "change", 171 | "z": "a072a174.a07318", 172 | "name": "Forward 50cm", 173 | "rules": [ 174 | { 175 | "t": "delete", 176 | "p": "payload", 177 | "pt": "msg" 178 | }, 179 | { 180 | "t": "set", 181 | "p": "payload.tellocmd", 182 | "pt": "msg", 183 | "to": "forward", 184 | "tot": "str" 185 | }, 186 | { 187 | "t": "set", 188 | "p": "payload.tellovalue", 189 | "pt": "msg", 190 | "to": "50", 191 | "tot": "str" 192 | } 193 | ], 194 | "action": "", 195 | "property": "", 196 | "from": "", 197 | "to": "", 198 | "reg": false, 199 | "x": 400, 200 | "y": 500, 201 | "wires": [ 202 | [ 203 | "d2e59cb6.21fb98" 204 | ] 205 | ] 206 | }, 207 | { 208 | "id": "7202ae05.e17e78", 209 | "type": "change", 210 | "z": "a072a174.a07318", 211 | "name": "Down 50cm", 212 | "rules": [ 213 | { 214 | "t": "delete", 215 | "p": "payload", 216 | "pt": "msg" 217 | }, 218 | { 219 | "t": "set", 220 | "p": "payload.tellocmd", 221 | "pt": "msg", 222 | "to": "down", 223 | "tot": "str" 224 | }, 225 | { 226 | "t": "set", 227 | "p": "payload.tellovalue", 228 | "pt": "msg", 229 | "to": "50", 230 | "tot": "str" 231 | } 232 | ], 233 | "action": "", 234 | "property": "", 235 | "from": "", 236 | "to": "", 237 | "reg": false, 238 | "x": 390, 239 | "y": 380, 240 | "wires": [ 241 | [ 242 | "d2e59cb6.21fb98" 243 | ] 244 | ] 245 | }, 246 | { 247 | "id": "ae2f2dda.c5088", 248 | "type": "change", 249 | "z": "a072a174.a07318", 250 | "name": "Left 50cm", 251 | "rules": [ 252 | { 253 | "t": "delete", 254 | "p": "payload", 255 | "pt": "msg" 256 | }, 257 | { 258 | "t": "set", 259 | "p": "payload.tellocmd", 260 | "pt": "msg", 261 | "to": "left", 262 | "tot": "str" 263 | }, 264 | { 265 | "t": "set", 266 | "p": "payload.tellovalue", 267 | "pt": "msg", 268 | "to": "50", 269 | "tot": "str" 270 | } 271 | ], 272 | "action": "", 273 | "property": "", 274 | "from": "", 275 | "to": "", 276 | "reg": false, 277 | "x": 380, 278 | "y": 460, 279 | "wires": [ 280 | [ 281 | "d2e59cb6.21fb98" 282 | ] 283 | ] 284 | }, 285 | { 286 | "id": "39cc6e40.f7bf8a", 287 | "type": "change", 288 | "z": "a072a174.a07318", 289 | "name": "Back 50cm", 290 | "rules": [ 291 | { 292 | "t": "delete", 293 | "p": "payload", 294 | "pt": "msg" 295 | }, 296 | { 297 | "t": "set", 298 | "p": "payload.tellocmd", 299 | "pt": "msg", 300 | "to": "back", 301 | "tot": "str" 302 | }, 303 | { 304 | "t": "set", 305 | "p": "payload.tellovalue", 306 | "pt": "msg", 307 | "to": "50", 308 | "tot": "str" 309 | } 310 | ], 311 | "action": "", 312 | "property": "", 313 | "from": "", 314 | "to": "", 315 | "reg": false, 316 | "x": 390, 317 | "y": 540, 318 | "wires": [ 319 | [ 320 | "d2e59cb6.21fb98" 321 | ] 322 | ] 323 | }, 324 | { 325 | "id": "a70dedb4.c9442", 326 | "type": "change", 327 | "z": "a072a174.a07318", 328 | "name": "cw 90º", 329 | "rules": [ 330 | { 331 | "t": "delete", 332 | "p": "payload", 333 | "pt": "msg" 334 | }, 335 | { 336 | "t": "set", 337 | "p": "payload.tellocmd", 338 | "pt": "msg", 339 | "to": "cw", 340 | "tot": "str" 341 | }, 342 | { 343 | "t": "set", 344 | "p": "payload.tellovalue", 345 | "pt": "msg", 346 | "to": "90", 347 | "tot": "str" 348 | } 349 | ], 350 | "action": "", 351 | "property": "", 352 | "from": "", 353 | "to": "", 354 | "reg": false, 355 | "x": 370, 356 | "y": 580, 357 | "wires": [ 358 | [ 359 | "d2e59cb6.21fb98" 360 | ] 361 | ] 362 | }, 363 | { 364 | "id": "e079685.6119718", 365 | "type": "change", 366 | "z": "a072a174.a07318", 367 | "name": "ccw 90º", 368 | "rules": [ 369 | { 370 | "t": "delete", 371 | "p": "payload", 372 | "pt": "msg" 373 | }, 374 | { 375 | "t": "set", 376 | "p": "payload.tellocmd", 377 | "pt": "msg", 378 | "to": "ccw", 379 | "tot": "str" 380 | }, 381 | { 382 | "t": "set", 383 | "p": "payload.tellovalue", 384 | "pt": "msg", 385 | "to": "90", 386 | "tot": "str" 387 | } 388 | ], 389 | "action": "", 390 | "property": "", 391 | "from": "", 392 | "to": "", 393 | "reg": false, 394 | "x": 380, 395 | "y": 620, 396 | "wires": [ 397 | [ 398 | "d2e59cb6.21fb98" 399 | ] 400 | ] 401 | }, 402 | { 403 | "id": "9c773423.e86258", 404 | "type": "change", 405 | "z": "a072a174.a07318", 406 | "name": "Flip f", 407 | "rules": [ 408 | { 409 | "t": "delete", 410 | "p": "payload", 411 | "pt": "msg" 412 | }, 413 | { 414 | "t": "set", 415 | "p": "payload.tellocmd", 416 | "pt": "msg", 417 | "to": "flip", 418 | "tot": "str" 419 | }, 420 | { 421 | "t": "set", 422 | "p": "payload.tellovalue", 423 | "pt": "msg", 424 | "to": "f", 425 | "tot": "str" 426 | } 427 | ], 428 | "action": "", 429 | "property": "", 430 | "from": "", 431 | "to": "", 432 | "reg": false, 433 | "x": 370, 434 | "y": 700, 435 | "wires": [ 436 | [ 437 | "d2e59cb6.21fb98" 438 | ] 439 | ] 440 | }, 441 | { 442 | "id": "cdb0b8e2.9ecce", 443 | "type": "change", 444 | "z": "a072a174.a07318", 445 | "name": "battery?", 446 | "rules": [ 447 | { 448 | "t": "delete", 449 | "p": "payload", 450 | "pt": "msg" 451 | }, 452 | { 453 | "t": "set", 454 | "p": "payload.tellocmd", 455 | "pt": "msg", 456 | "to": "battery?", 457 | "tot": "str" 458 | }, 459 | { 460 | "t": "set", 461 | "p": "payload.tellovalue", 462 | "pt": "msg", 463 | "to": "0", 464 | "tot": "str" 465 | }, 466 | { 467 | "t": "set", 468 | "p": "tellorequestbattery", 469 | "pt": "global", 470 | "to": "true", 471 | "tot": "bool" 472 | } 473 | ], 474 | "action": "", 475 | "property": "", 476 | "from": "", 477 | "to": "", 478 | "reg": false, 479 | "x": 380, 480 | "y": 780, 481 | "wires": [ 482 | [ 483 | "d2e59cb6.21fb98" 484 | ] 485 | ] 486 | }, 487 | { 488 | "id": "c2227156.48aa2", 489 | "type": "inject", 490 | "z": "a072a174.a07318", 491 | "name": "Land", 492 | "topic": "", 493 | "payload": "true", 494 | "payloadType": "bool", 495 | "repeat": "", 496 | "crontab": "", 497 | "once": false, 498 | "onceDelay": "", 499 | "x": 170, 500 | "y": 220, 501 | "wires": [ 502 | [ 503 | "77bf63bb.fff2a4" 504 | ] 505 | ] 506 | }, 507 | { 508 | "id": "630d40f4.cede3", 509 | "type": "inject", 510 | "z": "a072a174.a07318", 511 | "name": "TakeOff", 512 | "topic": "", 513 | "payload": "true", 514 | "payloadType": "bool", 515 | "repeat": "", 516 | "crontab": "", 517 | "once": false, 518 | "onceDelay": "", 519 | "x": 170, 520 | "y": 260, 521 | "wires": [ 522 | [ 523 | "eb68f2c7.63cf5" 524 | ] 525 | ] 526 | }, 527 | { 528 | "id": "cb86e38b.546f5", 529 | "type": "inject", 530 | "z": "a072a174.a07318", 531 | "name": "Up 50cm", 532 | "topic": "", 533 | "payload": "true", 534 | "payloadType": "bool", 535 | "repeat": "", 536 | "crontab": "", 537 | "once": false, 538 | "onceDelay": "", 539 | "x": 180, 540 | "y": 340, 541 | "wires": [ 542 | [ 543 | "108620de.169def" 544 | ] 545 | ] 546 | }, 547 | { 548 | "id": "e3f98849.a126c8", 549 | "type": "inject", 550 | "z": "a072a174.a07318", 551 | "name": "Down 50cm", 552 | "topic": "", 553 | "payload": "true", 554 | "payloadType": "bool", 555 | "repeat": "", 556 | "crontab": "", 557 | "once": false, 558 | "onceDelay": "", 559 | "x": 190, 560 | "y": 380, 561 | "wires": [ 562 | [ 563 | "7202ae05.e17e78" 564 | ] 565 | ] 566 | }, 567 | { 568 | "id": "bf4727a0.be0108", 569 | "type": "inject", 570 | "z": "a072a174.a07318", 571 | "name": "Right 50cm", 572 | "topic": "", 573 | "payload": "true", 574 | "payloadType": "bool", 575 | "repeat": "", 576 | "crontab": "", 577 | "once": false, 578 | "onceDelay": "", 579 | "x": 190, 580 | "y": 420, 581 | "wires": [ 582 | [ 583 | "e9ae399.91724c8" 584 | ] 585 | ] 586 | }, 587 | { 588 | "id": "36d8f7e2.930fc", 589 | "type": "inject", 590 | "z": "a072a174.a07318", 591 | "name": "Left 50cm", 592 | "topic": "", 593 | "payload": "true", 594 | "payloadType": "bool", 595 | "repeat": "", 596 | "crontab": "", 597 | "once": false, 598 | "onceDelay": "", 599 | "x": 180, 600 | "y": 460, 601 | "wires": [ 602 | [ 603 | "ae2f2dda.c5088" 604 | ] 605 | ] 606 | }, 607 | { 608 | "id": "a82894de.14784", 609 | "type": "inject", 610 | "z": "a072a174.a07318", 611 | "name": "Forward 50cm", 612 | "topic": "", 613 | "payload": "true", 614 | "payloadType": "bool", 615 | "repeat": "", 616 | "crontab": "", 617 | "once": false, 618 | "onceDelay": "", 619 | "x": 190, 620 | "y": 500, 621 | "wires": [ 622 | [ 623 | "af25dfd9.09f25" 624 | ] 625 | ] 626 | }, 627 | { 628 | "id": "6b3b14ac.835f6c", 629 | "type": "inject", 630 | "z": "a072a174.a07318", 631 | "name": "Back 50cm", 632 | "topic": "", 633 | "payload": "true", 634 | "payloadType": "bool", 635 | "repeat": "", 636 | "crontab": "", 637 | "once": false, 638 | "onceDelay": "", 639 | "x": 180, 640 | "y": 540, 641 | "wires": [ 642 | [ 643 | "39cc6e40.f7bf8a" 644 | ] 645 | ] 646 | }, 647 | { 648 | "id": "328bddb2.f08af2", 649 | "type": "inject", 650 | "z": "a072a174.a07318", 651 | "name": "cw 90º", 652 | "topic": "", 653 | "payload": "true", 654 | "payloadType": "bool", 655 | "repeat": "", 656 | "crontab": "", 657 | "once": false, 658 | "onceDelay": "", 659 | "x": 170, 660 | "y": 580, 661 | "wires": [ 662 | [ 663 | "a70dedb4.c9442" 664 | ] 665 | ] 666 | }, 667 | { 668 | "id": "9cb66ed3.d93ff8", 669 | "type": "inject", 670 | "z": "a072a174.a07318", 671 | "name": "Full 360º", 672 | "topic": "", 673 | "payload": "true", 674 | "payloadType": "bool", 675 | "repeat": "", 676 | "crontab": "", 677 | "once": false, 678 | "onceDelay": "", 679 | "x": 180, 680 | "y": 660, 681 | "wires": [ 682 | [ 683 | "9841e501.9d6cd8" 684 | ] 685 | ] 686 | }, 687 | { 688 | "id": "811e97a7.d68e18", 689 | "type": "inject", 690 | "z": "a072a174.a07318", 691 | "name": "Flip forward", 692 | "topic": "", 693 | "payload": "true", 694 | "payloadType": "bool", 695 | "repeat": "", 696 | "crontab": "", 697 | "once": false, 698 | "onceDelay": "", 699 | "x": 190, 700 | "y": 700, 701 | "wires": [ 702 | [ 703 | "9c773423.e86258" 704 | ] 705 | ] 706 | }, 707 | { 708 | "id": "76c620f8.f98658", 709 | "type": "inject", 710 | "z": "a072a174.a07318", 711 | "name": "Battery?", 712 | "topic": "", 713 | "payload": "true", 714 | "payloadType": "bool", 715 | "repeat": "", 716 | "crontab": "", 717 | "once": false, 718 | "onceDelay": "", 719 | "x": 180, 720 | "y": 780, 721 | "wires": [ 722 | [ 723 | "cdb0b8e2.9ecce" 724 | ] 725 | ] 726 | }, 727 | { 728 | "id": "3156f513.44061a", 729 | "type": "inject", 730 | "z": "a072a174.a07318", 731 | "name": "ccw 90º", 732 | "topic": "", 733 | "payload": "true", 734 | "payloadType": "bool", 735 | "repeat": "", 736 | "crontab": "", 737 | "once": false, 738 | "onceDelay": "", 739 | "x": 170, 740 | "y": 620, 741 | "wires": [ 742 | [ 743 | "e079685.6119718" 744 | ] 745 | ] 746 | }, 747 | { 748 | "id": "34e43013.bc4b38", 749 | "type": "delay", 750 | "z": "a072a174.a07318", 751 | "name": "", 752 | "pauseType": "delay", 753 | "timeout": "250", 754 | "timeoutUnits": "milliseconds", 755 | "rate": "1", 756 | "nbRateUnits": "1", 757 | "rateUnits": "second", 758 | "randomFirst": "1", 759 | "randomLast": "5", 760 | "randomUnits": "seconds", 761 | "drop": false, 762 | "x": 190, 763 | "y": 300, 764 | "wires": [ 765 | [ 766 | "f6a2364d.bac908" 767 | ] 768 | ] 769 | }, 770 | { 771 | "id": "9841e501.9d6cd8", 772 | "type": "change", 773 | "z": "a072a174.a07318", 774 | "name": "cw 360º", 775 | "rules": [ 776 | { 777 | "t": "delete", 778 | "p": "payload", 779 | "pt": "msg" 780 | }, 781 | { 782 | "t": "set", 783 | "p": "payload.tellocmd", 784 | "pt": "msg", 785 | "to": "cw", 786 | "tot": "str" 787 | }, 788 | { 789 | "t": "set", 790 | "p": "payload.tellovalue", 791 | "pt": "msg", 792 | "to": "360", 793 | "tot": "str" 794 | } 795 | ], 796 | "action": "", 797 | "property": "", 798 | "from": "", 799 | "to": "", 800 | "reg": false, 801 | "x": 380, 802 | "y": 660, 803 | "wires": [ 804 | [ 805 | "d2e59cb6.21fb98" 806 | ] 807 | ] 808 | }, 809 | { 810 | "id": "77bf63bb.fff2a4", 811 | "type": "change", 812 | "z": "a072a174.a07318", 813 | "name": "Land", 814 | "rules": [ 815 | { 816 | "t": "delete", 817 | "p": "payload", 818 | "pt": "msg" 819 | }, 820 | { 821 | "t": "set", 822 | "p": "payload.tellocmd", 823 | "pt": "msg", 824 | "to": "land", 825 | "tot": "str" 826 | }, 827 | { 828 | "t": "set", 829 | "p": "payload.tellovalue", 830 | "pt": "msg", 831 | "to": "0", 832 | "tot": "str" 833 | } 834 | ], 835 | "action": "", 836 | "property": "", 837 | "from": "", 838 | "to": "", 839 | "reg": false, 840 | "x": 370, 841 | "y": 220, 842 | "wires": [ 843 | [ 844 | "d2e59cb6.21fb98" 845 | ] 846 | ] 847 | }, 848 | { 849 | "id": "5eba69c0.fa37f8", 850 | "type": "udp out", 851 | "z": "a072a174.a07318", 852 | "name": "", 853 | "addr": "192.168.10.1", 854 | "iface": "", 855 | "port": "8889", 856 | "ipv": "udp4", 857 | "outport": "52955", 858 | "base64": false, 859 | "multicast": "false", 860 | "x": 590, 861 | "y": 140, 862 | "wires": [] 863 | }, 864 | { 865 | "id": "e31dd730.d8883", 866 | "type": "debug", 867 | "z": "a072a174.a07318", 868 | "name": "", 869 | "active": true, 870 | "console": "false", 871 | "complete": "false", 872 | "x": 550, 873 | "y": 100, 874 | "wires": [] 875 | }, 876 | { 877 | "id": "661694ec.caab84", 878 | "type": "debug", 879 | "z": "a072a174.a07318", 880 | "name": "", 881 | "active": false, 882 | "tosidebar": true, 883 | "console": false, 884 | "tostatus": false, 885 | "complete": "false", 886 | "x": 630, 887 | "y": 260, 888 | "wires": [] 889 | }, 890 | { 891 | "id": "59e4f77c.3cf28", 892 | "type": "comment", 893 | "z": "a072a174.a07318", 894 | "name": "1 - Connect to TELLO-NNNNNN WiFi", 895 | "info": "", 896 | "x": 200, 897 | "y": 40, 898 | "wires": [] 899 | }, 900 | { 901 | "id": "90aa51a8.563c1", 902 | "type": "comment", 903 | "z": "a072a174.a07318", 904 | "name": "Status \"set\" commands", 905 | "info": "Send set command to Tello and wait for its response.\nPossible set commands:\n- speed?: get current speed (cm/s): x: 1-100\n- battery?: get current battery percentage: x: 0-100\n- time?: get current fly time (s): time\n- height?: get height (cm): x: 0-3000\n- temp?: get temperature (°C): x: 0-90\n- attitude?: get IMU attitude data: pitch roll yaw\n- baro?: get barometer value (m): x\n- tof?: get distance value from TOF (cm): x: 30-1000\n- wifi?: get Wi-Fi SNR: snr", 906 | "x": 220, 907 | "y": 740, 908 | "wires": [] 909 | }, 910 | { 911 | "id": "d2e59cb6.21fb98", 912 | "type": "link out", 913 | "z": "a072a174.a07318", 914 | "name": "TelloControls-Out", 915 | "links": [ 916 | "4fc01edb.7887f" 917 | ], 918 | "x": 595, 919 | "y": 500, 920 | "wires": [] 921 | }, 922 | { 923 | "id": "9697aaa4.dabd28", 924 | "type": "function", 925 | "z": "a072a174.a07318", 926 | "name": "Send Individual Tello Command", 927 | "func": "var telloaction ;\n\nif((msg.payload.tellovalue) && (msg.payload.tellovalue != \"0\")) {\n telloaction = new Buffer( msg.payload.tellocmd + ' '+ msg.payload.tellovalue );\n} else {\n telloaction = new Buffer( msg.payload.tellocmd );\n}\n\nmsg.payload = telloaction;\t\nreturn msg;", 928 | "outputs": 1, 929 | "noerr": 0, 930 | "x": 290, 931 | "y": 120, 932 | "wires": [ 933 | [ 934 | "e31dd730.d8883", 935 | "5eba69c0.fa37f8" 936 | ] 937 | ] 938 | }, 939 | { 940 | "id": "4fc01edb.7887f", 941 | "type": "link in", 942 | "z": "a072a174.a07318", 943 | "name": "TelloControls-In", 944 | "links": [ 945 | "d2e59cb6.21fb98" 946 | ], 947 | "x": 95, 948 | "y": 120, 949 | "wires": [ 950 | [ 951 | "9697aaa4.dabd28" 952 | ] 953 | ] 954 | }, 955 | { 956 | "id": "52522ad3.fca3dc", 957 | "type": "udp in", 958 | "z": "a072a174.a07318", 959 | "name": "", 960 | "iface": "", 961 | "port": "52955", 962 | "ipv": "udp4", 963 | "multicast": "false", 964 | "group": "", 965 | "datatype": "buffer", 966 | "x": 820, 967 | "y": 120, 968 | "wires": [ 969 | [ 970 | "bb62a0d0.d7c158", 971 | "b4ded02a.17cb7" 972 | ] 973 | ] 974 | }, 975 | { 976 | "id": "b4ded02a.17cb7", 977 | "type": "debug", 978 | "z": "a072a174.a07318", 979 | "name": "", 980 | "active": false, 981 | "tosidebar": true, 982 | "console": false, 983 | "complete": "true", 984 | "x": 970, 985 | "y": 160, 986 | "wires": [] 987 | }, 988 | { 989 | "id": "bb62a0d0.d7c158", 990 | "type": "function", 991 | "z": "a072a174.a07318", 992 | "name": "parse incoming messages", 993 | "func": "var TELLO_CMD_ALT_LIMIT = 4182;\nvar TELLO_CMD_LIGHT_STRENGTH = 53;\nvar TELLO_CMD_LOW_BATT_THRESHOLD = 4183;\nvar TELLO_CMD_STATUS = 86;\nvar TELLO_CMD_LOG_HEADER_WRITE = 4176;\nvar TELLO_CMD_LOG_DATA_WRITE = 4177;\nvar TELLO_CMD_LOG_CONFIGURATION = 4178;\nvar seqID ;\nvar pacType;\n\nfunction parsePacket (bb) {\n //\tvar data = Buffer.from(msg);\n\tvar size = bb.length;\n\tvar payload = bb.slice(9,size);\n\tvar dataSize = 0;\n\tvar cmdID = 0;\n\n\tif (bb.length >= 11) {\n\t\t\tvar mark = bb.readUInt8(0);\n\t\t\tif (mark == 0xCC) {\t// start of packet\n size = bb.readUInt16LE(1) >> 3;\n var crc8 = bb.readUInt8(3);\n pacType = bb.readUInt8(4);\n cmdID = bb.readUInt16LE(5);\n seqID = bb.readUInt16LE(7);\n dataSize = size - 11;\n var data = false;\n var crc16 = bb.readUInt16LE(size - 2);\n\t\t\t} \n\n\t\t\tif (cmdID == TELLO_CMD_STATUS) {\n //\tthis.parseStatus(bb);\n } else if (cmdID == TELLO_CMD_LOG_HEADER_WRITE) {\n node.warn(\"TELLO_CMD_LOG_HEADER_WRITE \" + TELLO_CMD_LOG_HEADER_WRITE);\n\t\t\t} else if (cmdID == TELLO_CMD_LOG_DATA_WRITE) {\n // \n\t\t\t} else if (cmdID == TELLO_CMD_ALT_LIMIT) { \n node.warn(\"TELLO_CMD_ALT_LIMIT \"+TELLO_CMD_ALT_LIMIT);\n if (payload.length > 0) \n node.warn(payload.readUInt16LE(1));\n\t\t\t} else if (cmdID == TELLO_CMD_LIGHT_STRENGTH) { \n node.warn(\"TELLO_CMD_LIGHT_STRENGTH \"+TELLO_CMD_LIGHT_STRENGTH);\n // if (payload.length > 0) \n node.warn(payload.readUInt16LE(1));\n // node.warn(payload)\n\t\t\t} else if (cmdID == TELLO_CMD_LOW_BATT_THRESHOLD) { \n node.warn(\"TELLO_CMD_LOW_BATT_THRESHOLD \"+TELLO_CMD_LOW_BATT_THRESHOLD);\n if (payload.length > 0)\n node.warn(payload.readUInt16LE(1));\n\t\t\t} else if (cmdID == 69 ) { //VERSION\n node.warn(\"TELLO_CMD_VERSION \"+cmdID);\n node.warn(payload);\n node.warn(payload.slice(1,20).toString());\n\t\t\t} else if (cmdID == TELLO_CMD_LOG_CONFIGURATION) { \n node.warn(\"TELLO_CMD_LOG_CONFIGURATION command detected\");\n\t\t\t} else {\n node.warn(\"DBG unknown CMD \"+ cmdID);\n\t\t\t}\n\t} else {\n node.warn('wrong packet', bb);\n\t}\n\treturn cmdID;\n}\n\nif( msg.payload.toString() == \"ok\" ) {\n\tmsg.payload = \"ok\";\n\treturn msg;\n}\n\nvar batterystatus = global.get(\"tellorequestbattery\") || false;\nif ( batterystatus === true ) {\n msg.payload = { \"battery\":msg.payload.toString().trim() };\n global.set(\"tellorequestbattery\", false); \n return msg;\n}\n\nmsg.cmdID = parsePacket(msg.payload);\nmsg.seqID = seqID;\nmsg.pacType= pacType;\nglobal.set(\"tellomsg\",msg);\nreturn msg;", 994 | "outputs": 1, 995 | "noerr": 0, 996 | "x": 1040, 997 | "y": 120, 998 | "wires": [ 999 | [ 1000 | "7a44c1b5.f7f698" 1001 | ] 1002 | ] 1003 | }, 1004 | { 1005 | "id": "7a44c1b5.f7f698", 1006 | "type": "debug", 1007 | "z": "a072a174.a07318", 1008 | "name": "", 1009 | "active": true, 1010 | "tosidebar": true, 1011 | "console": false, 1012 | "tostatus": false, 1013 | "complete": "false", 1014 | "x": 1270, 1015 | "y": 120, 1016 | "wires": [] 1017 | }, 1018 | { 1019 | "id": "46f72dda.035844", 1020 | "type": "comment", 1021 | "z": "a072a174.a07318", 1022 | "name": "Receive Return Strings", 1023 | "info": "", 1024 | "x": 860, 1025 | "y": 80, 1026 | "wires": [] 1027 | }, 1028 | { 1029 | "id": "71b94368.87f30c", 1030 | "type": "comment", 1031 | "z": "a072a174.a07318", 1032 | "name": "Send Commands", 1033 | "info": "", 1034 | "x": 160, 1035 | "y": 80, 1036 | "wires": [] 1037 | } 1038 | ] 1039 | --------------------------------------------------------------------------------