cd /news/artificial-intelligence/nishi-daak-an-arduino-uno-q-that-cal… · home › topics › artificial-intelligence › article
[ARTICLE · art-160743] src=dev.to ↗ pub= topic=artificial-intelligence verified=true sentiment=↑ positive

Nishi Daak: an Arduino UNO Q that calls your name twice when you come home late (local Gemma, no cloud)

A developer built Nishi Daak, an Arduino UNO Q project that detects a resident's return via a Bluetooth presence scanner and then spells the person's name twice on an LED matrix, mimicking the Bengali Nishi spirit that calls only twice. The board runs Gemma 3 1B locally through llama.cpp's llama-server as a systemd service, generating short folk-horror tales at roughly 4.7 tokens per second with no cloud, accounts or API keys, and logs each event with timings in a web page served from the board on port 7000.

by read7 min views1 publishedOct 30, 2026

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine

In Bengal they say that late at night, someone may call your name from outside in the voice of a person you love. Do not answer. Not the first time, not the second. The Nishi (নিশি, "the night one") only ever calls twice, and whoever answers follows it into the dark and is never seen again. A real person, the story goes, will always call a third time.

Nishi Daak ("the night call") puts that spirit on a shelf. It is an Arduino UNO Q that:

No cloud, no accounts, no API keys. Nothing leaves the house.

The night call: the name, letter by letter, twice. Never a third time.

🎬 Watch the 74-second demo video (Google Drive)

The human scenes in the video are free stock footage from Pexels (a Kolkata evening, a walk home, a door at night). The board's behaviour, the timings and the numbers come from the real build. The tale typed out in the video is the few-shot example from the prompt; real ones from the board's night log are below.

This is the board's own web page, screenshotted live from the UNO Q on my home Wi-Fi. The eyes and the "2" show the spirit energy (5 Bluetooth devices nearby), the night log holds real tales Gemma wrote on the board, and the footer is the board's health check: presence ok, Gemma 3 1B ok, last reply 15 s at 4.7 tokens/s.

Three real entries from night_log.json, written by Gemma 3 1B on the board:

A single, crimson leaf drifted down the path, landing softly beside his feet. It mirrored a forgotten melody, a scent of rain and old roses. The Nishi smiles, a fleeting glimmer in the gloom.

(10.0 s, 4.7 tokens/s)

A single jasmine flower, bruised and drooping, drifted past the doorway. The rain smelled of dust and regret, a faint, silver light reflecting on it. It d, a single tear falling: a beautiful sorrow, like a forgotten song.

(15.0 s, 4.7 tokens/s)

A single, pale moonbeam traced a path down the stairs. It watched a small, worn book open. A faded map, perhaps? The Nishi smiles, a fleeting shadow.

(12.6 s, 4.7 tokens/s)

Each log entry records the time the board caught the phone. Tap the small red icon next to a name (a flask for a test call, Wi-Fi for a real one) and a card shows how it was caught, how long the person was away, and how fast Gemma wrote the tale.

It calls your name twice. Never answer.

An Arduino UNO Q that lives on a shelf and behaves like a Nishi, the night spirit of Bengali folklore. Built for the DEV Hacktoberfest Open-Source AI Challenge, Week 4 ("Ghost in the Machine"), Arduino category.

In Bengal they say the Nishi comes late at night and calls your name in the voice of someone you love. It calls only twice. If you answer, you follow it into the dark and are never seen again. So if you hear your name at night, wait for the third call: a real person will always call a third time.

Part File Runs on
Matrix eyes and planchette sketch/sketch.ino the UNO Q's STM32 microcontroller
App logic and web API python/main.py App Lab container on the Linux side
Web page (séance, night log) assets/index.html App Lab web_ui brick, port 7000
Presence scanner host/presence_service.py Debian host, systemd
The spirit's voice llama.cpp llama-server +gemma-3-1b-it Q4_K_M Debian host, systemd

