diff --git a/examples/lua/main.c b/examples/lua/main.c
deleted file mode 100644
index 22458153e0781a3e93a25848832959224071ea41..0000000000000000000000000000000000000000
--- a/examples/lua/main.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2018 FU Berlin
- *
- * 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     examples
- * @{
- *
- * @file
- * @brief       Basic lua example application
- *
- * @author      Daniel Petry <daniel.petry@fu-berlin.de>
- *
- * @}
- */
-
-#include <stdio.h>
-#include <errno.h>
-#include "lauxlib.h"
-#include "lualib.h"
-
-#include "main.lua.h"
-
-int lua_run_script(const char *buffer, size_t buffer_len)
-{
-
-    lua_State *L = luaL_newstate();
-
-    if (L == NULL) {
-        puts("cannot create state: not enough memory");
-        return ENOMEM;
-    }
-
-    luaL_openlibs(L);
-    luaL_loadbuffer(L, buffer, buffer_len, "lua input script");
-
-    if (lua_pcall(L, 0, 0, 0) != LUA_OK){
-        puts("Lua script running failed");
-        return EINTR;
-    }
-
-    lua_close(L);
-    return 0;
-}
-
-int main(void)
-{
-    puts("Lua RIOT build");
-    lua_run_script(main_lua, main_lua_len);
-    return 0;
-}
diff --git a/examples/lua_REPL/Makefile b/examples/lua_REPL/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..fb397b5319ff74ce7237b7059e0ef0c7557e103f
--- /dev/null
+++ b/examples/lua_REPL/Makefile
@@ -0,0 +1,78 @@
+APPLICATION = lua_repl
+
+# If no BOARD is found in the environment, use this default:
+BOARD ?= native
+
+# This has to be the absolute path to the RIOT base directory:
+RIOTBASE ?= $(CURDIR)/../..
+
+BOARD_INSUFFICIENT_MEMORY := bluepill calliope-mini cc2650-launchpad \
+                             cc2650stk maple-mini microbit nrf51dongle \
+                             nucleo-f030r8 nucleo-f031k6 nucleo-f042k6 \
+                             nucleo-f070rb nucleo-f072rb nucleo-f103rb \
+                             nucleo-f302r8 nucleo-f303k8 nucleo-f334r8 \
+                             nucleo-f410rb nucleo-l031k6 nucleo-l053r8 \
+                             opencm904 spark-core stm32f0discovery \
+                             stm32mindev airfy-beacon  arduino-mkr1000 \
+                             arduino-mkrfox1200 arduino-mkrzero arduino-zero \
+                             b-l072z-lrwan1  cc2538dk ek-lm4f120xl  feather-m0 \
+                             ikea-tradfri limifrog-v1 mbed_lpc1768  nrf6310 \
+                             nucleo-f091rc  nucleo-l073rz  nz32-sc151 \
+                             openmote-cc2538  pba-d-01-kw2x  remote-pa \
+                             remote-reva remote-revb samd21-xpro  saml21-xpro \
+                             samr21-xpro seeeduino_arch-pro  slstk3401a \
+                             sltb001a slwstk6220a sodaq-autonomo \
+                             sodaq-explorer  stk3600  stm32f3discovery \
+                             yunjia-nrf51822
+
+BOARD_BLACKLIST := arduino-duemilanove arduino-mega2560 arduino-uno \
+                   chronos hifive1 jiminy-mega256rfr2 mega-xplained mips-malta \
+                   msb-430 msb-430h pic32-clicker pic32-wifire telosb \
+                   waspmote-pro wsn430-v1_3b wsn430-v1_4 z1
+
+# Comment this out to disable code in RIOT that does safety checking
+# which is not needed in a production environment but helps in the
+# development process:
+DEVELHELP ?= 1
+
+# Uncomment the following lines to enable debugging features.
+#CFLAGS_OPT = -Og
+#CFLAGS += -DDEBUG_ASSERT_VERBOSE -DLUA_DEBUG
+
+# Change this to 0 show compiler invocation lines by default:
+QUIET ?= 1
+
+# This value is in excess because we are not sure of the exact requirements of
+# lua (see the package's docs). It must be fixed in the future by taking
+# appropriate measurements.
+CFLAGS += -DTHREAD_STACKSIZE_MAIN='(THREAD_STACKSIZE_DEFAULT+7000)'
+
+USEPKG += lua
+
+include $(RIOTBASE)/Makefile.include
+
+# The code below generates a header file from any .lua scripts in the
+# example directory. The header file contains a byte array of the
+# ASCII characters in the .lua script.
+
+LUA_PATH := $(BINDIR)/lua
+
+# add directory of generated *.lua.h files to include path
+CFLAGS += -I$(LUA_PATH)
+
+# generate .lua.h header files of .lua files
+LUA = $(wildcard *.lua)
+
+LUA_H := $(LUA:%.lua=$(LUA_PATH)/%.lua.h)
+
+$(LUA_PATH)/:
+	@mkdir -p $@
+
+# FIXME: This way of embedding lua code is not robust. A proper script will
+#        be included later.
+
+$(LUA_H): | $(LUA_PATH)/
+$(LUA_H): $(LUA_PATH)/%.lua.h: %.lua
+	xxd -i $< | sed 's/^unsigned/const unsigned/g' > $@
+
+$(RIOTBUILD_CONFIG_HEADER_C): $(LUA_H)
diff --git a/examples/lua_REPL/README.md b/examples/lua_REPL/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..fd1fb42ae9fdc75b97dc3ad409e81eb0478fe4c3
--- /dev/null
+++ b/examples/lua_REPL/README.md
@@ -0,0 +1,32 @@
+## Lua interactive interpreter
+
+### About
+
+This example shows how to run a [Lua](https://www.lua.org/) Read-Eval-Print loop.
+It works in a similar way to the lua shell that comes with your operating
+system's default lua installation.
+
+
+### How to run
+
+Type `make all flash` to program your board. The lua interpreter communicates
+via UART (like the shell).
+
+It is not recommended to use `make term` because the default RIOT terminal messes
+up the input and output and the REPL needs multi-line input. Instead, use something
+like `miniterm.py` from pyserial:
+
+```
+miniterm.py --eol LF --echo /dev/ttyACM0 115200
+```
+
+By default only some of the builtin modules are loaded, to preserve RAM. See
+the definition of `BARE_MINIMUM_MODS` in main.c.
+
+### Using the interpreter
+
+See the [Lua manual](https://www.lua.org/manual/5.3/) for the syntax of the language.
+
+Each piece of single or multi-line input is compiled as a chunk and run. For this
+reason, issuing "local" definitions may not work as expected: the definitions
+will be local to that chunk only.
diff --git a/examples/lua_REPL/main.c b/examples/lua_REPL/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..f985130430ad70d6436e4c65868aad2542bc3d06
--- /dev/null
+++ b/examples/lua_REPL/main.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin.
+ *
+ * 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     examples
+ * @{
+ *
+ * @file
+ * @brief       Lua shell in RIOT
+ *
+ * @author      Juan Carrano <j.carrano@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lua_run.h"
+#include "lua_builtin.h"
+#include "repl.lua.h"
+
+/* The basic interpreter+repl needs about 13k ram AT Minimum but we need more
+ * memory in order to do interesting stuff.
+ */
+#define MAIN_LUA_MEM_SIZE (40000)
+
+static char lua_memory[MAIN_LUA_MEM_SIZE] __attribute__ ((aligned(__BIGGEST_ALIGNMENT__)));
+
+#define BARE_MINIMUM_MODS (LUAR_LOAD_BASE | LUAR_LOAD_IO | LUAR_LOAD_CORO | LUAR_LOAD_PACKAGE)
+
+const struct lua_riot_builtin_lua _lua_riot_builtin_lua_table[] = {
+    { "repl", repl_lua, sizeof(repl_lua) }
+};
+
+
+const struct lua_riot_builtin_lua *const lua_riot_builtin_lua_table = _lua_riot_builtin_lua_table;
+
+const size_t lua_riot_builtin_lua_table_len = 1;
+
+int main(void)
+{
+    printf("Using memory range for Lua heap: %p - %p, %zu bytes\n",
+           lua_memory, lua_memory + MAIN_LUA_MEM_SIZE, sizeof(void *));
+
+    while (1) {
+        int status, value;
+        puts("This is Lua: starting interactive session\n");
+
+        status = lua_riot_do_module("repl", lua_memory, MAIN_LUA_MEM_SIZE,
+                                    BARE_MINIMUM_MODS, &value);
+
+        printf("Exited. status: %s, return code %d\n", lua_riot_strerror(status),
+               value);
+    }
+
+    return 0;
+}
diff --git a/examples/lua_REPL/repl.lua b/examples/lua_REPL/repl.lua
new file mode 100644
index 0000000000000000000000000000000000000000..ff81f4241e4abe0edec0a25b92526f48cb3993cf
--- /dev/null
+++ b/examples/lua_REPL/repl.lua
@@ -0,0 +1,94 @@
+--[[
+   @file repl.lua
+   @brief   Read-eval-print loop for LUA
+   @author  Juan Carrano <j.carrano@fu-berlin.de>
+   Copyright (C) 2018 Freie Universität Berlin. Distributed under the GNU Lesser General Public License v2.1.
+]]
+
+local _R_EVAL = 0
+local _R_CONT = 1
+local _R_EXIT = 2
+local _R_ERROR = 3
+
+--[[ Read code from standard input (whatever stdin means for lua)
+    @return action_code     what the eval loop should do
+    @return code_or_msg     either code (_R_EVAL) or a message (_R_ERROR) or nil
+                            (_R_CONT, _R_EXIT).
+]]
+
+local function re()
+    io.write("L> ")
+    io.flush()
+    local ln = io.read()
+
+    if not ln then
+        return _R_EXIT
+    elseif ln == "\n" then
+        return _R_CONT
+    end
+    -- Try to see if we have an expression
+    local maybe_code, compile_err = load("return "..ln)
+    -- Try to see if we have a single-line statement
+    if not maybe_code then
+        maybe_code, compile_err = load(ln)
+    end
+    -- Try a multiline statement
+    if not maybe_code then
+        -- It's not really necessary to use a coroutine, but it shows that they
+        -- work.
+        local _get_multiline = coroutine.create(
+            function ()
+                coroutine.yield(ln.."\n")   -- We already have the first line of input
+                while 1 do
+                    io.write("L.. ")
+                    io.flush()
+                    local l = io.read()
+                    if #l ~= 0 then
+                        l = l.."\n"
+                    end
+                    coroutine.yield(l)
+                end
+            end
+            )
+        local get_multiline = function()
+                local a, b = coroutine.resume(_get_multiline)
+                if a then
+                    return b
+                else
+                    return nil
+                end
+            end
+
+        maybe_code, compile_err = load(get_multiline)
+    end
+
+    if not maybe_code then
+        return _R_ERROR, compile_err
+    else
+        return _R_EVAL, maybe_code
+    end
+end
+
+local function repl()
+    io.write("Welcome to the interactive interpreter\n");
+
+    while 1 do
+        local action, fn_or_message = re()
+
+        if action == _R_EVAL then
+            local success, msg_or_ret = pcall(fn_or_message)
+            if not success then
+                print("Runtime error", msg_or_ret)
+            elseif msg_or_ret ~= nil then
+                print(msg_or_ret)
+            end
+        elseif action == _R_EXIT then
+            print()
+            return
+        elseif action == _R_ERROR then
+            print("Compile error:", fn_or_message)
+        end -- (action == _R_CONT) : do nothing
+    end
+end
+
+repl()
diff --git a/examples/lua/Makefile b/examples/lua_basic/Makefile
similarity index 93%
rename from examples/lua/Makefile
rename to examples/lua_basic/Makefile
index 57c5c89fd31f95b12241c71acdecfce60f2c5305..91a55145f5aafa9850abcec6b23a06d851c03e02 100644
--- a/examples/lua/Makefile
+++ b/examples/lua_basic/Makefile
@@ -1,4 +1,4 @@
-APPLICATION = lua
+APPLICATION = lua_basic
 
 # If no BOARD is found in the environment, use this default:
 BOARD ?= native
@@ -29,12 +29,10 @@ DEVELHELP ?= 1
 # Change this to 0 show compiler invocation lines by default:
 QUIET ?= 1
 
-USEMODULE += ps
-
 ifneq ($(BOARD),native)
   # This stack size is large enough to run Lua print() functions of
   # various lengths. Other functions untested.
-  CFLAGS += -DTHREAD_STACKSIZE_MAIN=4096
+  CFLAGS += -DTHREAD_STACKSIZE_MAIN='(THREAD_STACKSIZE_DEFAULT+2048)'
 endif
 
 USEPKG += lua
@@ -61,6 +59,6 @@ $(LUA_PATH)/:
 $(LUA_H): | $(LUA_PATH)/
 $(LUA_H): $(LUA_PATH)/%.lua.h: %.lua
 
-	xxd -i $< | sed 's/^unsigned/const/g' > $@
+	xxd -i $< | sed 's/^unsigned/const unsigned/g' > $@
 
 $(RIOTBUILD_CONFIG_HEADER_C): $(LUA_H)
diff --git a/examples/lua/README.md b/examples/lua_basic/README.md
similarity index 100%
rename from examples/lua/README.md
rename to examples/lua_basic/README.md
diff --git a/examples/lua_basic/main.c b/examples/lua_basic/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..0763ab4dae7a4d89fc92920e9d6f03b10c89a6b9
--- /dev/null
+++ b/examples/lua_basic/main.c
@@ -0,0 +1,61 @@
+/*
+  * Copyright (C) 2018 FU Berlin
+  *
+  * 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     examples
+ * @{
+ *
+ * @file
+ * @brief       Basic lua example application
+ *
+ * @author      Daniel Petry <daniel.petry@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "lauxlib.h"
+#include "lualib.h"
+#include "lua_run.h"
+
+#include "main.lua.h"
+
+#define LUA_MEM_SIZE (11000)
+static char lua_mem[LUA_MEM_SIZE] __attribute__ ((aligned(__BIGGEST_ALIGNMENT__)));
+
+int lua_run_script(const uint8_t *buffer, size_t buffer_len)
+{
+    lua_State *L = lua_riot_newstate(lua_mem, sizeof(lua_mem), NULL);
+
+    if (L == NULL) {
+        puts("cannot create state: not enough memory");
+        return ENOMEM;
+    }
+
+    lua_riot_openlibs(L, LUAR_LOAD_BASE);
+    luaL_loadbuffer(L, (const char *)buffer, buffer_len, "lua input script");
+
+    if (lua_pcall(L, 0, 0, 0) != LUA_OK) {
+        puts("Lua script running failed");
+        return EINTR;
+    }
+
+    lua_close(L);
+    return 0;
+}
+
+int main(void)
+{
+    puts("Lua RIOT build");
+    lua_run_script(main_lua, main_lua_len);
+    puts("Lua interpreter exited");
+
+    return 0;
+}
diff --git a/examples/lua/main.lua b/examples/lua_basic/main.lua
similarity index 100%
rename from examples/lua/main.lua
rename to examples/lua_basic/main.lua
diff --git a/pkg/lua/Makefile b/pkg/lua/Makefile
index d2f1a775de8fd1ea199d0e052e538766e3dbc31f..69170baa95e889d00123cf1fb4e16f3c26a84ecb 100644
--- a/pkg/lua/Makefile
+++ b/pkg/lua/Makefile
@@ -1,11 +1,12 @@
 PKG_NAME=lua
 PKG_URL=https://github.com/lua/lua.git
+# tag: v5-3-4
 PKG_VERSION=e354c6355e7f48e087678ec49e340ca0696725b1
 PKG_LICENSE=MIT
 
 .PHONY: all
 
-all:
+all: Makefile.lua
 	@cp Makefile.lua $(PKG_BUILDDIR)
 	"$(MAKE)" -C $(PKG_BUILDDIR) -f Makefile.lua
 
diff --git a/pkg/lua/Makefile.dep b/pkg/lua/Makefile.dep
new file mode 100644
index 0000000000000000000000000000000000000000..2079d072aa5101911e79a1196f7907e50da96e4d
--- /dev/null
+++ b/pkg/lua/Makefile.dep
@@ -0,0 +1,3 @@
+USEPKG += tlsf
+USEMODULE += lua-contrib
+USEMODULE += printf_float
diff --git a/pkg/lua/Makefile.include b/pkg/lua/Makefile.include
index 9fb515a3b4716b8208a5eacaaf12253ac463c8ce..ab9d3759b2a4cde815c043bafaa4d19317214a27 100644
--- a/pkg/lua/Makefile.include
+++ b/pkg/lua/Makefile.include
@@ -1 +1,3 @@
 INCLUDES += -I$(PKGDIRBASE)/lua
+INCLUDES += -I$(RIOTPKG)/lua/include
+DIRS += $(RIOTPKG)/lua/contrib
diff --git a/pkg/lua/Makefile.lua b/pkg/lua/Makefile.lua
index f0bd4d8a386ba29df5151cb210e9c9566b41aa8d..b22984b2a18702316bc45364b9d3f70d5f163e92 100644
--- a/pkg/lua/Makefile.lua
+++ b/pkg/lua/Makefile.lua
@@ -1,9 +1,9 @@
-SRC := $(filter-out lua.c luac.c,$(wildcard *.c))
+SRC := $(filter-out loadlib.c lua.c luac.c,$(wildcard *.c))
 
-# This builds for native using POSIX system calls and some extra libraries, and
-# removes a compiler warning that warns against using tmpnam().
-ifeq ($(BOARD),native)
-  CFLAGS += -DLUA_USE_LINUX
-endif
+CFLAGS += -fstack-usage -fconserve-stack \
+          -DLUA_MAXCAPTURES=16 \
+          -DL_MAXLENNUM=50
+#    Enable these options to debug stack usage
+#          -Wstack-usage=128 -Wno-error=stack-usage=128
 
 include $(RIOTBASE)/Makefile.base
diff --git a/pkg/lua/contrib/Makefile b/pkg/lua/contrib/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..50d6799441272fd5d35bdad79378bc27a9b85866
--- /dev/null
+++ b/pkg/lua/contrib/Makefile
@@ -0,0 +1,3 @@
+MODULE = lua-contrib
+
+include $(RIOTBASE)/Makefile.base
diff --git a/pkg/lua/contrib/binsearch.c b/pkg/lua/contrib/binsearch.c
new file mode 100644
index 0000000000000000000000000000000000000000..2aa7f9626429253fadd7f6034a44ca09e185b378
--- /dev/null
+++ b/pkg/lua/contrib/binsearch.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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.
+ */
+
+/**
+ * @file
+ *
+ * @brief   Generic binary search for tables containing strings.
+ * @author  Juan Carrano <j.carrano@fu-berlin.de>
+ *
+ */
+
+#include <string.h>
+
+#include "binsearch.h"
+
+int binsearch_str(const void *start, size_t offset, size_t stride, size_t nmemb,
+                  const char *str, size_t n)
+{
+    const uint8_t *cstart = (((const uint8_t *)start) + offset);
+    size_t lo = 0, hi = nmemb;
+
+    while (lo < hi) {
+        size_t mid = (lo + hi) / 2;
+        const char *target = *((const char *const *)(cstart + mid * stride));
+        int cmp = strncmp(str, target, n);
+
+        if (cmp == 0) {
+            return mid;
+        }
+        else if (cmp < 0) {
+            hi = mid;
+        }
+        else {   /* (cmp > 0) */
+            lo = mid + 1;
+        }
+    }
+    return (-ENOENT);
+}
+
+const void *binsearch_str_p(const void *start, size_t offset, size_t stride,
+                            size_t nmemb, const char *str, size_t n)
+{
+    int ix = binsearch_str(start, offset, stride, nmemb, str, n);
+
+    return (ix == (-ENOENT)) ? NULL : (const uint8_t *)start + ix * stride;
+}
diff --git a/pkg/lua/contrib/binsearch.h b/pkg/lua/contrib/binsearch.h
new file mode 100644
index 0000000000000000000000000000000000000000..9be7ca3db4c04dae8cd9cac9d15edec27d14ae44
--- /dev/null
+++ b/pkg/lua/contrib/binsearch.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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.
+ */
+
+/**
+ * @internal
+ * @{
+ * @file
+ *
+ * @brief   Generic binary search for tables containing strings.
+ * @author  Juan Carrano <j.carrano@fu-berlin.de>
+ *
+ * It is often the case that one has an array of structs, where one of the
+ * members of the struct is a string pointer containing a key that must be
+ * searched. If the array is sorted by this key and of known length, a binary
+ * search can be performed.
+ *
+ * To make the code generic we must reinterpret the structure array
+ * as an array of pointers to string with a stride (separation in bytes between
+ * elements) and offset (position of the first element relative to the start of
+ * the array) given by the struct definition.
+ *
+ * For example, given the following struct and array definitions and assuming
+ * a 32 bit platform with strict aligment:
+ *  struct s1 {
+ *      int a;      // Takes up 4 bytes
+ *      char *name; // Takes up 4 bytes
+ *      char m;     // Takes up 1 byte
+ *  };
+ *  struct s1 my_table[] = {......};
+ *
+ * Then each element of my_table will be aligned to 12 bytes. The address of the
+ * "name" field of the first elements will be 4 bytes more than the address of
+ * "my_table". With this two numbers we can compute the address of the i-th
+ * "name" field as:
+ *      [address of my_table] + offset + i*stride
+ * Where stride=12 bytes and offset = 4 bytes.
+ */
+
+#ifndef BINSEARCH_H
+#define BINSEARCH_H
+
+#include <stdint.h>
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Produce a compiler error if x is not an lvalue.
+ */
+#define _ENSURE_LVALUE(x) ((void)sizeof(&(x)))
+
+/**
+ * UNSAFE MACRO: Difference in bytes between the addresses of two consecutive
+ * array elements.
+ */
+#define _ARRAY_STRIDE(arr) ((size_t)((uint8_t *)((arr) + 1) - (uint8_t *)(arr)))
+
+/**
+ * UNSAFE MACRO: Offset in bytes from the start of the array to member "member"
+ * of the first element.
+ */
+#define _ARRAY_MEMBER_OFFS(arr, member) \
+    ((size_t)((uint8_t *)(&((arr)->member)) - (uint8_t *)(arr)))
+
+/**
+ * Find the index of the array element that contains "str" in
+ *     member "member".
+ *
+ * A compile-time error will be raised if arr is not an lvalue. This ensures the
+ * macro is safe.
+ *
+ * @return      Index of the array element containing the string.
+ * @return      (-ENOENT) if it is not found.
+ */
+#define BINSEARCH_STR(arr, nmemb, member, str, n) \
+    (_ENSURE_LVALUE(arr), \
+     (binsearch_str((arr), _ARRAY_MEMBER_OFFS(arr, member), _ARRAY_STRIDE(arr), \
+                    (nmemb), (str), (n))) \
+    )
+
+/**
+ * Find a pointer of the array element that contains "str" in
+ *     member "member".
+ *
+ * @return      Address of the element containing the string (as a void pointer).
+ * @return      Null if it is not found.
+ */
+#define BINSEARCH_STR_P(arr, nmemb, member, str, n) \
+    (_ENSURE_LVALUE(arr), \
+     (binsearch_str_p((arr), _ARRAY_MEMBER_OFFS(arr, member), _ARRAY_STRIDE(arr), \
+                      (nmemb), (str), (n))) \
+    )
+
+/**
+ * Search for an array element containing a string.
+ *
+ * This does NOT check for NULL pointers, though start can be NULL of the size
+ * (nmemb) is zero.
+ *
+ * @param   start   Pointer to start of array. The array must be ordered
+ *                  according to the search string.
+ * @param   offset  Offset of member containing string within structure. This
+ *                  can be determined using offsetof.
+ * @param   stride  Difference in bytes between the addresses of two consecutive
+ *                  array elements.
+ * @param   nmemb   Number of elements in the array.
+ * @param   str     String that will be compared against.
+ * @param   n       Compare up to n characters (see strncmp())
+ *
+ * @return      Index of the array element containing the string.
+ * @return      (-ENOENT) if it is not found.
+ */
+int binsearch_str(const void *start, size_t offset, size_t stride, size_t nmemb,
+                  const char *str, size_t n);
+
+/**
+ * Like binsearch_str but returns the pointer to the element.
+ *
+ * @return      Address of the element containing the string.
+ * @return      Null if it is not found.
+ */
+const void *binsearch_str_p(const void *start, size_t offset, size_t stride,
+                            size_t nmemb, const char *str, size_t n);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} */
+
+#endif /* BINSEARCH_H */
diff --git a/pkg/lua/contrib/lua_loadlib.c b/pkg/lua/contrib/lua_loadlib.c
new file mode 100644
index 0000000000000000000000000000000000000000..cedc2d193812ccbe9a0cfe7ad0085562a891196a
--- /dev/null
+++ b/pkg/lua/contrib/lua_loadlib.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 1994-2017 Lua.org, PUC-Rio.
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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  pkg_lua
+ * @{
+ * @file
+ *
+ * @brief   Replacement for the lua "package" module.
+ * @author  Juan Carrano <j.carrano@fu-berlin.de>
+ * @author  Roberto Ierusalimschy
+ *
+ * This file replaces the loadlib.c that comes with lua. It removes support
+ * for files (both lua files and c shared objects) and dynamic loading since
+ * none of these are present in RIOT.
+ *
+ * Instead, modules are searched in statically defined tables. In the case
+ * of C modules, the table contains pointers to C functions that act as module
+ * loaders. For pure Lua modules, the source code must be given as a string
+ * embedded in the application binary.
+ *
+ */
+
+#define loadlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+
+#include "binsearch.h"
+
+#include "lua_builtin.h"
+#include "lua_loadlib.h"
+
+/* ======================== 'searchers' functions =========================== */
+
+static int _ll_searcher_builtin_lua(lua_State *L, const char *name)
+{
+    const struct lua_riot_builtin_lua *lmodule =
+        BINSEARCH_STR_P(lua_riot_builtin_lua_table,
+                        lua_riot_builtin_lua_table_len,
+                        name, name, LUAR_MAX_MODULE_NAME);
+
+    if (lmodule != NULL) {
+        int load_result = luaL_loadbuffer(L, (const char *)lmodule->code,
+                                          lmodule->code_size,
+                                          lmodule->name);
+        if (load_result == LUA_OK) {
+            lua_pushstring(L, name);    /* will be 2nd argument to module */
+        }
+
+        return load_result;
+    }
+    else {
+        return LUAR_MODULE_NOTFOUND;
+    }
+}
+
+/**
+ * Search in the list of pure lua modules.
+ *
+ * If the module is found, the source code is compiled and the compiled chunk
+ * is placed on the lua stack, followed by the module name (as a string).
+ */
+static int searcher_builtin_lua(lua_State *L)
+{
+    const char *name = luaL_checkstring(L, 1);
+    int load_result = _ll_searcher_builtin_lua(L, name);
+
+    switch (load_result) {
+        case LUA_OK:
+            return 2; /* there are two elements in the stack */
+        case LUAR_MODULE_NOTFOUND:
+            return luaL_error(L, "Module '%s' not found in Lua-builtins",
+                              lua_tostring(L, 1));
+        default:
+            return luaL_error(L, "error loading module '%s' from Lua-builtins: \n%s",
+                              lua_tostring(L, 1), lua_tostring(L, 2));
+    }
+}
+
+static int _ll_searcher_builtin_c(lua_State *L, const char *name)
+{
+    const struct lua_riot_builtin_c *cmodule =
+        BINSEARCH_STR_P(lua_riot_builtin_c_table,
+                        lua_riot_builtin_c_table_len,
+                        name, name, LUAR_MAX_MODULE_NAME);
+
+    if (cmodule != NULL) {
+        lua_pushcfunction(L, cmodule->luaopen);
+        lua_pushstring(L, name);    /* will be 2nd argument to module */
+        return LUA_OK;
+    }
+    else {
+        return LUAR_MODULE_NOTFOUND;
+    }
+}
+
+/**
+ * Search in the list of C lua modules.
+ *
+ * If the module is found, the loader function is loaded with lua_pushcfunction
+ * and is returned.
+ */
+static int searcher_builtin_c(lua_State *L)
+{
+    const char *name = luaL_checkstring(L, 1);
+    int load_result = _ll_searcher_builtin_c(L, name);
+
+    if (load_result == LUA_OK) {
+        return 2;
+    }
+    else {
+        return luaL_error(L, "Module '%s' not found in C-builtins",
+                          lua_tostring(L, 1));
+    }
+}
+
+int lua_riot_getloader(lua_State *L, const char *name)
+{
+    int load_result;
+
+    load_result = _ll_searcher_builtin_lua(L, name);
+
+    if (load_result == LUAR_MODULE_NOTFOUND) {
+        load_result = _ll_searcher_builtin_c(L, name);
+    }
+
+    return load_result;
+}
+
+/* ======================== 'require' function ============================= */
+
+static int searcher_preload(lua_State *L)
+{
+    const char *name = luaL_checkstring(L, 1);
+
+    lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
+    if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */
+        lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
+    }
+    return 1;
+}
+
+static void findloader(lua_State *L, const char *name)
+{
+    int i;
+    luaL_Buffer msg; /* to build error message */
+
+    luaL_buffinit(L, &msg);
+    /* push 'package.searchers' to index 3 in the stack */
+    if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE) {
+        luaL_error(L, "'package.searchers' must be a table");
+    }
+    /*  iterate over available searchers to find a loader */
+    for (i = 1;; i++) {
+        if (lua_rawgeti(L, 3, i) == LUA_TNIL) { /* no more searchers? */
+            lua_pop(L, 1);                      /* remove nil */
+            luaL_pushresult(&msg);              /* create error message */
+            luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1));
+        }
+        lua_pushstring(L, name);
+        lua_call(L, 1, 2);              /* call it */
+        if (lua_isfunction(L, -2)) {    /* did it find a loader? */
+            return;                     /* module loader found */
+        }
+        else if (lua_isstring(L, -2)) { /* searcher returned error message? */
+            lua_pop(L, 1);              /* remove extra return */
+            luaL_addvalue(&msg);        /* concatenate error message */
+        }
+        else {
+            lua_pop(L, 2); /* remove both returns */
+        }
+    }
+}
+
+static int ll_require(lua_State *L)
+{
+    const char *name = luaL_checkstring(L, 1);
+
+    lua_settop(L, 1);           /* LOADED table will be at index 2 */
+    lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+    lua_getfield(L, 2, name);   /* LOADED[name] */
+    if (lua_toboolean(L, -1)) { /* is it there? */
+        return 1;               /* package is already loaded */
+    }
+    /* else must load package */
+    lua_pop(L, 1);                              /* remove 'getfield' result */
+    findloader(L, name);
+    lua_pushstring(L, name);                    /* pass name as argument to module loader */
+    lua_insert(L, -2);                          /* name is 1st argument (before search data) */
+    lua_call(L, 2, 1);                          /* run loader to load module */
+    if (!lua_isnil(L, -1)) {                    /* non-nil return? */
+        lua_setfield(L, 2, name);               /* LOADED[name] = returned value */
+    }
+    if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */
+        lua_pushboolean(L, 1);                  /* use true as result */
+        lua_pushvalue(L, -1);                   /* extra copy to be returned */
+        lua_setfield(L, 2, name);               /* LOADED[name] = true */
+    }
+    return 1;
+}
+
+/* ====================== 'package' module loader =========================== */
+
+static const luaL_Reg pk_funcs[] = {
+    /* placeholders */
+    { "preload", NULL },
+    { "searchers", NULL },
+    { "loaded", NULL },
+    { NULL, NULL }
+};
+
+
+static const luaL_Reg ll_funcs[] = {
+    { "require", ll_require },
+    { NULL, NULL }
+};
+
+LUAMOD_API int luaopen_package(lua_State *L)
+{
+    static const lua_CFunction searchers[] =
+    { searcher_preload, searcher_builtin_lua, searcher_builtin_c, NULL };
+    int i;
+
+    luaL_newlib(L, pk_funcs); /* create 'package' table */
+
+    /* create 'searchers' table */
+    lua_createtable(L, sizeof(searchers) / sizeof(searchers[0]) - 1, 0);
+    /* fill it with predefined searchers */
+    for (i = 0; searchers[i] != NULL; i++) {
+        lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */
+        lua_pushcclosure(L, searchers[i], 1);
+        lua_rawseti(L, -2, i + 1);
+    }
+
+    lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */
+
+    /* set field 'loaded' */
+    luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+    lua_setfield(L, -2, "loaded");
+
+    /* set field 'preload' */
+    luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
+    lua_setfield(L, -2, "preload");
+
+
+    lua_pushglobaltable(L);
+    lua_pushvalue(L, -2);           /* set 'package' as upvalue for next lib */
+    luaL_setfuncs(L, ll_funcs, 1);  /* open lib into global table */
+    lua_pop(L, 1);                  /* pop global table */
+    return 1;                       /* return 'package' table */
+}
+
+/** @} */
diff --git a/pkg/lua/contrib/lua_run.c b/pkg/lua/contrib/lua_run.c
new file mode 100644
index 0000000000000000000000000000000000000000..fcb57f16895689fec26d56f3577ed3df3b9802f1
--- /dev/null
+++ b/pkg/lua/contrib/lua_run.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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  pkg_lua
+ * @{
+ * @file
+ *
+ * @brief   Convenience functions for running Lua code.
+ * @author  Juan Carrano <j.carrano@fu-berlin.de>
+ *
+ */
+
+#define LUA_LIB
+
+#include "lprefix.h"
+
+#include <stdio.h>
+#include <setjmp.h>
+
+#include "kernel_defines.h"
+#include "tlsf.h"
+
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+
+#include "lua_run.h"
+#include "lua_loadlib.h"
+
+const char *lua_riot_str_errors[] = {
+    "No errors",
+    "Error setting up the interpreter",
+    "Error while loading a builtin library",
+    "Cannot find the specified module",
+    "Compilation / syntax error",
+    "Unprotected error (uncaught exception)",
+    "Out of memory",
+    "Internal interpreter error",
+    "Unknown error"
+};
+
+/* The lua docs state the behavior in these cases:
+ *
+ *  1.              ptr=?, size=0    -> free(ptr)
+        therefore   ptr=NULL, size=0 -> NOP
+ *  2.               ptr=? , size!=0  -> realloc(ptr, size)
+ *
+ * The  TLSF code for realloc says:
+ *  / * Zero-size requests are treated as free. * /
+ *  if (ptr && size == 0)
+ *  {
+ *      tlsf_free(tlsf, ptr);
+ *  }
+ *  / * Requests with NULL pointers are treated as malloc. * /
+ *  else if (!ptr)
+ *  {
+ *      p = tlsf_malloc(tlsf, size);
+ *  }
+ *
+ * Therefore it is safe to use tlsf_realloc here.
+ */
+static void *lua_tlsf_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
+{
+    tlsf_t tlsf = ud;
+
+    (void)osize;
+
+    return tlsf_realloc(tlsf, ptr, nsize);
+}
+
+LUALIB_API lua_State *lua_riot_newstate(void *memory, size_t mem_size,
+                                        lua_CFunction panicf)
+{
+    lua_State *L;
+
+    #ifdef LUA_DEBUG
+        Memcontrol *mc = memory;
+    #endif
+
+    /* If we are using the lua debug module, let's reserve a space for the
+     * memcontrol block directly. We don't use the allocator because we lose
+     * the pointer, so we won't be able to free it and we will get a false
+     * positive if we try to check for memory leaks.
+     */
+    #ifdef LUA_DEBUG
+        memory = (Memcontrol *)memory + 1;
+        mem_size -= (uint8_t *)memory - (uint8_t *)mc;
+    #endif
+
+    tlsf_t tlsf = tlsf_create_with_pool(memory, mem_size);
+
+    #ifdef LUA_DEBUG
+        luaB_init_memcontrol(mc, lua_tlsf_alloc, tlsf);
+        L = luaB_newstate(mc);
+    #else
+        L = lua_newstate(lua_tlsf_alloc, tlsf);
+    #endif
+
+    if (L != NULL) {
+        lua_atpanic(L, panicf);
+    }
+
+    return L;
+}
+
+static const luaL_Reg loadedlibs[LUAR_LOAD_O_ALL] = {
+    { "_G", luaopen_base },
+    { LUA_LOADLIBNAME, luaopen_package },
+    { LUA_COLIBNAME, luaopen_coroutine },
+    { LUA_TABLIBNAME, luaopen_table },
+    { LUA_IOLIBNAME, luaopen_io },
+    { LUA_OSLIBNAME, luaopen_os },
+    { LUA_STRLIBNAME, luaopen_string },
+    { LUA_MATHLIBNAME, luaopen_math },
+    { LUA_UTF8LIBNAME, luaopen_utf8 },
+    { LUA_DBLIBNAME, luaopen_debug },
+};
+
+LUALIB_API int lua_riot_openlibs(lua_State *L, uint16_t modmask)
+{
+    int lib_index;
+
+    #ifdef LUA_DEBUG
+        luaL_requiref(L, LUA_TESTLIBNAME, luaB_opentests, 1);
+        lua_pop(L, 1);
+    #endif
+
+    for (lib_index = 0; lib_index < LUAR_LOAD_O_ALL;
+         lib_index++, modmask >>= 1) {
+        const luaL_Reg *lib = loadedlibs + lib_index;
+
+        if (!(modmask & 1)) {
+            continue;
+        }
+        /* TODO: how can the loading fail? */
+        luaL_requiref(L, lib->name, lib->func, 1);
+        lua_pop(L, 1);  /* remove lib from stack (it is already global) */
+    }
+
+    return lib_index;
+}
+
+/**
+ * Jump back to a save point (defined with setjmp).
+ *
+ * @note    This function never returns!
+ */
+NORETURN static int _jump_back(lua_State *L)
+{
+    jmp_buf *jump_buffer = *(jmp_buf **)lua_getextraspace(L);
+
+    /* FIXME: I dont think it's OK to print a message */
+    lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
+                         lua_tostring(L, -1));
+
+    /* TODO: try to return some info about the error object. */
+
+    longjmp(*jump_buffer, 1);
+}
+
+static int lua_riot_do_module_or_buf(const uint8_t *buf, size_t buflen,
+                                     const char *modname, void *memory, size_t mem_size,
+                                     uint16_t modmask, int *retval)
+{
+    jmp_buf jump_buffer;
+    lua_State *volatile L = NULL;
+    volatile int tmp_retval = 0; /* we need to make it volatile because of the
+                                    setjmp/longjmp */
+    volatile int status = LUAR_EXIT;
+    int compilation_result;
+
+    if (setjmp(jump_buffer)) { /* We'll teleport back here if something goes wrong*/
+        status = LUAR_INTERNAL_ERR;
+        goto lua_riot_do_error;
+    }
+
+    L = lua_riot_newstate(memory, mem_size, _jump_back);
+    if (L == NULL) {
+        status = LUAR_STARTUP_ERR;
+        goto lua_riot_do_error;
+    }
+
+    /* lua_getextraspace() gives us a pointer to an are large enough to hold a
+     * pointer.
+     *
+     * We store a pointer to the jump buffer in that area.
+     *
+     * lua_getextraspace() is therefore a pointer to a pointer to jump_buffer.
+     */
+    *(jmp_buf **)lua_getextraspace(L) = &jump_buffer;
+
+    tmp_retval = lua_riot_openlibs(L, modmask);
+    if (tmp_retval != LUAR_LOAD_O_ALL) {
+        status = LUAR_LOAD_ERR;
+        goto lua_riot_do_error;
+    }
+
+    if (buf == NULL) {
+        compilation_result = lua_riot_getloader(L, modname);
+    }
+    else {
+        compilation_result = luaL_loadbufferx(L, (const char *)buf,
+                                              buflen, modname, "t");
+    }
+
+    switch (compilation_result) {
+        case LUAR_MODULE_NOTFOUND:
+            status = LUAR_NOMODULE;
+            goto lua_riot_do_error;
+        case LUA_ERRSYNTAX:
+            status = LUAR_COMPILE_ERR;
+            goto lua_riot_do_error;
+        case LUA_ERRMEM:    /* fallthrough */
+        case LUA_ERRGCMM:   /* fallthrough */
+        default:
+            status = LUAR_MEMORY_ERR;
+            goto lua_riot_do_error;
+        case LUA_OK:
+            break;
+    }
+
+    if (buf != NULL) {
+        lua_pushstring(L, modname);
+    }
+
+    switch (lua_pcall(L, 1, 1, 0)) {
+        case LUA_ERRRUN:    /* fallthrough */
+        case LUA_ERRGCMM:   /* fallthrough */
+        default:
+            status = LUAR_RUNTIME_ERR;
+            puts(lua_tostring(L, -1));
+            goto lua_riot_do_error;
+        case LUA_ERRMEM:
+            status = LUAR_MEMORY_ERR;
+            goto lua_riot_do_error;
+        case LUA_OK:
+            break;
+    }
+
+    tmp_retval = lua_tonumber(L, 1);
+
+lua_riot_do_error:
+
+    if (L != NULL) {
+        lua_riot_close(L);
+    }
+
+    if (retval != NULL) {
+        *retval = tmp_retval;
+    }
+
+    return status;
+}
+
+
+LUALIB_API int lua_riot_do_module(const char *modname, void *memory, size_t mem_size,
+                                  uint16_t modmask, int *retval)
+{
+    return lua_riot_do_module_or_buf(NULL, 0, modname, memory, mem_size, modmask,
+                                     retval);
+}
+
+LUALIB_API int lua_riot_do_buffer(const uint8_t *buf, size_t buflen, void *memory,
+                                  size_t mem_size, uint16_t modmask, int *retval)
+{
+    return lua_riot_do_module_or_buf(buf, buflen, "=BUFFER", memory, mem_size,
+                                     modmask, retval);
+}
+
+#define MAX_ERR_STRING ((sizeof(lua_riot_str_errors) / sizeof(*lua_riot_str_errors)) - 1)
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+LUALIB_API const char *lua_riot_strerror(int errn)
+{
+    return lua_riot_str_errors[MIN((unsigned int)errn, MAX_ERR_STRING)];
+}
+
+/** @} */
diff --git a/pkg/lua/doc.txt b/pkg/lua/doc.txt
index 944d45c632614b490f8a0c68b6b57675c8c3b934..50d6293ed4a5d73bfd24d2dcca3d55f6a805691a 100644
--- a/pkg/lua/doc.txt
+++ b/pkg/lua/doc.txt
@@ -1,6 +1,152 @@
 /**
  * @defgroup pkg_lua Lua ported to RIOT
  * @ingroup  pkg
- * @brief    Provides Lua support for RIOT
+ * @brief    Provides a Lua interpreter for RIOT
  * @see      https://github.com/lua/lua
+ * @see      sys_lua
+ *
+ * # Lua programming language support
+ *
+ * ## Introduction
+ *
+ * This package embeds a [Lua 5.3](https://www.lua.org/) interpreter into RIOT.
+ * With a few exceptions, all the APIs mentioned in the
+ * [official documentation](https://www.lua.org/manual/5.3/) are available in
+ * this package too.
+ *
+ * ## Running Lua code.
+ *
+ * lua_run.h contains functions that make it easy to setup the interpreter and
+ * catch errors in a safe way. The functions from Lua's auxlib can still be used
+ * but then you must supply your own allocator and panic routines, load the
+ * builtin modules, etc.
+ *
+ * To run a chunk of code stored in an array use:
+ * ```
+ * lua_riot_do_buffer(const char *buf, size_t buflen, void *memory,
+ *                            size_t mem_size, uint16_t modmask, int *retval);
+ * ```
+ * The interpreter will not use the global heap for allocations, instead the
+ * user must supply a memory buffer.
+ *
+ * To save some memory, some builtin modules can be left out. `modmask` specifies
+ * which builtins to load. Note that if a builtin is not loaded by C code, then
+ * it cannot be loaded by Lua code later.
+ *
+ * `lua_riot_do_buffer` takes care of setting up the Lua state, registering a panic
+ * handler that does not crash the application, configuring an allocator, loading
+ * libraries, etc.
+ *
+ * To run a module as a script use `lua_riot_do_module`. This is roughly equivalent
+ * to executing:
+ * ```
+ * require('modulename')
+ * ```
+ *
+ * ## Memory requirements
+ *
+ * While generally efficient, the Lua interpreter was not really designed for
+ * constrained devices.
+ *
+ * A basic interpreter session typically requires about 12kB RAM. The stack
+ * but it depends on the functions used (string handling tends to use more stack).
+ * It also depends on the platform.
+ *
+ * There is currently no easy way to determine the stack needs other than trial
+ * and error. Future versions of the package will include instrumentation to
+ * this end.
+ *
+ * ## Adding your own modules.
+ *
+ * `lua_loadlib.c` contains two loaders, one for modules written in Lua and
+ * another one for C extensions.
+ *
+ * An index to the modules is stored in a table (there are two, one for each
+ * kind of module). The tables are indexed by the module name and must be sorted
+ * in ascending order by this key.
+ *
+ * The definitions for the table types are in `lua_builtin.h`. The loader module
+ * containes empty tables, defined as weak symbols so they can be ovewritten
+ * by the application. The variables that must be defined are:
+ *
+ * ```
+ * /** Table containing all built in pure lua modules */
+ * const struct lua_riot_builtin_lua *const lua_riot_builtin_lua_table;
+ * /** Number of elements of lua_riot_builtin_lua_table */
+ * const size_t lua_riot_builtin_lua_table_len;
+ *
+ * /** Table containing all built in c lua modules */
+ * const struct lua_riot_builtin_c *const lua_riot_builtin_c_table;
+ * /** Number of elements of lua_riot_builtin_c_table */
+ * const size_t lua_riot_builtin_c_table_len;
+ * ```
+ *
+ * Currently, these must be defined manually in the application code. In the
+ * future a script will generate this tables, populating them with both RIOT
+ * modules and the user modules.
+ *
+ *
+ * ## Customizations
+ *
+ * The upstream Lua code is used without with the following modifications.
+ *
+ * Modifications that affect the API:
+ *
+ * - lua.c (the main interface to the interpreter) is replaced by our own
+ *   stripped-down version. The REPL is no longer included.
+ * - loadlib.c (the "package" module) is replaced by our own (simplified)
+ *   loader. All the code dealing with files and dynamic loading has been
+ *   removed.
+ * - os.tmpname() is removed as it caused compiler warnings and it is not
+ *   really possible to use it right. Use io.tmpfile() instead.
+ * - The test module has been modified to allow it run in the RIOT environment.
+ *   This is not a public API, though.
+ *
+ * Other modifications:
+ *
+ * - There is a patch changing the Makefile. This updated makefile is not used
+ *   in the package, but is provided to aid development in a PC.
+ * - Some patches to reduce stack and memory usage.
+ *
+ * ### Patches
+ *
+ * A version of Lua with the patches applied is available at
+ * https://github.com/riot-appstore/lua. It can be downloaded and compiled in
+ * desktop computer, and the official test suite (https://www.lua.org/tests/)
+ * can then be run.
+ *
+ * Alternatively, the patches in this package can be directly applied to the
+ * official distribution.
+ *
+ * The updated makefile creates two standalone executables. Tests should be run
+ * with the debug executable.
+ *
+ * ## TODO
+ *
+ * The following features are missing and will be eventually added:
+ *
+ * - Load source code incrementally. It can be done now, but then the rest of the
+ *   interpreter setup must be loaded manually.
+ * - Bindings to access RIOT functionality.
+ * - Support in the build system for easily including application-specific
+ *   modules.
+ * - Instrumentation to measure stack consumption (and maybe prevent overflow).
+ * - Support for "frozen tables" (i.e. tables that live in ROM).
+ * - Provide a better way of supplying data to a script and getting back results.
+ * - Specify a function to call inside a module (????)
+ * - Expand this readme into a proper manual.
+ *
+ */
+
+/* These are docs for the future (when we have the script to compile module tables) */
+/*
+ * # Running Lua and C code
+ *
+ * see \ref sys_lua for information on how to access RIOT modules from within
+ * Lua.
+ *
+ * While it is possible to include your application specific modules and run
+ * arbitrary Lua code only just using this interpreter, the \ref sys_lua module
+ * provides an automated way of handling Lua modules.
+ *
  */
diff --git a/pkg/lua/include/lua_builtin.h b/pkg/lua/include/lua_builtin.h
new file mode 100644
index 0000000000000000000000000000000000000000..fcfec30bdf58a9c311dc1d25a00d35b51fc1b496
--- /dev/null
+++ b/pkg/lua/include/lua_builtin.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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  pkg_lua
+ * @{
+ * @file
+ *
+ * @brief   Definitions for including built-in modules.
+ * @author  Juan Carrano <j.carrano@fu-berlin.de>
+ *
+ * The modules must be placed in the tables lua_riot_builtin_lua_table (for lua
+ * source code) and lua_riot_builtin_c_table (for C extensions) and the lengths
+ * of these tables must be in lua_riot_builtin_lua_table_len and
+ * lua_riot_builtin_c_table_len.
+ *
+ * These symbols are defined as weak, so there if they are not defined elsewhere
+ * they will default to zero (or NULL), that is, empty tables.
+ */
+
+#ifndef LUA_BUILTIN_H
+#define LUA_BUILTIN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Avoid compilation errors where there are no external modules defined */
+/**
+ * Attribute to make symbols weak.
+ *
+ * @todo This should be made part of RIOT.
+ */
+#define WEAK __attribute__((weak))
+
+/**
+ * Only the first `LUAR_MAX_MODULE_NAME` characters of a module name
+ * will be considered when performing a lookup.
+ */
+#define LUAR_MAX_MODULE_NAME 64
+
+/**
+ * Entry describing a pure lua module whose source is built into the
+ * application binary.
+ */
+struct lua_riot_builtin_lua {
+    const char *name;   /*!< Name of the module */
+    const uint8_t *code;   /*!< Lua source code buffer*/
+    size_t code_size;   /*!< Size of the source code buffer. */
+};
+
+/**
+ * Entry describing a c lua module built into the
+ * application binary.
+ */
+struct lua_riot_builtin_c {
+    const char *name;               /*!< Name of the module */
+    int (*luaopen)(lua_State *);    /*!< Loader function. It must place the module
+                                     *  table at the top of the lua stack.
+                                     *  @todo Add better docs.
+                                     */
+};
+
+/** Table containing all built in pure lua modules */
+extern WEAK const struct lua_riot_builtin_lua *const lua_riot_builtin_lua_table;
+/** Number of elements of lua_riot_builtin_lua_table */
+extern WEAK const size_t lua_riot_builtin_lua_table_len;
+
+/** Table containing all built in c lua modules */
+extern WEAK const struct lua_riot_builtin_c *const lua_riot_builtin_c_table;
+/** Number of elements of lua_riot_builtin_c_table */
+extern WEAK const size_t lua_riot_builtin_c_table_len;
+
+#ifdef __cplusplus
+extern "C" }
+#endif
+
+#endif /* LUA_BUILTIN_H */
+
+/** @} */
diff --git a/pkg/lua/include/lua_loadlib.h b/pkg/lua/include/lua_loadlib.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2ce87e14ef5523ecbb251b58fe60cfa589ec6de
--- /dev/null
+++ b/pkg/lua/include/lua_loadlib.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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  pkg_lua
+ * @{
+ * @file
+ *
+ * @brief   Lightweight C interface to the package loader.
+ * @author  Juan Carrano <j.carrano@fu-berlin.de>
+ *
+ */
+
+#ifndef LUA_LOADLIB_H
+#define LUA_LOADLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Error code for when a modules is not found.
+ *
+ * The numeric value is chosen so that there is no collision with Lua's
+ * own error codes.
+ */
+#define LUAR_MODULE_NOTFOUND 50
+
+/**
+ * Load a module as a chunk.
+ *
+ * This function is a lightweight "require". It does not require the "package"
+ * module to be loaded and does not register the module.
+ * Only the builtin tables are searched.
+ *
+ * Upon sucessful execution, the compiled chunk will be at the top of the lua
+ * stack.
+ *
+ * @param   L       Initialized Lua interpreter state.
+ * @param   name    Name of the module.
+ *
+ * @return      Same as lua_load. If the module is a C-module, then this will
+ *              always succeed and return LUA_OK.
+ */
+int lua_riot_getloader(lua_State *L, const char *name);
+
+#ifdef __cplusplus
+extern "C"
+}
+#endif
+
+#endif /* LUA_LOADLIB_H */
+
+/** @} */
diff --git a/pkg/lua/include/lua_run.h b/pkg/lua/include/lua_run.h
new file mode 100644
index 0000000000000000000000000000000000000000..dcdbde42f9c815360be7c20caa3338d970aed206
--- /dev/null
+++ b/pkg/lua/include/lua_run.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2018 Freie Universität Berlin
+ *
+ * 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  pkg_lua
+ * @file
+ * @{
+ *
+ * @brief   Convenience functions for running Lua code.
+ * @author  Juan Carrano <j.carrano@fu-berlin.de>
+ *
+ * This functions make it easy to create and use new Lua context:
+ * It provides:
+ *
+ * - Easy to use routines for executing modules as scrips.
+ * - Control over which modules get loaded.
+ * - Support for using a local heap allocator.
+ * - Out of memory handling via setjmp/longjmp.
+ *
+ * This library is not strictly required, as all of the functionality could be
+ * implemented in terms of the public lua api, but it covers most of the use
+ * cases, and thus avoids code repetition in applications.
+ *
+ */
+
+#ifndef LUA_RUN_H
+#define LUA_RUN_H
+
+#include <stdint.h>
+
+#include "lua.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Convert a library index into a bit mask.
+ */
+#define LUAR_LOAD_FLAG(n) (((uint16_t)1) << (n))
+
+/**
+ * Order in which the builtin libraries are loaded.
+ */
+enum LUAR_LOAD_ORDER {
+    LUAR_LOAD_O_BASE,
+    LUAR_LOAD_O_PACKAGE,
+    LUAR_LOAD_O_CORO,
+    LUAR_LOAD_O_TABLE,
+    LUAR_LOAD_O_IO,
+    LUAR_LOAD_O_OS,
+    LUAR_LOAD_O_STRING,
+    LUAR_LOAD_O_MATH,
+    LUAR_LOAD_O_UTF8,
+    LUAR_LOAD_O_DEBUG,
+    LUAR_LOAD_O_ALL,
+};
+
+/** Load the base globals (_G) */
+#define LUAR_LOAD_BASE      LUAR_LOAD_FLAG(LUAR_LOAD_O_BASE)
+/** Load ´package´ */
+#define LUAR_LOAD_PACKAGE   LUAR_LOAD_FLAG(LUAR_LOAD_O_PACKAGE)
+/** Load ´coroutine´ */
+#define LUAR_LOAD_CORO      LUAR_LOAD_FLAG(LUAR_LOAD_O_CORO)
+/** Load ´table´ */
+#define LUAR_LOAD_TABLE     LUAR_LOAD_FLAG(LUAR_LOAD_O_TABLE)
+/** Load ´io´ */
+#define LUAR_LOAD_IO        LUAR_LOAD_FLAG(LUAR_LOAD_O_IO)
+/** Load ´os´ */
+#define LUAR_LOAD_OS        LUAR_LOAD_FLAG(LUAR_LOAD_O_OS)
+/** Load ´string´ */
+#define LUAR_LOAD_STRING    LUAR_LOAD_FLAG(LUAR_LOAD_O_STRING)
+/** Load ´math´ */
+#define LUAR_LOAD_MATH      LUAR_LOAD_FLAG(LUAR_LOAD_O_MATH)
+/** Load ´utf8´ */
+#define LUAR_LOAD_UTF8      LUAR_LOAD_FLAG(LUAR_LOAD_O_UTF8)
+/** Load ´debug´ */
+#define LUAR_LOAD_DEBUG     LUAR_LOAD_FLAG(LUAR_LOAD_O_DEBUG)
+
+/* TODO: maybe we can implement a "restricted base" package containing a subset
+ * of base that is safe. */
+
+#define LUAR_LOAD_ALL        (0xFFFF)               /** Load all standard modules */
+#define LUAR_LOAD_NONE       (0x0000)               /** Do not load any modules */
+
+/** Errors that can be raised when running lua code. */
+enum LUAR_ERRORS {
+    LUAR_EXIT,          /** The program exited without error. */
+    LUAR_STARTUP_ERR,   /** Error setting up the interpreter. */
+    LUAR_LOAD_ERR,      /** Error while loading libraries. */
+    LUAR_NOMODULE,      /** The specified module could not be found. */
+    LUAR_COMPILE_ERR,   /** The Lua code failed to compile. */
+    LUAR_RUNTIME_ERR,   /** Error in code execution. */
+    LUAR_MEMORY_ERR,    /** Lua could not allocate enough memory */
+    LUAR_INTERNAL_ERR   /** Error inside the Lua VM.
+                         *  Right now, if this happens, you may leak memory from
+                         *  the heap. If your program is the only one using the
+                         *  dynamic allocation, just clean the heap.
+                         */
+};
+
+/**
+ * Human-readable description of the errors
+ */
+extern const char *lua_riot_str_errors[];
+
+/**
+ * Return a string describing an error from LUAR_ERRORS.
+ *
+ * @param   errn    Error number as returned by lua_riot_do_buffer() or
+ *                  lua_riot_do_buffer()
+ *
+ * @return A string describing the error, or "Unknown error".
+ */
+LUALIB_API const char *lua_riot_strerror(int errn);
+
+/**
+ * Initialize a lua state and set the panic handler.
+ *
+ * @todo    Use a per-state allocator
+ *
+ * @param   memory      Pointer to memory region that will be used as heap for
+ *                      the allocator. Currently this functionality is not
+ *                      supported and this must be set to NULL.
+ * @param   mem_size    Size of the memory region that will be used as heap.
+ *                      Currently this functionality is not supported and this
+ *                      must be set to 0.
+ * @param   panicf      Function to be passed to lua_atpanic. If set to NULL,
+ *                      a generic function that does nothing will be used.
+ *
+ * @return      the new state, or NULL if there is a memory allocation error.
+ */
+LUALIB_API lua_State *lua_riot_newstate(void *memory, size_t mem_size,
+                                        lua_CFunction panicf);
+
+/**
+ * Terminate the lua state.
+ *
+ * You must call this function if you want the finalizers (the __gc metamethods)
+ * to be called.
+ */
+#ifndef LUA_DEBUG
+    #define lua_riot_close lua_close
+#else
+    #define lua_riot_close luaB_close
+#endif /* LUA_DEBUG */
+
+/**
+ * Open builtin libraries.
+ *
+ * This is like luaL_openlibs but it allows selecting which libraries will
+ * be loaded.
+ *
+ * Libraries are loaded in the order specified by the LUAR_LOAD_ORDER enum. If
+ * there is an error the load sequence is aborted and the index of the library
+ * that failed is reported.
+ *
+ * If debuging is enabled (compile with the LUA_DEBUG macro), then the test
+ * library will be unconditionally loaded.
+ *
+ * @param   L           Lua state
+ * @param   modmask     Binary mask that indicates which modules should be
+ *                      loaded. The mask is made from a combination of the
+ *                      LUAR_LOAD_* macros.
+ *
+ * @return      The index of the library that failed to load, or LUAR_LOAD_O_ALL
+ *              if all libraries were loaded.
+ */
+LUALIB_API int lua_riot_openlibs(lua_State *L, uint16_t modmask);
+
+/**
+ * Initialize the interpreter and run a built-in module in protected mode.
+ *
+ * In addition to running code in protected mode, this also sets a panic
+ * function that long-jumps back to this function, in case there is an internal
+ * interpreter error (LUAR_INTERNAL_ERR).
+ * Right now the only things that the application can are either to abort(),
+ * or to manually reset the heap (only if there's no other thread using it).
+ *
+ * @param       modname     name of the module.
+ * @param       memory      @see lua_riot_newstate()
+ * @param       mem_size    @see lua_riot_newstate()
+ * @param       modmask     @see lua_riot_newstate()
+ * @param[out]  retval      Value returned by the lua code, if it is a number,
+ *                          or zero if no value is returned or the value is not
+ *                          a number. If retval is null, the value is not stored.
+ *
+ * @return      An error code ( @see LUAR_ERRORS). LUAR_EXIT indicates no error.
+ */
+LUALIB_API int lua_riot_do_module(const char *modname, void *memory, size_t mem_size,
+                                  uint16_t modmask, int *retval);
+
+/**
+ * Initialize the interpreter and run a user supplied buffer in protected mode.
+ *
+ * Only text data (i.e. lua source code) can be loaded by this function. The
+ * lua interpreter is not robust against corrupt binary code.
+ *
+ * @see lua_riot_do_module() for more information on internal errors.
+ *
+ * @param       buf     Text data (lua source code).
+ * @param       buflen  Size of the text data in bytes. If buf is
+ *                      a zero-terminated string, the zero must not be counted.
+ * @param       memory      @see lua_riot_newstate()
+ * @param       mem_size    @see lua_riot_newstate()
+ * @param       modmask     @see lua_riot_newstate()
+ * @param[out]  retval      @see lua_riot_do_module()
+ * @return      @see lua_riot_do_module().
+ */
+LUALIB_API int lua_riot_do_buffer(const uint8_t *buf, size_t buflen, void *memory,
+                                  size_t mem_size, uint16_t modmask, int *retval);
+
+#ifdef __cplusplus
+extern "C" }
+#endif
+
+/** @} */
+
+#endif /* LUA_RUN_H */
diff --git a/pkg/lua/patches/0001-Remove-dependency-on-nonexistent-RIOT-syscalls.patch b/pkg/lua/patches/0001-Remove-dependency-on-nonexistent-RIOT-syscalls.patch
deleted file mode 100644
index b4017f538264e4e7e8ce76d855242587e2152ac8..0000000000000000000000000000000000000000
Binary files a/pkg/lua/patches/0001-Remove-dependency-on-nonexistent-RIOT-syscalls.patch and /dev/null differ
diff --git a/pkg/lua/patches/0001-Remove-luaL_newstate.patch b/pkg/lua/patches/0001-Remove-luaL_newstate.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f7059b62a91d6b7228b5bdd53edd06c4f2bdbcf7
Binary files /dev/null and b/pkg/lua/patches/0001-Remove-luaL_newstate.patch differ
diff --git a/pkg/lua/patches/0002-Allow-LUAL_BUFFERSIZE-to-be-defined-in-the-command-l.patch b/pkg/lua/patches/0002-Allow-LUAL_BUFFERSIZE-to-be-defined-in-the-command-l.patch
new file mode 100644
index 0000000000000000000000000000000000000000..bb674624d4d72c82e543541c18ae91d606ce60e7
Binary files /dev/null and b/pkg/lua/patches/0002-Allow-LUAL_BUFFERSIZE-to-be-defined-in-the-command-l.patch differ
diff --git a/pkg/lua/patches/0003-Make-size-of-LoadF-buffer-configurable.patch b/pkg/lua/patches/0003-Make-size-of-LoadF-buffer-configurable.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0237936119a67584045049349469fdc4fd791e1a
Binary files /dev/null and b/pkg/lua/patches/0003-Make-size-of-LoadF-buffer-configurable.patch differ
diff --git a/pkg/lua/patches/0004-Remove-os.tmpname.patch b/pkg/lua/patches/0004-Remove-os.tmpname.patch
new file mode 100644
index 0000000000000000000000000000000000000000..c94f3490e433d96a494c83a1906238ab7757ab01
Binary files /dev/null and b/pkg/lua/patches/0004-Remove-os.tmpname.patch differ
diff --git a/pkg/lua/patches/0005-Do-not-allocate-buffers-on-the-stack.patch b/pkg/lua/patches/0005-Do-not-allocate-buffers-on-the-stack.patch
new file mode 100644
index 0000000000000000000000000000000000000000..fa9a70d17367a078191a7b18e886c420d2b66689
Binary files /dev/null and b/pkg/lua/patches/0005-Do-not-allocate-buffers-on-the-stack.patch differ
diff --git a/pkg/lua/patches/0006-Cleanup-test-module.patch b/pkg/lua/patches/0006-Cleanup-test-module.patch
new file mode 100644
index 0000000000000000000000000000000000000000..0d22dd9fcf6ab79d6313ff454aa73a859ad2b5bb
Binary files /dev/null and b/pkg/lua/patches/0006-Cleanup-test-module.patch differ
diff --git a/pkg/lua/patches/0007-Add-a-proper-makefile.patch b/pkg/lua/patches/0007-Add-a-proper-makefile.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f6eb73d24706a9e450dbde6c537ffe56600ac198
Binary files /dev/null and b/pkg/lua/patches/0007-Add-a-proper-makefile.patch differ
diff --git a/pkg/lua/patches/0008-Default-to-32-bit-build-and-small-buffer-size.patch b/pkg/lua/patches/0008-Default-to-32-bit-build-and-small-buffer-size.patch
new file mode 100644
index 0000000000000000000000000000000000000000..f9b79b0737be77ff26d383270f31b9418b554f28
Binary files /dev/null and b/pkg/lua/patches/0008-Default-to-32-bit-build-and-small-buffer-size.patch differ