Take a Photo and Transform It

CM3

This lambda captures a still frame using a Raspberry Pi camera on a remote edge device, then runs it through a WASM based transform to convert it to grayscale, right at the source of the signal.

The On Prem CLI is used to demonstrate manually triggering the lambda and taking delivery of the image from a remote desktop.

graph LR;

cli --> control_plane;
control_plane <-- tunnel --> agent;
agent --> camera;
agent <-- transform --> grayscale_wasm;

subgraph user_edge[User Edge]
cli[CLI];
end

subgraph cloud[Cloud <small>api.on-prem.net</small>]
control_plane[Control Plane];
end

subgraph device_edge[Device Edge]
agent[Agent];
camera[Camera];
grayscale_wasm[grayscale.wasm];
end

Note that manually triggering a lambda is unusual in that it requires device connectivity to the control plane. A more typical scenario is where Lambdas and their Controller's run loops run autonomously at the device edge, regardless of the device's connectivity to the control plane.

Build the Transformer WASM Module

First follow the instructions in the WASM/Image Transforms example to build a grayscale.wasm file and deploy it as a lambda to the control plane.

Deploy the Lambda to take a photo

Next deploy a lambda that takes the photo, then hands it off to the previously deployed chained lambda that will convert it to grayscale.

# rpi_capture_image_and_transform.yaml
id: d6mqiirhedqt4h0aec10
kind: Lambda
name: rpi_capture_image_and_transform
description: >
  Capture a still frame from a Raspberry Pi camera, then transform it to grayscale.
runAt:
  device:
    deviceId: ci2fabp32ckvhk1g9qe0
chainedLambdaId: d6hl1qhkun569e9bu6v0
scriptContentType: Lua
script: >
  local M = {}

  function M.handler(event, context)
    local filename = os.tmpname()
    os.execute('rpicam-jpeg --nopreview -o ' .. filename)
    local file = io.open(filename, 'rb')
    local event = {
      data = file:read('*a'),
    }
    io.remove(file)
    return event
  end

  return M
$ onprem apply rpi_capture_image_and_transform.yaml
Uploading import request...
Import complete
Operations performed:
┌────────────────────────┬─────────┬──────┬─────────┬─────────┬────────┐
│ Object                 ┆ Records ┆ Adds ┆ Updates ┆ Deletes ┆ Errors │
│ ---                    ┆ ---     ┆ ---  ┆ ---     ┆ ---     ┆ ---    │
│ str                    ┆ u32     ┆ u32  ┆ u32     ┆ u32     ┆ u32    │
╞════════════════════════╪═════════╪══════╪═════════╪═════════╪════════╡
│ ApiKeys                ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Controllers            ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ ControllerTemplates    ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Devices                ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Facilities             ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Lambdas                ┆ 1       ┆ 1    ┆ 0       ┆ 0       ┆ 0      │
│ LambdaTemplates        ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Roles                  ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Tags                   ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
│ Teams                  ┆ 0       ┆ 0    ┆ 0       ┆ 0       ┆ 0      │
└────────────────────────┴─────────┴──────┴─────────┴─────────┴────────┘

It will now show up in the cloud console.

Cloud Console

Invoke it

The CLI can display the returned event as JSON, which is inefficient:

$ onprem run lambda d6mqiirhedqt4h0aec10
{"data":[...much raw data...]}

But it can also pluck the data field out of the returned JSON, which is efficient and does not involve JSON parsing. This is because agents return event data fields separately for efficient transport encoding.

$ onprem run lambda d6mqiirhedqt4h0aec10 --event-data-to-file out.jpeg
Wrote event[data] to out.jpeg (371.0K)

$ open out.jpeg

Photo


©2026 Megalithic LLC | Website | GitLab | GitLab (Megalithic)