IoT Edge Computing — WebAssembly, K3s and ML Inference at the Edge
In this tutorial, you'll learn about IoT Edge Computing. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
IoT Edge Computing processes sensor data locally on gateways and devices near the data source, dramatically reducing latency, bandwidth costs, and cloud dependency while enabling real-time responses for mission-critical applications.
What You'll Learn
You'll master WebAssembly-based edge sandboxing for safe third-party module execution, K3s lightweight Kubernetes for edge Orchestration, TensorFlow Lite ML inference optimization on ARM devices, secure OTA firmware updates with cryptographic signing, and edge-to-cloud sync strategies using CRDTs for disconnected environments.
Why It Matters
A single industrial facility with 10,000 sensors generating 1KB of data per second produces 864 GB of data daily. Sending all of this to the cloud costs approximately $78 per day in bandwidth alone. Edge processing filters 90 percent of data locally, sending only meaningful events. At DodaTech, edge processing patterns power the on-device threat detection engine in Durga Antivirus Pro, analyzing files locally before any cloud upload.
Real-World Use
An oil refinery monitors 5,000 vibration sensors on pumps and compressors. Cloud-only processing means a 10-second delay between sensor reading and anomaly detection — too late to prevent a catastrophic pump failure. Edge processing with TensorFlow Lite running on a local gateway detects anomalies in under 10 milliseconds and triggers an automatic shutdown within 50 milliseconds of the first abnormal reading.
Edge Computing Architecture with K3s
flowchart TD
subgraph Cloud[Cloud Layer]
CloudML[ML Training]
DeviceMgmt[Device Management]
OTA[OTA Update Service]
DataLake[(Data Lake)]
end
subgraph Edge[Edge Gateway - K3s Cluster]
K3s[K3s Kubernetes]
WASM[WasmEdge Runtime]
TFLite[TensorFlow Lite]
LocalDB[(SQLite - Edge)]
MQTT[VerneMQ Broker]
end
subgraph Devices[Device Layer]
Sensor1[Sensor Array 1]
Sensor2[Sensor Array 2]
Actuator[Actuator Controller]
end
Sensor1 --> MQTT
Sensor2 --> MQTT
MQTT --> WASM
WASM -->|Filtered Events| K3s
K3s --> TFLite
TFLite -->|Anomaly| Actuator
TFLite -->|Telemetry| CloudML
OTA -->|Signed Updates| K3s
DeviceMgmt --> K3s
WebAssembly Sandboxing at the Edge
WebAssembly provides secure, sandboxed execution for third-party edge modules without container overhead.
// wasm_edge_module.rs — compiled to .wasm for edge deployment
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct SensorProcessor {
threshold: f32,
running_avg: f32,
sample_count: u32,
}
#[wasm_bindgen]
impl SensorProcessor {
pub fn new(threshold: f32) -> SensorProcessor {
SensorProcessor {
threshold,
running_avg: 0.0,
sample_count: 0,
}
}
pub fn process_reading(&mut self, value: f32) -> String {
self.sample_count += 1;
self.running_avg += (value - self.running_avg) / self.sample_count as f32;
let z_score = if self.sample_count > 1 {
(value - self.running_avg) / 0.1
} else {
0.0
};
if value > self.threshold || z_score.abs() > 3.0 {
format!("ALERT: value={:.2}, avg={:.2}, z={:.2}",
value, self.running_avg, z_score)
} else {
format!("OK: value={:.2}, avg={:.2}", value, self.running_avg)
}
}
pub fn get_stats(&self) -> String {
format!("samples={}, avg={:.2}",
self.sample_count, self.running_avg)
}
}
Expected behavior: The WebAssembly module runs in a WasmEdge sandbox on the edge gateway with zero container overhead. Each sensor data stream gets its own sandboxed processor instance. The sandbox can access only the memory and functions explicitly provided to it, preventing one malfunctioning module from crashing the entire gateway.
K3s for Edge Orchestration
K3s is a CNCF-certified Kubernetes distribution optimized for resource-constrained edge devices.
# edge-deployment.yaml — K3s deployment for edge ML inference
apiVersion: apps/v1
kind: Deployment
metadata:
name: edge-ml-inference
namespace: edge-processing
spec:
replicas: 2
selector:
matchLabels:
app: edge-ml-inference
template:
metadata:
labels:
app: edge-ml-inference
spec:
containers:
- name: tflite-inference
image: dodatech/edge-ml:arm64-1.2.0
resources:
limits:
memory: "256Mi"
cpu: "500m"
env:
- name: MODEL_PATH
value: "/models/anomaly_detection.tflite"
- name: THRESHOLD
value: "0.85"
volumeMounts:
- name: model-storage
mountPath: /models
- name: edge-data
mountPath: /data
volumes:
- name: model-storage
hostPath:
path: /opt/edge/models
- name: edge-data
hostPath:
path: /data/edge
---
apiVersion: v1
kind: Service
metadata:
name: edge-inference-svc
spec:
selector:
app: edge-ml-inference
ports:
- port: 8080
targetPort: 8080
Edge ML Inference Optimization
import numpy as np
import tflite_runtime.interpreter as tflite
import time
class OptimizedEdgeInference:
def __init__(self, model_path: str, num_threads: int = 4):
self.interpreter = tflite.Interpreter(
model_path=model_path,
num_threads=num_threads
)
self.interpreter.allocate_tensors()
self.input_details = self.interpreter.get_input_details()
self.output_details = self.interpreter.get_output_details()
# Quantization parameters
self.input_scale = self.input_details[0]["quantization"][0]
self.input_zero_point = self.input_details[0]["quantization"][1]
def preprocess(self, raw_data: list) -> np.ndarray:
arr = np.array(raw_data, dtype=np.float32).reshape(1, -1)
# Quantize to int8 for faster inference
if self.input_scale != 0:
arr = (arr / self.input_scale + self.input_zero_point).astype(np.int8)
return arr
def predict(self, sensor_data: list) -> dict:
start = time.perf_counter()
input_tensor = self.preprocess(sensor_data)
self.interpreter.set_tensor(self.input_details[0]["index"], input_tensor)
self.interpreter.invoke()
output = self.interpreter.get_tensor(self.output_details[0]["index"])
inference_ms = (time.perf_counter() - start) * 1000
return {
"anomaly_score": float(output[0][0]),
"is_anomaly": float(output[0][0]) > 0.85,
"inference_ms": round(inference_ms, 2),
}
edge_ml = OptimizedEdgeInference("anomaly_model.tflite")
result = edge_ml.predict([2.3, 45.0, 101.2, 3200.0])
print(f"Anomaly: {result['is_anomaly']}, Score: {result['anomaly_score']:.4f}, Time: {result['inference_ms']}ms")
Expected behavior: The optimized inference pipeline processes sensor data in under 5ms on a Raspberry Pi 4 using int8 quantized TFLite models. Quantization reduces model size by 75 percent and improves inference speed by 2-3x compared to float32 models.
Secure OTA Updates
Edge devices must cryptographically verify firmware updates before applying them.
import hashlib
import hmac
import json
import requests
class SecureOTAUpdater:
def __init__(self, device_id: str, public_key_path: str):
self.device_id = device_id
with open(public_key_path, "rb") as f:
self.public_key = f.read()
def check_and_apply_update(self, update_url: str):
manifest_response = requests.get(f"{update_url}/manifest.json")
manifest = manifest_response.json()
# Verify manifest signature
expected_hash = hmac.new(
self.public_key,
json.dumps(manifest["payload"]).encode(),
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected_hash, manifest["signature"]):
print("SIGNATURE MISMATCH — update rejected")
return False
# Verify firmware hash
firmware_response = requests.get(manifest["payload"]["firmware_url"])
actual_hash = hashlib.sha256(firmware_response.content).hexdigest()
if actual_hash != manifest["payload"]["firmware_hash"]:
print("FIRMWARE HASH MISMATCH — update rejected")
return False
# Apply update
with open("/opt/edge/firmware.bin", "wb") as f:
f.write(firmware_response.content)
print(f"Update applied: version {manifest['payload']['version']}")
return True
Common Errors
1. Underpowered Edge Hardware
Running full TensorFlow models on ESP32-class MCUs with 320KB RAM. Use TFLite Micro or WebAssembly sandboxing for constrained devices. Reserve heavier ML for gateway-class hardware.
2. No Offline Fallback
Edge devices that stop working when cloud connectivity drops. Implement local storage with SQLite and sync when connectivity is restored. Use CRDTs for conflict-free synchronization.
3. Unsecured OTA Updates
Edge devices that accept firmware updates without cryptographic verification can be hijacked. Always sign updates with a hardware-backed private key and verify signatures before applying.
4. Monolithic Edge Applications
Deploying a single binary that handles sensor ingestion, ML inference, cloud sync, and actuation creates a rigid system. Use K3s to containerize each function independently for isolated updates and scaling.
5. No Local Alerting
Edge devices that rely on cloud connectivity to trigger alerts introduce latency and single points of failure. Implement local alerting that triggers actuators within 50ms of anomaly detection.
6. Ignoring Thermal Throttling
Raspberry Pi and similar SBCs throttle CPU speed at 80°C, reducing inference performance by 50 percent. Monitor CPU temperature and reduce inference frequency during thermal events.
7. Inefficient Data Filtering
Sending all sensor readings to the cloud wastes bandwidth. Implement tiered filtering — simple threshold checks first, statistical anomaly detection second, ML inference only for borderline cases.
Practice Questions
1. Why use WebAssembly for edge processing instead of containers?
WebAssembly provides faster startup (microseconds vs seconds), smaller binary size, and stronger sandboxing guarantees than containers. WASM modules have no access to the host OS, making them safe for third-party edge modules.
2. How does K3s differ from standard Kubernetes for edge deployment?
K3s removes cloud-dependent components, uses SQLite instead of etcd for state storage, and runs in under 512MB RAM. It provides a lightweight Kubernetes API compatible with standard Kubernetes tooling.
3. What is model quantization and why is it important for edge ML?
Quantization converts model weights from 32-bit floating point to 8-bit integers. This reduces model size by 75 percent, improves inference speed by 2-3x, and enables ML inference on devices without FPU hardware.
4. How do CRDTs help with edge-cloud data synchronization?
CRDTs (Conflict-Free Replicated Data Types) allow multiple nodes to make concurrent edits without coordination. When nodes reconnect, changes merge automatically without conflicts, eliminating the need for complex reconciliation logic.
5. Challenge: Design a predictive maintenance system for a wind turbine. The edge gateway receives 200 sensor readings per second (vibration, temperature, RPM, power output). Use a three-tier filtering approach: simple threshold check, z-score anomaly detection, and a quantized TFLite autoencoder for remaining useful life prediction. Send alerts locally within 100ms and daily telemetry to the cloud.
Mini Project: Edge ML Pipeline
Build a complete edge ML pipeline:
- Simulate sensor data with a Python script (10 sensors, 4 readings each, 10Hz)
- Edge gateway running K3s with a WasmEdge module for data filtering
- TFLite anomaly detection model (train a simple autoencoder on sample data)
- Local SQLite store for filtered data with 7-day retention
- Cloud sync that sends only anomaly events plus hourly aggregated telemetry
- Secure OTA mechanism that signs and verifies model updates
- Measure and report: data reduction ratio, inference latency, cloud bandwidth savings
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro