How to fly a drone with Node.js Part 2

This is part 2 of a three-part blog series explaining how I wrote some code to control the basic features of a DJI Ryze Tello drone. I set myself this challenge ahead of a hackathon event held at our offices in Swansea, UK. In part 1 I explained how to connect to the drone and send commands to it to enable the drone to do tasks such as take off and land. You can read part 1 here. Below I take things one step forward allowing you to fly the drone in various directions.

Directional movement

Getting the drone to move in a specified direction is a very similar process to what we have already done but with one slight difference. The directional commands “left”, “right”, “forward” and “back” for the drone each allow an integer to be specified after the command, the integer is how many centimetres the drone will move in the specified direction.

First, we need to refactor our handleInput  function a little, as we will now be sending a value from 20 to 200 after some of our commands, writing a select to handle each and every possible combination we could send is a bad idea. Instead, we will use the string.startsWith  method to check that our line starts with a keyword such as “forward” or “left” and then take the amount from the end of the line using string.split.

Unfortunately, we can’t just add a boolean expression case to our switch due to the way switch works. In short, this is because it uses the strict equality operator (===) to check the value of the argument against whatever is on the right-hand side of the case keyword, this means no functions or boolean expressions will be evaluated.

Our way around this slight blocker is to go back to a good old if statement. First, let’s cater for the functionality that we already have. What I like to do here is to create simple functions that encapsulate the boolean logic that we can re-use, so I created functions like below:

function isTakeoff(line) {
    return line === "takeoff";
}
function isLand(line) {
    return line === "land";
}

The functions take in the line, and will simply return either true or false depending on how the boolean expression is evaluated.

We can then create a series of if statements using these functions and use them to execute our sendTakeoff  and sendLand  functions like below:

if(isTakeoff(line)) {
    try{
        await sendTakeOff(socket);
    catch (err) {
        console.log(err);
    }
};
if(isLand(line)) {
    try {
        await sendLand(socket);
    catch (err) {
        console.log(err);
    }
}

Once that refactoring is done the handleInput  function should look something like this

async function handleInput(line, socket) {
    function isTakeoff(line) {
        return line === "takeoff";
    }
    function isLand(line) {
        return line === "land";
    }
    if(isTakeoff(line)) {
        try{
            await sendTakeOff(socket);
        catch (err) {
            console.log(err);
        }
    };
    if(isLand(line)) {
        try {
            await sendLand(socket);
        catch (err) {
            console.log(err);
        }
    }
}

I’ve declared the isTakeoff  and isLand  functions within the scope of handleInput just for the sake of keeping everything together.

Now we can create a function to detect when we are sending a forward command to the drone. We can create our sendForward  function, this will be very similar to our sendTakeoff  and sendLand  functions that we created earlier, but this time we will add a distance parameter with a default value of 20. This is so that if a user neglects to send the command with a distance value, we can safely default it to the lowest value possible and still send a valid command to the drone.

function sendForward(socket, distance = 20) {
    return new Promise((resolve) => {
        socket.send(`forward ${distance}`,0,`forward ${distance}`.length,TELLO_CMD_PORT, TELLO_HOST, err => {
            if(err) {
                throw err;
            else {
                return resolve();
            }
        });
    });
}

Finally, we can add our if statement:

if(isForward(line)) {
    const [name, dist] = line.split(" ");
    try {
        await sendForward(socket, dist);
    catch (err) {
        console.log(err);
    }
}

Although it doesn’t look like much is going on here in terms of lines of code, there are two very important things happening. First, we are calling strings split method on our line, this function will split a string up into sections wherever a specified token is present. As we are splitting by a single white-space, and our line should be in the format like below:

The word “forward”, followed by a single white-space and then an integer. For example “forward 20”.

This split function will return us an array with two elements inside like this:  [“forward”,”20”].

flying the dji tello drone

As you can see from the example, the first element, in the 0 position of the array, will be “forward” and the second element, in the 1st position will be “20”. We are then using array de-structuring here to assign the first element in the array to the variable name and the second element in the array to the variable dist. The rest is simple, we then call our sendForwardfunction with the socket and dist as arguments. The command should then be sent to the drone.

Let’s give it a shot, fire up the application using:

> node ./src/app.js

Once we are up and running, issue the “takeoff” command to the drone. Once the take off sequence is complete we can then instruct the drone to move forward by issuing a command like below:

> forward 20

We should then see the drone move in a forward direction.

To add the other directions we can just repeat the above steps, but instead of sending “forward” we just need to send either “back”, “left” or “right”.

The output you should see in the terminal:

terminal screenshot

Summary

I can’t say I ever imagined that I would be writing code to control a drone, certainly not JavaScript code anyway, but I’m sure glad I did. While admittedly the real-life applications of the code above are little to non-existent, there’s actually quite a lot of scope for growth with this little project and is certainly a nice way to test out some skills and even learn some more.

A nice way to expand this would be to create some form of UI that’s not the command line because let’s be fair, typing commands is hardly practical. Perhaps taking the basic concepts I’ve touched on here and wrapping them in some form of Electron or React/Vue/Angular app. Even getting the stream of data that’s available from the drone and creating some sort of visualizations from it would be an interesting coding challenge. I will explore some of these in part 3.

All of the code I’ve written here (and some extras) can be found in my GitHub https://github.com/csscottc/drone-ctrl – Feel free to check it out and use it however you see fit. Part 1 of this blog series can be found here.

In part 3 I will explain how to build a simple UI that will complete the original task of allowing a non-coder the ability to control the drone.

Leave a Reply

Your email address will not be published. Required fields are marked *

Join the team

DevOps Specialist

Madrid, Helsinki, Tampere

Backend Developer

Helsinki, Tampere

Frontend Developer

Helsinki, Tampere