# OpenDTU: an ESP32 firmware that talks to Hoymiles inverters without the cloud

> OpenDTU replaces the vendor data unit that Hoymiles ships with its cloud, by reimplementing the inverter radio protocol on an ESP32 and publishing the results over MQTT and a local web API. Here is what that involves and where it will bite you.

**tbnobody/OpenDTU** — Software for ESP32 to talk to Hoymiles/TSUN/Solenso Inverters

- Repository: https://github.com/tbnobody/OpenDTU
- Stars: 2,193 · Forks: 633
- Language: C++
- License: GPL-2.0
- Published: 2026-10-08 · Updated: 2026-10-08 · Language: en
- Canonical page: https://hysenlabs.com/projects/tbnobody-opendtu

## Why the DTU existed at all

Hoymiles sells small inverter modules that come with a data unit, the DTU, which uploads production data to the vendor's cloud. OpenDTU started as a discussion on the Mikrocontroller.net forum, and the README states the goal plainly: replace the original Hoymiles DTU with their cloud access. Getting there required decrypting and analysing the Hoymiles protocol, and the README credits that reverse engineering as the thing that made the project possible.

That origin explains the shape of the software. This is not a dashboard that queries a cloud API. It is firmware that speaks to the inverter over its own radio link, so it runs on a $5 module next to the panels instead of depending on a phone app and a vendor account. The repository description names the three inverter families it targets, Hoymiles, TSUN and Solenso, and the topics list carries the same three names alongside `esp32`, `mqtt`, `solar` and `balkonkraftwerk`, which is the German term for a balcony solar plant. The intended user is therefore fairly specific: someone with a small residential installation who wants their data on their own network.

What the README does not do is name a single inverter model. It points to a supported inverter overview on the project's website instead. That is the single most important page to read before buying anything, because support is determined by which radio behaviour has been worked out, not by the brand on the box.

## The repository is a PlatformIO project with a bundled web app

The build is PlatformIO, and the tree shows the parts of that arrangement. `platformio.ini` holds the board and environment configuration, with `platformio_override.ini` sitting beside it for local overrides that should not be committed. `src/` is the firmware, `include/` the headers, `lib/` the vendored dependencies, and `test/` the tests. A `pio-scripts/` directory holds the extra script hooks PlatformIO runs, which is where extra image packaging or post build steps would live.

Three partition tables sit at the repository root, which is the clearest signal of what the firmware has to accommodate:

```text
partitions_custom_4mb.csv
partitions_custom_8mb.csv
partitions_custom_16mb.csv
```

One file per flash size, so the same firmware can be built for a 4 MB, 8 MB or 16 MB module. If you are following a build guide for a particular board, matching the partition CSV to the module's actual flash size is the step people get wrong.

The front end is checked in as well. There is a `webapp/` directory and a separate `webapp_dist/` directory, and the CI badges for Yarn linting and Yarn prettier in the README confirm a JavaScript toolchain is part of the build. A `lang/` directory at the root suggests translations for the interface. Taken together, this is a full firmware plus a web interface project, which is more moving parts than a serial-to-MQTT bridge would have.

## MQTT, Prometheus and the Web API endpoints that keep changing

Data leaves the device in more than one shape, and the project is unusually honest about the fact that the shapes change. The README keeps a breaking changes section generated from the commit log with this command:

```bash
git log --date=short --pretty=format:"* %h%x09%ad%x09%s" | grep BREAKING
```

The entries below it read like an API changelog rather than a firmware changelog, and they show how often consumer integrations had to be rewritten:

```text
* 8cab3335      2025-08-07      BREAKING CHANGE: WebAPI endpoint `/api/limit/config` requires different parameters
* 8372deaf      2025-04-18      BREAKING CHANGE: Logging newline changed from "\r\n" to "\n"
* 71d1b3b       2023-11-07      BREAKING CHANGE: Home Assistant Auto Discovery to new naming scheme
```

Home Assistant auto discovery changed naming scheme, the Prometheus API changed, and even the log line ending changed from CRLF to LF, which is the kind of change that breaks naive log scrapers. The Home Assistant entry matters most in practice, because auto discovery is how most people get MQTT sensors into their dashboard without writing configuration by hand, and a rename means every existing sensor is recreated.

One caveat about that list: its newest entry is dated 2025-08-07, while tagged releases run to v26.9.28 on 2026-09-28. Both are in the repository. The list is presented as generated from the log, so the plain reading is that no commit since 2025-08-07 carried a `BREAKING` marker, but a year of releases with no breaking changes is also the kind of claim worth checking against the commit log yourself rather than trusting a list that lives in a README. The version naming, which encodes the date as v26.9.28, gives you releases to compare against but says nothing about whether any of them broke an endpoint.

## The partition table upgrade that will brick a device if ignored

The first thing in the README, before any background or documentation, is an upgrade warning in capitals. If you are upgrading from a version before 15.03.2023, you have to upgrade the partition table of the ESP32, and the README points at `docs/UpgradePartition.md` for the procedure.

That warning is not padding. The breaking changes list shows what happens when it is ignored, because the partition table change is in that list too:

```text
* 318136d       2023-03-15      BREAKING CHANGE: Updated partition table: Make sure you have a configuration backup and completly reflash the device!
```

