TCP/IP in eLua

TCP/IP in eLua (WIP)

eLua's TCP/IP support was designed with flexibility and ease of use in mind. It might not provide all the functions of a "full-fledged" TCP/IP stack, but it's still fully functional, has a much smaller footprint and is probably easier to use than a "regular" (POSIX) TCP/IP stack. These are the services provided by the TCP/IP stack:

  • a set of functions for network access (defined in inc/elua_net.h)
  • a DHCP client
  • a DNS resolver
  • a module (net) which can be used from Lua to access the network functions
  • a Telnet miniclient, which is used to support the eLua shell via TCP/IP instead of serial connections.

TCP/IP configuration

To configure the TCP/IP subsystem, edit src/platform/<name>platform_conf.h and:

  1. #define BUILD_UIP to enable TCP/IP support
  2. if you'll be using the DHCP client, just #define BUILD_DHCPC to build the DHCP client. In any case, you must also define a static network configuration:

    #define ELUA_CONF_IPADDR0 ... ELUA_CONF_IPADDR3 : the IP address
    #define ELUA_CONF_NETMASK0 ... ELUA_CONF_NETMASK3 : the network mask
    #define ELUA_CONF_DEFGW0 ... ELUA_CONF_DEFGW3 : the default gateway
    #define ELUA_CONF_DNS0 ... ELUA_CONF_DNS3 : the DNS server

    Note that you must define both BUILD_DHCPC and the ELUA_CONF_* macros. If the DHCP client fails to obtain a valid IP address, the static configuration will be used instead. To use only the static configuration (and make the eLua image size a bit smaller) don't define the BUILD_DHCPC client.

  3. #define BUILD_DNS if you want support for the DNS server.
  4. #define BUILD_CON_TCP if you want support for shell over telnet instead of serial. Note that you must NOT define BUILD_CON_GENERIC in this case (see here for details).

You'll also need an uIP configuration file (src/platform/<name>/uip-conf.h) to configure the TCP/IP stack. For an example, look at src/platform/<lm3s>/uip-conf.h. The header if quite self-explanatory, below you have a list of parameters that you might want to change:

  • u8_t, u16_t: define these types to match your platform.
  • UIP_CONF_MAX_CONNECTIONS: the maximum number of TCP connections that can be active at a given time.
  • UIP_CONF_UDP_CONNS: same thing for UDP connections.
  • UIP_CONF_BYTE_ORDER: LITTLE_ENDIAN or BIG_ENDIAN, it's very important to match this with your architecture.
  • UIP_CONF_BUFFER_SIZE: the size of the buffer used by uIP for all its connections. You should keep it small to avoid memory consumption, but doing so when you have to transfer large amounts of data will slow the transfer speed. 1k seems to be a good compromise.
  • UIP_CONF_UDP: turn off UDP support. While eLua doesn't have support for UDP via its net module at this time, UDP can still be used (for example by DNS/DHCP), so be careful if you disable this.
  • ELUA_DHCP_TIMER_ID: the timer ID used for the TCP/IP subsystem. If not specified it defaults to the link:arch_platform_timers.html#the_system_timer[system timer]. If the system timer is not used, please note that this should be a dedicated timer, not available to the rest of the system (or available in "read-only" mode).

TCP/IP implementation internals

The TCP/IP support was designed in such a way that it doesn't require a specific TCP/IP stack implementation. To work with eLua, a TCP/IP stack must simply implement all the functions defined in the inc/elua_net.h file. This allows for easy integration of more than one TCP/IP stack. Currently only uIP is used in eLua, but lwIP (and possibly others) are planned to be added at some point. Another key point of the TCP/IP implementation (and of the whole eLua design for that matter) is that it should be as platform independent as possible: write everything in a platform-independent manner, except for some functions (as few as possible and as simple as possible) that must be implemented by each platform. To illustrate the above, a short overview of the uIP integration is given below.

uIP is a minimalistic TCP/IP stack designed specifically for resource constrained embedded systems. While the design and implementation of uIP are an excellent example of what can be done with a few kilobytes of memory, it has a number of quirks that make it hard to integrate with eLua. First, it uses a callback approach, as opposed to the sequential approach of "regular" TCP/IP stacks. It provides a "protosocket" library that can be used to write uIP applications in a more "traditional" way, but it's quite restrictive. So, to use it with eLua, a translation layer was needed. It is implemented in src/elua_uip.c, and its sole purpose is to "adapt" the uIP stack to the eLua model: implement the functions in inc/elua_net.h and you're ready to use the stack. In this case the "adaption layer" is quite large because of uIP's callback-based design.

To make the uIP implementation as platform-independent as possible, a special networking layer is added to the platform interface. There are only 4 functions that must be implemented by a backend to use the networking layer. They might change as more TCP/IP stacks are added to eLua, but probably the networking layer won't get much bigger than it is now.
For a more in-depth understanding of how the networking layer is implemented, look at the LM3S implementation in src/platform/lm3s/platform.c.