
A practical task arose: in a confined space (a narrow hallway, stairwell, or vestibule), monitor activity, take a photo of the area, and notify the user by forwarding the photos to Telegram. It is known that a standard household motion sensor, which turns on the lighting, is installed in the monitored area.
Of course, this could be achieved using the cheapest web camera available. However, in that case, we would be strictly tied to the camera’s proprietary ecosystem.
In the previous part, dedicated to creating and deploying a server for monitoring the ESP device family, we demonstrated integrating popular services within a Kubernetes cluster. The following services were involved:
Firmware esp8266 / esp32 — microcontroller firmware that publishes device status to an MQTT broker and receives configuration commands.
Mosquitto MQTT — implements the MQTT broker accepting data from the microcontrollers.
Esp-server — the core of the project, which monitors and modifies the configuration of registered microcontrollers via a client app, public API, or a self-hosted web admin interface.
iOS Client App — uses the public API for the initial configuration of ESP devices.
Workers: kworker, ai-worker, hworker — external services relative to esp-server that analyze Kafka events and notify the user accordingly (building aggregated state tables, tracking anomalous activity, forming webhooks for external notifications).
Docker — serving as the host base for the Minikube cluster.
Kafka — event-driven message store.
Kafdrop — Web UI for monitoring Kafka.
Vector — for Prometheus protocol support.
VictoriaMetrics — for time-series data storage.
Grafana — for event visualization.
Minikube Dashboard — for Kubernetes cluster administration.
Deployment and startup scripts for the K8s cluster.
This article describes expanding the cluster with the following components:
Hardware device based on the ESP32-CAM.
ESP32-CAM firmware implementing several basic scenarios.
MinIO — S3-compatible object storage for photos taken by the ESP32-CAM.
pworker — a service that extracts photo storage notifications from Kafka and generates secure access links to the file store.
N8n — automates photo processing workflows and sends images to a Telegram bot.
Telegram bot — displays the photos captured by the ESP32-CAM.
The overall data flow diagram is as follows:

Let us address the overall concept. If you are just starting out with custom hardware development, this entire setup may seem overwhelmingly redundant — especially after mentioning Kubernetes. However, ask yourself how much time and effort would be spent implementing functionality outside your main domain of responsibility. Every new feature requires diving into and building layers from business logic (which you will handle easily) to public APIs and user interfaces for external users. Development time grows exponentially. All of this must then be configured, monitored, and managed. Meanwhile, every engineer knows the feeling of having a flood of creative ideas waiting to be realized. Leveraging industrial-grade, established services frees you from inventing engineering mechanisms from scratch, allowing you to focus on creative logic. Using Kubernetes simplifies managing the entire stack — the cluster starts with a single script execution. Additionally, you are rewarded with fault tolerance: if any service crashes, it will be automatically restarted. Furthermore, you can scale the cluster across multiple independent servers as your needs grow.
Components such as the firmware, workers, client, esp-server, and startup scripts are provided as open-source code that you can use as-is or modify. The remaining services are third-party solutions that have earned reputations as industry standards, yet remain free and legal to use without requiring maintenance or debugging time on your part. At first glance, custom-tailored software might seem simpler, but in practice, that path proves far more expensive in terms of time and effort.
Hardware
The ESP32-CAM differs from standard ESP32 boards by having fewer available exposed pins — almost all of which are utilized in our project. The board used in this project features the following pinout:

Exercise caution and double-check your board’s pin layout against the silk screen, as some manufacturers offer alternative pinouts. Most pins are reserved for the camera sensor interface or the SD card slot. Even when idle, they cannot be reassigned easily. To implement our watchdog, the following pins are wired:

Right/Left orientation is defined as follows: looking at the camera sensor module, the label “ESP32-CAM” is located at the bottom of the board in an upright position.
Important hardware notes that may surprise software developers:
An external 5V power supply capable of delivering at least 1A is mandatory. Wi-Fi initialization and camera sensor operations cause current spikes that will trigger brownout resets on weaker power sources.
Because external power is used, the serial programmer connects using only RXD, TXD, and GND. DTR and 3V3 are left disconnected.
Do not power the board using the microcontroller’s 3.3V/VCC pins or the programmer’s 3V3 output, as they cannot supply sufficient current.
Prolonged storage of the ESP32-CAM module can cause the FPC connector latch for the camera ribbon cable to dry out or warp, leading to poor contact alignment.
To flash the firmware, GPIO0 must be shorted to GND before powering on or resetting, and kept shorted until flashing begins in the Serial Monitor. Unlike standard ESP32 boards, the ESP32-CAM requires manual intervention. On the back of the board, top-left, is a Reset button. Pressing Reset while GPIO0 is connected to GND enters flash mode. Once uploading starts, disconnect GPIO0 and press Reset again after completion. Adding a tactile button across GPIO0 and GND simplifies holding it down during boot.
The module gets noticeably warm. If possible, attach a small heatsink or copper thermal pad to dissipate excess heat.
Continuous camera streaming or photo capture will overheat and destroy the camera module within hours. (During testing, three hours of continuous capture rendered the camera sensor permanently non-functional while the ESP32 chip remained fine).
The board does not store images to an SD card despite having an onboard slot, freeing GPIO14 to read the photoresistor.
GPIO4 controls the bright white onboard flash LED. Keep in mind that turning it on increases thermal output and power consumption.

