Getting Started
lwIP-CE ships as a curated release surface for calculator applications. The release headers are not a dump of upstream lwIP. They are generated from what this build can actually provide, then filtered through the public API manifest.
Start the Stack
Most applications should include lwip_init_runtime.h and lwip.h and
use the app-facing socket API:
#include <lwip_init_runtime.h>
#include <lwip.h>
int main(void)
{
if (lwip_init_runtime() != 0) {
return 1;
}
if (!lwip_start()) {
return 1;
}
while (1) {
lwip_poll_network_events();
/* app work */
}
}
lwip_init_runtime() must be the first lwIP call in your program. It
locates the lwIP flash app, verifies its export table, and patches the libload
trampolines so that every other entry point becomes reachable. It returns
0 on success, 1 if the app is not found, or 2 on an export table
error. Nothing else will work if this step is skipped or called out of order.
lwip_start() initializes the resident network stack and USB Ethernet path.
lwip_poll_network_events() must run from the main loop. There is no OS
thread sitting behind the stack doing this for you.
Use the Socket API
lwip.h is an app-facing wrapper for programs that do not want to wire raw
TCP, UDP, ALTCP, and TLS callbacks by hand.
lwip_socket_connect() starts the socket attempt. It does not mean the
socket is ready. Watch socket.status or subscribe to
LWIP_SOCKET_EVENTF_STATE_CHANGE with lwip_socket_on_event().
lwip_socket_create() accepts a transport selector, a netif selector, an
optional static IPv4 configuration, and a timeout:
lwip_socket_create(&socket, LWIP_SOCKET_TCP, LWIP_NETIF_EXT, NULL, 30000);
NULL address info means DHCP mode. LWIP_NETIF_EXT rejects loopback and
waits for USB Ethernet, link-up, DHCP address, and gateway. A non-NULL
lwip_socket_addrinfo_t applies static ip/netmask/gateway instead of
starting DHCP.
Transport selectors:
Protocol |
Meaning |
|---|---|
|
Raw TCP via lwIP |
|
UDP via lwIP |
|
ALTCP using the default TCP allocator. |
|
ALTCP wrapped in the CE TLS client path. |
The service flags are netif-level startup requests for code that needs a service without creating a socket:
Flag |
Meaning |
|---|---|
|
Start DHCP on the resident interface. |
|
Start SNTP for time sync. |
|
Make DNS name resolution available for |
These flags do not create private services per socket. lwip_socket_create()
handles DHCP/DNS automatically in DHCP mode; apps can use
lwip_request_services() for optional services such as SNTP.
Received app bytes are copied into the socket RX ring and acknowledged to lwIP
immediately. The app drains them with lwip_socket_read(). There is no pbuf
ownership or recved call in the socket API.
Use lwip_socket_shutdown() for TCP-style half-close behavior. Use
lwip_socket_close() for orderly full close. Use lwip_socket_abort() when
the socket has to be torn down immediately and lwIP should stop delivering
traffic for that PCB. Use lwip_socket_destroy() when the handle is no longer
needed.
This is a stubbed full socket shape. The callbacks are intentionally small; real applications should move parsing, state transitions, and retry decisions into their own code.
#include <lwip_init_runtime.h>
#include <lwip.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
static bool done;
static bool want_close;
static char response[128];
static size_t response_len;
static bool response_complete(void)
{
/* Replace with application-specific response framing. */
return false;
}
static void on_event(void *arg, struct lwip_socket *socket,
lwip_socket_event_type_t type,
const void *data)
{
(void)arg;
(void)data;
if (type == LWIP_SOCKET_EVENT_STATE_CHANGE &&
lwip_socket_status(socket) == LWIP_STATUS_CONNECTED) {
static const uint8_t request[] =
"GET / HTTP/1.0\r\n"
"Host: example.com\r\n"
"\r\n";
if (lwip_socket_write(socket, request, sizeof(request) - 1) != LWIP_OK) {
done = true;
}
} else if (type == LWIP_SOCKET_EVENT_IO) {
size_t space = sizeof(response) - response_len - 1;
response_len += lwip_socket_read(socket,
(uint8_t *)response + response_len,
space);
response[response_len] = '\0';
if (response_complete()) {
want_close = true;
}
} else if (type == LWIP_SOCKET_EVENT_ERROR ||
(type == LWIP_SOCKET_EVENT_STATE_CHANGE &&
lwip_socket_status(socket) == LWIP_STATUS_CLOSED)) {
done = true;
}
}
int main(void)
{
struct lwip_socket socket;
if (lwip_init_runtime() != 0) {
return 1;
}
if (!lwip_start()) {
return 1;
}
if (lwip_socket_create(&socket, LWIP_SOCKET_TCP, LWIP_NETIF_EXT,
NULL, 30000) != LWIP_OK) {
return 1;
}
lwip_socket_on_event(&socket,
LWIP_SOCKET_EVENTF_STATE_CHANGE |
LWIP_SOCKET_EVENTF_IO,
on_event);
if (lwip_socket_connect(&socket, "example.com", 80) != LWIP_OK) {
lwip_socket_destroy(&socket);
return 1;
}
while (!done) {
lwip_poll_network_events();
if (want_close && socket.status == LWIP_STATUS_CONNECTED) {
lwip_socket_shutdown(&socket);
want_close = false;
}
if (socket.status == LWIP_STATUS_CLOSED ||
socket.status == LWIP_STATUS_ERROR) {
done = true;
}
/* UI, keys, timers, and app work go here. */
}
int rc = socket.status == LWIP_STATUS_ERROR ? 1 : 0;
if (socket.status != LWIP_STATUS_CLOSED) {
lwip_socket_close(&socket);
}
lwip_socket_destroy(&socket);
return rc;
}
Choose an API Layer
Use lwip.h when the program wants the app-facing stack and socket API. It
is a root-level umbrella header in the release and includes the curated
lwip/core/*.h surface.
Use lwip/core/*.h when the program needs lower-level lwIP control at PCB or
netif level. This is the closer match for upstream lwIP examples.
Use cryptography.h when the program wants the TLS project’s crypto
primitives directly, without opening a network socket. It is a root-level
umbrella header over lwip/cryptography/*.h.
Release Layout
The public release layout is:
Path |
Purpose |
|---|---|
|
Root-level umbrella for the app-facing socket API and curated
|
|
Root-level umbrella for |
|
Lower-level curated lwIP core, netif, socket, service, and PCB headers. |
|
Lower-level public cryptographic primitive and TLS helper headers. |
|
Release export/extern assembly surface for the dynamic library. |
The calculator is not a desktop lwIP target. There is no BSD sockets layer, no filesystem-backed resolver state, no preemptive multitasking, and no async runtime. Keep application loops explicit and keep ownership of buffers obvious.