I have a problem. I lose things. Soldering irons, the third M3 screw, the receipt I swore I left on the desk. So this weekend I did what any reasonable person would do: I taught my roombot to remember where my stuff is.
Not "store a row." Remember. The difference matters, and it's the whole reason moteDB exists.
Let me back up. The passion here isn't "I built a find-my-irons app." It's that I've been low-key obsessed for years with one question: why can't machines remember like we do? We walk into a room and know the red box is the one we grabbed three minutes ago. Robots forget that the instant the frame buffer clears. That gap bugged me enough to spend 18 months writing a database in Rust. This weekend I finally pointed it at something stupid and fun.
The build
Hardware: a Raspberry Pi 5, a webcam, and a robot arm I assembled from a kit that Definitely Had All The Parts (it did not).
The memory pipeline has two ends:
- Google AI (Gemini) turns what the camera sees into a multimodal embedding — image plus a short text caption. One vector that means "the soldering iron on the blue mat, tip facing left."
- moteDB stores that embedding on-device, next to the timestamp, the pose, and a text note. No server. No cloud round-trip at query time.
Why this split? Gemini is genuinely good at multimodal embeddings — better than anything I can run locally on a Pi. But I don't want my robot's memory living in someone else's API. So Gemini does the thinking, moteDB does the keeping. The embedding gets computed once and then lives in a 900KB binary on the device.
Here's the actual insert path:
use motedb::{Db, Episode};
// 1. Gemini gives us a multimodal embedding (image + caption)
let embedding = gemini::embed_multimodal(
GEMINI_EMBEDDING_MODEL, // gemini-embedding-001
image_bytes,
"soldering iron on blue mat, tip left",
)?;
// 2. moteDB keeps it on-device, as one atomic episode
let db = Db::open("room_memory.motedb")?;
let ep = db.episode("find_soldering_iron_001")?;
ep.log_frame(&embedding, now)?;
ep.log_scalar("pose_x", 0.42, now)?;
ep.log_text("note", "last seen on the workbench", now)?;
ep.close()?; // seals + builds the local index
Later, when I ask "where's my iron," the roombot snaps a frame, Gemini embeds it, and moteDB answers:
let query = gemini::embed_multimodal(GEMINI_EMBEDDING_MODEL, current_frame, "soldering iron")?;
let hits = db.search()
.vector("frame", &query)
.top_k(1)
.execute()?;
// → "blue mat, tip left, seen 4 min ago"
The numbers, because I don't trust vibes
Raspberry Pi 5, 8GB:
- Gemini embedding (image + caption) call: ~120ms over the network, once per new object
- moteDB store: ~0.3ms
- moteDB similarity search over 1,000 remembered objects: ~8ms
- RAM for the on-device index: ~22MB
The part I actually care about: the robot recalls where something is in under 10ms after the one-time embedding. No cloud dependency at query time. If my wifi dies, the roombot still knows where the iron is. That's the entire point of an embedded memory.
What surprised me
I expected the hard part to be the embeddings. It wasn't. The hard part was forgetting. My first version remembered every frame and the Pi's disk filled in an afternoon. moteDB's Episode API saved me here — I model "one search task" as one bounded, replayable episode, and I age them out by time instead of by frame count. "Remember where I put X" is naturally an episode. The robot's working memory became a stack of episodes, not an ever-growing pile of vectors.
The other surprise: atomic cross-modal writes matter more than I admitted. Early on I'd insert the embedding, then crash before the text note landed, and end up with a "ghost" memory — an object the robot "remembered" with no description. moteDB's transaction boundary (ep.close() commits frame + scalar + text together or rolls back) killed that class of bug.
Honest caveats
- Gemini embeddings cost a network call per new object. Fine for "where's my iron." Not fine for 200Hz sensor streams — there I use moteDB's local vector path without Gemini.
- moteDB will lose to Postgres on analytical queries. Don't use it for that. It wins on "small device, must remember, no server."
- The roombot still can't put the iron back. One problem at a time.
Why I'm posting this
This is my entry for DEV's Weekend Challenge: Passion Edition. The theme is "build something inspired by passion" — and honestly, giving machines a memory has been my hobby-horse for longer than I'd like to admit. Pointing Gemini's multimodal embeddings at an on-device Rust DB was the first time the idea felt real in my own apartment.
If you're building anything that needs to remember in the physical world — robots, drones, edge agents — what's the one thing you wish your memory layer could do that it can't today?
moteDB is open-source, 100% Rust, and runs in ~900KB on embedded hardware. cargo add motedb. GitHub: motedb/motedb