Firmware
Due to pin constraints, the ESP32 firmware from the “Esp-monitor” repository was customized. Source files are located under firmware/esp32-cam. Besides updating the active pin configurations sent via MQTT to esp-server, the following changes were introduced:
Added
CameraManager(CoreCamera.cpp) andCaptureManagerclasses.Added publishing functionality to the
photoMQTT topic.Added remote reading and persistent configuration storage for camera capture modes.
Added three photo capture modes:
Single photo every 5 seconds.
3-photo burst every 5 seconds.
Photo every 5 seconds for a 20-second window upon activation.
To prevent sensor overheating, photos are captured only when the photoresistor indicates sufficient ambient light (low resistance). Using the flash LED would compromise stealth. If dark capture is preferred, comment out the light check line in CoreCamera.cpp:
if (capturer.isNight()) return nullptr;
esp-server
The server core was updated with the following capabilities:
MinIO object storage integration.
Handling of the
photostopic, receiving binary image payloads, and uploading them to MinIO.Writing photo event notifications into the Kafka
photostopic.Adding a dedicated camera configuration panel to the web admin interface (rendered automatically whenever an MDNS hostname contains the
-camsuffix).
Additionally, page navigation was improved: saving configurations automatically redirects users back to the device list, streamlining multi-device management.

pworker
pworker is a specialized worker service bridging Kafka and MinIO. It monitors the photos Kafka topic for incoming events. When a new photo is logged, pworker generates a presigned URL for the object in MinIO and posts a JSON payload to an N8n webhook.
N8n downloads the image directly via Kubernetes internal DNS (http://minio-service:9000), avoiding issue with public presigned URLs inside private cluster networks. The presigned link is constrained by a short validity window (15 minutes). Like other system workers (kworker, ai-worker, hworker), pworker can be horizontally scaled across multiple instances.
Deployment
The infra.yaml manifest was updated to install MinIO and N8n during initial deployment. Cluster control scripts (en-start-cluster / ru-start-cluster) were updated to launch these services, expose their web interfaces locally, and attach a logging terminal for pworker.


MinIO
MinIO acts as the S3-compatible object store. Objects are organized under buckets using folder paths named after the source device’s SSDN identifier. This naming convention simplifies browsing and managing photos captured across multiple camera nodes via the MinIO Web Console.

Telegram
Telegram serves as the client notification channel. Before configuring N8n, perform the following setup:
Create a bot using @BotFather:
Open Telegram and search for
@BotFather.Start a conversation and send
/newbot.Follow prompts to set a name and a username ending in
_bot.Copy and save the HTTP API token provided.
Retrieve your Telegram User ID:
Search for
@userinfobotin Telegram.Start the bot to receive your account attributes.
Copy and save your numeric
id.
N8n
N8n coordinates workflow automation without custom code. Our scenario involves:
Receiving the webhook payload containing object references from
pworker.Executing an HTTP GET request to fetch binary image data from MinIO.
Forwarding the binary photo payload to the Telegram Bot node.
Future extensions could pass images to AI classification models to trigger conditional workflows (e.g., sounding alarms or unlocking access doors). The workflow file is located at k8s/n8n-esp32-photo.json. Import this JSON file into N8n upon initial launch, then configure the Telegram node with your Chat Id and bot credentials token. Finally, click Publish in the top right corner.



Important Note on N8n Workflow Execution: Modern N8n versions employ a Draft / Publishedenvironment model. Any modifications saved in the UI remain in a Draft state and will not process production webhooks from
pworkeruntil you click Publish in the upper-right corner.
Keeping this in mind saves debugging time, as users accustomed to older versions often look for an “Active” toggle switch while incoming webhooks fail to trigger draft workflows.

Starting the K8s Cluster
Clone the project repository and install Docker on the host machine.
Run
setup-k8s-cluster.shto initialize settings and pull required container images.Launch the cluster using
en-start-cluster(orru-start-cluster).On first launch, import the N8n workflow and enter your Telegram credentials.
Subsequent restarts require only executing en-start-cluster. The startup script automatically opens web interfaces for Grafana, Esp-server, MinIO, N8n, Kafdrop, and the Minikube Dashboard. Allow roughly 1 minute for all pods to transition to a green/healthy state.
Connecting ESP Devices
Initial device provisioning to your local Wi-Fi network can be performed via three methods:
iOS Application: Compile and run the iOS client (
clients/iPhone), connect to the ESP device's AP mode, and apply Wi-Fi settings.cURL Script: Connect your host Wi-Fi to the device AP (
password: 1qazxsw2) and executeclients/curl/configure.sh.REST API Client: Issue a POST request directly to the device’s default IP while connected to its AP.

Conclusion
Despite the comprehensive architectural overview, daily operation is straightforward: launching a single startup script spins up the Kubernetes cluster and microservices. When the motion-triggered light activates the watchdog, high-resolution snapshots are dispatched directly to Telegram. From there, N8n enables endless flexibility for routing, analyzing, and acting on event data without writing custom backend code.
Source code and configuration manifests are available on GitHub