The UNO Q is two computers on one board: an STM32 microcontroller that drives the LED matrix, and a quad-core Linux side (4 GB of RAM on mine) running Debian. App Lab connects them with a Bridge, so Python on the Linux side can call Bridge.call("spell", "TUSHAR TUSHAR") and the sketch fades the letters in and out on the matrix.

llama.cpp builds on the board itself (47 minutes with -j2; 3.6 GB of RAM was enough). llama-server runs as a systemd service with Gemma 3 1B instruct, Q4_K_M (a 769 MB file):

What Measured on the board
Generation speed 4.7 to 4.9 tokens/s, 4 threads
One séance answer 8.5 to 9.1 s
A night tale 10 to 15 s
First reply after a restart 38.8 s (it processes the persona once)

A 1B model does not follow rules well, but it copies examples beautifully. So instead of a long list of "never do X", the prompt is a short persona plus two few-shot turns: one night tale and one dodged request for advice. A small tidy() pass then strips markdown, emojis and any "as an AI" talk, and keeps at most three sentences, so whatever Gemma says always fits the spirit. If Gemma is down, the Nishi falls back to a canned line, so the call never fails.

From the demo video: the moment the phone rejoins the home Wi-Fi and the board decides to call. The footage is stock and the times are illustrative; the decision logic is the real code below.

The Nishi needs to know who came home, but I did not want cameras or an app on anyone's phone. Phones randomize their Bluetooth addresses constantly, so Bluetooth only feeds the "energy" count. Wi-Fi is different: Android and iOS keep one stable private MAC per network. A small root service on the host runs arp-scan on the home network and serves the result as JSON, and the app turns it into arrivals:

def arrivals(macs, now):
    """(name, seconds away) for known people whose phone reappeared after being away."""
    back = []
    for p in CFG["people"]:
        mac = p["mac"].lower()
        if mac not in macs:
            continue
        prev = last_seen.get(mac)
        if prev is not None and now - prev > away_seconds():
            back.append((p["name"], now - prev))
        last_seen[mac] = now  # first sighting after app start never counts as an arrival
    return back

Then reality got in the way, twice.

Locked phones vanish. A locked phone in Wi-Fi power save answers ARP after up to 2 seconds, and the default arp-scan gives up after 500 ms. With --retry=4 --timeout=1500 it went from 4 of 6 scans to 6 of 6. But during a 30-minute test with the phone locked on the table, it was seen once in 62 polls: a sleeping phone drops off Wi-Fi entirely, and no scan can see a disconnected phone. So the "away" threshold is 3 hours, not 20 minutes. Otherwise the Nishi would call my name every time my phone took a nap next to me.

The board kept dying after about 15 hours. Ping worked, SSH refused, and only a power cycle brought it back. The journal of the dead boot had the answer: The maximum number of active connections for UID 0 has been reached (max_connections_per_user=256). Each BLE scan used asyncio.run(), a fresh event loop every time, and bleak opened a new system D-Bus connection for each loop without closing the old one. The connections piled up until root hit the limit, and from then on nothing running as root could talk to D-Bus. The fix is one event loop for every scan:

BLE_LOOP = asyncio.new_event_loop()  # one loop for every scan: bleak keeps a D-Bus connection per loop

def scan_ble():
    found = BLE_LOOP.run_until_complete(BleakScanner.discover(timeout=BLE_SECONDS, return_adv=True))

Before the fix, the service's socket count grew by one with every scan. After it, the count stayed flat at 6.

This project only makes sense with an open model running locally:

config.json, and nothing depends on a hosted model's mood that week. I built this with Claude Code as a pair, running the board over SSH from my laptop. The session below is a curated slice of the real build: the board setup, the locked-phone tests, the crash that turned out to be a D-Bus leak, and the night log's details card.

The spookiest moment wasn't a ghost story. It was the board going silent after 15 hours: it still answered a ping, but nothing else. Something was still home, but it wouldn't come to the door. It turned out to be 256 D-Bus connections that never closed, which is a very modern kind of haunting.

It is an old story, living in a small board, made out of open pieces anyone can read and change.

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @arduino uno q 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/nishi-daak-an-arduin…] indexed:0 read:7min 2026-10-30 · —