For a long time I wanted to build something that does not stop at the edge of a screen. Not just code that runs, but code with a visible, measurable effect on the physical world. My thesis finally gave me that: a box in my flat where software decides when to switch the lights on, when to move the air and when to water a plant, and where I can walk over and check whether it was right.
The thing is called Tiny Greenhouse: a home-sized mini greenhouse with sensors, actuators, a camera, and an LLM assistant that can explain what is happening inside it. I built it as my computer science thesis, defended it in July 2026, and it is the project I promised to write about. This post is the overview: what it is, what it could not do, what I learned. A separate technical write-up on how it is all wired together is coming.
Why a greenhouse and not another web app
I wanted the thesis to touch the physical world. After ten years of building things that live entirely behind a screen, the appeal of code that moves water and switches lights was hard to argue with.
The spark was FarmBot, the open source CNC farming machine that started as a student project and now sits in a few hundred schools. It has its critics, and I have read them: the Hacker News verdict back in 2016 was roughly "a several thousand dollar watering can." Honestly, I do not care much whether that criticism is fair. I believe that automated and assisted agricultural work has enormous potential, and every experiment in the area is useful, including the ones that fail. I just wanted a much simpler version, sized for a home instead of a garden bed.
Everything that already exists falls into two camps. Closed kits like AeroGarden and Click and Grow are easy to start with and give you nothing back: no raw data, no way to change the control logic, no explanation of why anything happened, and usually a subscription to proprietary pods. Open source automation like Home Assistant or OpenSprinkler gives you total freedom and asks for wiring, config files and networking in return.
Neither camp explains anything. That was the gap I aimed at: measure and control the environment like the DIY camp, plus an assistant that knows both the specific plant and the actual sensor history, and can tell you when something is off and why.
Three skill zones, one project
The project sat on three areas I care about, and my experience in them was wildly uneven. That imbalance turned out to be the interesting part.
Web development: ten years in, and still a new experience. This should have been the easy layer, and by one measure it was. By another it was the most unfamiliar thing I have done in years, because for the first time I took the product manager role instead of the developer role. I did not write even one line of code, but I knew what every line does, and I was focusing on the whole architecture instead.
The workflow was deliberate. I planned everything in detail first, writing the plan together with a Claude agent into a ticket, then let the agent implement from that ticket, then verified the result myself through the Playwright MCP rather than trusting a green checkmark. Plan, spec, implement, verify. It is the loop I described when I wrote about becoming a manager of AI agents, except this time the whole application came out of it. The payoff was not "look, no hands." It was time: the web layer stopped eating my evenings, and all of that time went into the cloud and hardware layers instead, which is exactly where I needed it.
Cloud and AI: practising after the certificate. I had already passed Azure AI-102, and I did not go straight from the exam to the thesis. The months in between went into smaller experiments and side projects, which is where most of the theory started turning into something I could actually use. This was simply the first project big enough to test how much of it had really stuck. The split was clean: Azure OpenAI Service and the whole RAG data side were familiar territory. Azure IoT Hub and App Service were completely new to me, and learning them with a real device publishing real telemetry taught me more than any lab exercise.
IoT: my first ever ESP32, and no war stories. This is where I started from zero. Maybe a bit unexpected, but I did not run into any significant difficulties. It was simply new. Working alongside ChatGPT and Claude Code flattened the learning curve so much that a web developer with no electronics background went from never having held an ESP32 to a working sensor and actuator loop without the usual weeks of yak shaving.
I want to be straight about what that means. I cannot claim hard-won hardware scars, because I did not earn any. What I can claim is that the barrier to entry for an unfamiliar domain has genuinely collapsed, and that is worth saying out loud instead of pretending the soldering iron humbled me.
What it actually is
Two independent ESP32 boards. The main one reads seven sensors and publishes a telemetry snapshot every 300 seconds over MQTT to Azure IoT Hub: air temperature, humidity and pressure, illuminance, four capacitive soil probes through an external ADC, and a float switch in the tank. It also drives three 12V loads through a MOSFET module: LED lighting, a pump, and two fans. The second board is an ESP32-CAM with no physical link to the first one, taking one photo a day and posting it straight to the backend, because images are far too big to push through IoT Hub.
Behind that sits a Node.js backend, a React portal with Firebase authentication, telemetry in Firestore, images in Azure Blob Storage, and the knowledge base as vectors in Supabase. GPT-4o-mini through Azure OpenAI answers questions and describes the photos.
One decision is worth stating even at this altitude: the control logic runs locally on the ESP32, not in the cloud. Lights, fan cycles and the pump's dry-run protection keep working from the last known settings if the Wi-Fi drops. A greenhouse that stops watering because a server is briefly unreachable is not automation, it is a liability.
The physical build is OSB and plexiglass with the interior painted white to bounce the LED light around. The growing bed inside is 35 by 53 cm with about 17 cm of soil depth, enough for low crops without turning the box into furniture. The interior splits into three zones: plants in the middle, electronics on one side, water reservoir on the other, so a leak or a clumsy refill never reaches the control board. The LED panel mounts at three heights so the distance can follow the plant, and the temperature, humidity and light sensors hang below the panel on a small tube, so they always measure at a fixed distance from the light no matter which height the panel is set to.
That is the tour. Component choices, the control loop, the API design and the RAG indexing all belong in the technical post.
Does the assistant actually earn its place?
The fair objection to all of this is that an LLM on top of a plant box is a chatbot wrapper on a dashboard you could read in two seconds. Commercial grow controllers ship "AI" features now too, so novelty is not an argument. What does this assistant do that ChatGPT in another tab cannot?
Two things, and they are the whole answer: RAG data about the specific plant, and the historical sensor data.
Take a real question: why are my plant's leaves yellow? The assistant can pull the recommended maximum temperature for that exact variety out of the knowledge base, then look at what the temperature actually did over the last week, or check the soil moisture history, and propose that as the likely cause. A general chatbot can do neither half. It does not know your variety's numbers and it has never seen your box.
The knowledge base is deliberately boring: 183 short chunks split between YAML files holding the numeric targets per crop, variety and growth stage, and Markdown files holding the explanations. Numbers and prose are maintained separately and retrieved together, and every answer comes back with the sources it used rendered next to the text, so you can check where a recommendation came from instead of taking it on faith.
There is a second use I did not expect to like as much as I do. On first run there is a setup wizard where you can discuss with the assistant which plant would suit your conditions and how to plant it. So it is not only a diagnostic tool for after something goes wrong. It is there at the decision point, before anything is in the soil.
I evaluated twelve questions manually against expected answers. My favourite result came back on lighting, where the assistant separated two things that look identical on a dashboard: the light schedule is running correctly, and the light intensity is a limitation of this prototype. That is the difference between "the lights are on" and "the lighting is good enough to grow with," and a chart cannot express it.
The honest caveat: RAG lowers the risk of an invented answer, it does not eliminate it. With a thin knowledge base, irrelevant chunks or a lying sensor, an answer can still be wrong with full confidence and a tidy citation list attached. It is a helper for explanation and orientation, not an expert system that decides for you. That is also what separates it from the academic work nearby: there is a published system that puts an LLM on plant sensors purely through prompt personas, so the plant says things like "I am thirsty." Charming, and grounded in nothing you can check.
Three things the box could not do
This is the part the build-log genre skips, so let me not skip it.
Temperature. I could not control it. With the room at 24 to 25 degrees, the inside of the prototype sat around 30. The fans move air perfectly well, but moving air is not cooling: you cannot push a closed box below the temperature of the air you pull into it. Active cooling was never in the design, and it shows in every chart.
Light. This one surprised me most. Lighting turns out to be an enormous part of indoor growing, and proper horticultural lights are both expensive and, in many cases, mains powered. I deliberately kept 220V out of an enclosure holding water, high humidity, metal and electronics in a small volume. That left 12V LED strips, which keep the whole actuator side low-voltage DC and let one MOSFET module drive everything. A reasonable prototype compromise, and not a good grow light: the measured ceiling of 610 lux says so plainly.
Size. The prototype ended up too big for casual home growing and far too small to produce anything meaningful. It is stuck in the least useful part of the range, which is a design outcome I only fully understood once it was built and standing in my flat.
A quieter fourth limitation: soil moisture readings are relative, not absolute. Capacitive probes avoid the corrosion that ruins cheap resistive sensors, but the value still depends on substrate, insertion depth and where the local wet spot happens to be. Four probes reduce the risk of a bad read. They do not remove it.
The pump that correctly stayed off
The experiment ran for several consecutive days at home, in automatic mode, with a real plant in the soil.
| Parameter | Min | Max | Comment |
|---|---|---|---|
| Temperature | 25.5 C | 31 C | above optimal for part of the period |
| Air humidity | 43.6 % | 59.9 % | in a suitable range |
| Illuminance | 0 lux | 610 lux | limited LED lighting |
| Substrate moisture | 71.6 % | 76.1 % | above the watering threshold |
Substrate moisture stayed above the lower watering bound for the whole period, which means the pump was never supposed to run. And it did not.
That needs one clarification, because on its own it sounds like a component that failed. The pump works. I tested it on its own, it fires when the soil drops below the threshold, it fires when I trigger it manually from the portal, and it refuses to fire when the float switch says the tank is empty. During this particular run it simply was not needed, and staying off was the correct behaviour.
That is the outcome I am proudest of. A schedule-driven system would have watered anyway, on the clock, regardless of what the soil was doing, and over a few days that is how you drown a plant. Automation does not mean constant action, it means correct action only when the conditions are met. Evaluating a control loop is not only about whether a device turns on when it should, it is equally about whether it stays off when it should.
The part I liked most: when I asked the assistant whether to water, it said no, citing the current reading against the recommended band and the knowledge base notes on letting the substrate dry back. That answer was produced completely independently of the control loop, and it matched what the hardware had already decided on its own. The system did not just decline to water. It could explain why.
The crop had to change, not the code
I started with strawberries, the Clery variety, and they were the wrong choice. Strawberries want cool nights, and my box runs hot at night by construction. No configuration change fixes that, because the constraint is physical.
So I switched the main experiment to Bolivian Rainbow, an ornamental chilli that tolerates warmer and drier conditions and stays compact. It worked out nicely, and the peppers suited the prototype well.
The lesson generalises well beyond gardening: in a physical system you do not only set the target values, you pick the requirement that fits the constraint you cannot remove. I spent a while thinking about how to cool the box. The answer was to grow something that likes it warm. It is also why the setup wizard's plant discussion earns its place, since the most consequential decision in the whole system happens before anything is planted.
What a v2 would look like
Two directions interest me, and they point opposite ways.
The first is smaller: a single-plant unit, nicer looking, something that belongs on a shelf rather than dominating a corner. That fixes the size problem by picking a side.
The second is much bigger: indoor growing for bio-restaurants, or for schools and kindergartens where the point is as much the learning as the lettuce. My reason for thinking it is realistic is structural, not optimistic. The base is already built and does not change. The control layer, the cloud plumbing, the backend, the portal and the assistant are a fixed cost whether you run one growing bed or twenty. Scaling up needs more soil, more space and a few more sensors, and sensors are cheap. Lighting is the part that genuinely scales with area, and lighting is the expensive part.
To be completely clear, this is thinking, not a plan. I have not spoken to a single restaurant, school or kindergarten. I have been mapping where the scaling paths are strong and where they are weak, and that is the honest state of it.
Where it ended up
The thesis was defended in July 2026 and got a 6, the top mark on the Bulgarian scale.
The better ending is what happened afterwards. At the defence a group of the professors' assistants were genuinely curious and kept asking about the prototype. The thing is large and I do not have space at home to keep it running indefinitely, so I offered to donate it to the university. It stayed there, and it will be shown to new students.
I am happier with that than I expected to be. If I had carried it home, it would already be disassembled, the ESP32 in a drawer and the OSB in the basement. Instead it is still standing and still doing something useful. And the chilli plant, for the record, is still alive.
The full technical write-up is next: the component choices and why each beat its alternatives, the control logic and its hysteresis, the API and data model, and how the RAG layer is actually put together.
If you are building something in this space, or you think I got the trade-offs wrong, reach out. I would genuinely enjoy that conversation.



