diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep
index 71783893fa9063bc0657f99505b79169c85c3930..9ac61dfb6646628e4e93c944a87077ee38eda4af 100644
--- a/drivers/Makefile.dep
+++ b/drivers/Makefile.dep
@@ -203,3 +203,13 @@ ifneq (,$(filter xbee,$(USEMODULE)))
   USEMODULE += xtimer
   USEMODULE += netif
 endif
+
+ifneq (,$(filter uart_half_duplex,$(USEMODULE)))
+    FEATURES_REQUIRED += periph_gpio
+    FEATURES_REQUIRED += periph_uart
+    USEMODULE += xtimer
+endif
+
+ifneq (,$(filter feetech,$(USEMODULE)))
+    USEMODULE += uart_half_duplex
+endif
diff --git a/drivers/Makefile.include b/drivers/Makefile.include
index 971e91089bf734a37af926f7387e4b35dc8f03c3..434371db6d69df8b81334f2b21532bd2f4ed5ae6 100644
--- a/drivers/Makefile.include
+++ b/drivers/Makefile.include
@@ -106,3 +106,9 @@ endif
 ifneq (,$(filter adxl345,$(USEMODULE)))
     USEMODULE_INCLUDES += $(RIOTBASE)/drivers/adxl345/include
 endif
