diff --git a/config/miosix_settings.h b/config/miosix_settings.h
index 0bebe9aca1212dc2c41d4f6651f7c1f3c88cc32b..cbff3deff95cc2f31d788e98048ab2f821f41b88 100644
--- a/config/miosix_settings.h
+++ b/config/miosix_settings.h
@@ -85,12 +85,12 @@ namespace miosix {
 /// \def WITH_FILESYSTEM
 /// Allows to enable/disable filesystem support to save code size
 /// By default it is defined (filesystem support is enabled)
-//#define WITH_FILESYSTEM
+#define WITH_FILESYSTEM
 
 /// \def WITH_DEVFS
 /// Allows to enable/disable DevFs support to save code size
 /// By default it is defined (DevFs is enabled)
-//#define WITH_DEVFS
+#define WITH_DEVFS
     
 /// \def SYNC_AFTER_WRITE
 /// Increases filesystem write robustness. After each write operation the
@@ -130,7 +130,7 @@ const unsigned char MAX_OPEN_FILES=8;
 /// \def WITH_BOOTLOG
 /// Uncomment to print bootlogs on stdout.
 /// By default it is defined (bootlogs are printed)
-//#define WITH_BOOTLOG
+#define WITH_BOOTLOG
 
 /// \def WITH_ERRLOG
 /// Uncomment for debug information on stdout.
diff --git a/sbs.conf b/sbs.conf
index a04e677d3c998b5b2d966d2b60900bd82bd08360..13f75518d260dd3f8dc06e3ba195d7f508080cce 100644
--- a/sbs.conf
+++ b/sbs.conf
@@ -64,6 +64,10 @@ Files:      src/shared/drivers/canbus/CanManager.cpp
             src/shared/drivers/canbus/CanSocket.cpp 
             src/shared/drivers/canbus/CanInterrupt.cpp 
 
+[piksi]
+Type:       srcfiles
+Files:      src/shared/drivers/piksi/piksi.cpp
+
 # Common files (like event scheduler)
 [shared]
 Type:       srcfiles
@@ -181,3 +185,11 @@ BinName:    test-busfault
 Include:    %shared %i2c %math %anakin-data
 Defines:   
 Main:       test-busfault
+
+[test-piksi]
+Type:       board
+BoardId:    stm32f429zi_skyward_anakin
+BinName:    test-piksi
+Include:    %piksi
+Defines:   
+Main:       test-piksi
diff --git a/src/entrypoints/test-piksi.cpp b/src/entrypoints/test-piksi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7e6f2e0bf324ad17e9d14172efc72070afe5e930
--- /dev/null
+++ b/src/entrypoints/test-piksi.cpp
@@ -0,0 +1,35 @@
+#include <drivers/piksi/piksi.h>
+#include <time.h>
+#include <iostream>
+
+using namespace std;
+
+#ifdef _MIOSIX
+
+#include <miosix.h>
+
+using namespace miosix;
+
+#endif //_MIOSIX
+
+int main(int argc, char *argv[])
+{
+    Piksi piksi("/dev/gps");
+    for(;;)
+    {
+        auto gps=piksi.waitForGpsData();
+        #ifdef _MIOSIX
+        long long now=getTick();
+        #else //_MIOSIX
+        long long now=clock()/(CLOCKS_PER_SEC/1000);
+        #endif //_MIOSIX
+        cout<<" t: "<<now-gps.timestamp
+            <<" lat: "<<gps.latitude
+            <<" lon: "<<gps.longitude
+            <<" h: "<<gps.height
+            <<" vn: "<<gps.velocityNorth
+            <<" ve: "<<gps.velocityEast
+            <<" vd: "<<gps.velocityDown
+            <<" ns: "<<gps.numSatellites<<endl;
+    }
+}
\ No newline at end of file
diff --git a/src/shared/drivers/piksi/contiguous_queue.h b/src/shared/drivers/piksi/contiguous_queue.h
new file mode 100644
index 0000000000000000000000000000000000000000..51c7ef1a9ea1c2e29575bb8f3918942d11888214
--- /dev/null
+++ b/src/shared/drivers/piksi/contiguous_queue.h
@@ -0,0 +1,110 @@
+
+#ifndef CONTIGUOUS_QUEUE
+#define CONTIGUOUS_QUEUE
+
+#include <stdexcept>
+
+/**
+ * A fixed size FIFO queue whose elements are stored in a contiguous array,
+ * which allows direct access to the array in order to add/remove multiple
+ * elements.
+ * 
+ * NOTE: This queue is most efficent if the data is removed form the queue in
+ * large chunks. Removing one element at a time is most inefficient since
+ * shifting all the other elements in the queue is needed. There are no
+ * performance constraints on inserting.
+ * 
+ * The queue is not synchronized.
+ * 
+ * \param T type of the element
+ * \param N maximum number of elements (fixed size queue)
+ */
+template<typename T, unsigned N>
+class ContiguousQueue
+{
+public:
+    /**
+     * Constructor
+     */
+    ContiguousQueue() {}
+
+    /**
+     * \return a pointer to the side of the queue where elements can be added.
+     * Call availableToAdd() to know how much space is left. The pointer is
+     * meant to be used as an array, so as to add more than one item.
+     * 
+     * NOTE: if availableToAdd() returns 0 the pointer is past the end of the
+     * array and should not be dereferenced
+     */
+    T* addEnd() { return elements+size; }
+    
+    /**
+     * \return a pointer to the side of the queue where elements can be removed.
+     * Call availableToRemove() to know how many elements are available. The
+     * pointer is meant to be used as an array, so as to read more than one item.
+     * 
+     * NOTE: if availableToRemove() returns 0 the pointer should not be
+     * dereferenced
+     */
+    T* removeEnd() { return elements; }
+    
+    /**
+     * \return a pointer to the side of the queue where elements can be removed.
+     * Call availableToRemove() to know how many elements are available. The
+     * pointer is meant to be used as an array, so as to read more than one item.
+     * 
+     * NOTE: if availableToRemove() returns 0 the pointer should not be
+     * dereferenced
+     */
+    const T* removeEnd() const { return elements; }
+
+    /**
+     * \return how much space is available to add elements
+     */
+    unsigned int availableToAdd() const { return N-size; }
+    
+    /**
+     * \return how many items are available to read
+     */
+    unsigned int availableToRemove() const { return size; }
+
+    /**
+     * After having added elements to the pointer obtained through addEnd(),
+     * call this function to let the queue know how many elements have been
+     * added
+     * \param n elements added
+     * \throws range_error if too many elements were addded. Memory corruption
+     * has already occurred at this point, though
+     */
+    void added(unsigned int n)
+    {
+        if(size+n>N) throw std::range_error("ContiguousBuffer::added");
+        size+=n;
+    }
+
+    /**
+     * After having removed elements to the pointer obtained through
+     * removeEnd(), call this function to let the queue know how many elements
+     * have been added removed
+     * \param n elements removed
+     * \throws range_error if too many elements were removed. Memory corruption
+     * has already occurred at this point, though
+     */
+    void removed(unsigned int n)
+    {
+        if(n>size) throw std::range_error("ContiguousBuffer::removed");
+        if(n==0) return;
+        for(unsigned int i=0;i<size-n;i++)
+            elements[i]=std::move(elements[i+n]);
+        size-=n;
+    }
+
+private:
+    ContiguousQueue(const ContiguousQueue&)=delete;
+    ContiguousQueue& operator=(const ContiguousQueue&)=delete;
+
+    T elements[N];
+    unsigned int size=0;
+};
+
+#endif //CONTIGUOUS_QUEUE
diff --git a/src/shared/drivers/piksi/piksi.cpp b/src/shared/drivers/piksi/piksi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..680a6e1849381838350b97eea967320947d638fe
--- /dev/null
+++ b/src/shared/drivers/piksi/piksi.cpp
@@ -0,0 +1,224 @@
+
+#include "piksi.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <time.h>
+#include <algorithm>
+
+using namespace std;
+
+#ifdef _MIOSIX
+
+#include <miosix.h>
+
+using namespace miosix;
+
+#endif //_MIOSIX
+
+/*
+ * Contrary to the standard CCITT CRC that starts from 0xffff, the Piksi
+ * people decided to start from 0. So we need a special CRC16 just for them
+ */
+
+static inline void crc16piksiUpdate(unsigned short& crc, unsigned char data)
+{
+    unsigned short x=((crc>>8) ^ data) & 0xff;
+    x^=x>>4;
+    crc=(crc<<8) ^ (x<<12) ^ (x<<5) ^ x;
+}
+
+static unsigned short crc16piksi(const void *message, unsigned int length)
+{
+    const unsigned char *m=reinterpret_cast<const unsigned char*>(message);
+    unsigned short result=0;
+    for(unsigned int i=0;i<length;i++) crc16piksiUpdate(result,m[i]);
+    return result;
+}
+
+//
+// class Piksi
+//
+
+Piksi::Piksi(const char *serialPath)
+{
+    fd=open(serialPath,O_RDWR);
+    if(fd<0) throw runtime_error(string("Cannot open ")+serialPath);
+    if(isatty(fd))
+    {
+        termios t;
+        tcgetattr(fd,&t);
+        t.c_lflag &= ~(ISIG | ICANON | ECHO);
+        #ifndef _MIOSIX
+        cfsetospeed(&t,B115200);
+        cfsetispeed(&t,B115200);
+        #endif //_MIOSIX
+        tcsetattr(fd,TCSANOW,&t);
+    }
+    pthread_create(&thread,NULL,&threadLauncher,this);
+    pthread_mutex_init(&mutex,NULL);
+    pthread_cond_init(&cond,NULL);
+}
+
+GPSData Piksi::getGpsData()
+{
+    GPSData result;
+    pthread_mutex_lock(&mutex);
+    if(!firstFixReceived)
+    {
+        pthread_mutex_unlock(&mutex);
+        throw runtime_error("No fix");
+    }
+    result=data;
+    pthread_mutex_unlock(&mutex);
+    return result;
+}
+
+GPSData Piksi::waitForGpsData()
+{
+    GPSData result;
+    pthread_mutex_lock(&mutex);
+    pthread_cond_wait(&cond,&mutex);
+    result=data;
+    pthread_mutex_unlock(&mutex);
+    return result;
+}
+
+Piksi::~Piksi()
+{
+    close(fd);
+    pthread_mutex_destroy(&mutex);
+    pthread_cond_destroy(&cond);
+    quit=true;
+    pthread_join(thread,NULL);
+}
+
+void* Piksi::threadLauncher(void* arg)
+{
+    reinterpret_cast<Piksi*>(arg)->run();
+    return nullptr;
+}
+
+void Piksi::run()
+{
+    do {
+        bytes.added(readData(bytes.addEnd(),bytes.availableToAdd()));
+        bytes.removed(lookForMessages(bytes.removeEnd(),bytes.availableToRemove()));
+    } while(quit==false);
+}
+
+unsigned int Piksi::readData(unsigned char *buffer, unsigned int size)
+{
+    for(;;)
+    {
+        int result=read(fd,buffer,size);
+        if(result>0) return result;
+        usleep(10000); //We want to retry but avoid 100% CPU utilization
+        return 0;      //To go to a loop of run() and notice the quit flag
+    }
+}
+
+unsigned int Piksi::lookForMessages(uint8_t *buffer, unsigned int size)
+{
+//     puts("###");
+//     for(unsigned int i=0;i<size;i++) printf("%02x ",buffer[i]);
+//     puts("");
+    
+    const uint8_t preamble=0x55;
+    unsigned int consumed=0;
+    auto consume=[&](unsigned int n)
+    {
+        consumed+=n;
+        buffer+=n;
+        size-=n;
+    };
+    for(;;)
+    {
+        uint8_t *index=find(buffer,buffer+size,preamble);
+        consume(index-buffer); //Consume eventual characters between messages
+        
+        if(size<sizeof(Header)) return consumed; //We don't have the header
+        auto header=reinterpret_cast<Header*>(buffer);
+        unsigned int messageSize=sizeof(Header)+header->length+crcSize;
+        if(messageSize>size) return consumed; //We don't have the entire message
+
+        uint16_t crc=*reinterpret_cast<uint16_t*>(buffer+messageSize-crcSize);
+        if(crc16piksi(buffer+1,messageSize-crcSize-1)==crc)
+        {
+            processValidMessage(buffer,messageSize);
+            consume(messageSize);
+        } else {
+            //TODO: fault counter?
+            consume(1); //Consume the preamble of the invalid message
+        }
+    }
+}
+
+void Piksi::processValidMessage(uint8_t *buffer, unsigned int size)
+{
+    Header *header=reinterpret_cast<Header*>(buffer);
+    switch(header->type)
+    {
+        case MSG_POS_LLH:
+            if(size<sizeof(MsgPosLlh)) /* TODO: fault counter? */;
+            else processPosLlh(reinterpret_cast<MsgPosLlh*>(buffer));
+            break;
+        case MSG_VEL_NED:
+            if(size<sizeof(MsgVelNed)) /* TODO: fault counter? */;
+            else processVelNed(reinterpret_cast<MsgVelNed*>(buffer));
+            break;
+        default:
+            //A valid message we're not interested in
+            break;
+    }
+}
+
+void Piksi::processPosLlh(Piksi::MsgPosLlh* msg)
+{
+    partialData.latitude=msg->lat;
+    partialData.longitude=msg->lon;
+    partialData.height=msg->height;
+    partialData.numSatellites=msg->n_sats;
+    #ifdef _MIOSIX
+    partialData.timestamp=getTick();
+    #else //_MIOSIX
+    partialData.timestamp=clock()/(CLOCKS_PER_SEC/1000);
+    #endif //_MIOSIX
+    
+    if(vel && gpsTimestamp==msg->ms)
+    {
+        vel=pos=false;
+        
+        pthread_mutex_lock(&mutex);
+        data=partialData;
+        firstFixReceived=true;
+        pthread_cond_broadcast(&cond);
+        pthread_mutex_unlock(&mutex);
+    } else {
+        pos=true;
+        gpsTimestamp=msg->ms;
+    }
+}
+
+void Piksi::processVelNed(Piksi::MsgVelNed* msg)
+{
+    partialData.velocityNorth=static_cast<float>(msg->n)/1000.f;
+    partialData.velocityEast=static_cast<float>(msg->e)/1000.f;
+    partialData.velocityDown=static_cast<float>(msg->d)/1000.f;
+    
+    if(pos && gpsTimestamp==msg->ms)
+    {
+        vel=pos=false;
+        
+        pthread_mutex_lock(&mutex);
+        data=partialData;
+        firstFixReceived=true;
+        pthread_cond_broadcast(&cond);
+        pthread_mutex_unlock(&mutex);
+    } else {
+        vel=true;
+        gpsTimestamp=msg->ms;
+    }
+}
diff --git a/src/shared/drivers/piksi/piksi.h b/src/shared/drivers/piksi/piksi.h
new file mode 100644
index 0000000000000000000000000000000000000000..51c7aae8f6350ce598194b7bceedf2b117b6daca
--- /dev/null
+++ b/src/shared/drivers/piksi/piksi.h
@@ -0,0 +1,172 @@
+
+#ifndef PIKSI_H
+#define PIKSI_H
+
+#include <pthread.h>
+#include "contiguous_queue.h"
+
+/**
+ * The GPS information
+ */
+struct GPSData
+{
+    /// timestamp in ms (anakin time, not GPS time). getTick()-timestamp tells
+    /// you how "old" the data is.
+    long long timestamp;
+
+    double latitude;      ///< [deg] //TODO: cast to float??
+    double longitude;     ///< [deg] //TODO: cast to float??
+    double height;        ///< [m]   //TODO: cast to float??
+    float  velocityNorth; ///< [m/s]
+    float  velocityEast;  ///< [m/s]
+    float  velocityDown;  ///< [m/s]
+    int    numSatellites; ///< [1]
+};
+
+/**
+ * Class to access the Piksi GPS.
+ * 
+ * Should be connected to the Piksi UARTB configured as
+ * MODE                    SBP
+ * SBP message mask        65280
+ * telemetry radio on boot False
+ * baudrate                115200
+ */
+class Piksi
+{
+public:
+    /**
+     * Constructor
+     * \param serialPath path to the device file of the Piksi serial port
+     */
+    Piksi(const char *serialPath);
+    
+    /**
+     * \return the latest GPS data, or throws if the GPS has not yet got a fix.
+     * If the GPS has lost the fix, the same data is returened repeatedly,
+     * use the timestamp field of the GPSData struct to know this.
+     * \throws runtime_error is no data is available
+     */
+    GPSData getGpsData();
+    
+    /**
+     * \return the latest GPS data. If the GPS has yet got a fix or has lost
+     * the fix, this function will block until the fix is regained
+     */
+    GPSData waitForGpsData();
+    
+    /**
+     * Destructor
+     */
+    ~Piksi();
+
+private:
+    
+    Piksi(const Piksi&)=delete;
+    Piksi& operator=(const Piksi&)=delete;
+    
+    struct __attribute__((packed)) Header
+    {
+        uint8_t  preamble;
+        uint16_t type;
+        uint16_t sender;
+        uint8_t  length;
+    };
+
+    static const unsigned int crcSize=2;
+    
+    static const uint16_t MSG_POS_LLH=0x0201;
+    
+    struct __attribute__((packed)) MsgPosLlh
+    {
+        Header   header;
+        uint32_t ms;         // [ms]
+        double   lat;        // [deg]
+        double   lon;        // [deg]
+        double   height;     // [m]
+        uint16_t h_accuracy; // Piksi says unimplemented
+        uint16_t v_accuracy; // Piksi says unimplemented
+        uint8_t  n_sats;
+        uint8_t  flags;
+    };
+    
+    static const uint16_t MSG_VEL_NED=0x0205;
+    
+    struct __attribute__((packed)) MsgVelNed
+    {
+        Header header;
+        uint32_t ms;         // [ms]
+        int32_t  n;          // [mm/s]
+        int32_t  e;          // [mm/s]
+        int32_t  d;          // [mm/s]
+        uint16_t h_accuracy; // Piksi says unimplemented
+        uint16_t v_accuracy; // Piksi says unimplemented
+        uint8_t  n_sats;
+        uint8_t  flags;
+    };
+    
+    /**
+     * Launches run() from the background thread
+     * \param arg this
+     */
+    static void *threadLauncher(void *arg);
+    
+    /**
+     * Piksi main processing loop
+     */
+    void run();
+    
+    /**
+     * Fill a buffer from the serial port where the piksi is connected
+     * \param buffer where to store read data
+     * \param size how many bytes to read
+     */
+    unsigned int readData(unsigned char *buffer, unsigned int size);
+
+    /**
+     * Tries to find one or more piksi message in the buffer. There are no
+     * alignment requirements, the given buffer can begin and end in the middle
+     * of a packet.
+     * \param buffer buffer read from the serial port
+     * \param size buffer size
+     * \return the number of consumed characters. the last size-consumed bytes
+     * of the buffer are not processed yet, most likely because they contain an
+     * incomplete message, and must be prepended to the buffer given at the
+     * next call in order not to miss some packets.
+     */
+    unsigned int lookForMessages(uint8_t *buffer, unsigned int size);
+
+    /**
+     * Called on a message that has already passed the CRC check.
+     * \param buffer pointer to the first message byte (0x55)
+     * \param size message size
+     */
+    void processValidMessage(uint8_t *buffer, unsigned int size);
+    
+    /**
+     * Processes a POS_LLH message
+     * \param msg the message
+     */
+    void processPosLlh(MsgPosLlh* msg);
+    
+    /**
+     * Processes a VEL_NED message
+     * \param msg the message
+     */
+    void processVelNed(MsgVelNed* msg);
+
+    //The queue should be large enough to contain the largest message (256+8)
+    ContiguousQueue<uint8_t,384> bytes;
+    int fd;
+    pthread_t thread;
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    GPSData data, partialData;
+    uint32_t gpsTimestamp=0;
+    bool pos=false;
+    bool vel=false;
+    bool firstFixReceived=false;
+    volatile bool quit=false;
+};
+
+#endif //PIKSI_H