Net (HTTP / TCP / UDP)

The net functions let a Lua plugin talk directly to your own server, without going through obniz Cloud. They send and receive data over the network interface that the device is currently connected through, and cover three protocols: HTTP (net.http), raw TCP (net.tcp), and UDP (net.udp).

Use them when a device needs to reach an existing API, a private endpoint, or a service that obniz Cloud does not handle.


Common Behavior and Constraints

Read this section before using any of the net functions.

  • They follow the active network interface. HTTP and TCP run over whichever interface is currently connected — Wi-Fi, Ethernet, or LTE. UDP runs over Wi-Fi or Ethernet only, and is not available over LTE.
  • They block until they finish. Each call runs synchronously and does not return until the operation completes, fails, or times out. The communication watchdog is fed during the wait, so a long transfer does not reset the device. The handler that called the function, however, stays blocked in the meantime. Avoid long transfers inside on_online_loop() or on_offline_loop(), which are expected to return quickly.
  • Plain HTTP only — TLS is not supported. Connections are unencrypted. Use http:// URLs; https:// does not work. Send sensitive data only over a network you trust.
  • Keep to one socket at a time. Open a single connection, finish with it, and close it before opening the next. Depending on available memory and the active network interface, a second socket may or may not succeed, so do not rely on holding several open at once. Always close a socket you no longer need.
  • Errors are returned, not raised. On failure, a function returns nil followed by a short error string. Check the first return value before using the result.

net.http

net.http.get(url)

Performs an HTTP GET request and waits for the full response.

Argument Type Description
url string The target URL. Begins with http://. https:// is not supported.

Returns three values on success, or nil and an error string on failure.

Return Type Description
status number The HTTP status code, such as 200.
body string The response body.
headers string The raw response headers.

net.http.post(url, body, contentType)

Performs an HTTP POST request with the given body.

Argument Type Description
url string The target URL.
body string The request body to send.
contentType string Optional. The Content-Type header. Defaults to application/octet-stream.

The return values are the same as net.http.get().

net.http.request(options)

Performs a request with full control over the method, headers, body, and timeout. options is a table.

Field Type Description
url string Required. The target URL.
method string Optional. The HTTP method. Defaults to GET.
headers table Optional. Request headers, as { ["Key"] = "Value" } or { "Key: Value" }.
body string Optional. The request body.
timeout number Optional. The time to wait, in milliseconds. Defaults to 30000.

The return values are the same as net.http.get().

HTTP constraints

The HTTP client is intentionally small, so be aware of the following limits.

  • It sends Connection: close and reads until the server closes the connection. Keep-alive is not used.
  • It does not follow redirects. A 3xx response is returned as is.
  • It does not decode chunked transfer encoding or decompress gzip. The body is returned exactly as received, so request an uncompressed, unchunked response when you can.
  • The host name must be shorter than 128 characters.

net.tcp

net.tcp.connect(host, port)

Opens a plain TCP connection and returns a socket id. TLS is not supported.

Argument Type Description
host string The host name or IP address.
port number The port number.

Returns the socket id on success, or nil and an error string on failure. Pass the id to the other net.tcp functions.

net.tcp.write(id, data)

Sends data over the connection. The call returns once all of the data has been sent, and times out after 30 seconds.

Argument Type Description
id number The socket id from net.tcp.connect().
data string The data to send.

Returns the number of bytes written, or nil and an error string on failure.

net.tcp.read(id, maxlen)

Reads data that has already arrived. This call does not wait for new data.

Argument Type Description
id number The socket id.
maxlen number Optional. The maximum number of bytes to read. Defaults to 1024, with an upper limit below 4096.
Return Type Description
data string | nil The received data. An empty string ("") means no data has arrived yet. nil means the connection has closed or an error occurred.

Because read returns immediately, poll it across loop iterations rather than expecting a full message in a single call.

net.tcp.close(id)

Closes the connection and releases the socket.


net.udp

UDP runs over Wi-Fi or Ethernet only. It is not available over LTE: on a cellular connection, net.udp.open() fails with udp not supported on cellular.

net.udp.open(localPort)

Opens a UDP socket and returns its id.

Argument Type Description
localPort number Optional. The local port to bind to. When omitted, the system assigns one.

Returns the socket id on success, or nil and an error string on failure.

net.udp.sendTo(id, host, port, data)

Sends a datagram to the given destination.

Argument Type Description
id number The socket id.
host string The destination host name or IP address.
port number The destination port.
data string The datagram payload.

Returns the number of bytes sent, or nil and an error string on failure.

net.udp.receive(id, maxlen)

Receives one datagram. This call does not wait; it returns nil when no datagram is waiting.

Argument Type Description
id number The socket id.
maxlen number Optional. The maximum number of bytes to read. Defaults to 1024, with an upper limit below 4096.
Return Type Description
data string | nil The received payload, or nil when nothing is waiting.
ip string The sender's IP address.
port number The sender's port.

net.udp.close(id)

Closes the socket.


Example: Posting an Analog Reading Every Minute

The following plugin reads the voltage on IO0 once a minute and sends it directly to your own server as JSON over plain HTTP. It runs entirely on the device and does not go through obniz Cloud.

last = 0;

function on_online_loop()
  -- Run once every 60 seconds
  if os.getTick() - last < 60 * 1000 then
    return;
  end
  last = os.getTick();

  -- Read the analog voltage on IO0
  local voltage = ad.get(0);
  local body = '{"voltage":' .. tostring(voltage) .. '}';

  -- POST it to your own server
  local status, resp = net.http.post("http://example.com/api/measurements", body, "application/json");

  if status == nil then
    -- On failure, the second value holds the error string
    os.log("post failed: " .. tostring(resp));
    return;
  end

  os.log("posted, status: " .. tostring(status));
end