+ifneq (,$(filter uart_half_duplex,$(USEMODULE)))
+    USEMODULE_INCLUDES += $(RIOTBASE)/drivers/uart_half_duplex/include
+endif
+ifneq (,$(filter feetech,$(USEMODULE)))
+    USEMODULE_INCLUDES += $(RIOTBASE)/drivers/feetech/include
+endif
diff --git a/drivers/feetech/Makefile b/drivers/feetech/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2
--- /dev/null
+++ b/drivers/feetech/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/feetech/feetech.c b/drivers/feetech/feetech.c
new file mode 100644
index 0000000000000000000000000000000000000000..ad3802bf699fb9864d6adc93f2288c4f197782e6
--- /dev/null
+++ b/drivers/feetech/feetech.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @ingroup     drivers_feetech
+ * @{
+ *
+ * @file
+ * @brief       Driver implementation for Feetech devices
+ *
+ * @author      Loïc Dauphin <loic.dauphin@inria.fr>
+ *
+ * @}
+ */
+
+#include "feetech.h"
+
+#include "feetech_protocol.h"
+#include "feetech_reader.h"
+#include "feetech_writer.h"
+
+#include "periph/uart.h"
+#include "xtimer.h"
+#include "byteorder.h"
+
+#include <string.h>
+
+void feetech_init(feetech_t *device, uart_half_duplex_t *stream, feetech_id_t id)
+{
+    device->stream = stream;
+    device->id = id;
+}
+
+int feetech_ping(uart_half_duplex_t *stream, feetech_id_t id)
+{
+    feetech_writer_t pw;
+
+    uart_half_duplex_set_tx(stream);
+    feetech_writer_init(&pw, stream->buffer, stream->size);
+    feetech_writer_ping_make(&pw, id);
+    uart_half_duplex_send(stream, pw.size);
+
+    uart_half_duplex_set_rx(stream);
+    if (uart_half_duplex_recv(stream, FEETECH_ACK_SIZE) != FEETECH_ACK_SIZE) {
+        return FEETECH_TIMEOUT;
+    }
+
+    return FEETECH_OK;
+}
+
+int feetech_write(feetech_t *device, feetech_addr_t reg, const uint8_t *data, size_t length)
+{
+    uart_half_duplex_set_tx(device->stream);
+    if (device->stream->size < length) {
+        return FEETECH_BUFFER_TOO_SMALL;
+    }
+
+    feetech_writer_t pw;
+
+    feetech_writer_init(&pw, device->stream->buffer, device->stream->size);
+    feetech_writer_write_make(&pw, device->id, reg, data, length);
+    uart_half_duplex_send(device->stream, pw.size);
+
+    uart_half_duplex_set_rx(device->stream);
+    if (uart_half_duplex_recv(device->stream, FEETECH_ACK_SIZE) != FEETECH_ACK_SIZE) {
+        return FEETECH_TIMEOUT;
+    }
+
+    return FEETECH_OK;
+}
+
+int feetech_write8(feetech_t *device, feetech_addr_t reg, uint8_t value)
+{
+    return feetech_write(device, reg, &value, 1);
+}
+
+int feetech_write16(feetech_t *device, feetech_addr_t reg, uint16_t value)
+{
+    value = HTONS(value);
+    return feetech_write(device, reg, (uint8_t*)&value, 2);
+}
+
+int feetech_read(feetech_t *device, feetech_addr_t reg, uint8_t *data, size_t length)
+{
+    uart_half_duplex_set_tx(device->stream);
+    if (device->stream->size < length) {
+        return FEETECH_BUFFER_TOO_SMALL;
+    }
+
+    feetech_writer_t pw;
+
+    feetech_writer_init(&pw, device->stream->buffer, device->stream->size);
+    feetech_writer_read_make(&pw, device->id, reg, length);
+    uart_half_duplex_send(device->stream, pw.size);
+
+    uart_half_duplex_set_rx(device->stream);
+    const size_t esize = FEETECH_RESPONSE_SIZE(length);
+    if (uart_half_duplex_recv(device->stream, esize) != esize) {
+        return FEETECH_TIMEOUT;
+    }
+
+    feetech_reader_t pr;
+    feetech_reader_init(&pr, device->stream->buffer, esize);
+    if (!feetech_reader_is_valid(&pr)) {
+        return FEETECH_INVALID_MESSAGE;
+    }
+
+    if (feetech_reader_response_get_payload_size(&pr) != length) {
+        return FEETECH_INVALID_MESSAGE;
+    }
+
+    memcpy(data, feetech_reader_response_get_payload(&pr), length);
+    return FEETECH_OK;
+}
+
+int feetech_read8(feetech_t *device, feetech_addr_t reg, uint8_t *value)
+{
+    return feetech_read(device, reg, value, 1);
+}
+
+int feetech_read16(feetech_t *device, feetech_addr_t reg, uint16_t *value)
+{
+    const int ret = feetech_read(device, reg, (uint8_t*)value, 2);
+    if (ret == FEETECH_OK) {
+        *value = NTOHS(*value);
+    }
+    return ret;
+}
diff --git a/drivers/feetech/include/feetech_protocol.h b/drivers/feetech/include/feetech_protocol.h
new file mode 100644
index 0000000000000000000000000000000000000000..98ddfd4c5d5e18ff73b689e6ace284a108647d6a
--- /dev/null
+++ b/drivers/feetech/include/feetech_protocol.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @ingroup     drivers_feetech
+ *
+ * @{
+ *
+ * @file
+ * @brief       Feetech protocol definitions
+ *
+ * @author      Loïc Dauphin <loic.dauphin@inria.fr>
+ */
+
+#ifndef FEETECH_PROTOCOL_H
+#define FEETECH_PROTOCOL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FEETECH_START (0xFF)
+
+typedef enum {
+    SCS15_B_1M     = 0,
+    SCS15_B_0_5M   = 1,
+    SCS15_B_250K   = 2,
+    SCS15_B_128K   = 3,
+    SCS15_B_115200 = 4,
+    SCS15_B_76800  = 5,
+    SCS15_B_57600  = 6,
+    SCS15_B_38400  = 7
+} scs15_baudrate_t;
+
+typedef enum {
+    SCS15_ID                     = 5,
+    SCS15_BAUD_RATE              = 6,
+    SCS15_RETURN_DELAY_TIME      = 7,
+    SCS15_RETURN_LEVEL           = 8,
+    SCS15_LIMIT_TEMPERATURE      = 13,
+    SCS15_MAX_LIMIT_VOLTAGE      = 14,
+    SCS15_MIN_LIMIT_VOLTAGE      = 15,
+    SCS15_ALARM_LED              = 18,
+    SCS15_ALARM_SHUTDOWN         = 19,
+    SCS15_COMPLIANCE_P           = 21,
+    SCS15_COMPLIANCE_D           = 22,
+    SCS15_COMPLIANCE_I           = 23,
+    SCS15_CW_DEAD                = 26,
+    SCS15_CCW_DEAD               = 27,
+    SCS15_TORQUE_ENABLE          = 40,
+    SCS15_LED                    = 41,
+    SCS15_LOCK                   = 48,
+    SCS15_PRESENT_VOLTAGE        = 62,
+    SCS15_PRESENT_TEMPERATURE    = 63,
+    SCS15_REGISTERED_INSTRUCTION = 64,
+    SCS15_ERROR                  = 65,
+    SCS15_MOVING                 = 66,
+} scs15_register8_t;
+
+typedef enum {
+    SCS15_MODEL_NUMBER         = 0,
+    SCS15_VERSION              = 3,
+    SCS15_MIN_ANGLE_LIMIT      = 9,
+    SCS15_MAX_ANGLE_LIMIT      = 11,
+    SCS15_MAX_TORQUE           = 16,
+    SCS15_PUNCH                = 24,
+    SCS15_IMAX                 = 28,
+    SCS15_OFFSET               = 30,
+    SCS15_GOAL_POSITION        = 42,
+    SCS15_GOAL_TIME            = 44,
+    SCS15_GOAL_SPEED           = 46,
+    SCS15_PRESENT_POSITION     = 56,
+    SCS15_PRESENT_SPEED        = 58,
+    SCS15_PRESENT_LOAD         = 60,
+    SCS15_VIR_POSITION         = 67,
+    SCS15_CURRENT              = 69,
+} scs15_register16_t;
+
+typedef enum {
+    INST_PING       = 0x01,
+    INST_READ       = 0x02,
+    INST_WRITE      = 0x03,
+    INST_REG_WRITE  = 0x04,
+    INST_ACTION     = 0x05,
+    INST_RESET      = 0x06,
+    INST_SYNC_WRITE = 0x83,
+} feetech_intruction_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/** @} */
diff --git a/drivers/feetech/include/feetech_reader.h b/drivers/feetech/include/feetech_reader.h
new file mode 100644
index 0000000000000000000000000000000000000000..a119819454dd5adb4c0956d013190aa349984ffa
--- /dev/null
+++ b/drivers/feetech/include/feetech_reader.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @ingroup     drivers_feetech
+ *
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for Feetech packet reader
+ *
+ * @author      Loïc Dauphin <loic.dauphin@inria.fr>
+ */
+
+#ifndef FEETECH_READER_H
+#define FEETECH_READER_H
+
+#include "feetech_protocol.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FEETECH_ACK_SIZE           (6)
+#define FEETECH_RESPONSE_SIZE(len) (6 + len)
+
+/**
+ * @brief Feetech packet reader struct
+ */
+typedef struct {
+    const uint8_t *buffer; /**< data buffer */
+    size_t size;           /**< data buffer's size */
+} feetech_reader_t;
+
+/**
+ * @brief Initialize the Feetech packet reader
+ *
+ * @param[out] reader    the packet reader
+ * @param[in] buffer     the buffer used to store data
+ * @param[in] size       the size of the buffer
+ */
+static inline void feetech_reader_init(feetech_reader_t *reader, const uint8_t *buffer, size_t size)
+{
+    reader->buffer = buffer;
+    reader->size = size;
+}
+
+/**
+ * @brief Compute the packet's sum
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the sum of the packet
+ */
+uint8_t feetech_reader_compute_sum(const feetech_reader_t *reader);
+
+/**
+ * @brief Check if the packet has the minimum required size
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return true if the packet has the minimum required size
+ * @return false otherwise
+ */
+static inline bool feetech_reader_check_minsize(const feetech_reader_t *reader)
+{
+    return 5 < reader->size;
+}
+
+
+/**
+ * @brief Check if the packet begins with 2 FEETECH_START bits
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return true if the packet begins with 2 FEETECH_START bits
+ * @return false otherwise
+ */
+static inline bool feetech_reader_check_start(const feetech_reader_t *reader)
+{
+    return
+            reader->buffer[0] == FEETECH_START &&
+            reader->buffer[1] == FEETECH_START;
+}
+
+/**
+ * @brief Check if the packet's size is the same as the buffer's size
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return true if the packet's size is the same as the buffer's size
+ * @return false otherwise
+ */
+static inline bool feetech_reader_check_size(const feetech_reader_t *reader)
+{
+    return reader->size == (size_t)(reader->buffer[3] + 4);
+}
+
+/**
+ * @brief Check if the computed sum and the sum of the packet are equal
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return true if the computed sum and the sum of the packet are equal
+ * @return false otherwise
+ */
+static inline bool feetech_reader_check_sum(const feetech_reader_t *reader)
+{
+    return feetech_reader_compute_sum(reader) == reader->buffer[reader->size - 1];
+}
+
+/**
+ * @brief Check if the packet is valid
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return true if the packet is valid
+ * @return false otherwise
+ */
+bool feetech_reader_is_valid(const feetech_reader_t *reader);
+
+/**
+ * @brief Get the packet's device id
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the packet's device id
+ */
+static inline uint8_t feetech_reader_get_id(const feetech_reader_t *reader)
+{
+    return reader->buffer[2];
+}
+
+/**
+ * @brief Get the packet's instruction code
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the packet's instruction code
+ */
+static inline uint8_t feetech_reader_get_instr(const feetech_reader_t *reader)
+{
+    return reader->buffer[4];
+}
+
+/**
+ * @brief Get the packet's payload (response)
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the addess of the begining of the payload
+ */
+static inline const uint8_t *feetech_reader_response_get_payload(const feetech_reader_t *reader)
+{
+    return &reader->buffer[5];
+}
+
+/**
+ * @brief Get the packet's payload size (response)
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the size of the payload
+ */
+static inline size_t feetech_reader_response_get_payload_size(const feetech_reader_t *reader)
+{
+    return reader->buffer[3] - 2;
+}
+
+/**
+ * @brief Get the packet's payload (WRITE)
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the begining addess of the payload
+ */
+static inline const uint8_t *feetech_reader_write_get_payload(const feetech_reader_t *reader)
+{
+    return &reader->buffer[6];
+}
+
+/**
+ * @brief Get the packet's payload size (WRITE)
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the size of the payload
+ */
+static inline size_t feetech_reader_write_get_payload_size(const feetech_reader_t *reader)
+{
+    return reader->buffer[3] - 3;
+}
+
+/**
+ * @brief Get the packet's target register address (WRITE)
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the register address
+ */
+static inline uint8_t feetech_reader_write_get_reg(const feetech_reader_t *reader)
+{
+    return reader->buffer[5];
+}
+
+/**
+ * @brief Get the packet's READ size
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the READ size
+ */
+static inline size_t feetech_reader_read_get_size(const feetech_reader_t *reader)
+{
+    return reader->buffer[6];
+}
+
+/**
+ * @brief Get the packet's target register address (READ)
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the register address
+ */
+static inline uint8_t feetech_reader_read_get_reg(const feetech_reader_t *reader)
+{
+    return reader->buffer[5];
+}
+
+/**
+ * @brief Get the packet items' payload size (SYNC_WRITE)
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the size of the items' payload
+ */
+static inline size_t feetech_reader_sync_write_get_payload_size(const feetech_reader_t *reader)
+{
+    return reader->buffer[6];
+}
+
+/**
+ * @brief Get the packet's target register address (SYNC_WRITE)
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the register address
+ */
+static inline uint8_t feetech_reader_sync_write_get_reg(const feetech_reader_t *reader)
+{
+    return reader->buffer[5];
+}
+
+/**
+ * @brief Get the packet items' count (SYNC_WRITE)
+ *
+ * @param[in] reader      the packet reader
+ *
+ * @return the number of items in the packet
+ */
+size_t feetech_reader_sync_write_get_items_count(const feetech_reader_t *reader);
+
+/**
+ * @brief Get the packet item's device id (SYNC_WRITE)
+ *
+ * @param[in] reader      the packet reader
+ * @param[in] index       the item index
+ *
+ * @return the item's device id
+ */
+uint8_t feetech_reader_sync_write_item_get_id(const feetech_reader_t *reader, uint8_t index);
+
+/**
+ * @brief Get the packet item's payload (SYNC_WRITE)
+ *
+ * @param[in] reader      the packet reader
+ * @param[in] index       the item index
+ *
+ * @return the begining addess of the payload
+ */
+const uint8_t *feetech_reader_sync_write_item_get_payload(const feetech_reader_t *reader, uint8_t index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/** @} */
diff --git a/drivers/feetech/include/feetech_writer.h b/drivers/feetech/include/feetech_writer.h
new file mode 100644
index 0000000000000000000000000000000000000000..a09210430d97ffad4be97207b2a643b663d2fb75
--- /dev/null
+++ b/drivers/feetech/include/feetech_writer.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @ingroup     drivers_feetech
+ *
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for Feetech packet writer
+ *
+ * @author      Loïc Dauphin <loic.dauphin@inria.fr>
+ */
+
+#ifndef FEETECH_WRITER_H
+#define FEETECH_WRITER_H
+
+#include "feetech_protocol.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Feetech packet writer struct
+ */
+typedef struct {
+    uint8_t *buffer; /**< data buffer */
+    size_t size;     /**< packet's size */
+    size_t buffer_max_size;    /**< data buffer's size */
+} feetech_writer_t;
+
+/**
+ * @brief Initialize the Feetech packet writer
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] buffer     the buffer used to store data
+ * @param[in] buffer_max_size      the size of the buffer (= maximum packet size)
+ */
+void feetech_writer_init(feetech_writer_t *writer, uint8_t *buffer, size_t buffer_max_size);
+
+/**
+ * @brief Get the data buffer to send
+ *
+ * @param[out] writer    the packet writer
+ *
+ * @return the begining address of the buffer
+ */
+const uint8_t *feetech_writer_get_data(const feetech_writer_t *writer);
+
+/**
+ * @brief Get the data buffer's size to send
+ *
+ * @param[out] writer    the packet writer
+ *
+ * @return the buffer's size
+ */
+size_t feetech_writer_get_size(const feetech_writer_t *writer);
+
+/**
+ * @brief Build a response packet
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the responder's id
+ * @param[in] buffer     the response data
+ * @param[in] size       the response size
+ */
+void feetech_writer_response_make(feetech_writer_t *writer, uint8_t id, const uint8_t *buffer, size_t size);
+
+/**
+ * @brief Build an ack packet
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the responder's id
+ */
+void feetech_writer_ack_make(feetech_writer_t *writer, uint8_t id);
+
+/**
+ * @brief Build a PING packet
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the destination's id
+ */
+void feetech_writer_ping_make(feetech_writer_t *writer, uint8_t id);
+
+/**
+ * @brief Build a WRITE packet
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the destination's id
+ * @param[in] reg        the register to write in
+ * @param[in] buffer     the data buffer to write
+ * @param[in] size       the data buffer's size
+ */
+void feetech_writer_write_make(feetech_writer_t *writer, uint8_t id, uint8_t reg, const uint8_t *buffer, size_t size);
+
+/**
+ * @brief Build a WRITE packet (8 bits)
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the destination's id
+ * @param[in] reg        the register to write in
+ * @param[in] value      the value to write in the register
+ */
+void feetech_writer_write8_make(feetech_writer_t *writer, uint8_t id, uint8_t reg, uint8_t value);
+
+/**
+ * @brief Build a WRITE packet (16 bits)
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the destination's id
+ * @param[in] reg        the register to write in
+ * @param[in] value      the value to write in the register
+ */
+void feetech_writer_write16_make(feetech_writer_t *writer, uint8_t id, uint8_t reg, uint16_t value);
+
+/**
+ * @brief Build a READ packet
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the destination's id
+ * @param[in] reg        the register to read
+ * @param[in] size       the size to read
+ */
+void feetech_writer_read_make(feetech_writer_t *writer, uint8_t id, uint8_t reg, size_t size);
+
+/**
+ * @brief Begin to build a SYNC_WRITE packet
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] reg        the register to write in
+ * @param[in] size       the data buffer's size
+ */
+void feetech_writer_sync_write_begin(feetech_writer_t *writer, uint8_t reg, size_t size);
+
+/**
+ * @brief End the building of a SYNC_WRITE packet
+ *
+ * @param[out] writer    the packet writer
+ */
+void feetech_writer_sync_write_end(feetech_writer_t *writer);
+
+/**
+ * @brief Add an item to a SYNC_WRITE packet
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the destination's id
+ * @param[in] buffer     the data buffer to write
+ * @param[in] size       the data buffer's size
+ */
+void feetech_writer_sync_write_add(feetech_writer_t *writer, uint8_t id, const uint8_t *buffer, size_t size);
+
+/**
+ * @brief Add an item to a SYNC_WRITE packet (8 bits)
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the destination's id
+ * @param[in] value      the value to write
+ */
+void feetech_writer_sync_write_add_8bits(feetech_writer_t *writer, uint8_t id, uint8_t value);
+
+/**
+ * @brief Add an item to a SYNC_WRITE packet (16 bits)
+ *
+ * @param[out] writer    the packet writer
+ * @param[in] id         the destination's id
+ * @param[in] value      the value to write
+ */
+void feetech_writer_sync_write_add_16bits(feetech_writer_t *writer, uint8_t id, uint16_t value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/** @} */
diff --git a/drivers/feetech/reader.c b/drivers/feetech/reader.c
new file mode 100644
index 0000000000000000000000000000000000000000..62950aa0c597f771e413898a3e01229aeb2a62ee
--- /dev/null
+++ b/drivers/feetech/reader.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @ingroup     drivers_feetech
+ * @{
+ *
+ * @file
+ * @brief       Feetech messages reader
+ *
+ * @author      Loïc Dauphin <loic.dauphin@inria.fr>
+ *
+ * @}
+ */
+
+#include "feetech_reader.h"
+
+static uint8_t _compute_sum(const feetech_reader_t *reader)
+{
+    uint8_t sum = 0;
+    for (size_t i = 2 ; i < reader->size-1 ; i++) {
+        sum += reader->buffer[i];
+    }
+    return sum;
+}
+
+uint8_t feetech_reader_compute_sum(const feetech_reader_t *reader)
+{
+    return ~_compute_sum(reader);
+}
+
+bool feetech_reader_is_valid(const feetech_reader_t *reader)
+{
+    return
+            feetech_reader_check_minsize(reader) &&
+            feetech_reader_check_start(reader)   &&
+            feetech_reader_check_size(reader)    &&
+            feetech_reader_check_sum(reader);
+}
+
+size_t feetech_reader_sync_write_get_items_count(const feetech_reader_t *reader)
+{
+    return (reader->buffer[3] - 4) / (reader->buffer[6] + 1);
+}
+
+uint8_t feetech_reader_sync_write_item_get_id(const feetech_reader_t *reader, uint8_t index)
+{
+    return reader->buffer[7 + index * (feetech_reader_sync_write_get_payload_size(reader) + 1)];
+}
+
+const uint8_t *feetech_reader_sync_write_item_get_payload(const feetech_reader_t *reader, uint8_t index)
+{
+    return &reader->buffer[7 + index * (feetech_reader_sync_write_get_payload_size(reader) + 1) + 1];
+}
diff --git a/drivers/feetech/writer.c b/drivers/feetech/writer.c
new file mode 100644
index 0000000000000000000000000000000000000000..c8b14db8ed33be7a93a6b856758827c40d170d8c
--- /dev/null
+++ b/drivers/feetech/writer.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @ingroup     drivers_feetech
+ * @{
+ *
+ * @file
+ * @brief       Feetech messages writer
+ *
+ * @author      Loïc Dauphin <loic.dauphin@inria.fr>
+ *
+ * @}
+ */
+
+#include "feetech_writer.h"
+
+void feetech_writer_init(feetech_writer_t *writer, uint8_t *buffer, size_t buffer_max_size)
+{
+    writer->buffer = buffer;
+    writer->size = 0;
+    writer->buffer_max_size = buffer_max_size;
+}
+
+const uint8_t *feetech_writer_get_data(const feetech_writer_t *writer)
+{
+    return (const uint8_t*)writer->buffer;
+}
+
+size_t feetech_writer_get_size(const feetech_writer_t *writer)
+{
+    return writer->size;
+}
+
+void feetech_writer_response_make(feetech_writer_t *writer, uint8_t id, const uint8_t *buffer, size_t size)
+{
+    const size_t len = 2 + size;
+    if (len + 4 <= writer->buffer_max_size) {
+        writer->size = len + 4;
+
+        uint8_t sum = 0;
+
+        writer->buffer[0] = FEETECH_START;
+        writer->buffer[1] = FEETECH_START;
+        sum += writer->buffer[2] = id;
+        sum += writer->buffer[3] = len;
+        sum += writer->buffer[4] = 0;
+
+        for (size_t i = 0 ; i < size ; i++) {
+            sum += writer->buffer[5 + i] = buffer[i];
+        }
+
+        writer->buffer[size - 1] = ~sum;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+
+void feetech_writer_ack_make(feetech_writer_t *writer, uint8_t id)
+{
+    const size_t len = 2;
+    if (len + 4 <= writer->buffer_max_size) {
+        writer->size = len + 4;
+
+        uint8_t sum = 0;
+
+        writer->buffer[0] = FEETECH_START;
+        writer->buffer[1] = FEETECH_START;
+        sum += writer->buffer[2] = id;
+        sum += writer->buffer[3] = len;
+        sum += writer->buffer[4] = 0;
+        writer->buffer[5] = ~sum;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+void feetech_writer_ping_make(feetech_writer_t *writer, uint8_t id)
+{
+    const size_t len = 2;
+    if (len + 4 <= writer->buffer_max_size) {
+        writer->size = len + 4;
+
+        uint8_t sum = 0;
+
+        writer->buffer[0] = FEETECH_START;
+        writer->buffer[1] = FEETECH_START;
+        sum += writer->buffer[2] = id;
+        sum += writer->buffer[3] = len;
+        sum += writer->buffer[4] = INST_PING;
+        writer->buffer[5] = ~sum;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+void feetech_writer_write8_make(feetech_writer_t *writer, uint8_t id, uint8_t reg, uint8_t value)
+{
+    const size_t len = 4;
+    if (len + 4 <= writer->buffer_max_size) {
+        writer->size = len + 4;
+
+        uint8_t sum = 0;
+
+        writer->buffer[0] = FEETECH_START;
+        writer->buffer[1] = FEETECH_START;
+        sum += writer->buffer[2] = id;
+        sum += writer->buffer[3] = len;
+        sum += writer->buffer[4] = INST_WRITE;
+
+        sum += writer->buffer[5] = reg;
+        sum += writer->buffer[6] = value;
+
+        writer->buffer[7] = ~sum;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+void feetech_writer_write16_make(feetech_writer_t *writer, uint8_t id, uint8_t reg, uint16_t value)
+{
+    const size_t len = 5;
+    if (len + 4 <= writer->buffer_max_size) {
+        writer->size = len + 4;
+
+        uint8_t sum = 0;
+
+        writer->buffer[0] = FEETECH_START;
+        writer->buffer[1] = FEETECH_START;
+        sum += writer->buffer[2] = id;
+        sum += writer->buffer[3] = len;
+        sum += writer->buffer[4] = INST_WRITE;
+
+        sum += writer->buffer[5] = reg;
+        sum += writer->buffer[6] = (value >> 8) & 0xFF;
+        sum += writer->buffer[7] = value & 0xFF;
+
+        writer->buffer[8] = ~sum;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+void feetech_writer_write_make(feetech_writer_t *writer, uint8_t id, uint8_t reg, const uint8_t *buffer, size_t size)
+{
+    const size_t len = 3 + size;
+    if (len + 4 <= writer->buffer_max_size) {
+        writer->size = len + 4;
+
+        uint8_t sum = 0;
+
+        writer->buffer[0] = FEETECH_START;
+        writer->buffer[1] = FEETECH_START;
+        sum += writer->buffer[2] = id;
+        sum += writer->buffer[3] = len;
+        sum += writer->buffer[4] = INST_WRITE;
+
+        sum += writer->buffer[5] = reg;
+        for (size_t i = 0 ; i < size ; i++) {
+            sum += writer->buffer[6 + i] = buffer[i];
+        }
+
+        writer->buffer[writer->size - 1] = ~sum;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+void feetech_writer_read_make(feetech_writer_t *writer, uint8_t id, uint8_t reg, size_t size)
+{
+    const size_t len = 4;
+    if (len + 4 <= writer->buffer_max_size) {
+        writer->size = len + 4;
+
+        uint8_t sum = 0;
+
+        writer->buffer[0] = FEETECH_START;
+        writer->buffer[1] = FEETECH_START;
+        sum += writer->buffer[2] = id;
+        sum += writer->buffer[3] = len;
+        sum += writer->buffer[4] = INST_READ;
+
+        sum += writer->buffer[5] = reg;
+        sum += writer->buffer[6] = (uint8_t)size;
+
+        writer->buffer[7] = ~sum;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+size_t feetech_writer_sync_write_required(feetech_writer_t *writer)
+{
+    if (8 <= writer->size && writer->buffer[4] == INST_SYNC_WRITE) {
+        return writer->buffer[6];
+    }
+    return 0;
+}
+
+void feetech_writer_sync_write_end(feetech_writer_t *writer)
+{
+    if (writer->size <= 8) {
+        writer->size = 0;
+    }
+}
+
+void feetech_writer_sync_write_add(feetech_writer_t *writer, uint8_t id, const uint8_t *buffer, size_t size)
+{
+    if (feetech_writer_sync_write_required(writer) == size &&
+        size != 0 && writer->size + size + 1 <= writer->buffer_max_size) {
+
+        uint8_t sum = ~writer->buffer[writer->size - 1];
+
+        sum += writer->buffer[writer->size - 1] = id;
+        for (size_t i = 0 ; i < size ; i++) {
+            sum += writer->buffer[writer->size + i] = buffer[i];
+        }
+
+        writer->buffer[3] += size + 1;
+        writer->buffer[writer->size + size] = ~(sum + size + 1);
+        writer->size += size + 1;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+void feetech_writer_sync_write_add_8bits(feetech_writer_t *writer, uint8_t id, uint8_t value)
+{
+    if (feetech_writer_sync_write_required(writer) == 1 &&
+        writer->size + 2 <= writer->buffer_max_size) {
+
+        uint8_t sum = ~writer->buffer[writer->size - 1];
+
+        sum += writer->buffer[writer->size - 1] = id;
+        sum += writer->buffer[writer->size] = value;
+
+        writer->buffer[3] += 2;
+        writer->buffer[writer->size + 1] = ~(sum + 2);
+        writer->size += 2;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+void feetech_writer_sync_write_add_16bits(feetech_writer_t *writer, uint8_t id, uint16_t value)
+{
+    if (feetech_writer_sync_write_required(writer) == 2 &&
+        writer->size + 3 <= writer->buffer_max_size) {
+
+        uint8_t sum = ~writer->buffer[writer->size - 1];
+
+        sum += writer->buffer[writer->size - 1] = id;
+        sum += writer->buffer[writer->size + 0] = (value >> 8) & 0xFF;
+        sum += writer->buffer[writer->size + 1] = value & 0xFF;
+
+        writer->buffer[3] += 3;
+        writer->buffer[writer->size + 2] = ~(sum + 3);
+        writer->size += 3;
+    }
+    else {
+        writer->size = 0;
+    }
+}
+
+void feetech_writer_sync_write_begin(feetech_writer_t *writer, uint8_t reg, size_t size)
+{
+    const size_t len = 4;
+    if (len + 4 <= writer->buffer_max_size) {
+        writer->size = len + 4;
+
+        uint8_t sum = 0;
+
+        writer->buffer[0] = FEETECH_START;
+        writer->buffer[1] = FEETECH_START;
+        sum += writer->buffer[2] = 0xFF;
+        sum += writer->buffer[3] = len;
+        sum += writer->buffer[4] = INST_SYNC_WRITE;
+
+        sum += writer->buffer[5] = reg;
+        sum += writer->buffer[6] = (uint8_t)size;
+
+        writer->buffer[7] = ~sum;
+    }
+}
diff --git a/drivers/include/feetech.h b/drivers/include/feetech.h
new file mode 100644
index 0000000000000000000000000000000000000000..1a155f5d91c8f7d48c02464a6152454320bf52db
--- /dev/null
+++ b/drivers/include/feetech.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @defgroup    drivers_feetech Feetech driver
+ * @ingroup     drivers_actuators
+ *
+ * This module contains drivers for any device using feetech's servomotors communication bus.
+ * The bus is mainly used for servomotors, but a device can be anything : sensors, other actuators.
+ *
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for Feetech devices driver
+ *
+ * @author      Loïc Dauphin <loic.dauphin@inria.fr>
+ */
+
+#ifndef FEETECH_H
+#define FEETECH_H
+
+#include <stdlib.h>
+
+#include "feetech_protocol.h"
+#include "uart_half_duplex.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint8_t feetech_id_t;    /**< device id type */
+typedef uint8_t feetech_addr_t;  /**< address type */
+
+/**
+ * @brief Descriptor struct for a feetech device
+ */
+typedef struct {
+    uart_half_duplex_t *stream; /**< the stream used */
+    feetech_id_t id;            /**< the device address */
+} feetech_t;
+
+/**
+ * @brief   Possible feetech return values
+ */
+enum {
+    FEETECH_OK,               /**< Success */
+    FEETECH_TIMEOUT,          /**< No response from the device */
+    FEETECH_BUFFER_TOO_SMALL, /**< Buffer is too small for the message */
+    FEETECH_INVALID_MESSAGE,  /**< Invalid message received */
+};
+
+/**
+ * @brief Send a PING message to a device
+ *
+ * @param[in] stream   the stream
+ * @param[in] id       the device address
+ *
+ * @return             FEETECH_OK if a device answered
+ * @return             FEETECH_TIMEOUT if the device did not answer
+ * @return             FEETECH_BUFFER_TOO_SMALL if buffer is too small for the message
+ * @return             FEETECH_INVALID_MESSAGE if an invalid message was received
+ */
+int feetech_ping(uart_half_duplex_t *stream, feetech_id_t id);
+
+/**
+ * @brief Initialize a Feetech device
+ *
+ * @param[out] device  the Feetech device
+ * @param[in] stream   the stream
+ * @param[in] id       the device address
+ */
+void feetech_init(feetech_t *device, uart_half_duplex_t *stream, feetech_id_t id);
+
+/**
+ * @brief Write to a device 8bits address
+ *
+ * @param[in] device   the Feetech device
+ * @param[in] addr     the address to write
+ * @param[in] value    the value to write
+ *
+ * @return             FEETECH_OK on success
+ * @return             FEETECH_TIMEOUT if the device did not answer
+ * @return             FEETECH_BUFFER_TOO_SMALL if buffer is too small for the message
+ * @return             FEETECH_INVALID_MESSAGE if an invalid message was received
+ */
+int feetech_write8(feetech_t *device, feetech_addr_t addr, uint8_t value);
+
+/**
+ * @brief Write to a device 16bits address
+ *
+ * @param[in] device   the Feetech device
+ * @param[in] addr     the address to write
+ * @param[in] value    the value to write
+ *
+ * @return             FEETECH_OK on success
+ * @return             FEETECH_TIMEOUT if the device did not answer
+ * @return             FEETECH_BUFFER_TOO_SMALL if buffer is too small for the message
+ * @return             FEETECH_INVALID_MESSAGE if an invalid message was received
+ */
+int feetech_write16(feetech_t *device, feetech_addr_t addr, uint16_t value);
+
+/**
+ * @brief Write to a device address
+ *
+ * @param[in] device   the Feetech device
+ * @param[in] addr     the address to start write
+ * @param[in] data     the data to write
+ * @param[in] length   the data length
+ *
+ * @return             FEETECH_OK on success
+ * @return             FEETECH_TIMEOUT if the device did not answer
+ * @return             FEETECH_BUFFER_TOO_SMALL if buffer is too small for the message
+ * @return             FEETECH_INVALID_MESSAGE if an invalid message was received
+ */
+int feetech_write(feetech_t *device, feetech_addr_t addr, const uint8_t *data, size_t length);
+
+/**
+ * @brief Read from a device 8bits address
+ *
+ * @param[in] device   the Feetech device
+ * @param[in] addr     the address to read
+ * @param[out] value   the value to read
+ *
+ * @return             FEETECH_OK on success
+ * @return             FEETECH_TIMEOUT if the device did not answer
+ * @return             FEETECH_BUFFER_TOO_SMALL if buffer is too small for the message
+ * @return             FEETECH_INVALID_MESSAGE if an invalid message was received
+ */
+int feetech_read8(feetech_t *device, feetech_addr_t addr, uint8_t *value);
+
+/**
+ * @brief Read from a device 16bits address
+ *
+ * @param[in] device   the Feetech device
+ * @param[in] addr     the address to read
+ * @param[out] value   the value to read
+ *
+ * @return             FEETECH_OK on success
+ * @return             FEETECH_TIMEOUT if the device did not answer
+ * @return             FEETECH_BUFFER_TOO_SMALL if buffer is too small for the message
+ * @return             FEETECH_INVALID_MESSAGE if an invalid message was received
+ */
+int feetech_read16(feetech_t *device, feetech_addr_t addr, uint16_t *value);
+
+/**
+ * @brief Read from a device address
+ *
+ * @param[in] device   the Feetech device
+ * @param[in] addr      the address to start read
+ * @param[out] data    the data buffer to fill
+ * @param[in] length   the data length
+ *
+ * @return             FEETECH_OK on success
+ * @return             FEETECH_TIMEOUT if the device did not answer
+ * @return             FEETECH_BUFFER_TOO_SMALL if buffer is too small for the message
+ * @return             FEETECH_INVALID_MESSAGE if an invalid message was received
+ */
+int feetech_read(feetech_t *device, feetech_addr_t addr, uint8_t *data, size_t length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/** @} */
diff --git a/drivers/uart_half_duplex/Makefile b/drivers/uart_half_duplex/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2
--- /dev/null
+++ b/drivers/uart_half_duplex/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/uart_half_duplex/include/uart_half_duplex.h b/drivers/uart_half_duplex/include/uart_half_duplex.h
new file mode 100644
index 0000000000000000000000000000000000000000..7e694d75f527e6352b4eec70ad612e1ee66ab654
--- /dev/null
+++ b/drivers/uart_half_duplex/include/uart_half_duplex.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @defgroup    drivers_uart_half_duplex half-duplex UART Driver
+ * @ingroup     drivers_actuators
+ *
+ * This module contains drivers for UART half-duplex communication bus.
+ * It needs to manage the communication direction by enabling or disabling TX.
+ *
+ * @{
+ *
+ * @file
+ * @brief       Interface definition for half-duplex UART driver
+ *
+ * @author      Loïc Dauphin <loic.dauphin@inria.fr>
+ */
+
+#ifndef UART_HALF_DUPLEX_H
+#define UART_HALF_DUPLEX_H
+
+#include <stdlib.h>
+
+#include "periph/uart.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef UART_HALF_DUPLEX_DEFAULT_TIMEOUT_US
+#define UART_HALF_DUPLEX_DEFAULT_TIMEOUT_US (20000LU) /**< Default recv timeout (in microseconds) */
+#endif
+
+/**
+ * @brief half-duplex UART direction management method type
+ */
+typedef int uart_half_duplex_dir_t;
+
+#define UART_HALF_DUPLEX_DIR_NONE           (0)  /**< Don't manage direction */
+
+#define UART_HALF_DUPLEX_DIR_PIN_SET(pin)   (((pin + 1) << 1))     /**< pin set enables TX */
+#define UART_HALF_DUPLEX_DIR_PIN_CLEAR(pin) (((pin + 1) << 1) | 1) /**< pin clear enables TX */
+
+/**
+ * @brief Configuration for half-duplex UART
+ */
+typedef struct {
+    uart_t uart;                /**< the half-duplex UART bus to use */
+    uint32_t baudrate;          /**< the baudrate to use */
+    uart_half_duplex_dir_t dir; /**< the direction management method */
+} uart_half_duplex_params_t;
+
+/**
+ * @brief Descriptor struct for half-duplex UART
+ */
+typedef struct {
+    uint8_t *buffer;                  /**< the buffer used for TX and RX */
+    size_t size;                      /**< the number of available elements for TX/RX */
+    size_t buffer_max_size;                     /**< the buffer size */
+    uint32_t timeout_us;              /**< the maximum duration (in microseconds) for waiting data */
+    uart_half_duplex_params_t params; /**< the half-duplex UART configuration */
+} uart_half_duplex_t;
+
+/**
+ * @brief   Possible UART_HALF_DUPLEX return values
+ */
+enum {
+    UART_HALF_DUPLEX_OK     = UART_OK,       /**< everything in order */
+    UART_HALF_DUPLEX_NODEV  = UART_NODEV,    /**< invalid UART device given */
+    UART_HALF_DUPLEX_NOBAUD = UART_NOBAUD,   /**< given baudrate is not applicable */
+    UART_HALF_DUPLEX_INTERR = UART_INTERR,   /**< all other internal errors */
+    UART_HALF_DUPLEX_NOMODE = UART_NOMODE,   /**< given mode is not applicable */
+    UART_HALF_DUPLEX_NOBUFF = -5             /**< invalid buffer given */
+};
+
+/**
+ * @brief Initialize the half-duplex UART bus to communicate with devices
+ *
+ * @param[out] dev             the device
+ * @param[in] buffer           the buffer used for TX and RX
+ * @param[in] buffer_max_size  the buffer size
+ * @param[in] params           the initialization parameters
+ *
+ * @return     UART_HALF_DUPLEX_OK if everything is in order
+ * @return     UART_HALF_DUPLEX_NODEV if invalid UART device was given
+ * @return     UART_HALF_DUPLEX_NOBAUD if given baudrate is not applicable
+ * @return     UART_HALF_DUPLEX_INTERR if an other internal error occured
+ * @return     UART_HALF_DUPLEX_NOMODE if the given mode is not applicable
+ * @return     UART_HALF_DUPLEX_NOBUFF if an invalid buffer was given
+ */
+int uart_half_duplex_init(uart_half_duplex_t *dev, uint8_t *buffer, size_t buffer_max_size, const uart_half_duplex_params_t *params);
+
+/**
+ * @brief Set the half-duplex UART bus in TX mode
+ *
+ * @param[in] dev        the device
+ */
+static inline void uart_half_duplex_set_tx(uart_half_duplex_t *dev)
+{
+    dev->size = dev->buffer_max_size;
+}
+
+/**
+ * @brief Set the half-duplex UART bus in RX mode
+ *
+ * @param[in] dev        the device
+ */
+static inline void uart_half_duplex_set_rx(uart_half_duplex_t *dev)
+{
+    dev->size = 0;
+}
+
+/**
+ * @brief Send the data contained in the driver's buffer
+ *
+ * @param[in] dev        the device
+ * @param[in] size       the number of characters to send
+ *
+ * @return               the number of characters actually sent
+ */
+size_t uart_half_duplex_send(uart_half_duplex_t *dev, size_t size);
+
+/**
+ * @brief Recv data an fill the driver's buffer
+ *
+ * @param[in] dev        the device
+ * @param[in] size       the number of characters to receive
+ *
+ * @return               the number of characters actually received
+ */
+size_t uart_half_duplex_recv(uart_half_duplex_t *dev, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+/** @} */
diff --git a/drivers/uart_half_duplex/uart_half_duplex.c b/drivers/uart_half_duplex/uart_half_duplex.c
new file mode 100644
index 0000000000000000000000000000000000000000..7147899b77ac557df00b2417b5c0bf0db8ad2de9
--- /dev/null
+++ b/drivers/uart_half_duplex/uart_half_duplex.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+/**
+ * @ingroup     drivers_uart_half_duplex
+ * @{
+ *
+ * @file
+ * @brief       Driver implementation for half-duplex UART devices
+ *
+ * @author      Loïc Dauphin <loic.dauphin@inria.fr>
+ *
+ * @}
+ */
+
+#include "uart_half_duplex.h"
+
+#include "periph/uart.h"
+#include "periph/gpio.h"
+#include "xtimer.h"
+
+#define IS_SET(dir)   ((dir & 1) == 0)
+#define IS_CLEAR(dir) ((dir & 1) == 1)
+
+#define IS_PIN(dir)    (dir >= UART_HALF_DUPLEX_DIR_PIN_SET(0))
+#define GET_PIN(dir)   ((dir >> 1) - 1)
+
+static inline void _enable_tx(uart_half_duplex_dir_t dir)
+{
+    if (IS_PIN(dir)) {
+        if (IS_SET(dir)) {
+            gpio_set(GET_PIN(dir));
+            return;
+        }
+        if (IS_CLEAR(dir)) {
+            gpio_clear(GET_PIN(dir));
+            return;
+        }
+    }
+}
+
+static inline void _disable_tx(uart_half_duplex_dir_t dir)
+{
+    if (IS_PIN(dir)) {
+        if (IS_SET(dir)) {
+            gpio_clear(GET_PIN(dir));
+            return;
+        }
+        if (IS_CLEAR(dir)) {
+            gpio_set(GET_PIN(dir));
+            return;
+        }
+    }
+}
+
+static void _rx_cb(void* data, uint8_t c)
+{
+    uart_half_duplex_t *dev = data;
+    if (dev->size < dev->buffer_max_size) {
+        dev->buffer[dev->size++] = c;
+    }
+}
+
+int uart_half_duplex_init(uart_half_duplex_t *dev, uint8_t *buffer, size_t buffer_max_size, const uart_half_duplex_params_t *params)
+{
+    if (buffer == NULL || buffer_max_size <= 7) {
+        return UART_HALF_DUPLEX_NOBUFF;
+    }
+
+    dev->buffer = buffer;
+    dev->buffer_max_size = buffer_max_size;
+    dev->params = *params;
+    dev->timeout_us = UART_HALF_DUPLEX_DEFAULT_TIMEOUT_US;
+
+    if (IS_PIN(dev->params.dir)) {
+        gpio_init(GET_PIN(dev->params.dir), GPIO_OUT);
+    }
+
+    int ret = uart_init(dev->params.uart, dev->params.baudrate, _rx_cb, dev);
+
+    _disable_tx(dev->params.dir);
+    uart_half_duplex_set_rx(dev);
+
+    return ret;
+}
+
+size_t uart_half_duplex_send(uart_half_duplex_t *dev, size_t size)
+{
+    _enable_tx(dev->params.dir);
+    uart_write(dev->params.uart, dev->buffer, size);
+    _disable_tx(dev->params.dir);
+    return size;
+}
+
+size_t uart_half_duplex_recv(uart_half_duplex_t *dev, size_t size)
+{
+    const uint32_t begin = xtimer_now_usec();
+    while (xtimer_now_usec() - begin < dev->timeout_us) {
+        if (dev->size >= size) {
+            break;
+        }
+    }
+    return dev->size;
+}
diff --git a/tests/driver_feetech/Makefile b/tests/driver_feetech/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..7bcc6f2da02efe9f3a7e7409df98d70051d4cc7a
--- /dev/null
+++ b/tests/driver_feetech/Makefile
@@ -0,0 +1,13 @@
+APPLICATION = driver_feetech
+include ../Makefile.tests_common
+
+# chronos : USART_1 undeclared
+BOARD_BLACKLIST += chronos
+
+# mips-malta : undefined reference to uart_write
+BOARD_BLACKLIST += mips-malta
+
+USEMODULE += feetech
+USEMODULE += shell
+
+include $(RIOTBASE)/Makefile.include
diff --git a/tests/driver_feetech/README.md b/tests/driver_feetech/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2a89c5198f010f573b11befb804aa838fbc0efdb
--- /dev/null
+++ b/tests/driver_feetech/README.md
@@ -0,0 +1,40 @@
+# About
+
+This application is intended for testing Feetech TTL bus.
+It is especially designed to test Feetech SCS15 servomotors.
+
+# Usage
+
+To have the list of available commands :
+```
+help
+```
+
+You will need to initialize one UART at 1000000 baudrate (if the servomotor is in factory configuration) :
+```
+init 1 1000000
+```
+
+To ping the servomotor :
+```
+ping 1
+```
+
+Be careful ! If 2 servomotors with the same ID are connected on the same bus, you will have no response.
+Factory configuration ID is 1, you need to change this to connect an other servo.
+
+To scan every connected servomotors (IDs from 0 to 253) :
+```
+scan
+```
+
+To read a servo register :
+```
+read 1 PRESENT_POSITION
+```
+
+To move a servo, you need to enable the torque and set the goal position [0-1024] :
+```
+write 1 ENABLE_TORQUE 1
+write 1 GOAL_POSITION 512
+```
diff --git a/tests/driver_feetech/main.c b/tests/driver_feetech/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..8eb6379096a4f705e0bb8eee14bfa0a379d2bbb4
--- /dev/null
+++ b/tests/driver_feetech/main.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2017 Inria
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser General
+ * Public License v2.1. See the file LICENSE in the top level directory for more
+ * details.
+ */
+
+#include "feetech.h"
+#include "shell.h"
+#include "shell_commands.h"
+#include "uart_stdio.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define ARRAY_LEN(array) (sizeof(array)/sizeof(array[0]))
+
+typedef struct {
+    const char *name;
+    int addr;
+} reg_name_addr_t;
+
+static const reg_name_addr_t regs8[] = {
+    { "ID", SCS15_ID },
+    { "BAUD_RATE", SCS15_BAUD_RATE },
+    { "RETURN_DELAY_TIME", SCS15_RETURN_DELAY_TIME },
+    { "RETURN_LEVEL", SCS15_RETURN_LEVEL },
+    { "LIMIT_TEMPERATURE", SCS15_LIMIT_TEMPERATURE },
+    { "MAX_LIMIT_VOLTAGE", SCS15_MAX_LIMIT_VOLTAGE },
+    { "MIN_LIMIT_VOLTAGE", SCS15_MIN_LIMIT_VOLTAGE },
+    { "ALARM_LED", SCS15_ALARM_LED },
+    { "ALARM_SHUTDOWN", SCS15_ALARM_SHUTDOWN },
+    { "COMPLIANCE_P", SCS15_COMPLIANCE_P },
+    { "COMPLIANCE_D", SCS15_COMPLIANCE_D },
+    { "COMPLIANCE_I", SCS15_COMPLIANCE_I },
+    { "CW_DEAD", SCS15_CW_DEAD },
+    { "CCW_DEAD", SCS15_CCW_DEAD },
+    { "TORQUE_ENABLE", SCS15_TORQUE_ENABLE },
+    { "LED", SCS15_LED },
+    { "LOCK", SCS15_LOCK },
+    { "PRESENT_VOLTAGE", SCS15_PRESENT_VOLTAGE },
+    { "PRESENT_TEMPERATURE", SCS15_PRESENT_TEMPERATURE },
+    { "REGISTERED_INSTRUCTION", SCS15_REGISTERED_INSTRUCTION },
+    { "ERROR", SCS15_ERROR },
+    { "MOVING", SCS15_MOVING },
+};
+
+static const reg_name_addr_t regs16[] = {
+    { "MODEL_NUMBER", SCS15_MODEL_NUMBER },
+    { "VERSION", SCS15_VERSION },
+    { "MIN_ANGLE_LIMIT", SCS15_MIN_ANGLE_LIMIT },
+    { "MAX_ANGLE_LIMIT", SCS15_MAX_ANGLE_LIMIT },
+    { "MAX_TORQUE", SCS15_MAX_TORQUE },
+    { "PUNCH", SCS15_PUNCH },
+    { "IMAX", SCS15_IMAX },
+    { "OFFSET", SCS15_OFFSET },
+    { "GOAL_POSITION", SCS15_GOAL_POSITION },
+    { "GOAL_TIME", SCS15_GOAL_TIME },
+    { "GOAL_SPEED", SCS15_GOAL_SPEED },
+    { "PRESENT_POSITION", SCS15_PRESENT_POSITION },
+    { "PRESENT_SPEED", SCS15_PRESENT_SPEED },
+    { "PRESENT_LOAD", SCS15_PRESENT_LOAD },
+    { "VIR_POSITION", SCS15_VIR_POSITION },
+    { "CURRENT", SCS15_CURRENT },
+};
+
+static const int32_t baudrates[] = {
+    1000000L,
+    500000L,
+    250000L,
+    128000L,
+    115200L,
+    76800L,
+    57600L,
+    38400L,
+};
+
+static uint8_t feetech_buffer[128];
+static uart_half_duplex_t stream;
+
+static int parse_uart(char *arg)
+{
+    unsigned uart = (unsigned)atoi(arg);
+    if (uart >= UART_NUMOF) {
+        printf("Error: Invalid UART_DEV device specified (%u).\n", uart);
+        return -1;
+    }
+    else if (UART_DEV(uart) == UART_STDIO_DEV) {
+        printf("Error: The selected UART_DEV(%u) is used for the shell!\n", uart);
+        return -2;
+    }
+    return uart;
+}
+
+static int32_t parse_baud(char *arg)
+{
+    int32_t baud = (int32_t)atoi(arg);
+
+    for (size_t i = 0 ; i < ARRAY_LEN(baudrates) ; i++) {
+        if(baud == baudrates[i]) {
+            return baud;
+        }
+    }
+
+    printf("Error: Invalid baudrate (%s)\n", arg);
+    return -1;
+}
+
+static int parse_dev(char *arg)
+{
+    int dev = (int)atoi(arg);
+    if (dev < 0 || 254 < dev) {
+        printf("Error: Invalid device id (%s)\n", arg);
+        return -1;
+    }
+    return dev;
+}
+
+static void parse_reg(char *arg, int *reg8, int *reg16)
+{
+    *reg8 = -1;
+    *reg16 = -1;
+
+    for (size_t i = 0 ; i < ARRAY_LEN(regs8) ; i++) {
+        if(strcmp(arg, regs8[i].name) == 0) {
+            *reg8 = regs8[i].addr;
+            return;
+        }
+    }
+
+    for (size_t i = 0 ; i < ARRAY_LEN(regs16) ; i++) {
+        if(strcmp(arg, regs16[i].name) == 0) {
+            *reg16 = regs16[i].addr;
+            return;
+        }
+    }
+
+    printf("Error: Invalid register (%s)\n", arg);
+}
+
+void print_registers(void) {
+    puts("available 8bits registers :");
+    for (size_t i = 0 ; i < ARRAY_LEN(regs8) ; i++) {
+        printf("\t%s\n", regs8[i].name);
+    }
+
+    puts("available 16bits registers :");
+    for (size_t i = 0 ; i < ARRAY_LEN(regs16) ; i++) {
+        printf("\t%s\n", regs16[i].name);
+    }
+}
+
+static int cmd_init(int argc, char **argv) {
+    int uart = -1;
+    int baud = -1;
+    uint32_t timeout = -1;
+
+    if (argc != 3 && argc != 4) {
+        printf("usage; %s <uart> <baudrate> [<timeout_us>]\n", argv[0]);
+        puts("available baudrates :");
+        for (size_t i = 0 ; i < ARRAY_LEN(baudrates) ; i++) {
+            printf("\t%ld\n", (long int)baudrates[i]);
+        }
+        return 1;
+    }
+    /* parse parameters */
+    uart = parse_uart(argv[1]);
+    if (uart < 0) {
+        return -1;
+    }
+
+    baud = parse_baud(argv[2]);
+    if (baud < 0) {
+        return -1;
+    }
+
+    if (argc == 4) {
+        timeout = (uint32_t)atol(argv[3]);
+        if (timeout == 0) {
+            printf("Error : Invalid timeout (%s)", argv[3]);
+            return -1;
+        }
+    }
+
+    /* init */
+    uart_half_duplex_params_t params = {
+        .uart = uart,
+        .baudrate = baud,
+        .dir = UART_HALF_DUPLEX_DIR_NONE,
+    };
+
+    int ret = uart_half_duplex_init(&stream, feetech_buffer, ARRAY_LEN(feetech_buffer), &params);
+
+    if (argc == 4) {
+        stream.timeout_us = timeout;
+    }
+
+    if (ret == UART_HALF_DUPLEX_NODEV) {
+        puts("Error: invalid UART device given");
+        return -1;
+    }
+    if (ret == UART_HALF_DUPLEX_NOBAUD) {
+        puts("Error: given baudrate is not applicable");
+        return -1;
+    }
+    if (ret == UART_HALF_DUPLEX_INTERR) {
+        puts("Error: internal error");
+        return -1;
+    }
+    if (ret == UART_HALF_DUPLEX_NOMODE) {
+        puts("Error: given mode is not applicable");
+        return -1;
+    }
+    if (ret == UART_HALF_DUPLEX_NOBUFF) {
+        puts("Error: invalid buffer given");
+        return -1;
+    }
+
+    printf("Successfully initialized Feetech TTL bus UART_DEV(%i)\n", uart);
+    return 0;
+}
+
+static int cmd_ping(int argc, char **argv) {
+    int id = -1;
+
+    if (argc != 2) {
+        printf("usage; %s <dev_id>\n", argv[0]);
+        return 1;
+    }
+    /* parse parameters */
+    id = parse_dev(argv[1]);
+    if (id < 0) {
+        return -1;
+    }
+
+    /* ping */
+    if (feetech_ping(&stream, id) == FEETECH_OK) {
+        printf("Device %i responded\n", id);
+    }
+    else {
+        printf("No response from %i\n", id);
+    }
+    return 0;
+}
+
+static int cmd_scan(int argc, char **argv) {
+    int min = -1;
+    int max = -1;
+
+    if (argc == 3) {
+        min = atoi(argv[1]);
+        max = atoi(argv[2]);
+        if (min < 0) {
+            return -1;
+        }
+        if (max > 254) {
+            return -1;
+        }
+        if (max < min) {
+            return -1;
+        }
+    }
+    else if (argc == 1) {
+        min = 0;
+        max = 254;
+    }
+    else {
+        printf("usage; %s [<min_id> <max_id>]\n", argv[0]);
+        return 1;
+    }
+
+    /* ping */
+    puts("Scanning...");
+    for (int id = min ; id < max ; id++) {
+        if (feetech_ping(&stream, id) == FEETECH_OK) {
+            printf("Device %i available\n", id);
+        }
+    }
+    puts("End");
+    return 0;
+}
+
+static int cmd_read(int argc, char **argv) {
+    int id = -1;
+    int reg8 = -1;
+    int reg16 = -1;
+
+    if (argc != 3) {
+        printf("usage; %s <dev_id> <reg>\n", argv[0]);
+        print_registers();
+        return 1;
+    }
+    /* parse parameters */
+    id = parse_dev(argv[1]);
+    if (id < 0) {
+        return -1;
+    }
+
+    parse_reg(argv[2], &reg8, &reg16);
+    if (reg8 < 0 && reg16 < 0) {
+        return -1;
+    }
+
+    /* read */
+    feetech_t dev;
+    feetech_init(&dev, &stream, id);
+    if (reg8 >= 0) {
+        uint8_t val = 0;
+        int ret = feetech_read8(&dev, reg8, &val);
+        if (ret != FEETECH_OK) {
+            printf("Error[%i] : No response from %i\n", ret, id);
+            return -1;
+        }
+        printf("%i\n", (int)val);
+    }
+    else {
+        uint16_t val = 0;
+        int ret = feetech_read16(&dev, reg16, &val);
+        if (ret != FEETECH_OK) {
+            printf("Error[%i] : No response from %i\n", ret, id);
+            return -1;
+        }
+        printf("%i\n", (int)val);
+    }
+    return 0;
+}
+
+static int cmd_write(int argc, char **argv) {
+    int id = -1;
+    int reg8 = -1;
+    int reg16 = -1;
+
+    if (argc != 4) {
+        printf("usage; %s <dev_id> <reg> <value>\n", argv[0]);
+        print_registers();
+        return 1;
+    }
+    /* parse parameters */
+    id = parse_dev(argv[1]);
+    if (id < 0) {
+        return -1;
+    }
+
+    parse_reg(argv[2], &reg8, &reg16);
+    if (reg8 < 0 && reg16 < 0) {
+        return -1;
+    }
+
+    int val = atoi(argv[3]);
+    if (val < 0) {
+        return -1;
+    }
+
+    /* read */
+    feetech_t dev;
+    feetech_init(&dev, &stream, id);
+    if (reg8 >= 0) {
+        int ret = feetech_write8(&dev, reg8, val);
+        if (ret != FEETECH_OK) {
+            printf("Error[%i] : No response from %i\n", ret, id);
+            return -1;
+        }
+        printf("Written %i at address %i\n", (int)val, reg8);
+    }
+    else {
+        int ret = feetech_write16(&dev, reg16, val);
+        if (ret != FEETECH_OK) {
+            printf("Error[%i] : No response from %i\n", ret, id);
+            return -1;
+        }
+        printf("Written %i at address %i\n", (int)val, reg16);
+    }
+    return 0;
+}
+
+static const shell_command_t shell_commands[] = {
+    { "init", "Initialize a Feetech TTL bus with a given baudrate", cmd_init },
+    { "ping", "Ping a Feetech device", cmd_ping },
+    { "scan", "Find all Feetech devices between min_id and max_id", cmd_scan },
+    { "read", "Read a Feetech device register", cmd_read },
+    { "write", "Write in a Feetech device register", cmd_write },
+    { NULL, NULL, NULL }
+};
+
+int main(void)
+{
+    puts("\nManual Feetech device driver test");
+    puts("===================================");
+    puts("This application is intended for testing Feetech TTL bus\n");
+
+    char line_buf[SHELL_DEFAULT_BUFSIZE];
+    shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
+
+    return 0;
+}