Hacking The Jetson
Updated:
Danger! This is a Work in Progress article. Content and code are updating frequently until this notice is removed.
I bought an NVIDIA Jetson Orin Nano developer kit, dropped it into a Yahboom CubeNano case, and then spent a few weeks turning it into something between a desk toy and a real edge-AI node. It runs a MiNiFi C++ agent that reports into Cloudera Edge Flow Manager and produces to Kafka. It has a tiny OLED on the case that I hacked into a sci-fi strobe. It falls into a Matrix digital-rain screensaver when it’s idle. And it answers HTTP requests that launch things on its own physical display. This post is the whole build — the box, the hacks, the edge-AI plumbing, what broke, and what I’d do next. Every path, port, bus number, and I2C address below is real and taken off the live device (hostname tunastreet, an ARM Cortex-A78AE / Ampere Jetson Orin Nano on Ubuntu 24.04, JetPack/L4T R39).

This is the build post. The deep enterprise integration — persisted EFM on Kubernetes, MiNiFi binaries for Windows/Linux/Jetson, the full CSO stack next to it, model metrics into Prometheus — lives in its own companion writeups linked at the end. Here I’m showing you the box and everything I bolted onto it.
The hardware
Two parts stack up on my desk:
- NVIDIA Jetson Orin Nano Super Developer Kit — the compute, and the reason this whole thing is worth doing. 67 TOPS (Sparse INT8), a 1024-core Ampere GPU with 32 Tensor Cores, a 6-core Arm Cortex-A78AE CPU up to 1.7 GHz, 8GB of 128-bit LPDDR5 at 102 GB/s, in a 7W–25W envelope. NVIDIA lists it at $249. That’s a data-center-shaped AI stack on a board that draws less than a light bulb — plenty to run MiNiFi, drive a display, and front a nearby inference server at the same time.
- Jetson MINI Cube Nano case — the case (supports Jetson Nano / Orin Nano / Orin NX / Xavier NX / TX2 NX), with a 40-pin passthrough, a fan, and a small 128×32 SSD1306 OLED on the header. The OLED is the thing that makes this build fun.
I also tried a Waveshare Environment Sensor module on the same header — it didn’t make the cut, and the short version of why is in The environment sensor I didn’t ship below.
Confirmed parts. Jetson Orin Nano Super Developer Kit — NVIDIA, ASIN B0BZJTQ5YP, $249, 67 TOPS. Jetson MINI Cube Nano case — ASIN B0GDFTZ64S.
Hack #1 — the CubeNano OLED, from stats display to CORDY CEPT strobe
The CubeNano case puts a 128×32 SSD1306 OLED on the 40-pin header, driven over I2C at /dev/i2c-7, address 0x3c. Yahboom ships a stats loop for it. Mine lives in ~/CubeNano/oled.py: it auto-probes I2C buses [1, 0, 7, 8] until it finds the panel, then writes four lines — CPU%, time, RAM, disk, IP — refreshing about 10× a second, managed by a systemd unit (yahboom_oled.service, enabled so it survives reboot).
That’s the baseline. On top of it I built a second display mode: a full-screen black/white strobe at 0.25s intervals with bold, letter-spaced sci-fi text reading C O R D Y / C E P T in the Orbitron font. That’s ~/CubeNano/oled_strobe.py.
Symptom: an OS update wiped the strobe
I ran an apt batch that pulled a glibc upgrade, which needs a reboot to take. Rebooted — and the OLED came back showing the plain CPU/RAM/IP stats screen, not my CORDY strobe.
First instinct is “did I kill the panel?” I ruled out hardware in 30 seconds: i2cdetect -y -r 7 still showed 0x3c acking, and a live Adafruit_SSD1306.begin() against bus 7 succeeded with no exceptions. The panel and bus were never the problem.
Diagnosis: I never made the strobe survive a reboot
yahboom_oled.service is the only thing wired into systemd, and it runs oled.py (the stats display). oled_strobe.py I had only ever started by hand with python3 oled_strobe.py &. A bare & backgrounds a process in the current shell’s process group — which gets torn down when that shell exits. The reboot killed it, and only the systemd-managed stats display came back. Nothing to do with glibc; any reboot would have done it.
Fix: detach it, then give it its own systemd unit
Immediate fix — kill the stats process and start the strobe fully detached so it survives the shell:
pkill -f "python3 /home/tunastreet/CubeNano/oled.py"
cd ~/CubeNano
setsid nohup python3 -u oled_strobe.py > /tmp/oled_strobe.log 2>&1 < /dev/null &
setsid is the part that matters — it puts the process in its own session so it doesn’t die with the launching shell. But that’s still just a background process; the next reboot blanks it again. The real fix is a dedicated systemd unit and retiring the stats one so they can’t fight over the same OLED on boot.
~/CubeNano/cordy_oled.service:
[Unit]
Description=cordy_oled strobe service
After=multi-user.target
[Service]
Type=idle
User=tunastreet
ExecStart=/bin/sh -c "python3 /home/tunastreet/CubeNano/oled_strobe.py"
WorkingDirectory=/home/tunastreet
Restart=on-failure
RestartSec=2
[Install]
WantedBy=multi-user.target
Install it and disable the stats unit in one pass (~/CubeNano/install_cordy_oled_service.sh):
sudo systemctl disable --now yahboom_oled.service
pkill -f oled_strobe.py
sudo cp /home/tunastreet/CubeNano/cordy_oled.service /etc/systemd/system/cordy_oled.service
sudo systemctl daemon-reload
sudo systemctl enable --now cordy_oled.service
To flip back to the stats display:
sudo systemctl disable --now cordy_oled.service
sudo systemctl enable --now yahboom_oled.service
I keep a few other renders in ~/CubeNano/ for the same panel: oled_cordy.py (static two-line CORDY/CEPT), oled_cool.py (a typewriter-reveal “WAKE UP… THE MATRIX HAS YOU…” boot message), and oled_basic_test.py (raw black/white/black sanity check to confirm the panel is healthy).
Don’t persist a display process with a bare &. It looks alive right up until the next reboot or logout, then it’s gone with no error anywhere to point at. If you want it to survive, it needs setsid (for the current session) and ultimately a systemd unit (for boot). And before you assume a blanked OLED is dead hardware, check whether the right process is even running — i2cdetect plus a live begin() is 30 seconds that saves you pulling the board off the header.
sudo needs a real TTY. Every install step here is wrapped in a bash ~/CubeNano/install_*.sh one-liner on purpose — the automation driving this box can’t type a sudo password into an interactive prompt, so the sudo calls get committed into a script and run in one shot. Same pattern shows up everywhere on this device.
Hack #2 — a Matrix digital-rain screensaver, done without xscreensaver
When the Jetson sits idle for two minutes, its monitor falls into a full-screen Matrix-style falling-code effect (binary 0/1, canvas-based, with a small glowing clock in the corner). It’s a self-contained HTML file rendered by Chromium in kiosk mode, driven by a standalone idle-watcher — deliberately not xscreensaver.
Why not xscreensaver
I started with xscreensaver’s programs: hack mechanism to launch Chromium. It was janky. xscreensaver hacks are supposed to render into a window ID that xscreensaver hands them so they stay correctly stacked. Chromium’s kiosk mode always opens its own independent top-level window, so it rendered underneath xscreensaver’s black saver window. The screen just looked black, and moving the mouse flashed the scene for a moment right before xscreensaver killed the child.
So I ditched the hack model. xscreensaver is stopped, ~/.xscreensaver has mode: off, and a systemd user service polls real idle time and manages Chromium directly — no competing window ever gets created.
The pieces
-
~/matrix-screensaver.html— the scene, self-contained, no network calls, works as afile://URL. -
~/.local/bin/lofi-idle-watcher.sh— pollsxprintidle; once idle crosses the threshold it launches Chromium kiosk at the HTML file and force-fullscreens the window. Kills Chromium the moment activity resumes. -
~/.config/systemd/user/lofi-idle-watcher.service— runs the watcher as a persistent user service; inheritsDISPLAY/XAUTHORITYfrom the systemd user environment.
Current idle threshold is 2 minutes (IDLE_THRESHOLD_MS in the watcher).
The rain itself is written to be cheap on the Nano’s GPU. An early version redrew a 90-character trail every frame across ~200 columns — roughly 15k fillText calls per frame — and visibly bogged down. The current version draws exactly one new glyph per column per frame and lets a very faint semi-transparent black overlay (rgba(0,0,0,0.035)) fade the trail. That’s the actual technique behind the original effect, and it’s roughly 60× cheaper per frame while still producing a long, slow-fading trail. Runs at a fixed 24 FPS via setInterval (not requestAnimationFrame) to keep the draw rate cheap and independent of monitor refresh.
Three failure modes worth knowing (all fixed, all verified)
Chromium under kiosk mode fought me three separate ways, and every fix is worth carrying to any Chromium-kiosk build:
-
Silent failure to launch — no window, no error. Chromium was using its default shared snap profile. Every kill was an abrupt
pkill, which could leave a staleSingletonLockpointing at a dead PID. On the next launch Chromium tried to hand the URL to what it thought was a live instance over a dead socket, failed silently, and just exited. Fix: give the kiosk its own isolated--user-data-dir(~/snap/chromium/common/lofi-screensaver-profile) and clear staleSingleton*files before each launch. The profile dir has to live somewhere the snap confinement can write —~/snap/chromium/common/works,/run/user/1000/does not (Chromium throws a “Failed To Create Data Directory” dialog storm and silently falls back to the shared profile). -
Stacked/duplicate windows.
stop_saver()matched the kill on the target URL (pkill -f matrix-screensaver.html) — but that string only appears in the top-level browser process’s argv, not its zygote/renderer/gpu children. So a kill removed only the parent; the children and their window survived, and the next tick launched a second instance on top. Fix: match/kill on--user-data-dir=$PROFILE_DIRinstead — every process in Chromium’s tree carries that flag, so onepkill -ftakes down the whole tree. -
Window comes up the wrong size. Chromium’s own
--kiosk/--start-fullscreen/--window-position=0,0flags don’t reliably get Mutter to grant real X11 fullscreen — the window came up at, say,1854×1011at80,118instead of1920×1080at0,0. Fix: after launch, poll for the window and force_NET_WM_STATE_FULLSCREENwithwmctrl. The poll has to be patient — right after a reboot, or with apt/snap updates running, Chromium can take 7+ seconds to create its window, well past a naive 5-second timeout. I extended the poll loop to up to a minute (240 × 0.25s).
To tweak it (glyph size/speed/color, or the idle threshold) edit the HTML or the watcher script, then:
systemctl --user restart lofi-idle-watcher.service
The edge-AI plumbing — MiNiFi C++ + Cloudera EFM + Kafka
This is where the Jetson stops being a toy. It runs a MiNiFi C++ agent (nifi-minifi-cpp-1.26.02, agent class NvidiaNano) that reports into a remote Cloudera Edge Flow Manager for centralized flow management, and produces data to Kafka. EFM and Kafka run on my Windows desktop across the LAN; the Jetson enrolls over the network and shows up in the EFM UI as a manageable agent.
Install layout on the device:
- Install dir:
/home/tunastreet/nifi-minifi-cpp-1.26.02, binarybin/minifi - Managed by systemd (
minifi.service, enabled, starts on boot) - Config in
conf/:minifi.properties(C2/EFM settings),minifi.properties.d/drop-ins, andconfig.yml(the actual flow definition)
EFM (C2) connection:
- Server:
http://192.168.1.121:10090/efm/api(heartbeat at.../c2-protocol/heartbeat) - Agent class
NvidiaNano, heartbeat period 5000ms - Kafka bootstrap
192.168.1.121:31623, producer client idminifi-agent-nvidia
Health check from the device:
systemctl status minifi --no-pager
tail -f /home/tunastreet/nifi-minifi-cpp-1.26.02/logs/minifi-app.log | grep -i "heartbeat\|kafka"
# port reachability, no nc needed
timeout 3 bash -c "cat < /dev/null > /dev/tcp/192.168.1.121/10090" && echo open || echo closed
timeout 3 bash -c "cat < /dev/null > /dev/tcp/192.168.1.121/31623" && echo open || echo closed
This build carries a few extra C++ extensions staged into it — execute-process, lua-script, python-script, opc, and llamacpp — which pushes it to 79 processors versus the stock 74. The python-script and llamacpp extensions are the ones that make “AI at the edge” real: you can run Python inside the agent, or point it at a local model.
A remote EFM/Kafka restart has a recognizable signature — don’t chase it. When I restart the stack on the Windows side, the Jetson’s minifi-app.log fills with Kafka Connection refused on every broker, sometimes with a "verify that security.protocol is correctly configured" hint. That hint is noise from the broker bouncing, not an auth change. It settles on its own within ~15–20 minutes of the ports reopening. No Jetson-side action needed.
For the full install story — persisted EFM on Kubernetes, staging the Windows/Linux/Jetson binaries, the WSL2 mirrored-vs-NAT networking that lets the Jetson reach EFM across the LAN — see the companion posts linked at the end. Here I care about what the agent does.
Python over HTTP — making the edge box do things on command
The most fun endpoint on this Jetson: POST :8081/streamChatListener with a body of {"streamer":"<name>"} opens Chromium to https://www.twitch.tv/<name> on the Jetson’s physical display, fullscreen. It’s a MiNiFi ListenHTTP processor feeding an ExecuteScript that runs launch_stream.py. Simple idea. It took four stacked bugs to get right, and each one is a lesson.
Bug 1 — ListenHTTP dropped the request before Python ever saw it
The endpoint returned an HTTP response but Chromium never opened. The log had the answer:
[warning] ListenHTTP buffer is NOT full 1/5, 'POST' request for
'/streamChatListener' uri was dropped
I’d copy-configured the listener from another endpoint with Batch Size: 5 / Buffer Size: 5. MiNiFi won’t turn a request into a FlowFile until the buffer fills — so a single test POST just sat there and got dropped. The endpoint I copied from “worked” only because it gets hammered with back-to-back requests that fill the buffer. Fix: Batch Size: 1 / Buffer Size: 1, pushed via EFM.
Bug 2 — an XAUTHORITY placeholder that was never filled in
The script had a literal, angle-brackets-and-all placeholder:
env["XAUTHORITY"] = "/home/<jetson-desktop-user>/.Xauthority" # fill in real desktop user
That path never existed. The real values, pulled from the live GNOME session’s own process environment:
env["DISPLAY"] = ":0"
env["XAUTHORITY"] = "/run/user/1000/gdm/Xauthority"
Bug 3 — the agent ran as root, the desktop is uid 1000
ps showed minifi running as root (no User= in the unit), while the GNOME/X11 session belongs to tunastreet (uid 1000). Root’s environment has no XDG_RUNTIME_DIR or D-Bus session address, which snap-confined Chromium needs. Fix: add User=tunastreet to the systemd unit. That immediately caused a follow-on: the service crash-looped with Failed opening file .../minifi-app.log for writing: Permission denied, because the logs and RocksDB state dirs had been created root:root while the agent ran as root. sudo chown -R tunastreet:tunastreet /home/tunastreet/nifi-minifi-cpp-1.26.02 and a restart brought it up clean.
Bug 4 — Chromium opened but wouldn’t go fullscreen
Same failure mode as the screensaver: Chromium’s own fullscreen flags don’t get Mutter to grant real X11 fullscreen. The window came up at 912×991 at 88,122 on a 1920×1080 screen. The fix has the same shape as the screensaver’s — after launch, background a detached poller that waits (up to 240 × 0.25s) for a window title ending in " - Twitch - Chromium", then wmctrl -r "<title>" -b add,fullscreen, then xdotool clicks the video center and sends f to trigger Twitch’s own player fullscreen. Two things I learned live:
- The poll-and-fullscreen step must be a backgrounded/detached subprocess, not inline in
onTrigger. MiNiFi’sExecuteScriptruns on a single shared thread — a blocking wait of up to a minute would stall the whole flow (heartbeats, Kafka, the other listener). - Forcing fullscreen via
wmctrlbypasses Chromium’s own fullscreen toggle, so F11/Esc won’t undo it. If a test window gets stuck fullscreen, the escape hatch iswmctrl -r "<title>" -b remove,fullscreen, not the keyboard.
xdotool wasn’t installed on this Jetson (only wmctrl was) — sudo apt-get install -y xdotool fixed that. Verified end to end: curl -X POST :8081/streamChatListener -d '{"streamer":"xqc"}' → HTTP 200 → a real fullscreen Chromium window on the Jetson’s display, both window-manager and Twitch-player fullscreen applied, no manual steps.
ListenHTTP on MiNiFi C++ is fire-and-forget. There’s no HandleHttpRequest/HandleHttpResponse pair — that’s Java-NiFi only. The caller gets its response the instant the request lands, before your script runs. Design for async: don’t wait on the HTTP response for the result of the work. My “How to AI with MiNiFi” post goes deep on this and on the two different Python paths (ExecuteScript vs ExecutePythonProcessor) that trip everyone up.
The environment sensor I didn’t ship
I wanted the Jetson to read its own environment — temp, humidity, pressure, light, UV, motion, air quality — so I tried a Waveshare Environment Sensor module (I2C, with its own 1.3” OLED) on the same header. The four core sensors — BME280 (temp/humidity/pressure), TSL2591 (light), LTR390 (UV), ICM20948 (9-DOF IMU) — all read fine over I2C bus 7. The board’s onboard OLED never did: address 0x3C returned a hard I2C NACK on every driver and every probe, so zero bytes ever reached the chip. A replacement unit through Waveshare’s RMA showed the identical symptom out of the box. Two dead OLEDs on two boards is a hardware pattern, not a config problem — so I returned both and left the sensor out of this build.
The full diagnostic trail — schematic reading, the Python-2-to-3 driver patches, and every ruled-out theory — is kept in my internal notes in case a future board on this SKU shows the same symptom. It’s a dead end for this build, not a mystery worth re-solving.
What’s next
The build isn’t done. The direction it’s going:
- Environment data into the flow. Wire a working I2C sensor into a MiNiFi Python processor and produce readings to Kafka alongside everything else — the Jetson reporting its own room conditions to the same stack it manages flows from. This is the piece the Waveshare board was meant to fill; I’ll pick a different sensor.
- A buzzer and more I2C peripherals on the header for physical alerts driven by flow conditions.
-
Robotics and messaging. App-to-robot messaging over Telegram, a robot camera streamed to Twitch, live-streaming robots — the
streamChatHTTP-to-display pattern already proves the shape: an HTTP endpoint that makes the physical device do something. - Metrics and analytics. System + processor + model metrics from the edge agent into the Prometheus instance inside the CSO stack, the same way the enterprise integration post does it for the cluster.
-
Bigger local models. The
llamacppextension is already staged in this agent. The natural next step up the ladder is a real local-AI workstation (an NVIDIA DGX Spark class box) for the heavy inference, with the Jetson as the always-on edge node in front of it.
What NOT to do — the traps in one place
-
Don’t persist a display process with a bare
&. It survives until the next reboot/logout, then vanishes with no error. Usesetsid, then a systemd unit. -
Don’t assume a blank OLED is dead hardware before checking whether the right process is running and whether the address ACKs (
i2cdetect -y -r 7). The converse also holds: a hard NACK on every driver — like the Waveshare OLED — really is dead, and no software fixes that. -
Don’t copy a
ListenHTTPconfig withBuffer Size > 1onto an endpoint that gets single requests — MiNiFi drops the request until the buffer fills. -
Don’t run the agent as root against a user’s desktop session. Set
User=to the desktop uid, and fix the log/state dir ownership afterward. -
Don’t block inside
ExecuteScript— its thread is shared with the whole flow. Background anything that waits. -
Don’t force fullscreen with
wmctrland expect Esc to undo it — usewmctrl ... -b remove,fullscreen.
Terminal History
The recipe above is the clean path. Here’s the real trail off the device — the actual commands, in roughly the order I ran them, wrong turns and all. Every path, port, and bus number is live.
# --- CubeNano OLED: is the panel even there? ---
i2cdetect -y -r 7 # 0x3c ack = SSD1306 present on bus 7
python3 ~/CubeNano/oled.py debug # init prints for the stats loop
python3 ~/CubeNano/oled.py clear # blank it
# --- the CORDY strobe, and why it kept vanishing on reboot ---
python3 oled_strobe.py & # <- the mistake: bare & dies with the shell on reboot
# after a glibc-upgrade reboot the stats screen came back, not the strobe. ruled out hardware first:
i2cdetect -y -r 7 # 0x3c still acks -> panel/bus fine, it was the process
pkill -f "python3 /home/tunastreet/CubeNano/oled.py"
cd ~/CubeNano
setsid nohup python3 -u oled_strobe.py > /tmp/oled_strobe.log 2>&1 < /dev/null & # setsid = survives the shell
# real fix: give the strobe its own unit, retire the stats one (sudo scripted - no TTY for the password)
bash ~/CubeNano/install_cordy_oled_service.sh
systemctl is-active cordy_oled.service ; systemctl is-enabled cordy_oled.service
# --- Matrix screensaver (systemd USER service, not root) ---
systemctl --user status lofi-idle-watcher.service
systemctl --user restart lofi-idle-watcher.service # after editing the html or IDLE_THRESHOLD_MS
# --- MiNiFi C++ agent health ---
systemctl status minifi --no-pager
tail -f /home/tunastreet/nifi-minifi-cpp-1.26.02/logs/minifi-app.log | grep -i "heartbeat\|kafka"
# EFM + Kafka live on the Windows desktop across the LAN - check the ports, no nc needed:
timeout 3 bash -c "cat < /dev/null > /dev/tcp/192.168.1.121/10090" && echo open || echo closed # EFM
timeout 3 bash -c "cat < /dev/null > /dev/tcp/192.168.1.121/31623" && echo open || echo closed # Kafka bootstrap
# note: a burst of Kafka "Connection refused" right after a remote restart is NOT an auth problem - it settles in ~15-20 min
# --- native Prometheus publisher on the edge (port 9936 on the Nano itself) ---
ss -tlnp | grep 9936
curl -s http://127.0.0.1:9936/metrics | wc -l # 204 lines = publisher up
sudo systemctl restart minifi # only reliable apply path - needs the interactive sudo password
# --- streamChat: HTTP -> Chromium -> Twitch on the physical display ---
sudo apt-get install -y xdotool # only wmctrl was installed; the fullscreen fix needs both
curl -X POST http://localhost:8081/streamChatListener -d '{"streamer":"xqc"}'
ps -ef | grep chromium # confirm it launched as tunastreet, not root
wmctrl -l # find the "<name> - Twitch - Chromium" window
wmctrl -r "xQc - Twitch - Chromium" -b add,fullscreen
wmctrl -r "xQc - Twitch - Chromium" -b remove,fullscreen # <- ESCAPE HATCH: Esc/F11 won't undo a wmctrl fullscreen
# --- Waveshare env sensor: the OLED that never ACKed (dropped from the build) ---
i2cdetect -y -r 7 # sensors at 0x29/0x53/0x68/0x76 answer; 0x3c NACKs every time
# same hard NACK on the vendor driver, on luma.oled, and on a raw i2cget - and identical on the RMA unit -> DOA, returned
Appendix
The reusable operational pieces, grouped by purpose. These are the “your exact command” forms — copy-paste ready.
1. Install the CORDY strobe as a service, retire the stats display
sudo systemctl disable --now yahboom_oled.service
pkill -f oled_strobe.py
sudo cp /home/tunastreet/CubeNano/cordy_oled.service /etc/systemd/system/cordy_oled.service
sudo systemctl daemon-reload
sudo systemctl enable --now cordy_oled.service
Flip back to the stats display:
sudo systemctl disable --now cordy_oled.service
sudo systemctl enable --now yahboom_oled.service
2. MiNiFi agent service control
# minifi.sh (preferred) — both the script and systemctl work
sudo /home/tunastreet/nifi-minifi-cpp-1.26.02/bin/minifi.sh start
sudo /home/tunastreet/nifi-minifi-cpp-1.26.02/bin/minifi.sh stop
sudo /home/tunastreet/nifi-minifi-cpp-1.26.02/bin/minifi.sh restart
sudo /home/tunastreet/nifi-minifi-cpp-1.26.02/bin/minifi.sh status
sudo systemctl restart minifi # the only reliable apply-a-config-change path
systemctl status minifi --no-pager
3. Full clean reinstall of the MiNiFi agent
# remove completely
sudo pkill -9 minifi
sudo systemctl stop minifi 2>/dev/null
sudo systemctl disable minifi 2>/dev/null
sudo rm -f /usr/local/lib/systemd/system/minifi.service
sudo systemctl daemon-reload
sudo rm -rf /home/tunastreet/nifi-minifi-cpp-1.26.02
rm -rf ~/.cache/minifi ~/.config/minifi ~/.local/share/minifi
sudo rm -rf /var/lib/minifi 2>/dev/null
# reinstall (after re-extracting the tarball to the same path)
sudo /home/tunastreet/nifi-minifi-cpp-1.26.02/bin/minifi.sh install
# run as the desktop user (uid 1000), not root — add `User=tunastreet` to the unit, then fix ownership
sudo chown -R tunastreet:tunastreet /home/tunastreet/nifi-minifi-cpp-1.26.02
sudo systemctl daemon-reload
sudo systemctl restart minifi
4. Native Prometheus publisher on the edge agent
conf/minifi.properties.d/95-metrics.properties (drop-in — don’t edit minifi.properties directly):
nifi.metrics.publisher.agent.identifier=4ca82a0d-8e04-4ede-b59d-379de1495f2b
nifi.metrics.publisher.class=PrometheusMetricsPublisher
nifi.metrics.publisher.PrometheusMetricsPublisher.port=9936
nifi.metrics.publisher.metrics=QueueMetrics,RepositoryMetrics,DeviceInfoNode,FlowInformation
sudo systemctl restart minifi
ss -tlnp | grep 9936
curl -s http://127.0.0.1:9936/metrics | wc -l
5. Trigger streamChat manually, and the fullscreen escape hatch
curl -X POST http://localhost:8081/streamChatListener -d '{"streamer":"xqc"}'
wmctrl -l # find the window title
wmctrl -r "xQc - Twitch - Chromium" -b add,fullscreen # force it fullscreen
wmctrl -r "xQc - Twitch - Chromium" -b remove,fullscreen # undo — Esc/F11 will NOT
NVIDIA Jetson developer resources
- NVIDIA Jetson Orin family — the Orin Nano Super Developer Kit specs live here
- JetPack SDK (Linux for Tegra / L4T)
- NVIDIA Jetson developer forums and Jetson developer resources
Companion Posts
- How to AI with NiFi and Python — Custom Python Processor success with AI with examples
- Edge to AI For Dummies — Go grab a copy of this and give it a read
- Cloudera Edge Flow Manager on Kubernetes — the deep enterprise integration: persisted EFM on Kubernetes next to the full CSO stack, model execution inside MiNiFi, metrics into Prometheus.
Hacking The Jetson
If you would like a deeper dive, hands on experience, demos, or are interested in speaking with me further about Hacking The Jetson please reach out to schedule a discussion.