The commit message asks for a configuration backup and a complete reflash, which is the honest description of a flash layout change. On an ESP32 the partition table decides where the firmware, the filesystem and the user data live. If the new firmware expects a layout the installed table does not provide, the device can boot to nothing after an apparently normal flash, and the settings you had configured are gone with it. So the sequence is: back up the configuration first, reflash completely, and only then flash the new firmware.

There is a separate `docs/` directory in the tree, which is where that document lives. It is worth noting that this upgrade path is also where a self hosted setup differs from a vendor cloud setup. The vendor's cloud never asks you to reflash anything, and the burden of getting a firmware upgrade right sits entirely with you.

## Two license files and no licensing statement in the README

The repository records GPL-2.0 as its license. The tree has both `COPYING` and `LICENSE` at the root, which is the convention GNU licenses normally use, and the README says nothing at all about licensing.

That gap is worth naming rather than filling in with a guess. GPL-2.0 with two license files at the root is consistent with the standard arrangement where `LICENSE` holds the GPL text and `COPYING` holds either the same text or a bundled notice, and for a firmware project it is normal to have both. What the repository does not state is whether any of the bundled libraries under `lib/` carry separate terms, which is exactly the question that matters if you intend to redistribute a build. Since the README is silent, the honest reading is that the license position is defined by the files rather than explained anywhere in prose, and the practical step is to read both files and check what `lib/` contains before you ship an image to someone else.

The project is not archived and the last push was on 2026-09-28, the same day as the v26.9.28 release, which is the pattern you want to see from firmware that people depend on for monitoring. Releases arrive regularly enough that v26.3.28 and v26.3.30 exist within three days of each other, so patch releases do happen.

## A project that asks you not to use generated text on its trackers

The last section of the README is a development policy about AI, and it is unusually strict. LLM output is expressly prohibited for direct communication in issues, comments, feature requests, pull request bodies, forum posts and chat. The stated reasoning is that posters are expected to understand what they are posting rather than paste a dump, and that violations will lead to closure or deletion of the item. There is one carve out, for LLM assisted translations, and it requires explicitly noting that a translation was produced that way, ideally with the original language version alongside.

This has a direct practical effect on anyone who wants to help. A hardware compatibility report or a bug report about an unsupported inverter is exactly the kind of content a maintainer would want to receive, and a generated one is likely to be closed. So the on-ramp for a new contributor is to read the hardware documentation, form an opinion about which inverter behaves how, and write it in your own words. It is also a signal about how the project thinks about maintainer load, which is consistent with the breaking changes discipline elsewhere in the README.

Documentation itself lives in a separate repository, OpenDTU-docs, and the README asks for pull requests against it. The published site is opendtu.solar. So the documentation is not vendored into this repository, which means a version of the docs you find online may describe firmware newer than the tag you are running.

## Conclusion

OpenDTU is for the specific situation of a balcony or small rooftop installation running Hoymiles, TSUN or Solenso hardware where the vendor data unit is the part you do not trust or cannot get. It gives you local data over MQTT and a web API, and it was built by reverse engineering the protocol, which is both the project's value and the source of its fragility, since an unsupported inverter model stays unsupported until someone works out its radio behaviour. Start with the hardware page for the supported inverter overview and the `UpgradePartition.md` document before flashing anything, because upgrading from a build older than 15.03.2023 means the partition table has to be replaced and the device reflashed. If your installation is not on one of those three inverter brands, or you want guaranteed support for a model you just bought, this firmware is the wrong dependency and the vendor cloud is the shorter path.

## FAQ

### What is OpenDTU and what does it replace?

OpenDTU is ESP32 firmware that speaks to Hoymiles, TSUN and Solenso inverters directly and publishes the data locally, replacing the vendor's DTU and its cloud upload. The protocol was decrypted through reverse engineering according to the README.

### Which inverter models does OpenDTU support?

The README does not list models itself and instead points to a supported inverter overview on the project's website at opendtu.solar. Support depends on which inverter radio behaviour has been worked out, so that page is the thing to check before buying hardware.

### How do I upgrade OpenDTU firmware on an existing device?

If you are coming from a version older than 15.03.2023 you must upgrade the ESP32 partition table first, following the procedure in docs/UpgradePartition.md. The project asks for a configuration backup and a complete reflash, because the flash layout changed.

### Does OpenDTU work with Home Assistant and MQTT?

It publishes over MQTT and the README's breaking changes list records a 2023-11-07 change to the Home Assistant auto discovery naming scheme. That rename means existing auto discovered sensors are recreated, so an upgrade can leave you with duplicate or reconfigured entities.

### Can I contribute hardware compatibility findings to OpenDTU?

The README prohibits LLM generated text in issues, comments, feature requests, pull requests and forum posts, and requires that you understand and write what you submit. Documentation pull requests go to the separate OpenDTU-docs repository instead.

## Sources

- [Issues](https://github.com/tbnobody/OpenDTU/issues)
- [License: GPL-2.0](https://github.com/tbnobody/OpenDTU/blob/master/LICENSE)
- [README](https://github.com/tbnobody/OpenDTU/blob/master/README.md)
- [Releases](https://github.com/tbnobody/OpenDTU/releases)
- [tbnobody/OpenDTU on GitHub](https://github.com/tbnobody/OpenDTU)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tbnobody-opendtu
