Kokemuksia Piilaaksosta

Kahdeksan kuukauden työpestistäni Piilaaksossa on reilu vuosi, ja nyt on aika summata kokemuksiani tuosta it-alan mekasta. Kaksi vuotta sitten ilmoittauduin Aalto-yliopiston StartupLifers-ohjelmaan ja sen kautta minuun otti yhteyttä muutama Piilaaksossa toimiva yritys, joiden kanssa jatkoin keskustelua työhaastatteluiden merkeissä. Työhaastatteluissa yritykset painottivat ongelmanratkaisutaitoja niin loogisten pähkinöiden, kuin algoritmiongelmien muodoissa. Lopulta otin työpaikan vastaan alle kymmenen hengen startupista, UpbeatPR-yrityksestä. Olin jo silloin töissä Goforella ohjelmistokehittäjänä ja sain vapaata määräaikaisen Piilaakso-työkokemuksen ajaksi. Päädyin hakemaan Piilaaksoon muutaman kaverini kertomien kokemuksien pohjalta ja korvikkeena vaihto-opiskeluajalle, jota en yliopistoaikana ehtinyt viettää. Olin myös vieraillut Piilaaksossa aikaisemmin suurempien yritysten toimistoilla, joten minulla oli jo pieni tuntuma siitä, mitä tuleva voisi sisältää.

Oikaisemassa vinoumaa

UpbeatPR automatisoi yrityksille sopivien journalistien etsinnän koneoppimisen keinoin. Asiakkaitamme olivat pääasiassa pienet yritykset, jotka kaipasivat näkyvyyttä mediaan. Jouduimme valikoimaan omat asiakkaamme tarkoin, koska kaikkien asiakkaiden julkaisut eivät päädy suurellakaan panostuksella merkittäviin lehtiin. Automaattinen palvelumme oli huomattavasti perinteisiä markkinointitoimistoja halvempi ja järjestelmällisempi vaihtoehto. Tarjosimme muun muassa asiakkaillemme statistiikkaa siitä, kuinka moni journalisti on avannut sähköpostilla lähettämämme yrityksen kertomuksen, ja onko yritystä mainittu heidän kirjoittamissaan viimeaikaisissa julkaisuissa. Kamppailimme myös ostettavia maililistoja vastaan täsmällisemmillä viesteillä ja laajemmalla valikoimalla. Massamailit yleisille listoille päätyvät helposti roskapostiansoihin eivätkä viestit pääse tavoitelluille henkilöille. Loppuen lopuksi artikkelien julkaisu tuntuu olevan vielä melko vinoutunutta ja sattumanvaraista bisnestä. Kuitenkin yrityksemme keräsi satoja asiakkaita vuoden aikana, ja edesautoimme julkaisuja niin New York Times kuin Wall Street Business Journal -lehtiin.
Työtehtäväni vaihtelivat laajasti kiinnostusteni mukaan. Tunnetusti startup-hengessä moni asia on vapaampaa ja kädet saa upottaa niin moneen saveen kuin haluaa. Minun kohdallani tämä tarkoitti työtehtävien vaihtelua web-kehityksen, palvelusuunnittelun ja data-analytiikan välillä. Syvensin osaamistani ohjelmointikielissä, kehityin esiintymistaidoissani ja opin sietämään suomalaista aksenttiani.

Työkulttuurien yhteensulauma

Työnteossa en kokenut suurta eroa suomalaiseen työkulttuuriin. Kävimme päivittäin läpi jokaisen työtehtävät ja suunnittelimme tulevia tehtäviä. Prosessimme oli iteratiivinen, mutta emme seuranneet tarkalleen mitään ketterän kehityksen ohjelinjoja. Ajoittain väki keskittyi ehkä turhankin intensiivisesti omaan tekemiseensä, mutta aikaa riitti myös työtovereille. Lähes kaikilla oli kunnianhimoinen asenne työnjälkeä kohtaan päivästä riippumatta. Osittain kunnianhimo ajoi työntekijöitä optimoimaan henkilökohtaista tehokkuuttaan, mikä näkyi myös heidän vapaa-ajan projekteistaan. Eräskin kollegani päätyi koodaamaan oman pretty-printerin Pythoniin, kun standardin JSON-formaatti ei miellyttänyt hänen silmiään. Tällä hetkellä tuo pretty-printteri on matkalla Pythonin standardiin.
Uskon, että suomalaiset pärjäävät hyvin ohjelmistopuolella Piilaaksossa. Siellä arvostetaan kyseenalaistamista ja avointa ilmapiiriä, mikä onnistuu suomalaiselta luonnostaan. Suomalainen koulutus on myös riittävä Amerikan huippuyliopistoja vastaan. Kollegoinani oli alumneja niin Stanfordista kuin Harvardista, enkä koe koulutukseni muodostuneen pullonkaulaksi verrattuna kollegoiden koulutuksiin. Esiintymistaidoissaan he olivat minua huomattavasti edellä, mutta ainakin pääsin oppimaan paraatipaikalta.
Työtehtävissä kiinnitin huomiota erityisen huolelliseen koodin katselmointiin. Katselmoinnissa tarkistettiin, että koodi toimii mahdollisilla virhesyötteillä ja on kirjoitettu selkeästi, ja että se näyttää myös elegantilta. Ajoittain pohdin uusimpien syntaksikikkojen käyttämisen järkevyyttä ja mietin, luoko se pelkkää lisäkuormaa uusille kehittäjille. Toisaalta tällä tyylillä varmistetaan, että koko tiimi pysyy teknologioiden kehityksessä mukana ja että kehittäjät voivat olla ylpeitä työnjäljestään.
Monet luulevat, että Piilaakson työpäivät ovat pidempiä kuin Suomessa, mutta en huomannut siinä asiassa suurta eroa. Toisaalta olin kuullut myös kauhutarinoita muiden yritysten pitkistä työpäivistä, joten työpäivien skaala on varmasti kirjavampi kuin Suomessa. Yleensä olin ensimmäisenä työpaikalla kello kymmeneltä, kun muut saapuivat yhdentoista aikoihin. Työskentelin usein kello kuuteen asti tai jopa pidempään. Huomasin, että normityötä tekeviltä työntekijöiltämme ei kuitenkaan odotettu päälle 40 tunnin viikkoja. Yrityksessämme työaikaa ei seurattu lainkaan, mutta tuloksia sen sijaan enemmänkin. Uskon että nimenomaan paikallinen ilmapiiri inspiroi myös minua opettelemaan syvällisemmin eri teknologioita vapaa-ajalla. Minulle muodostui myös tavaksi käydä esimerkiksi erilaisissa alan tapahtumissa töiden jälkeen.
Piilaakson työtavat haastoivat minut ajattelemaan avarammin. Tänään se näkyy laajemman tehtäväkuvan ylläpitämisessä. Opin arvostamaan enemmän elegantteja ratkaisuja, sekä keskittymään ohjelmistokoodin katselmointiin ja muun toiminnallisuuden arviointiin. Tunnen, että Goforella minulle on annettu enemmän vastuuta työkokemukseni jälkeen. Suosittelen suomalaisia it-alan asiantuntijoita haastamaan itsensä kansainvälisellä tasolla ja luottamaan omaan osaamiseensa.

