diff --git a/include/kovan/ardrone.h b/include/kovan/ardrone.h
new file mode 100644
index 0000000..f1d3f78
--- /dev/null
+++ b/include/kovan/ardrone.h
@@ -0,0 +1,207 @@
+/**************************************************************************
+ * Copyright 2013 KISS Institute for Practical Robotics *
+ * *
+ * This file is part of libkovan. *
+ * *
+ * libkovan is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * libkovan is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with libkovan. Check the LICENSE file in the project root. *
+ * If not, see . *
+ **************************************************************************/
+
+#ifndef _ARDRONE_H_
+#define _ARDRONE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief This function will establish a connection between the drone and the CBC for transmitting information.
+ */
+void drone_connect();
+
+/*
+ * \brief This function will be used to disconnect the drone from the CBC.
+ */
+void drone_disconnect();
+
+/**
+ * \brief This function will be used to make the drone takeoff and stabilize itself.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \post The drone should reach its normal operating height
+ */
+void drone_takeoff();
+
+/**
+ * \brief This function will be used to land the drone at its current position.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \post The drone should slowly descend to the ground from its current height.
+ */
+void drone_land();
+
+/**
+ * \brief This function will be used to immediately turn off the drone rotors or reset the emergency toggle
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \post The drone will stop moving and fall to the ground.
+ */
+void drone_emergency();
+
+/**
+ * \brief retrieves the cached battery value
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \return An integer representing the current battery level
+ * \TODO Specify the range in the above comment
+ */
+int drone_get_battery();
+
+/**
+ * \brief Retrieves the x value relative to the drones starting position. Negative values indicate
+ * the drone has moved to the left of it's starting position.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \return x value away from the drones starting position in milimeters
+ * \TODO verify it is in fact milimeters
+ */
+float drone_get_x();
+
+/**
+ * \brief Retrieves the y value relative to the drones starting position. Negative values indicate
+ * the drone has moved backwards from it's starting position.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \return y value away from the drones starting position in milimeters
+ * \TODO verify it is in fact milimeters
+ **/
+float drone_get_y();
+
+/**
+ * \brief Retrieves the y value relative to the drones starting position. Negative values indicate
+ * the drone has moved down from it's starting position.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \return z value away from the drones starting position in milimeters
+ * \TODO verify it is in fact milimeters also verify that the z can be negative is it negative from the ground or its air starting position
+ */
+float drone_get_z();
+
+/**
+ * \brief Retrieves the current velocity in the right or left direction.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \return A float indicating the millimeters per second
+ */
+float drone_get_x_velocity();
+
+/**
+ * \brief Retrieves the current velocity in the forward or backwards direction.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \return A float indicating the velocity in millimeters per second
+ */
+float drone_get_y_velocity();
+
+/**
+ * \brief Retrieves the current velocity in the upward or downwards direction.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \return A float indicating the velocity in millimeters per second
+ */
+float drone_get_z_velocity();
+
+/**
+ * \brief Retrieves the current rotation in the clockwise (positive) or counterclockwise (negative) direction.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \return A float indicating the degrees rotated from the original orientation
+ */
+float drone_get_yaw();
+
+/**
+ * \brief Switches the drone vision feed to the front facing camera
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ */
+void drone_front_camera();
+
+/**
+ * \brief Switches the drone vision feed to the downward facing camera
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ */
+void drone_down_camera();
+
+/**
+ * \brief Starts sending video data to the cbc for vision tracking.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ */
+void enable_drone_vision();
+
+/**
+ * \brief Stops the drone from sending data to the cbc for vision tracking.
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ */
+void disable_drone_vision();
+
+/**
+ * \brief Sets the Drone's Mac Address Pair to be the given string
+ * \param macAddress A string representing the Mac Address of your CBC
+ */
+void set_drone_Mac_Address(char * macAddress);
+
+/**
+ * \brief Tells the drone to move with the given parameters
+ * \param enable A value indicating if movement is enabled. 0 - True 1 - False
+ * \param x_tilt A value from zero to one indicating the percentage of maximum tilt in the left or right direction
+ * negative values are left and positive values are right. Ex: -.5 means Half of the total tilt left
+ * \param y_tilt A value from zero to one indicating the percentage of maximum tilt in the forward or backward direction
+ * negative values are left and positive values are right. Ex: -.5 means Half of the total tilt backwards.
+ * \param yaw_vel A value indicating the rotational velocity of the dronein milieters per second
+ * \param z_vel A value indicating the change in altitude in milimeters per second
+ */
+void drone_move(float x_tilt, float y_tilt, float yaw_vel, float z_vel);
+
+/**
+ * \brief Tells the drone that it should stop moving and hover at its current location
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ */
+void drone_hover();
+
+/**
+ * \brief Tells the drone to hover over the Roundel that it has detected
+ * \param shouldHover A integer representing if the drone should hover or not
+ * 1 == True meaning the drone will attempt to hover on a roundel
+ * 0 == False meaning the drone will resume normal flight
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ * \pre A roundel must have been detected in the drones vision tags
+ */
+void drone_hover_on_roundel(int shouldHover);
+
+/**
+ * \brief Switches the channel the drone's altitude meter operates on so that interferance does not occur between two drones
+ * \param channel A integer representing which channel to operate on
+ * 1 == True turns the drone to channel A
+ * 0 == False turns the drone to channel B
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ */
+void drone_set_ultrasound_channel(int channel);
+
+/**
+ * \brief Initializes the drone's onboard video detection
+ * \param channel A integer representing which channel to operate on
+ * 0 == Turns the drones detection off
+ * 1 == Detects green enemies via the front camera
+ * 2 == Detects yellow enemies via the front camera
+ * 3 == Detects blue enemies via the front camera
+ * 4 == Detects orange/green ground stripes via the bottom camera
+ * 5 == Detects yellow/blue ground stripes via the bottom camera
+ * 6 == Detects roundels via the bottom camera
+ * \pre drone_connect must have been previously called to establish a connection to the drone.
+ */
+void drone_set_detection(int detectType);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/kovan/ardrone.hpp b/include/kovan/ardrone.hpp
new file mode 100644
index 0000000..e7cd5a9
--- /dev/null
+++ b/include/kovan/ardrone.hpp
@@ -0,0 +1,62 @@
+/**************************************************************************
+ * Copyright 2013 KISS Institute for Practical Robotics *
+ * *
+ * This file is part of libkovan. *
+ * *
+ * libkovan is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation, either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * libkovan is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with libkovan. Check the LICENSE file in the project root. *
+ * If not, see . *
+ **************************************************************************/
+
+#ifndef _ARDRONE_HPP_
+#define _ARDRONE_HPP_
+
+class DroneController;
+
+class ARDrone
+{
+public:
+ enum State
+ {
+ Disconnected = 0,
+ Landed,
+ TakenOff
+ };
+
+ ~ARDrone();
+
+ bool connect(const char *ip = "192.168.1.1");
+ void disconnect();
+
+ void flatTrim();
+ void takeoff();
+ void land();
+
+ void hover();
+ void move(const float x, const float y, const float z, const float yaw);
+
+ ARDrone::State state() const;
+
+ static ARDrone *instance();
+
+private:
+ ARDrone();
+
+ DroneController *m_controller;
+
+ int m_controlFd;
+};
+
+
+
+#endif
diff --git a/include/kovan/kovan.h b/include/kovan/kovan.h
index 277339c..62db48b 100644
--- a/include/kovan/kovan.h
+++ b/include/kovan/kovan.h
@@ -27,6 +27,7 @@
#ifndef _KOVAN_H_
#define _KOVAN_H_
+#include "ardrone.h"
#include "audio.h"
#include "motors.h"
#include "servo.h"
diff --git a/include/kovan/kovan.hpp b/include/kovan/kovan.hpp
index b3b8e6f..964fae3 100644
--- a/include/kovan/kovan.hpp
+++ b/include/kovan/kovan.hpp
@@ -41,6 +41,7 @@
#define _KOVAN_HPP_
#include "motors.hpp"
+#include "ardrone.hpp"
#include "servo.hpp"
#include "analog.hpp"
#include "digital.hpp"
diff --git a/src/ardrone.cpp b/src/ardrone.cpp
new file mode 100644
index 0000000..0ad1daf
--- /dev/null
+++ b/src/ardrone.cpp
@@ -0,0 +1,311 @@
+#include "kovan/ardrone.hpp"
+#include "kovan/thread.hpp"
+#include "kovan/util.h"
+#include "ardrone_constants_p.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+class DroneSequencer
+{
+public:
+ typedef unsigned sequence_t;
+
+ DroneSequencer();
+ void wait(const unsigned times = 1);
+ DroneSequencer::sequence_t next();
+ void reset();
+
+private:
+ sequence_t m_current;
+};
+
+DroneSequencer::DroneSequencer()
+{
+ reset();
+}
+
+void DroneSequencer::wait(const unsigned times)
+{
+ sequence_t current = m_current;
+ while(current + times < m_current) msleep(10);
+}
+
+DroneSequencer::sequence_t DroneSequencer::next()
+{
+ return ++m_current;
+}
+
+void DroneSequencer::reset()
+{
+ m_current = 0;
+}
+
+struct DroneCommand
+{
+ DroneCommand();
+
+ char data[ARDRONE_MAX_CMD_LENGTH];
+};
+
+DroneCommand::DroneCommand()
+{
+ memset(data, 0, ARDRONE_MAX_CMD_LENGTH);
+}
+
+class DroneController : public Thread
+{
+public:
+ DroneController();
+ ~DroneController();
+
+ void setup(const sockaddr_in &addr, int controlFd);
+ void invalidate();
+
+ void flatTrim();
+
+ void land(const bool emergency = false);
+ void takeoff();
+
+ void move(const float x, const float y, const float z, const float yaw);
+
+ void run();
+
+ void stop();
+ bool isStopped() const;
+
+private:
+ void pushCommand(const char *const command);
+ void popCommand();
+
+ void oneTimeCommand(const char *const command);
+
+ bool update();
+
+ sockaddr_in m_addr;
+ int m_controlFd;
+ Mutex m_mutex;
+ std::stack m_commandStack;
+ DroneSequencer m_seq;
+ bool m_stop;
+};
+
+DroneController::DroneController()
+ : m_controlFd(-1)
+{
+}
+
+DroneController::~DroneController()
+{
+}
+
+void DroneController::setup(const sockaddr_in &addr, int controlFd)
+{
+ m_addr = addr;
+ m_controlFd = controlFd;
+}
+
+void DroneController::invalidate()
+{
+ m_mutex.lock();
+ m_controlFd = -1;
+ m_mutex.unlock();
+}
+
+void DroneController::flatTrim()
+{
+ char command[ARDRONE_MAX_CMD_LENGTH];
+ sprintf(command, "%s%%u\r", ARDRONE_AT_FTRIM);
+ oneTimeCommand(command);
+}
+
+void DroneController::land(const bool emergency)
+{
+ popCommand();
+ char command[ARDRONE_MAX_CMD_LENGTH];
+ sprintf(command, "%s%%u,%u\r", ARDRONE_AT_REF, 0x11540000 | ((emergency ? 1 : 0) << 8));
+ pushCommand(command);
+}
+
+void DroneController::takeoff()
+{
+ popCommand();
+ char command[ARDRONE_MAX_CMD_LENGTH];
+ sprintf(command, "%s%%u,%u\r", ARDRONE_AT_REF, 0x11540200);
+ pushCommand(command);
+}
+
+void DroneController::move(const float x, const float y, const float z, const float yaw)
+{
+ unsigned ix = *(unsigned *)&x;
+ unsigned iy = *(unsigned *)&y;
+ unsigned iz = *(unsigned *)&z;
+ unsigned iyaw = *(unsigned *)&yaw;
+
+ const bool hoverMode = x == 0.0f && y == 0.0f
+ && z == 0.0f && yaw == 0.0f;
+
+ popCommand();
+ char command[ARDRONE_MAX_CMD_LENGTH];
+ sprintf(command, "%s%%u,%u,%u,%u,%u,%u\r", ARDRONE_AT_PCMD, hoverMode ? 0 : 1,
+ ix, iy, iz, iyaw);
+ pushCommand(command);
+}
+
+void DroneController::run()
+{
+ while(!m_stop) {
+ m_mutex.lock();
+ bool success = update();
+ m_mutex.unlock();
+ if(!success) break;
+ msleep(33); // ~30 FPS
+ }
+ m_stop = false;
+ m_seq.reset();
+}
+
+void DroneController::stop()
+{
+ m_mutex.lock();
+ m_stop = true;
+ m_mutex.unlock();
+}
+
+bool DroneController::isStopped() const
+{
+ return m_stop;
+}
+
+void DroneController::pushCommand(const char *const command)
+{
+ DroneCommand container;
+ strncpy(container.data, command, ARDRONE_MAX_CMD_LENGTH);
+
+ m_mutex.lock();
+ m_commandStack.push(container);
+ m_mutex.unlock();
+}
+
+void DroneController::popCommand()
+{
+ m_mutex.lock();
+ if(!m_commandStack.empty()) m_commandStack.pop();
+ m_mutex.unlock();
+}
+
+void DroneController::oneTimeCommand(const char *const command)
+{
+ pushCommand(command);
+ m_seq.wait(10);
+ popCommand();
+}
+
+bool DroneController::update()
+{
+ // Nothing to do.
+ if(m_commandStack.empty() || m_controlFd < 0) return true;
+
+ char realCommand[ARDRONE_MAX_CMD_LENGTH];
+ sprintf(realCommand, m_commandStack.top().data, m_seq.next());
+ printf("Sending %s\n", realCommand);
+ if(sendto(m_controlFd, realCommand, strlen(realCommand), 0,
+ (const sockaddr *)&m_addr, sizeof(m_addr)) < 0) {
+ perror("DroneController::run -> sendto");
+ return false;
+ }
+
+ return true;
+}
+
+ARDrone::~ARDrone()
+{
+ disconnect();
+}
+
+bool ARDrone::connect(const char *ip)
+{
+ m_controlFd = socket(AF_INET, SOCK_DGRAM, 0);
+ if(m_controlFd < 0) return false;
+
+ sockaddr_in si_other;
+ memset(&si_other, 0, sizeof(si_other));
+ si_other.sin_family = AF_INET;
+ si_other.sin_port = htons(ARDRONE_AT_PORT);
+ if(inet_aton(ip, &si_other.sin_addr) == 0) {
+ perror("inet_aton");
+ close(m_controlFd);
+ return false;
+ }
+
+ m_controller->setup(si_other, m_controlFd);
+
+ return true;
+}
+
+void ARDrone::disconnect()
+{
+ // Already disconnected?
+ if(m_controlFd < 0) return;
+
+ m_controller->land(true);
+ msleep(100);
+ // while(state() != ARDrone::Landed) msleep(100);
+
+ m_controller->invalidate();
+
+ close(m_controlFd);
+}
+
+void ARDrone::flatTrim()
+{
+ m_controller->flatTrim();
+}
+
+void ARDrone::takeoff()
+{
+ m_controller->takeoff();
+}
+
+void ARDrone::land()
+{
+ m_controller->land();
+}
+
+void ARDrone::hover()
+{
+ m_controller->move(0.0f, 0.0f, 0.0f, 0.0f);
+}
+
+void ARDrone::move(const float x, const float y, const float z, const float yaw)
+{
+ m_controller->move(x, y, z, yaw);
+}
+
+ARDrone::State ARDrone::state() const
+{
+ // TODO: Stub
+ return ARDrone::Disconnected;
+}
+
+ARDrone *ARDrone::instance()
+{
+ static ARDrone s_instance;
+ return &s_instance;
+}
+
+ARDrone::ARDrone()
+ : m_controller(new DroneController)
+ , m_controlFd(-1)
+{
+ m_controller->start();
+}
\ No newline at end of file
diff --git a/src/ardrone_constants_p.hpp b/src/ardrone_constants_p.hpp
new file mode 100644
index 0000000..c57ab14
--- /dev/null
+++ b/src/ardrone_constants_p.hpp
@@ -0,0 +1,20 @@
+#ifndef _ARDRONE_CONSTANTS_P_HPP_
+#define _ARDRONE_CONSTANTS_P_HPP_
+
+#define ARDRONE_AT_PREFIX "AT*"
+#define ARDRONE_AT_SUFFIX "="
+
+#define ARDRONE_AT_REF (ARDRONE_AT_PREFIX "REF" ARDRONE_AT_SUFFIX)
+#define ARDRONE_AT_PCMD (ARDRONE_AT_PREFIX "PCMD" ARDRONE_AT_SUFFIX)
+#define ARDRONE_AT_FTRIM (ARDRONE_AT_PREFIX "FTRIM" ARDRONE_AT_SUFFIX)
+#define ARDRONE_AT_CONFIG (ARDRONE_AT_PREFIX "CONFIG" ARDRONE_AT_SUFFIX)
+#define ARDRONE_AT_LED (ARDRONE_AT_PREFIX "LED" ARDRONE_AT_SUFFIX)
+#define ARDRONE_AT_ANIM (ARDRONE_AT_PREFIX "ANIM" ARDRONE_AT_SUFFIX)
+#define ARDRONE_AT_COMWDG (ARDRONE_AT_PREFIX "COMWDG" ARDRONE_AT_SUFFIX)
+
+#define ARDRONE_CMD_SEPARATOR ((char)10)
+#define ARDRONE_MAX_CMD_LENGTH (1024)
+
+#define ARDRONE_AT_PORT (5556)
+
+#endif
diff --git a/src/thread.cpp b/src/thread.cpp
index 29a0ed8..4524c49 100644
--- a/src/thread.cpp
+++ b/src/thread.cpp
@@ -55,9 +55,10 @@ Mutex::Mutex(const Mutex &)
{
}
-void *__runThread(void *data)
+static void *__runThread(void *data)
{
Thread *t = reinterpret_cast(data);
+ if(!t) return NULL;
t->run();
return NULL;
}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 0b33247..4e1a61f 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory(ardrone)
add_subdirectory(battery)
add_subdirectory(draw)
add_subdirectory(servo)
diff --git a/test/ardrone/CMakeLists.txt b/test/ardrone/CMakeLists.txt
new file mode 100644
index 0000000..ed78873
--- /dev/null
+++ b/test/ardrone/CMakeLists.txt
@@ -0,0 +1,2 @@
+ADD_EXECUTABLE(ardrone ardrone.cpp)
+TARGET_LINK_LIBRARIES(ardrone kovan)
\ No newline at end of file
diff --git a/test/ardrone/ardrone.cpp b/test/ardrone/ardrone.cpp
new file mode 100644
index 0000000..8e54a19
--- /dev/null
+++ b/test/ardrone/ardrone.cpp
@@ -0,0 +1,29 @@
+#include "kovan/kovan.hpp"
+#include
+
+int main(int argc, char *argv[])
+{
+ ARDrone *drone = ARDrone::instance();
+ if(!drone->connect()) {
+ return EXIT_FAILURE;
+ }
+
+ drone->flatTrim();
+ msleep(100);
+
+ drone->takeoff();
+ msleep(5000);
+
+ drone->move(0.1f, 0.0f, 0.0f, 1.0f);
+ msleep(10000);
+
+ drone->hover();
+ msleep(10000);
+
+ drone->land();
+ msleep(2000);
+
+ drone->disconnect();
+
+ return EXIT_SUCCESS;
+}
\ No newline at end of file