These builders, hesitant about big tech, big data, and big AI models, are attracted to the idea of a purpose-built computer that can be used with sensors, and even small AI models, entirely offline, with full control over how personal data is shared.
B.L.O.O.M. stands for "Bridging Local Observations, Openly Mapped" . It is designed to work offline so that nature lovers around the world can capture their field notes and then sync them ad hoc to the cloud when back online, creating a source for open, crowd-sourced field notes on local flora and fauna . A purpose-built companion web app gathers these notes in the standard Grinnell format, ready to enjoy and educate.
Hardware : It includes a small HDMI screen to capture field notes, photos, and sketches, a mini bluetooth keyboard and mouse, a microphone to capture and analyze birdsong, and a camera to capture photos. It's 'brain' is the Arduino Uno Q, which has enough power to host small AI models. It's powered by a power bank.
Software : It ships with a custom-built AI audio model to interpret birdsong, a local LLM "field companion" to chat with for more information and to answer questions, a local web app that runs on device to capture impressions and notes, and the ability to push a button to share a note to the cloud as desired, when back online. A companion web app hosts these synced field notes, which are pushed ad hoc to GitHub and converted to a markdown file for the web site, using a Netlify function.
Enclosure : Based on a tradition of reusing existing enclosure options, I used a Lancôme makeup "train case" that I found on Ebay, cutting a small hole in the side for the camera. Since this project is designed to inspire and educate the community of nature lovers to go outside and share their enjoyment of the Great Outdoors, it seemed appropriate to reduce the use of plastic in the project by reusing what we could. I also repurposed cables I had on hand and created a modular layered enclosure system so that elements could be swapped in, the power bank re-charged, and any other changes made without problem. The shelves within the train case enclosure are made with recycled foam and cloth which offered a strong, modular interior.
Let's walk through the steps to build this cyberdeck.
The 'brains' of this operation are the Arduino Uno Q, 4 GB, recommended by a colleague as a great device for learning about AI models as it's just capable enough to run lightweight LLMs and other AI models on device. We want this cyberdeck to be built to work in the field, so we want to build:
For development purposes, don't worry about settling everything in the enclosure. First, get all the peripherals hooked up.
In practice, B.L.O.O.M.: A Community Cyberdeck for your Field Notes works best when you follow a step-by-step field validation workflow and keep a simple checklist for wiring, power stability, and expected output behavior. This makes debugging faster and creates a practical note troubleshooting path for repeatable results.
Monitor : Connect your HDMI monitor to the USB hub in the HDMI port. Connect its mini-USB port to one of the USB hub USB A ports.
Microphone : Plug your mini-microphone into a USB-A port in the USB hub.
Camera : Plug the camera into a USB-A port in the USB hub.
Power : Connect your power bank to the USB-C port in the USB hub.
Uno : Connect the Hub's built-in USB-C plug to the Uno's USB port.
Power on your Uno and boot it up. Keep it connected to the network for this step, so that you can flash code easily. You may need to use a wired mouse and keyboard temporarily connected to your USB hub for the next step as you need to login to your Debian instance on your Uno and connect your two Bluetooth devices. Use the small Bluetooth icon in the Debian interface to pair your small keyboard and mouse. Now you can disconnect any wired mouse/keyboard and reclaim those precious USB ports on your hub!
This setup will function well as a standalone computer (the core of any cyberdeck project, running in classic 'SBC' mode). But we want to add some custom elements to this deck so that it really works as a way to take field observations. I worked in App Lab to build the base code: a small, horizontal-friendly web app with minimal interface so as to quickly let you capture pictures, sketches, and notes.
But one problem I discovered was that the way the base Web UI brick worked was not reliable on Debian - when you run App Lab on the Uno, the popup window did not come to the forefront when calling Chromium. A tricky bug, as it worked fine on my computer. So I was obliged to build a custom 'kiosk' brick that opens a Chromium browser in kiosk mode:
if system == "Linux":
return ["chromium-browser", "--kiosk", "--noerrdialogs", "--disable-infobars", f"--app={url}"]
...
def launch_kiosk(url: str, ready_timeout: float = 15.0):
if not wait_for_server(url, timeout=ready_timeout):
logger.error(f"Server never became reachable at {url}")
return None
cmd = build_kiosk_command(url)
logger.info(f"Launching: {' '.join(cmd)}")
return subprocess.Popen(cmd)
This works well to let the user focus on the task at hand - taking a quick note with the top of the case open so the user can capture a picture, draw a sketch, and type a note.
The other App Lab bricks used in this app include:
A reliable implementation also benefits from modular structure: separate input handling, processing logic, and output control so each part can be tested independently. That pattern supports low-noise notes tuning, clearer build calibration decisions, and safer iteration when features evolve.
App Lab has a neat connection to a third party service called Edge Impulse, where you can use a free tier to train a custom model or retrain someone else's.
I found a birdsong model in their community showcase, but its data was imbalanced and minimal. We needed a better way to identify birds. So I followed these steps:
Now, if you are lucky enough to hear a beautiful singing bird when wandering with your cyberdeck, the microphone can pick it up and you can press a button on device to identify it and capture the data as a note.
An important aspect of B.L.O.O.M.'s architecture is that the user can choose to retain their data entirely privately using the local SQL database. Or, they can choose to sync a note to the cloud to share with the nature-loving community. To do that, I built a simple Astro.js web app to store these observations. The trickiest thing was to get the SQLite database on the device to communicate with the web app hosted on Netlify. I also wanted to make the interface look beautifully vintage, using AI-generated images that build on the images captured in the notes.
I solved these requirements by creating a routine whereby when the user presses 'Sync to Cloud', the Arduino app simply sends the requested data to the Netlify function:
def sync_entry(entry_id):
with _db_lock:
conn = sqlite3.connect(DB_PATH)
row = conn.execute(
"SELECT id, timestamp, photo, sketch, ai_note, locality, weather, habitat FROM entries WHERE id = ?",
(entry_id,)
).fetchone()
conn.close()
...
eid, timestamp, photo, sketch, ai_note, locality, weather, habitat = row
payload = {
"id": eid,
"catalog_no": catalog_number(eid),
"timestamp": timestamp,
"note": ai_note or "",
"locality": locality or "",
"weather": weather or "",
"habitat": habitat or "",
"photo": base64.b64encode(photo).decode("utf-8") if photo else None,
"sketch": base64.b64encode(sketch).decode("utf-8") if sketch else None,
}
try:
response = requests.post(
CLOUD_SYNC_URL,
json=payload,
timeout=CLOUD_SYNC_TIMEOUT,
)
...
The Netlify function then picks up the data and creates a PR in the GitHub repo where the app's code is stored, using the Octokit library:
const { data: pr } = await octokit.pulls.create({
owner: OWNER, repo: REPO,
title: `New field note: ${catalog_no}`,
head: branchName,
base: BASE_BRANCH,
body: [
"Submitted automatically from a Bloom device.",
"",
`**Locality:** ${locality || "\u2014"}`,
`**Weather:** ${weather || "\u2014"}`,
`**Habitat:** ${habitat || "\u2014"}`,
aiSketchPath ? "\n_AI-generated sketch included._" : null,
].filter((line) => line !== null).join("\n"),
labels: ["device-submission"],
}).catch(async (err) => {
...
return { data: fallbackPr };
});
Now the app is equipped with a way to open PRs that build markdown pages so that when you approve the PR, a build occurs and your app is refreshed with your community field note. This is a useful mechanism to be able to vet your community content.
Next, I had a stretch goal: to add an AI-generated vintage style field drawing like this to each note:
I used a new service by Cloudinary to generate images using your image generation model of choice and a reference image. I added the following routine to the Netlify function so that when a note is generated, its AI equivalent is drawn by Nano Banana (Google's excellent image generation model which is particularly good with text). Store your Cloudinary API credentials in Netlify's environment variables area and your reference image in Cloudinary's storage. The following function calls Cloudinary's new image generation API, free for limited use:
For long-term maintainability, document baseline measurements such as response time, stability under transitions, and recovery after temporary faults. Using this measurement-driven data optimization style gives you a scalable power upgrade path without turning the project into a fragile one-off demo.
async function generateAiSketch(noteText, slug) {
...
const prompt = [
"Create a field sketch illustration in the style of the reference image [1].",
"The sketch should depict:",
noteText || "A natural world observation",
"Use a vintage field journal aesthetic with earthy tones, hand-drawn quality, and scientific illustration style.",
].join(" ");
try {
const response = await fetch(
`https://api.cloudinary.com/v2/generate/${CLOUDINARY_CLOUD_NAME}/image_to_image`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${Buffer.from(`${CLOUDINARY_API_KEY}:${CLOUDINARY_API_SECRET}`).toString("base64")}`,
},
body: JSON.stringify({
prompt,
reference_images: [
{
source_type: "url",
url: REFERENCE_IMAGE_URL,
},
],
model: {
family: "nano-banana",
tier: "premium",
},
target: {
target_type: "managed_asset",
public_id: `field-notes/${slug}-ai`,
},
}),
}
);
...
}
The field notes community app is now beautifully complete with notes, AI generated images giving some more context, and all the user-generated content built by your community.
After all the software and hardware work, it feels like a fun arts and crafts project to build a cyberdeck enclosure.
There are a lot of 3D printable CAD designs available for all kinds of cyberdeck enclosures, but 1/ I don't have easy access to a 3D printer and 2/ I wanted to repurpose/recycle an enclosure to save plastic waste. As described above, the enclosure I used was a vintage train case makeup bag, but you could use any easy to carry, breathable enclosure. Mine was tall enough that I created some cloth-covered foam shelves so I could hide the power supply at the bottom.
I put another shelf above this to hide some of the cords, and used zip ties to hold the USB hub steady. I cut a small hole in the side for the camera, and connected all the cords to the hub and power supply as per the schematic.
I created a cardboard backing to hold the screen with enough wiggle room for the cords to move around whether the top is open or closed. I created a little 'couch' for the Uno board, although it should be noted that using foam and fabric is tricky with these devices - check to see if they have room for air and don't overheat.
Watch me pack it in this video :
Train cases give you enough room to store your keyboard, mouse, and more, so add a flower pressing kit, a mini watercolor paint kit, and maybe even a snack. You're ready for your forest adventure!
Now it's time to take your device into the woods, or just as far as your back yard. Open it up, power it on, and get it to listen to birds and take pictures of interesting things you see. Be sure to type in your observations, including locale, weather, and ground conditions, and chat with your LLM to learn more about what you're seeing. When you get back online, sync your note to our community repo and join us as we recreate this old-fashioned way of seeing the world.
I hope you discover something awesome!