Avatar

Joel Huttunen

Joel on kokenut Full-Stack kehittäjä, jonka sydäntä lähellä ovat etenkin data-analytiikka ja backend puoli. Hän on lapsuudestaan asti työskennellyt tietokoneiden parissa ja aloittanut ohjelmointikisaharrastuksensa vuonna 2008. Vuodesta 2012 hän on toiminut useissa eri ohjelmistoprojekteissa, joissa hän soveltaa laajasti ketterän kehityksen menetelmiä.

Piditkö lukemastasi? Jaa se myös muille.

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.

Avatar

Scott Carpenter

A Certified Scrum Master (Scrum Alliance), Scott is a Senior Software Developer based in the Gofore UK office in Swansea. Passionate about the JavaScript family of technologies (node/React/Angular) and very much enjoys creating awesome apps that run on the client or the server. Scott is also very interested in cloud computing, specifically Amazon Web Services and Google Cloud as well as microservices.

Piditkö lukemastasi? Jaa se myös muille.

At Gofore we pride ourselves for having the best of the best when it comes to talent. Finding and securing the best people is not easy and we have to find creative and interesting ways to attract them. In an effort to attract a new crop of talented developers to our office in South Wales, UK, we recently invited 15 students from around the area to join us in the Swansea office for a hackathon event. The idea was to identify students with the potential to grow into tomorrows experts.
We devised a task to create an application that could control a DJI Ryze Tello drone with nothing but code written during the hackathon. The students could use any language or tools they wanted and the only firm requirement was that there was some element of user interaction involved, in other words, the ability for a non-coder to control the drone. The students then faced a series of races/challenges to test out the code they had written.
So with that in mind, I thought it was only fair to attempt some of the challenges myself. So in this blog, I’ll show you my take on getting a drone to fly with Node.js

Getting started

The first part, as with any project is getting it all set up. I decided to use JavaScript (Node.js) simply because I’m looking to get something up and running as quickly as possible. JavaScript is also the main language that I use at the moment in my day-to-day activities, so its fresh in my mind. Also if I decide to add some form of UI using web technologies such as React, Vue or Angular then this will be (slightly) easier with a full-stack JS application.
In theory, you can use any programming language for this, the one requirement is that the language somehow allows you to open up a datagram socket as the way we will communicate with the drone is via UDP.
We can connect to the drone directly via WiFi as the Tello drone has its own hot-spot built in. Once we are connected we can start to send commands to the drone. The drone has an SDK that we can use which allows us to send plaintext commands to it.
More information is available in the official SDK documentation
I won’t go into too much detail about how I set up my project as there’s nothing that special going on. This is a simple “vanilla” Node.js project with no dependencies other than some of the core modules that come with Node. That being said, here are the steps you will need to follow to get started on your own:
Create a new folder on your system for the project, I’ve chosen to name the project tello-ctrl but feel free to use anything you would like.

  • (optional) Initalize a new git repository (git init) and link to a repository on GitHub/BitBucket/GitLab
  • Inside the project folder run npm init -y 
this command will run the usual npm init command and will accept the default values that npm init provides.
  • Create a new source folder ‘src’ in the tello-ctrl folder, this is where all of our code will go. In order to do this, I’m going to make use of the ‘readline’ package that’s available as part of node. First we need to import the package (as I’m not using Babel or any other pre-processor here I’ll have to use the older require style syntax to do this) then once we have this imported, we can use the createInterface function which will take two streams as arguments; one stream to read from and one stream to write to. We can pass in the process.stdin and process.stdout streams for read and write respectively.

All of the code in this project is contained in my GitHub account https://github.com/csscottc/drone-ctrl.
Once all that is done, you should have a directory structure that looks something like this (when viewed in vs code)
dji tello code

Preparing for take off

Code for this section can be found in the basicio branch.

Once the project setup is done its time to actually write some code. The first thing we need our app to do is to accept some basic inputs from the terminal, down the line this will allow us to send commands to the drone when we detect certain strings as input. For example, if the user enters “takeoff” we can, in turn, send a take off command to the drone.

const readline = require("readline");
const rl = readline.createInterface({ "input": process.stdin, "output": process.stdout});

Once that’s done we can add an event listener to rl for the “line” event that is fired. This event will be fired whenever a line is detected, which in our use case will be whenever the user hits the enter/return key after typing in a command. The event listener will take a function that it will fire whenever the line event is detected, it will pass the line that has been entered as the first argument to our function. For now, we will just pass the line that’s received straight over to console.log, This means that whenever a line is detected from the user, the line will be logged to the console.

