diff --git a/cpu/atmega1281/include/cpu_conf.h b/cpu/atmega1281/include/cpu_conf.h
index 438cbc70ed6368c41fab0af13a111b229279fee7..5109c6074d5bf7b5aa9ddffc409178be0444329a 100644
--- a/cpu/atmega1281/include/cpu_conf.h
+++ b/cpu/atmega1281/include/cpu_conf.h
@@ -43,6 +43,13 @@ extern "C" {
 #endif
 /** @} */
 
+/**
+ * @name    EEPROM configuration
+ * @{
+ */
+#define EEPROM_SIZE                (4096U)  /* 4kB */
+/** @} */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cpu/atmega1284p/include/cpu_conf.h b/cpu/atmega1284p/include/cpu_conf.h
index e87b7d2a01553ae7ecc01ed334e79544af6c4931..0731dc3964b576184d20bf3147b84103d2a35cf6 100644
--- a/cpu/atmega1284p/include/cpu_conf.h
+++ b/cpu/atmega1284p/include/cpu_conf.h
@@ -44,6 +44,13 @@ extern "C" {
 #define THREAD_STACKSIZE_IDLE      (128)
 /** @} */
 
+/**
+ * @name    EEPROM configuration
+ * @{
+ */
+#define EEPROM_SIZE                (4096U)  /* 4kB */
+/** @} */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cpu/atmega2560/include/cpu_conf.h b/cpu/atmega2560/include/cpu_conf.h
index 1e7f0eac9a056e7c7a63dfb7efbe5935dd7315c6..a070cccca24590685d0e7ab8b934818a2a750fd9 100644
--- a/cpu/atmega2560/include/cpu_conf.h
+++ b/cpu/atmega2560/include/cpu_conf.h
@@ -42,6 +42,14 @@ extern "C" {
 #define THREAD_STACKSIZE_IDLE      (128)
 /** @} */
 
+/**
+ * @name    EEPROM configuration
+ * @{
+ */
+#define EEPROM_SIZE                (4096U)  /* 4kB */
+/** @} */
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cpu/atmega256rfr2/include/cpu_conf.h b/cpu/atmega256rfr2/include/cpu_conf.h
index 0a4cee93685b722983d36571087315a479db86e4..b21ef3c3c5d0ccb3926728148fd9c58fe24816c3 100644
--- a/cpu/atmega256rfr2/include/cpu_conf.h
+++ b/cpu/atmega256rfr2/include/cpu_conf.h
@@ -47,5 +47,13 @@ extern "C" {
 #ifdef __cplusplus
 }
 #endif
+
+/**
+ * @name    EEPROM configuration
+ * @{
+ */
+#define EEPROM_SIZE                (8192U)  /* 8kB */
+/** @} */
+
 #endif /* CPU_CONF_H */
 /** @} */
diff --git a/cpu/atmega328p/include/cpu_conf.h b/cpu/atmega328p/include/cpu_conf.h
index 91ac74da4d81e946d804bd006fd074b0edc69e34..1f0aab3eab7a8d9ba8826c28d8d081ef388960e6 100644
--- a/cpu/atmega328p/include/cpu_conf.h
+++ b/cpu/atmega328p/include/cpu_conf.h
@@ -42,6 +42,13 @@ extern "C" {
 #define THREAD_STACKSIZE_IDLE      (128)
 /** @} */
 
+/**
+ * @name    EEPROM configuration
+ * @{
+ */
+#define EEPROM_SIZE                (1024U)  /* 1kB */
+/** @} */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cpu/atmega_common/Makefile.features b/cpu/atmega_common/Makefile.features
index 7a418ea511cb94cb5a2daed61773d99257e33013..ac0dd81727799a51f1c69a5b98e50cfa993506c7 100644
--- a/cpu/atmega_common/Makefile.features
+++ b/cpu/atmega_common/Makefile.features
@@ -1 +1,2 @@
 FEATURES_PROVIDED += periph_pm
+FEATURES_PROVIDED += periph_eeprom
diff --git a/cpu/atmega_common/periph/eeprom.c b/cpu/atmega_common/periph/eeprom.c
new file mode 100644
index 0000000000000000000000000000000000000000..2a3827336ddd08532d444a4137837c61fef83052
--- /dev/null
+++ b/cpu/atmega_common/periph/eeprom.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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     cpu_atmega_common
+ * @ingroup     drivers_periph_eeprom
+ * @{
+ *
+ * @file
+ * @brief       Low-level EEPROM driver implementation for ATmega family
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ * @}
+ */
+
+#include <stdint.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "periph/eeprom.h"
+
+uint8_t eeprom_read_byte(uint32_t pos)
+{
+    assert(pos < EEPROM_SIZE);
+
+    /* Wait for completion of previous operation */
+    while (EECR & (1 << EEPE)) {}
+
+    /* Set up address register */
+    EEAR = pos;
+
+    /* Start eeprom read by writing EERE */
+    EECR |= (1 << EERE);
+
+    /* Return data from Data Register */
+    return EEDR;
+}
+
+void eeprom_write_byte(uint32_t pos, uint8_t data)
+{
+    assert(pos < EEPROM_SIZE);
+
+    /* Wait for completion of previous operation */
+    while (EECR & (1 << EEPE)) {}
+
+    /* Set up address and Data Registers */
+    EEAR = pos;
+    EEDR = data;
+
+    /* Write logical one to EEMPE */
+    EECR |= (1 << EEMPE);
+
+    /* Start eeprom write by setting EEPE */
+    EECR |= (1 << EEPE);
+}
diff --git a/cpu/stm32_common/Makefile.include b/cpu/stm32_common/Makefile.include
index e172bfbacf5f900550d44cf51c9959147e221bb6..1bb08442965d40b627c00d8de83db9d7478a2dc6 100644
--- a/cpu/stm32_common/Makefile.include
+++ b/cpu/stm32_common/Makefile.include
@@ -8,6 +8,12 @@ USEMODULE += periph_common
 # include stm32 common functions and stm32 common periph drivers
 USEMODULE += stm32_common stm32_common_periph
 
+# flashpage and eeprom periph implementations share flash lock/unlock functions
+# in periph_flash_common
+ifneq (,$(filter periph_flashpage periph_eeprom,$(FEATURES_REQUIRED)))
+  FEATURES_REQUIRED += periph_flash_common
+endif
+
 # For stm32 cpu's we use the stm32_common.ld linker script
 export LINKFLAGS += -L$(RIOTCPU)/stm32_common/ldscripts
 LINKER_SCRIPT ?= stm32_common.ld
diff --git a/cpu/stm32_common/periph/eeprom.c b/cpu/stm32_common/periph/eeprom.c
new file mode 100644
index 0000000000000000000000000000000000000000..470ae47276ca167a6d9725ccecdd205d594e9607
--- /dev/null
+++ b/cpu/stm32_common/periph/eeprom.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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     cpu_stm32_common
+ * @ingroup     drivers_periph_eeprom
+ * @{
+ *
+ * @file
+ * @brief       Low-level eeprom driver implementation
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ *
+ * @}
+ */
+
+#include <assert.h>
+
+#include "cpu.h"
+
+#define ENABLE_DEBUG        (0)
+#include "debug.h"
+
+#include "periph/eeprom.h"
+
+extern void _lock(void);
+extern void _unlock(void);
+
+#ifndef EEPROM_START_ADDR
+#error "periph/eeprom: EEPROM_START_ADDR is not defined"
+#endif
+
+uint8_t eeprom_read_byte(uint32_t pos)
+{
+    assert(pos < EEPROM_SIZE);
+
+    DEBUG("Reading data from EEPROM at pos %lu\n", pos);
+    return *(uint8_t *)(EEPROM_START_ADDR + pos);
+}
+
+void eeprom_write_byte(uint32_t pos, uint8_t data)
+{
+    assert(pos < EEPROM_SIZE);
+
+    DEBUG("Writing data '%c' to EEPROM at pos %lu\n", data, pos);
+    _unlock();
+    *(uint8_t *)(EEPROM_START_ADDR + pos) = data;
+    _lock();
+}
diff --git a/cpu/stm32_common/periph/flash_common.c b/cpu/stm32_common/periph/flash_common.c
new file mode 100644
index 0000000000000000000000000000000000000000..34f92e75f06fcf084e8485c88eb02733d881d529
--- /dev/null
+++ b/cpu/stm32_common/periph/flash_common.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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     cpu_stm32_common
+ * @{
+ *
+ * @file
+ * @brief       Low-level flash lock/unlock implementation
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ *
+ * @}
+ */
+
+#include "cpu.h"
+
+#define ENABLE_DEBUG        (0)
+#include "debug.h"
+
+#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
+/* Data EEPROM and control register unlock keys */
+#define FLASH_KEY1             ((uint32_t)0x89ABCDEF)
+#define FLASH_KEY2             ((uint32_t)0x02030405)
+#define CNTRL_REG              (FLASH->PECR)
+#define CNTRL_REG_LOCK         (FLASH_PECR_PELOCK)
+#define KEY_REG                (FLASH->PEKEYR)
+#else
+#define CNTRL_REG              (FLASH->CR)
+#define CNTRL_REG_LOCK         (FLASH_CR_LOCK)
+#define KEY_REG                (FLASH->KEYR)
+#endif
+
+void _unlock(void)
+{
+    DEBUG("[flash-common] unlocking the flash module\n");
+    if (CNTRL_REG & CNTRL_REG_LOCK) {
+        KEY_REG = FLASH_KEY1;
+        KEY_REG = FLASH_KEY2;
+    }
+}
+
+void _lock(void)
+{
+    DEBUG("[flash-common] locking the flash module\n");
+    CNTRL_REG |= CNTRL_REG_LOCK;
+}
diff --git a/cpu/stm32_common/periph/flashpage.c b/cpu/stm32_common/periph/flashpage.c
index 1884579c11c0b6ab77ed68ef6327d8404acc8607..e4454b18183b3cbb6a8c5e0ce1589a8267d0a5bc 100644
--- a/cpu/stm32_common/periph/flashpage.c
+++ b/cpu/stm32_common/periph/flashpage.c
@@ -32,32 +32,26 @@
 #include "periph/flashpage.h"
 
 #if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
-/* Data EEPROM and control register unlock keys */
-#define FLASH_KEY1             ((uint32_t)0x89ABCDEF)
-#define FLASH_KEY2             ((uint32_t)0x02030405)
 /* Program memory unlock keys */
 #define FLASH_PRGKEY1          ((uint32_t)0x8C9DAEBF)
 #define FLASH_PRGKEY2          ((uint32_t)0x13141516)
 #define CNTRL_REG              (FLASH->PECR)
 #define CNTRL_REG_LOCK         (FLASH_PECR_PELOCK)
-#define KEY_REG                (FLASH->PEKEYR)
 #define FLASH_CR_PER           (FLASH_PECR_ERASE | FLASH_PECR_PROG)
 #define FLASH_CR_PG            (FLASH_PECR_FPRG | FLASH_PECR_PROG)
 #define FLASHPAGE_DIV          (4U) /* write 4 bytes in one go */
 #else
 #define CNTRL_REG              (FLASH->CR)
 #define CNTRL_REG_LOCK         (FLASH_CR_LOCK)
-#define KEY_REG                (FLASH->KEYR)
 #define FLASHPAGE_DIV          (2U)
 #endif
 
-static void _unlock(void)
+extern void _lock(void);
+extern void _unlock(void);
+
+static void _unlock_flash(void)
 {
-    DEBUG("[flashpage] unlocking the flash module\n");
-    if (CNTRL_REG & CNTRL_REG_LOCK) {
-        KEY_REG = FLASH_KEY1;
-        KEY_REG = FLASH_KEY2;
-    }
+    _unlock();
 
 #if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
     DEBUG("[flashpage] unlocking the flash program memory\n");
@@ -71,12 +65,6 @@ static void _unlock(void)
 #endif
 }
 
-static void _lock(void)
-{
-    DEBUG("[flashpage] locking the flash module\n");
-    CNTRL_REG |= CNTRL_REG_LOCK;
-}
-
 static void _erase_page(void *page_addr)
 {
 #if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
@@ -90,7 +78,7 @@ static void _erase_page(void *page_addr)
 #endif
 
    /* unlock the flash module */
-    _unlock();
+    _unlock_flash();
 
     /* make sure no flash operation is ongoing */
     DEBUG("[flashpage] erase: waiting for any operation to finish\n");
@@ -153,7 +141,7 @@ void flashpage_write_raw(void *target_addr, const void *data, size_t len)
 #endif
 
     DEBUG("[flashpage_raw] unlocking the flash module\n");
-    _unlock();
+    _unlock_flash();
 
     DEBUG("[flashpage] write: now writing the data\n");
 #if !(defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1))
diff --git a/cpu/stm32f0/Makefile.features b/cpu/stm32f0/Makefile.features
index d286ea06f23ab207993df550a7d7cf0e760ee7a4..e2a50dd8655e01458a40f536415b6127a15dbb0d 100644
--- a/cpu/stm32f0/Makefile.features
+++ b/cpu/stm32f0/Makefile.features
@@ -1,4 +1,5 @@
 ifeq (,$(filter nucleo-f031k6,$(BOARD)))
+  FEATURES_PROVIDED += periph_flash_common
   FEATURES_PROVIDED += periph_flashpage
   FEATURES_PROVIDED += periph_flashpage_raw
 endif
diff --git a/cpu/stm32f1/Makefile.features b/cpu/stm32f1/Makefile.features
index 5e5e8b9118b419c443eed05bbce504daae169842..e5e8c0f6d8c71ef93ae9da82b80ddc934dd9d416 100644
--- a/cpu/stm32f1/Makefile.features
+++ b/cpu/stm32f1/Makefile.features
@@ -1,3 +1,4 @@
+FEATURES_PROVIDED += periph_flash_common
 FEATURES_PROVIDED += periph_flashpage
 FEATURES_PROVIDED += periph_flashpage_raw
 
diff --git a/cpu/stm32l0/Makefile.features b/cpu/stm32l0/Makefile.features
index ba0527ef1772adc63ee652cbb27d2857efd8665e..79346e3fde7d24e7bad26dc3f25c3642a1ffe49a 100644
--- a/cpu/stm32l0/Makefile.features
+++ b/cpu/stm32l0/Makefile.features
@@ -1,5 +1,7 @@
+FEATURES_PROVIDED += periph_flash_common
 FEATURES_PROVIDED += periph_flashpage
 FEATURES_PROVIDED += periph_flashpage_raw
+FEATURES_PROVIDED += periph_eeprom
 FEATURES_PROVIDED += periph_hwrng
 
 BOARDS_WITHOUT_HWRNG += nucleo-l031k6
diff --git a/cpu/stm32l0/include/cpu_conf.h b/cpu/stm32l0/include/cpu_conf.h
index d8cb2b5ed42303acd4869aad8cb40dc549ac4a7b..bae780aef6d9ef6ba880d7eab0c640b2c0728df9 100644
--- a/cpu/stm32l0/include/cpu_conf.h
+++ b/cpu/stm32l0/include/cpu_conf.h
@@ -79,6 +79,20 @@ extern "C" {
 #define FLASHPAGE_RAW_ALIGNMENT    (4U)
 /** @} */
 
+/**
+ * @name    EEPROM configuration
+ * @{
+ */
+#define EEPROM_START_ADDR          (0x08080000)
+#if defined(CPU_MODEL_STM32L073RZ) || defined(CPU_MODEL_STM32L072CZ)
+#define EEPROM_SIZE                (6144U)  /* 6kB */
+#elif defined(CPU_MODEL_STM32L053R8)
+#define EEPROM_SIZE                (2048U)  /* 2kB */
+#elif defined(CPU_MODEL_STM32L031K6)
+#define EEPROM_SIZE                (1024U)  /* 1kB */
+#endif
+/** @} */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cpu/stm32l1/Makefile.features b/cpu/stm32l1/Makefile.features
index 5e5e8b9118b419c443eed05bbce504daae169842..a49d3982606d994b90b0c56290c1cea3d5fafd48 100644
--- a/cpu/stm32l1/Makefile.features
+++ b/cpu/stm32l1/Makefile.features
@@ -1,4 +1,6 @@
+FEATURES_PROVIDED += periph_flash_common
 FEATURES_PROVIDED += periph_flashpage
 FEATURES_PROVIDED += periph_flashpage_raw
+FEATURES_PROVIDED += periph_eeprom
 
 -include $(RIOTCPU)/stm32_common/Makefile.features
diff --git a/cpu/stm32l1/include/cpu_conf.h b/cpu/stm32l1/include/cpu_conf.h
index cb86a3bf531cc7ea6612740562f7c76b8f47ada5..af78bf0516f48af5d2ece40c9dce5f1ef9168175 100644
--- a/cpu/stm32l1/include/cpu_conf.h
+++ b/cpu/stm32l1/include/cpu_conf.h
@@ -93,6 +93,18 @@ extern "C" {
 #define FLASHPAGE_RAW_ALIGNMENT    (4U)
 /** @} */
 
+/**
+ * @name    EEPROM configuration
+ * @{
+ */
+#define EEPROM_START_ADDR          (0x08080000)
+#if defined(CPU_MODEL_STM32L152RE)
+#define EEPROM_SIZE                (16384UL)  /* 16kB */
+#elif defined(CPU_MODEL_STM32L151RC)
+#define EEPROM_SIZE                (8192U)    /* 8kB */
+#endif
+/** @} */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/drivers/include/periph/eeprom.h b/drivers/include/periph/eeprom.h
new file mode 100644
index 0000000000000000000000000000000000000000..14750c594ee3c8b97dc5b376d7dbe40fb44e3560
--- /dev/null
+++ b/drivers/include/periph/eeprom.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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_periph_eeprom EEPROM driver
+ * @ingroup     drivers_periph
+ * @brief       Low-level EEPROM interface
+ *
+ * @{
+ * @file
+ * @brief       Low-level eeprom driver interface
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ *
+ */
+
+#ifndef PERIPH_EEPROM_H
+#define PERIPH_EEPROM_H
+
+#include <stdint.h>
+
+#include "cpu.h"
+#include "periph_cpu.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef EEPROM_SIZE
+#error "periph/eeprom: EEPROM_SIZE is not defined"
+#endif
+
+/**
+ * @brief   Read a byte at the given position in eeprom
+ *
+ * @param[in]  pos      position to read
+ *
+ * @return the byte read
+ */
+uint8_t eeprom_read_byte(uint32_t pos);
+
+/**
+ * @brief   Read @p len bytes from the given position
+ *
+ * @param[in]  pos      start position in eeprom
+ * @param[out] data     output byte array to write to
+ * @param[in]  len      the number of bytes to read
+ *
+ * @return  the number of bytes read
+ */
+size_t eeprom_read(uint32_t pos, uint8_t *data, size_t len);
+
+/**
+ * @brief   Write a byte at the given position
+ *
+ * @param[in] pos       position to write
+ * @param[in] data      byte address to write to
+ */
+void eeprom_write_byte(uint32_t pos, uint8_t data);
+
+/**
+ * @brief   Write @p len bytes at the given position
+ *
+ * @param[in] pos       start position in eeprom
+ * @param[in] data      input byte array to read into
+ * @param[in] len       the number of bytes to read
+ *
+ * @return the number of bytes written
+ */
+size_t eeprom_write(uint32_t pos, const uint8_t *data, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PERIPH_EEPROM_H */
+/** @} */
diff --git a/drivers/periph_common/eeprom.c b/drivers/periph_common/eeprom.c
new file mode 100644
index 0000000000000000000000000000000000000000..f04c7af866ca9d0b4ce0e2e6a7503b59ed009d91
--- /dev/null
+++ b/drivers/periph_common/eeprom.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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
+ * @{
+ *
+ * @file
+ * @brief       Common eeprom functions implementation
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ *
+ * @}
+ */
+
+#include <string.h>
+#include "cpu.h"
+#include "assert.h"
+
+/* guard this file, must be done before including periph/eeprom.h */
+#if defined(EEPROM_SIZE)
+
+#include "periph/eeprom.h"
+
+size_t eeprom_read(uint32_t pos, uint8_t *data, size_t len)
+{
+    assert(pos + len < EEPROM_SIZE);
+
+    for (size_t i = 0; i < len; i++) {
+        data[i] = eeprom_read_byte(pos++);
+    }
+
+    return len;
+}
+
+size_t eeprom_write(uint32_t pos, const uint8_t *data, size_t len)
+{
+    assert(pos + len < EEPROM_SIZE);
+
+    for (size_t i = 0; i < len; i++) {
+        eeprom_write_byte(pos++, data[i]);
+    }
+
+    return len;
+}
+
+#endif
diff --git a/tests/periph_eeprom/Makefile b/tests/periph_eeprom/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..a34d7a7e28e3dc88162c9f56d2932cdb39cb3e76
--- /dev/null
+++ b/tests/periph_eeprom/Makefile
@@ -0,0 +1,9 @@
+BOARD ?= b-l072z-lrwan1
+include ../Makefile.tests_common
+
+FEATURES_REQUIRED += periph_eeprom
+
+USEMODULE += shell
+USEMODULE += shell_commands  # provides reboot command
+
+include $(RIOTBASE)/Makefile.include
diff --git a/tests/periph_eeprom/README.md b/tests/periph_eeprom/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..6dbe0d879aa2bec51e3047bf266eab01814d70f4
--- /dev/null
+++ b/tests/periph_eeprom/README.md
@@ -0,0 +1,17 @@
+Expected result
+===============
+
+Use the provided shell commands to read and write bytes from/to the MCU's
+internal EEPROM memory.
+
+    # Read 10 bytes from the beginning of the eeprom
+    > read 0 10
+
+    # Write HelloWorld starting from the 10th position in the eeprom
+    > write 10 HelloWorld
+
+Background
+==========
+
+This test application provides shell commands to verify the implementations of
+the `eeprom` peripheral driver interface.
diff --git a/tests/periph_eeprom/main.c b/tests/periph_eeprom/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..0de526c83a5dc3498e0487dff2a190b7809116a1
--- /dev/null
+++ b/tests/periph_eeprom/main.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 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     tests
+ * @{
+ *
+ * @file
+ * @brief       Manual test application for the EEPROM peripheral drivers
+ *
+ * @author      Alexandre Abadie <alexandre.abadie@inria.fr>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "shell.h"
+
+#include "periph/eeprom.h"
+
+#ifndef BUFFER_SIZE
+#define BUFFER_SIZE     (42U)
+#endif
+
+static char buffer[BUFFER_SIZE + 1];
+
+static int cmd_info(int argc, char **argv)
+{
+    (void)argc;
+    (void)argv;
+
+#ifdef EEPROM_START_ADDR
+    printf("EEPROM start addr:\t0x%08x\n", (int)EEPROM_START_ADDR);
+#endif
+    printf("EEPROM size:\t\t%i\n", (int)EEPROM_SIZE);
+
+    return 0;
+}
+
+static int cmd_read(int argc, char **argv)
+{
+    if (argc < 3) {
+        printf("usage: %s <pos> <count>\n", argv[0]);
+        return 1;
+    }
+
+    uint32_t pos = atoi(argv[1]);
+    uint8_t count = atoi(argv[2]);
+
+    if (!count) {
+        puts("Count should be greater than 0");
+        return 1;
+    }
+
+    if (count > BUFFER_SIZE) {
+        puts("Count exceeds buffer size");
+        return 1;
+    }
+
+    if (pos + count >= EEPROM_SIZE) {
+        puts("Failed: cannot read out of eeprom bounds");
+        return 1;
+    }
+
+    size_t ret = eeprom_read(pos, (uint8_t *)buffer, count);
+    buffer[count] = '\0';
+
+    printf("Data read from EEPROM (%d bytes): %s\n", (int)ret, buffer);
+
+    return 0;
+}
+
+static int cmd_write(int argc, char **argv)
+{
+    if (argc < 3) {
+        printf("usage: %s <pos> <data>\n", argv[0]);
+        return 1;
+    }
+
+    uint32_t pos = atoi(argv[1]);
+
+    if (pos + strlen(argv[2]) >= EEPROM_SIZE) {
+        puts("Failed: cannot write out of eeprom bounds");
+        return 1;
+    }
+
+    size_t ret = eeprom_write(pos, (uint8_t *)argv[2], strlen(argv[2]));
+    printf("%d bytes written to EEPROM\n", (int)ret);
+
+    return 0;
+}
+
+static const shell_command_t shell_commands[] = {
+    { "info", "Print information about eeprom", cmd_info },
+    { "read", "Read bytes from eeprom", cmd_read },
+    { "write", "Write bytes to eeprom", cmd_write},
+    { NULL, NULL, NULL }
+};
+
+int main(void)
+{
+    puts("EEPROM read write test\n");
+    puts("Please refer to the README.md for more details\n");
+
+    cmd_info(0, NULL);
+
+    /* run the shell */
+    char line_buf[SHELL_DEFAULT_BUFSIZE];
+    shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
+    return 0;
+}