A Svelte web interface for Plant133

Posted on November 10, 2025 • 7 min read • 1,361 words
Creating a modern, self-hosted web interface for an ESP32 device
A Svelte web interface for Plant133

Plant133 is a device that waters indoor plants. It is based on the ESP321 microprocessor, and has a web interface for configuration and control. To this point, the interface has been functional but no-frills. This post describes how I built a much slicker web interface for the device using the Svelte2 web framework. The process could be replicated for almost any ESP32-based project.

The original interface  

The original web interface for Plant133 is implemented through the og33 framework. This allows software to register groups of variables representing configuration values, sensor readings, and commands. It automatically generates HTML tables to display the values, and web forms to allow users to set configuration values. It also has built-in forms for configuring common utilities such as WiFi and MQTT.

The interface is based on the first ESP-based firmware I ever tried: mitsubishi2MQTT. I found it through the blog post “Hacking A Mitsubishi Heat Pump” (part 1, 2) when I was looking for ways to control our minisplits over WiFi. The commercial solution was unappealing: more than $200 per minisplit. This ESP-based solution initiated my downward spiral during the pandemic: discovering Home Assistant, learning about ESP boards, re-learning how to solder and design circuit boards, and eventually starting this blog.

But I digress. The relevant point is that the web interface relies fully on server/device-side rendering, with a little CSS to add polish and make it phone-friendly.

Original Plant133 web interface
Original Plant133 web interface

Later versions added separate configuration for the 4 different plants that the newer Plant1337 device supports, but the basic look and functionality remained.

Striving for something better  

I wondered whether it would be possible to build a much nicer web interface using a modern Javascript framework. The device would serve up the web interface, then the browser would make AJAX-style requests back to the device for up-to-date information or to send commands. Ideally, the web framework would be extremely compact so it could be fully hosted by an ESP32. Some web searching indicated that the Svelte2 framework was a good candidate. It has a bundle footprint of just 2KB, and people are already using it with ESP32 devices. The svelteesp32 compiles a Svelte application into a C++ header file for inclusion in a PlatformIO project.

Vibe coding  

All I needed was a Svelte-based interface. This was not necessarily going to be easy for me. I made my first web pages in the 1990s, and my web skills stagnated from there. To make sure the server side of the application was going to work, I first hand-coded a simple web page, with some plain Javascript. This made AJAX GET requests to the Plant133 application to get updated sensor readings as JSON. It also made POST requests to device’s pump-test. I got this to work, though I needed to switch to the ESP32Async version of the ESPAsyncWebServer to process Javscript in requests.

I expected writing my first Svelte interface to be a slow process, given my lack familiarity with Node.js and Javascript frameworks. So I figured this was a good time to try out that newfangled AI stuff the kids are using these days, and do some “vibe coding.” First, I asked Google Gemini to make a Svelte web application for my plant-watering device. Gemini replied that it only made React-based applications. I did a web search asking which AI was best for building a Svelte application, and the recommendation was Claude.ai.

So I asked Claude to make my Svelte application. I looked at the result, and saw it import a bunch of stuff from React. So I asked Claude “Is that a svelte app or a react app?”. It replied:

You’re absolutely right - that’s a React app, not a Svelte app! My apologies for the confusion. Let me create an actual Svelte app for you.

It did actually create a Svelte application this time. I was able to download the code and followed the included instructions to run the application locally on my laptop. The calls to the device were mocked-out, which was OK to start with.

With some further prompting, I was able to get Claude to add configuration pages, a gauge for the moisture sensor, and to format the interface to work better on a smaller screen such as a phone.

Integrating into firmware  

I used the svelteesp32 demo app as a template to generate a header file from my application and integrate it into the Plant133 firmware. After I got the web interface loading from the device, I replaced the mocked-out AJAX calls to the device with actual web GET and PUT calls, and implemented the server-side responses in the firmware. I eventually got it working!

The part of the setup() call that initializes the Svelte interface and most of the GET handlers to serve JSON responses to the application is incredibly simple:

  initSvelteStaticFiles(&s_app.web_server());
  s_app.web_server().on("/api/plants", HTTP_GET, apiGetPlants);
  s_app.web_server().on("/api/wifi", HTTP_GET, apiGetWifi);
  s_app.web_server().on("/api/mqtt", HTTP_GET, apiGetMqtt);
  s_app.web_server().on("/api/moisture", HTTP_GET, apiGetMoisture);
  s_app.web_server().on("/api/status", HTTP_GET, apiGetStatus);

Registering handlers for requests that include JSON information is slightly more involved.

I was happy to find that with the Svelte interface, the compiled application is not much larger than without it. By inspecting the generated Svelte application I was able to learn enough to tweak the layout and add some additional information.

The interface  

This is the overview page of the resulting interface:

Svelte-based web interface: Overview page
Svelte-based web interface: Overview page
Every 10 seconds, the page updates the room temperature and humidity and the status of the water reservoir. The status of each plant is updated on a similar schedule. The moisture level of the plant is displayed on a gauge that also indicates its target moisture range. In the example above, the alocasia is in the process of being watered. After 3 doses, each 15 minutes apart, the moisture level has almost risen to the top of the target range.

Since requests for updates from the device operate in the background, the web interface remains responsive.

Clicking the “settings” gear icon for a plant selects the configuration view for plant watering.

Plant configuration page (top portion)
Plant configuration page (top portion)
The target moisture range can be set, along with ADC calibration settings for the moisture sensor. The allowed number of doses and the number of milliseconds the pump should run for each dose can also be set.

These are the pages for changing the WiFi and MQTT broker settings:

WiFi configuration page
WiFi configuration page

MQTT configuration page
MQTT configuration page

I asked Claude if I could integrate its code into an MIT-licensed open source project. It responded this was fine, so I’ve updated my Plant133 Github repository with the new code.

Build and CI  

I should probably incorporate building the Svelte application into the PlatformIO build process for the firmware, but for now I use a separate script. After that running the script, I run pio run to build the firmware with the included interface.

It wasn’t difficult to update the platformio-run.yaml file in the project GitHub workflows so that the web interface is built and included as part of the Continuous Integration checks. If a proposed change to the interface won’t compile, GitHub will report this.

Conclusions  

It is possible to build a responsive, modern web interface for ESP32 projects using the Svelte web framework.

The Plant133 Github repository is one example of such an interface for a working project. The svelteesp32 system made integration into the device firmware very easy. The Svelte application would have taken a while to learn how to write, but Claude.ai was able to write it for me, and I’m happy with the result. I’m not enough of a web developer to recognize if it could have been done much better, but the experience of using the interface is as good as I could have hoped, and the code seems nice and compact.

References  


  1. ESP32: a family of microcontrollers made by Espressif Systems. A successor to the ESP8266 microcontroller. ↩︎

  2. Svelte is a free and open-source component-based front-end software framework and language created by Rich Harris and maintained by the Svelte core team. Among comparable frontend libraries, Svelte has one of the smallest bundle footprints at merely 2KB. ↩︎ ↩︎

  3. og3 is my C++ utility library for ESP microprocessors, published on GitHub↩︎