console.log(`Lets get started!`);
console.log(`Please enter a command:`);
rl.on("line", line => console.log(line));

Once that’s done we can run our app for the first time. To do this head back to the terminal and then run:

> node src/app.js

You should notice that the lines “Lets get started!” And “Please enter a command:” are printed to the terminal. If you enter some text and then press enter/return you should see whatever text you entered repeated.
Now that we have some rudimentary IO set up we can start to look out for when the user enters specific commands, such as “takeoff”, “land”, “forward”, “back”, “left”, “right”. To do this we will add a new function “handleInput” which will take in the line passed to it by the event listener and will perform a simple switch statement on the content of the line. By doing this, depending on what the line received from the user contains we can then execute specific functionality in our app.

function handleInput(line) {
    switch (line) {
        case "takeoff":
            console.log("Detected takeoff command.");
            break;
        case "land":
            console.log("Detected land command.");
            break;
        default:
            break;
    }
}

Once we’ve created our handleInput function, we can then pass the line we receive from our event listener to it like below:

rl.on(“line",handleInput);

If we run the application this time then only when the line is equal to either “takeoff” or “land” should we see something printed to the terminal.

dji tello code

Take Off & Landing

The code for this section is available in the basic-movement branch.

It’s now time to finally connect to our drone and send some of the most important commands to it. Take off and Land.
In order to send commands to the drone, as mentioned previously we will use the UDP protocol. In order to create a UDP socket in Node.js we need to make use of the “dgram” module, similarly to how we made use of the “readline” module earlier for our basic IO. The createSocket function that’s available as part of the “dgram” module can be used to create a socket which can be bound to a port of our choosing. Once we have a socket bound to a port, it can be configured to listen to incoming messages as well as to send outgoing messages for us.
When you connect to the Tello drone over Wi-Fi, it will be listening for command type messages on port 8889. It will assign its self the IP address 192.168.10.1 on the network it hosts – we will need this information when creating our socket.
To keep things tidy we will create a function called “getSocket”, the function will create a socket using the “createSocket” function that’s imported from the “dgram” package and will then bind this socket to the Tello command port, which is 8889. It will then return to us this socket that is ready to be used for communication with the drone.

function getSocket() {
    const socket = createSocket("udp4");
    socket.bind(TELLO_CMD_PORT);
    return socket;
}

Another thing we will do at this point is to wrap up some of our existing code in an Immediately Invoked Function Expression (IIFE). This will allow us to declare the function as async which will allow us to make use of Async/Await, enhancing the readability and maintainability of the code later on. To do this we will take everything other than the handleInput function and the require statements for “dgram” and “readline”, wrapping them in an IIFE as shown below.

(async function(){
    console.log(`Lets get started!`);
    console.log(`Please enter a command:`);
    rl.on("line", line => handleInput(line, server));
})();

To learn more about what exactly an IIFE is and why they are useful in JavaScript, Kyle Simpson does a much better job explaining them than I could ever hope to do in his book “You don’t know JS: Up & Going” which is available free I’d highly recommend checking it out.
All we really need to know about IIFE’s for the purposes of this app is that the function will essentially be called as soon as it has been created. Our next step is to add the call to our new getSocket function within the IIFE that we just created.

const socket = getSocket();

The socket that we created emits some events that will be very useful for us when debugging and running our app, in order to make use of these events we need to register some event handlers.
The events that can be emitted from the socket that we care about are:
“message” – This event is fired when a message is received by the socket. An event handler can be provided that will be called with the message that has been received as its first argument, the message can then be used as desired by the developer, in our case we will just be logging messages to the terminal. The event handler is also called with another argument called rinfo, which contains information about where the message was received from.
“error” – This event is fired when an error occurs with the socket connection. An event handler can be provided that will be called with the error that occurred as its first argument, the error can then be inspected and used for logging and error handing purposes.
“listening” – This event is fired when the Socket has been created and is listening (ready to accept) incoming messages. An event handler can be provided, when it is called it is not passed any arguments.
The handlers that we are going to add to the above events are

socket.on("message", (msg) => {
    console.log(`Message from drone: ${msg.toString()}`);
});
socket.on("error", (err) => {
    console.log(`There was an error: ${err}`);
});
socket.on("listening", () => {
    console.log("Socket is listening");
});

In order to send a message, we will use the send() method thats available on our socket. The send method accepts 6 arguments:
“msg” – The message to send.
“offset” – The offset in the buffer where the message starts.
“length” – The number of bytes in the message.
“port” – The destination port, this is the port the message will be sent to.
“address” – The destination host name or ip address, this is where the message will be sent on the network.
“callback” – A callback function that is executed on completion of sending the message, the first argument can be an error, if the error is truthy this indicates that an error occurred and should be dealt with appropriately.
Some more info on this method is available here.

Get the drone into SDK mode

Before we can start sending meaningful commands to our drone, such as “takeoff and “land”, we need to get the drone into SDK mode. Once the drone is in SDK mode we can start to issue it with other commands, and it will (hopefully) respond to them. We can do this in the same way as we would send any other command to the drone, by using our socket’s send() method.
To do this, and to keep things nice and tidy we can create a new function called “sendInitCommand”. The function will take the socket created earlier as an argument and will then make use of the socket.send method to send the command over to the drone. The command that we will be sending is the string “command”.
As the socket.send method is async (It takes in a callback function that is executed on completion of the send operation) we will make our sendInitCommand function return a new Promise. Using promises will allow us to use the async/await syntax that we mentioned earlier, and will make our code easier to read and maintain.
The callback function that we will provide to socket.send will take one parameter, and will follow the standard “error-first” convention with node, this means that any argument passed in as the first argument will be an error object. We can perform a quick check on the value of the argument and if the value is ‘truthy’ this means an error occurred while sending the message, conversely if the value is ‘falsy’ then this means there was no error and everything went as expected (The command was sent to the drone successfully).
In the event that the error is ‘falsy’ (No error occurred), we can safely resolve our promise using the resolve function.
As we are using async/await, if the error is ‘truthy’ (Something went wrong) we will just throw the error and handle it later on rather than rejecting our promise as we normally would.

function sendInitCommand(socket) {
    return new Promise((resolve) => {
        socket.send("command",0,"command".length,TELLO_CMD_PORT, TELLO_HOST, err => {
            if(err) {
                throw err;
            else {
                return resolve();
            }
        });
    });
}

The next step is to actually call the function that we just created. A good place to add this call is under where we added the “listening” event handler.
After all that, our IIFE should now look something like this:

(async function(){
    console.log(`Lets get started!`);
    const socket = getSocket();
    socket.on("message", (msg) => {
        console.log(`Message from drone: ${msg.toString()}`);
    });
    socket.on("error", (err) => {
        console.log(`There was an error: ${err}`);
    });
    socket.on("listening", () => {
        console.log("Socket is listening");
    });
    await sendInitCommand(socket);
    console.log(`Please enter a command:`);
    rl.on("line", line => handleInput(line, socket));
})();

The next steps are to get our drone to take off and land, we can finally start adding the code for sending our takeoff and landing commands to the drone. To do this we can create two new functions, sendTakeOff and sendLand, the implementation of these functions is almost exactly the same as the sendInitCommand that we just created, the only differences being the function names, and the string that they are sending to the drone in the socket.send method. The commands required for takeoff and landing are “takeoff” and “land”.

function sendTakeOff(socket) {
    return new Promise((resolve) => {
        socket.send("takeoff",0,"takeoff".length,TELLO_CMD_PORT, TELLO_HOST, err => {
            if(err) {
                throw err;
            else {
                return resolve();
            }
        });
    });
}
function sendLand(socket) {
    return new Promise((resolve) => {
        socket.send("land",0,"land".length,TELLO_CMD_PORT, TELLO_HOST, err => {
            if(err) {
                throw err;
            else {
                return resolve();
            }
        });
    });
}

Once we have these functions, things can start to come together. It’s time to wire them up to the switch statement within the handleInput function that we created earlier, essentially what we will do here is to call either the sendTakeOff function or the sendLand function depending on the value of what the user has submitted.

async function handleInput(line, socket) {
    switch (line) {
        case "takeoff":
            console.log("Detected takeoff command.");
            try {
                await sendTakeOff(socket);
            catch (err) {
                console.log(err);
            }
            break;
        case "land":
            console.log("Detected land command.");
            try {
                await sendLand(socket);
            catch (err) {
                console.log(err);
            }
            break;
        default:
            break;
    }
}

Now we are finally in a position where we can connect to our drone and get it to fly!
The first step is to turn on the drone and to connect to it via WiFi. The drone will normally use an SSID in the following format TELLO-XXXXX – Where XXXXX is a random set of numbers and characters. My drone uses the SSID “TELLO-D3F981”
Once the drone is connected, as we did previously, we can start our app by running:

> node /src/app.js

If everything Is working as expected we should see the following output in the terminal.
dji tello code
Notice that we are now seeing “Socket is listening” and “Message from drone: ok” in our output. These are messages that have come from the event handlers we added to the “listening” and “message” events earlier on.
When we send commands to our drone, it will sometimes acknowledge that a command has been executed successfully by responding with either the string “ok” or, if the command was not executed successfully a string that represents an error (the value of which will differ depending on the nature of the error).
For other commands the drone will respond with a value, for example, if we send the “battery?” command, the drone will respond with a number between 0 and 100 which is representative of the current battery percentage of the drone.
To get the drone to take off, type “takeoff” and then hit enter. If all has gone as expected the drone should take off and you should see “Message from drone: ok”.
dji tello code
Note: The drone will land automatically if it detects no commands within a 15 second time window.
Once the drone is in the air lets bring it back down to earth. Type “land” and then hit enter, this should make the drone auto-land.
Note: Sometimes after executing a command the drone needs a small amount of time to be ready for the next command. I’m not exactly sure what causes this but normally waiting for a second or so before sending the next command to the drone works.

You’re flying!

So now we have mastered sending commands to our drone and taking off and landing, in the next part of this blog I will show you how to send directional commands. This will allow you to fly your drone forward, back, up, down and on trajectories. Happy hacking!
You can read Part 2 here: https://gofore.com/en/fly-a-drone-with-node-js-part-2/

Avatar

Scott Carpenter

A Certified Scrum Master (Scrum Alliance), Scott is a Senior Software Developer based in the Gofore UK office in Swansea. Passionate about the JavaScript family of technologies (node/React/Angular) and very much enjoys creating awesome apps that run on the client or the server. Scott is also very interested in cloud computing, specifically Amazon Web Services and Google Cloud as well as microservices.

Piditkö lukemastasi? Jaa se myös muille.

Sometimes there’s a need to fork a git repository and continue development with your own additions. It’s recommended to make a pull request to upstream so that everyone could benefit from your changes but in some situations, it’s not possible or feasible. When continuing development in a forked repo there are some questions which come to mind when starting. So here are some common questions and answers that I found useful when we forked a repository in Github and continued to develop it with our specific changes.

Repository name: new or fork?

If you’re releasing your own package (to e.g. npm or mvn) from the forked repository with your additions then it’s logical to also rename the repository to that package name.
If it’s an npm package and you’re using scoped packages then you could also keep the original repository name.

Keeping master and continuing developing on a branch?

Using master is the sane thing to do. You can always sync your fork with an upstream repository. See: syncing a fork.
Generally, you want to keep your local master branch as a close mirror of the upstream master and execute any work in feature branches (that might become pull requests later).

How you should do versioning?

Suppose that the original repository (origin) is still in active development and does new releases. How should you do versioning in your forked repository as you probably want to bring the changes done in the origin to your fork? And still maintain semantic versioning.
In short, semver doesn’t support prepending or appending strings to version. So adding your tag to the version number from the origin which your version is following breaks the versioning. So, you can’t use something like ”1.0.0@your-org.0.1” or ”1.0.0-your-org.1”. This has been discussed i.a. semver #287. The suggestion was to use a build meta tag to encode the other version as shown in semver spec item-10. But the downside is that ”Build metadata SHOULD be ignored when determining version precedence. Thus two versions that differ only in the build metadata, have the same precedence.”
If you want to keep relation the original package version and follow semver then your options are short. The only option is to use build meta tag: e.g. ”1.0.0+your-org.1”.
It seems that when following semantic versioning your only option is to differ from origin version and continue as you go.
If you don’t need to or want to follow semver you can track upstream version and mark your changes using similar markings as semverpre-releases: e.g. ”1.0.0-your-org.1”.

npm package: scoped or unscoped?

Using scoped packages is a good way to signal official packages for organizations. Example of using scoped packages can be seen from Storybook.
It’s more of a preference and naming convention of your packages. If you’re using something like your-org-awesome-times-ahead-package and your-org-patch-the-world-package then using scoped packages seems redundant.

Who should be the author?

At least add yourself to contributors in package.json.

Forking only for patching an npm library?

Don’t fork, use patch-package which lets app authors instantly make and keep fixes to npm dependencies. Patches created by patch-package are automatically and gracefully applied when you use npm(>=5) or yarn. Now you don’t need to wait around for pull requests to be merged and published. No more forking repos just to fix that one tiny thing preventing your app from working.

 
If you have any other questions, then post them in the comments below.

Avatar

Marko Wallin

Marko Wallin työskentelee ohjelmistosuunnittelijana Goforella ja muuttaa maailmaa paremmaksi digitalisaation keinoin. Hänellä on vuosien kokemus ohjelmistokehityksestä, ketteristä menetelmistä ja ohjelmoinnista sekä käyttöliittymän, taustapalveluiden että tietokantojen osalta. Vapaa-ajallaan Marko jakaa teknistä osaamistaan blogiensa kautta ja kehittämällä muun muassa avoimen lähdekoodin mobiilisovelluksia. Sovelluskehityksen lisäksi hän harrastaa maastopyöräilyä.

Piditkö lukemastasi? Jaa se myös muille.

Kun työpaikkailmoitukset vilisevät trendisanoja työpaikan organisaation ketteryydestä, matalasta hierarkiasta, itseohjautuvuudesta, ja arvopohjaisesta kulttuurista, ollaan lähellä sitä kulttuuria, jossa olet parhaimmillasi ja onnistut yhdessä tiimisi kanssa. Mutta mitä tämä tarkoittaa käytännössä? Kävin ottamassa asiasta syvempää oppia Neuroleadershipin kurssilla ”FLOW – Brain-based coaching for agile teams” ja oppimassa lisää kollegoiden mentoroinnista, johon olen itse Goforella käytettävissä. Goforella kehitetään Gofore Crew:lle tarjottavaa mentorointia ja coachausta tiimin ja henkilön kehittymisen tueksi, ja tätä tehdään toisten goforelaisten toimesta.

”Kohti kulttuuria, jossa toisiaan arvostavat asiantuntijat tekevät yhdessä töitä asiakasarvon luomiseksi? – FLOW: Brain-based coaching for agile teams”

FLOW-ohjelman fokus on yhdessä onnistuminen vaativassa asiantuntijatyössä paineen alla. Nelipäiväisen ohjelman aikana kehitettiin valmiuksia yhä itseohjautuvampaan ja ketterämpään toimintaan ja opittiin käytännön keinoja olla itse parhaimmillaan ja onnistua yhdessä tiimin kanssa.
Ohjelma alkoi tunnin virtuaalisella orientaatiosessiolla ja jatkui kahdella kahden päivän lähijaksolla.

FLOW: Brain-based coaching for agile teams

Neuroleadershipin vetämässä FLOW-ohjelmassa avainsisältöjä olivat:

  • itsensä johtaminen: oman mielen toiminta, stressin hallinta, tunnetaidot, ajattelun työkalut.
  • vuorovaikutus: oivaltava ongelmanratkaisu, oppimista edistävä seuranta, konfliktien ratkaisu.
  • organisaatioymmärrys: mitä jokaisen on hyvä tietää johtamisen isosta kuvasta, jotta ymmärtää paremmin toimintatapojen eroja, niin oman organisaation sisällä, kuin asiakkaiden ja yhteistyökumppanien kanssa toimiessa.

Paljon tiukkaa asiaa tiiviissä paketissa, kun ohjelma vielä perustuu osallistuvaan keskusteluun ja käytännön harjoituksiin. Mutta se juuri tekee FLOW-ohjelmasta toimivan, ja oman lisänsä aiheen käsittelyyn tuovat näkökulmat eri organisaatioiden käytäntavoista.
FLOW-ohjelmassa käsitellyt aihepiirit voi jakaa pääteemojen ”aivot ja yhdessä onnistuminen”, ”onnistu yhdessä tiimisi kanssa” ja ”avaimia oivaltavaan ja aivoystävälliseen keskusteluun” alle. Tässä lyhyt yhteenveto ohjelmassa käsitellyistä aiheista täydennettynä asiaan liittyvällä lisämateriaalilla. Kuvat ovat FLOW-ohjelman materiaalista (Virpi Oinonen/Businessillustrator.com) ja muistiinpanot niihin kirjoittajan. Suosittelen osallistumaan FLOW-ohjelmaan, sillä se tarjoaa ajattelemisen aihetta ja herättää kysymyksiä ketterämmässäkin organisaatiossa.

Aivot ja yhdessä onnistuminen

Aivot ovat tärkeässä osassa päivittäistä tekemistämme, etenkin tietotyössä. Tämän huomioiminen auttaa sekä ymmärtämään omaa toimintaamme, että onnistumaan yhdessä. FLOW-ohjelmassa asiaa käsiteltiin muun muassa norsu – kuski, U-käyrä ja SCARF-mallin kautta, sekä johtamisen ison kuvan ja Cynefin-mallin avulla.

Norsu – kuski

Voidaan kuvitella, että korviemme välissä asustaa erottamaton parivaljakko, norsu ja kuski. Kuski edustaa tietoista, harkitsevaa mieltä: suunnitelmallisuutta, empaattisuutta ja joustavuutta sekä kykyä havainnoida itseään. Norsu on tiedostamaton puoli, jossa tapahtuu automaattisesti huikea määrä asioita. Muutosta saadaan aikaan, kun sekä norsu että kuski kulkevat samaan suuntaan. Tarvitaan norsun energiaa ja tarmoa, sekä kuskin ohjastamista.
”Kun rutiinit saavat kyytiä ja tunnemme olomme uhatuksi, nakkaa norsu kuskin pöpelikköön ja lähtee rymistelemään karkuun äärimmäisen energisesti. Ilman kuskia norsu voi toimia tehokkaasti, suoraviivaisesti ja refleksiivisesti, aiemmin opitun varassa ja energiaa säästäen. Järkipuhetta on turha yrittää, koska logiikka on kuskin kanssa puskassa”, Arto Miekkavaara sanoo.
Norsun ja kuskin suhde näkyy myös impulsikontrollissa. Kuski asustaa aivojen etuotsalohkon kuorikerroksella, ja vastaa impulssikontrollista – siinä määrin kuin kykenee, jaksaa tai viitsii. Norsu on perusolemukseltaan impulsiivinen ja kuskin impulssikontrollin alla, sikäli kun kuski on tehtäviensä tasalla: 0,5 sekuntin reakointiajasta norsu käyttää 0,3 sekunttia ja kuskille jää 0,2 sekunttia tehdä päätöksiä. Ettei norsu mene mihin mieleen juolahtaa, pitää homman hallussa toivoaksemme levollisen valpas kuski. Kolhut päähän vielä heikentävät impulssikontrollia ja norsu pääsee rymistelemään herkemmin. Eli kriittisessä tilanteessa etuotsalohkosta sammuu valot. Impulssikontrollia voi harjoitella käyttämällä ”jos…, niin…” testiä (implementation intention -strategia). Esimerkiksi ”jos Repe soittaa bisselle ja koitan päästä eroon röökistä, niin en lähde.”

(Impulssikontrolli: kuskilla 0,2 sekunttia aikaa ohjastaa norsua)
Norsu – kuski -jaottelu kuvaa hyvin myös parivaljakon energiankulutusta: tietoisen mielen käyttäminen ja rutiinien ravistelu on energiaa kuluttavaa, ja kuski vie yli puolet parivaljakon energiasta. Voidaan sanoa kuskin olevan käytössä noin 1,5 tuntia vuorokaudessa, ja jokainen tietoinen päätös kuluttaa energiaa. Kannattaa siis organisoida arki niin, ettei tarvitse käyttää tahdonvoimaa. Opitut rutiinit ja tavat menevät norsun voimilla. Norsun kulkeminen autopilotilla näkyy hyvin esimerkiksi silloin, jos muutat uuteen asuntoon ja ajatkin kotimatkalla vanhaan osoitteeseen.

(Norsu ja kuski, FLOW-materiaali ja muistiinpanot)
Uusia tapoja ja rutiineita kannattaa opetella, mutta se vie aikaa: vähintään 2-3 kuukautta. Tapojen vahvistaminen vaatii neljää asiaa:

  1. Aika
  2. Huomio
  3. Toisto
  4. Positiivinen palaute

Yksi hyvä keino on hyödyntää 20 sekunnin sääntöä. Jos haluat eroon esimerkiksi Facebookin käytöstä, poista appi puhelimesta, jolloin sovelluksen avaaminen selaimessa kestää liian kauan ja kiusausta on helpompi vastustaa. Tai jos haluat aloittaa aamulenkit, laita illalla juoksuvarusteet valmiiksi. Uusien tapojen oppimisen työläyteen liittyen on hyvä esimerkki hieman viritetty polkupyörä, jonka ajamisen hankaluutta video hauskasti kuvaa.

U-käyrä

Käänteisen U-käyrän laki (Yerkes-Dodson laki, 1908) näyttää stressin tai luovan jännitteen riippuvuuden suorituskykyyn. Kiire ja stressi laskevat vireystilaa, jolloin kuski karkaa unille ja norsun rutiinit ja tavat ottavat vallan. Fokus hajaantuu, työt ei etene, ja koneelliset ja rutiininomaiset työt onnistuvat. Flow-tila löytyy käyrän keskivaiheilta, jolloin stressi ja luovan jännite ovat balansissa. Kun on riittävästi aikaa miettiä, mutta pieni jännite, syntyy myös oivalluksia.
Töissä ja arjessa on hyödyllistä kiinnittää huomio omaan U-käyrään, harjoitella taitoa pysähtyä ja ajatella niissä tilanteissa, joissa huomaat luisuvasi alas U-käyrän oikeaa puolta. Eli havaita kun “norsu hermostuu”.

(U-käyrä, FLOW-materiaali)

SCARF-malli

David Rockin neurotieteellisistä tutkimuksista yhteen kokoama SCARF-malli kuvaa toimintaamme ohjaavia tunne- tai kokemusreaktioita. Se perustuu tutkimustuloksiin, joissa on huomattu yksilön asemaa muuttavat muutokset keskeisiksi reagoinnin syiksi. Yksilö kokee nämä muutokset joko uhkina tai palkintoina. Uhkia paetaan ja palkintoja kohti pyritään. SCARF-malli pyrkii tekemään näkyväksi sellaiset sosiaaliset ilmiöt, joita koemme joka päivä työpaikalla ja vapaa-ajalla, ja se on väline, jolla voi paremmin ymmärtää omia sekä muiden reaktioita.
SCARF-mallin sosiaaliset ulottuvuudet ovat: status, ennustettavuus, autonomia, yhteenkuuluvuus ja oikeudenmukaisuus. ”Managing with the Brain in Mind”-artikkeli avaa hyvin mallin merkitystä käytännössä.

(SCARF-malli, FLOW-materiaali)

Johtamisen iso kuva

Johtamisen iso kuva oli yksi iso teema FLOW-ohjelmassa. Frederick Laloux kuvaa kirjassaan ”Reinventing Organizations”, miten maailmankuvamme on kehittynyt. Vanhan maailmankuvan mukainen ajattelutapa ei ole enää toiminut, mikä on johtanut kehittyneempien ajattelumallien syntyyn ja jokaisella uudella tasolla olemme kehittäneet uuden, radikaalisti toimivamman tavan organisoitua. Organisaation kypsyyttä on käsitellyt myös Jari Hietaniemi Scrum is Dead -kirjoituksessaan ja hän antaa suuntaviivoja kohti sinivihreää.
Laloux kuvaa organisaatioiden tasoja värein, ja jokainen taso on tuonut mukanaan merkittäviä läpimurtoja. Tiiviisti asian voi katsoa 9 minuutin videosta ”Lean and agile adoption with the Laloux culture model” ja kattavamman otoksen saa ”Reinventing organizations” luennosta. Kuulemma äärimmäisen hyvin käytetty 1h 40min.
Organisaatioissa syntyy yleensä kitkaa, kun esimerkiksi ”oranssi” organisaatio pyrkii ottamaan käyttöön ”vihreitä” menettelyjä pelkästään pintatasolla, tai kun joku menettely tuottaa ”telttoja jalkapallokentälle”. Yhtenä välitehtävänä oli havainnoida ympäröivää maailmaa ”värien” ja ”jalkapallokenttien” kannalta ja olemaan tuomitsematta itseäsi ja muita. Organisaatioiden eri värit ovat kyllä helposti havaittavissa työelämässä, kun työskentelee asiakasprojektissa monitoimittajaympäristössä.
Useimmat ketterät sovelluskehitysfirmat toimivat vihreällä tasolla, joissa avainsanoina ovat perhe, arvopohjainen kulttuuri, työntekijöiden voimauttaminen ja sidosryhmien huomioiminen. Siitä pitäisi nousta seuraavalle tasolle turkoosiin (teal), jossa sanat ovat organismi, itseorganisoituminen, kokonaisvaltaisuus ja evolutiivinen tarkoitus.
Kehittyneimmälle ajattelumallille turkoosille on Suomessa myös oma itseohjautuvasti toimiva yhteisö, jonka tarkoituksena on auttaa Suomea ja suomalaisia siirtymään kohti merkityksellistä työtä, sekä kokonaisvaltaista ihmisyyttä.

(Organisaatiomallien evoluutiotasot, Laloux: Reinventing Organizations)

Cynefin

Yksi johtajan tärkeä ominaisuus on ymmärtää kompleksisuutta ja osaltaan hallita sitä. Aihepiiriä käsiteltiin Dave Snowdenin Cynefin-mallin mukaan (Simple-Complicated-Complex-Chaos). Tiivistettynä kompleksisuusteoriasta voi katsoa 3 minuutin videosta, miten järjestää lastenjuhlat.
Cynefin-mallia on käsitelty myös aikaisemmin Goforen blogissa liittyen projektin onnistumiseen, osa yksi kertoo Cynefin-mallista ja toinen osa jatkaa aiheesta ketteränä menetelmänä.

(Cynefin-malli, Onnistu projektissa ketterästi)

Lisämateriaaleja aiheesta kiinnostuneille

FLOW-kurssin vetäjä Arto Miekkavaara tarjosi oivaltavan kurssin lisäksi myös viitteitä lisäopiskeluun aiheeseen liittyen. Tässä muutamia aihepiirejä, joita Miekkavaara lähetti kurssin jälkeen luettavaksi.
Teresa Amabile puhe Progress principlestä on hieman kryptinen, mutta hyödyllinen 18 minuutin video, jos “ihmisasiat” kiinnostavat. Tärkein asia, joka edistää aikaanaamista ja motivaatiota on “progress in meaningful work”, eli edistymisen kokemus merkityksellisessä työssä.
Mentäessä vielä syvemmälle psykologiaan, on sosiaalinen ”pehmeä” asia ihan yhtä ”kovaa” ellei kovempaa kuin fyysinen puoli. Lieberman-Eisenbergerin labra on kirjoittanut asiasta ja niitä löytyy aihepiirettäin jaoteltuna labran sivustolta. Artikkeleista poimittakoon ”Pains and Pleasures of Social Life” ja ”Why Rejection Hurts: What Social Neuroscience Has Revealed About the Brain’s Response to Social Rejection”. Kiinnostavaa luettavaa on myös ”viraalisen” viestin leviämisen neurotiede (Persuasion, Attitude Change, & Message Propagation). Erittäin arvostettu labra, valovoimaiset ja pidetyt vetäjät, ja silmiini ei ole osunut merkittävää vastustusta heidän työlleen.
Altaan syvästä päästä: Peter Senge Otaniemessä Systeemiälyn laboratorion 30v-juhlassa pitämä tunnin puhe: “Systems Thinking for a Better World”. Senge on maailmanluokan ajattelija, jonka 1990 julkaistu “Fifth Discipline” pitää edelleen pintansa. Youtubesta löytyy myös kaikenlaista em. hakusanoilla.
Edelliseen liittyen, jos aihe puhuttelee, niin sitä voi käydä läpi pitkän kaavan mukaan u.labin kurssilla “Leading From the Emerging Future”, joka johdattelee Theory U -metodiin. “Tämän pitemmälle ei oikein tällä hetkellä taida päästä.”, kirjoittaa Miekkavaara.

Avatar

Marko Wallin

Marko Wallin työskentelee ohjelmistosuunnittelijana Goforella ja muuttaa maailmaa paremmaksi digitalisaation keinoin. Hänellä on vuosien kokemus ohjelmistokehityksestä, ketteristä menetelmistä ja ohjelmoinnista sekä käyttöliittymän, taustapalveluiden että tietokantojen osalta. Vapaa-ajallaan Marko jakaa teknistä osaamistaan blogiensa kautta ja kehittämällä muun muassa avoimen lähdekoodin mobiilisovelluksia. Sovelluskehityksen lisäksi hän harrastaa maastopyöräilyä.

Piditkö lukemastasi? Jaa se myös muille.

The fourth incarnation of Disobey, the Nordic security event was held in Helsinki in January. This event is a place for people interested in hacker culture, information security, making and breaking, and to meet like-minded people, learn new things and share knowledge. This was my second time attending. The first one was mostly spent getting to know my way around such an event, the second one was much easier when I knew what to expect. Too bad there’s almost too much to do, so you’ll have to prioritise… So here are my personal experiences from this year’s event and hopefully some tips for the first-timer in 2020.
Photographing or recording video at the event is forbidden (see https://en.wikipedia.org/wiki/Chatham_House_Rule), so no pictures or names, sorry. The speaker list is public though, go check it out, if you want.

Talks

There were plenty of great talks, from disclosing 35-year-old vulnerabilities (https://sintonen.fi/advisories/scp-client-multiple-vulnerabilities.txt), Tor anonymity, timing side-channel attacks, hotel room lock security, data breach dump related thingies and browser 0-days to mechanical master-key systems. Lots of cool stuff, but you can’t just sit on your backside for the whole two days! I had picked a few talks I really wanted to see (I really dig mechanical locks, client-side vulns, and breaking things) and tried to remember to attend. And I did. Most of the others fell in the category ”oh that’d be cool oh crap it’s already starting and all the seats are taken”. But I’ll surely watch the recordings of most of them later. It’s not like re:Invent-crowded, but if you want to have a good seat where you can concentrate on the content, be early (smile)
At the time of writing, the talks are being uploaded to Disobey’s Youtube channel.
Also a new podcast, ”We need to talk about InfoSec”, recorded its first episode at Disobey, go check it out.

Workshops and things to do

In addition to talking heads, you get to do things yourself with a more experienced instructor. This year workshops included e.g. hacking Chinese web browsers, using Python for bad (and good), threat modelling and a few other topics. Pick something that interests you, check if there’s a pre-registration needed (this year there wasn’t) and enjoy the ride. I attended only the most interesting one (to me), but would’ve enjoyed many of them, I’m sure.
In addition to shorter (up to three hours) workshops, there was a lockpicking village where you could try picking different types of mechanical locks and learn from a pro.

CTF

Capture the flag, or CTF for short is a competition where teams try to solve different types of hacking puzzles for points. The puzzles range from web vulnerabilities (SQL injections, path traversal/local file inclusion, security misconfigurations etc.) to listening to radio broadcasts from a dummy satellite. The proof of success is a flag, which is entered to the competition system. Who gets the most points, wins. And this is fun, for some people at least, myself included. We had a team of seven people which included four Goforeans. Fourth place doesn’t suck that bad when there were two teams of infosec company employees ahead of us. Next year we’ll try harder!
As the CTF network is considered ”hostile” and doesn’t offer internet access, I recommend bringing a burner laptop (which you can wipe clean afterwards) or e.g. using 1) a virtual machine (Kali works fine) for the hacking 2) USB tethering (because airwaves are a bit crowded and your keyphrase strength might be tested…) to deliver internet to your host OS 3) a USB ethernet adapter which you present to your virtual machine (and the VM only) so that you can easily search the internet for things from the host and access the CTF network from the VM without extra hassle of switching cables back and forth.
Gofore had some web related challenges (created by me) of their own in the contest. Of course, my team members had to solve those without me. More info about these below, there’s even a virtual machine image you can spin up and try to get the flags yourself.
Gofore CTF challenges

The people

To everyone I met: I’m glad we met. Let’s do it again sometime. To everyone else, I hope we’ll meet someday. A few of my friends/acquaintances told me afterwards that they kinda forgot most of the program while just mingling so be careful 🙂

Gofore CTF challenges

Download link in the setup instructions. See if you can hack your way in. Encrypted (sic) walkthrough can be found at http://<machine IP>/walkthrough.txt if you’re interested in cheating…
The challenges:

  1. The flag is somewhere on the filesystem. Find the file’s location on the site and the contents of the flag. URL path: /blog
  2. Some page is behind closed doors. Find the keys and step in. URL path: /*
  3. Our customer database might contain more than meets the eye. URL path: /customerdb

Setup:

  1. Download the OVA image here.  (Avaa: open, Lataa: download)
  2. Import it in e.g. VirtualBox, connect it to a host-only network
  3. Find the virtual machine’s IP with for example nmap -sn <your host-only network>
  4. Point your browser (and other tools) to http://<VM_IP>:80/
  5. Have fun!
Avatar

Tapio Vuorinen

Piditkö lukemastasi? Jaa se myös muille.