Yevhenii Kolesnyk

DevOps Engineer

System Engineer

Computer engineer

Software developer

Web developer

Yevhenii Kolesnyk

DevOps Engineer

System Engineer

Computer engineer

Software developer

Web developer

Blog Post

Building OpenEnvRouter — Designing an ESP8266 Environmental Monitoring Router with Multi-Wi-Fi Failover

July 12, 2026 Development, Embedded Systems, IoT

Introduction

Embedded development has always fascinated me because it combines software engineering with real-world hardware. Unlike traditional web applications, embedded systems interact directly with the physical environment, making every design decision immediately visible in practice.

Over the years I have worked on web platforms, Linux infrastructure, backend development, and automation projects. Eventually, I wanted to build something that connected those different areas of experience into a single device.

The result became OpenEnvRouter.

OpenEnvRouter is an open-source firmware project for the ESP8266 that transforms an inexpensive Wemos D1 mini into a compact environmental monitoring platform with integrated networking capabilities. Instead of creating another simple temperature sensor that only prints values to a serial console, I wanted to design something that could operate as a practical standalone appliance.

The device continuously measures environmental conditions, provides a modern web-based administration interface, exposes a REST API, stores multiple Wi-Fi profiles, automatically reconnects to available networks, and can even operate as a small IPv4 router using the networking capabilities available in the ESP8266 SDK.

Although the hardware costs only a few euros, the software architecture follows the same principles that I normally apply when developing larger production systems.

Project Summary

PlatformWemos D1 mini / ESP8266
SensorBosch BME280 over I²C
FirmwareC++ with the Arduino ESP8266 Core
NetworkingAccess Point + Station mode, multi-Wi-Fi profiles, automatic failover, and IPv4 NAPT
InterfacesResponsive web dashboard and JSON REST API
Data pipelinePython collector, systemd service, and MySQL storage
LicenseMIT License

Contents

The Original Idea

Like many engineering projects, OpenEnvRouter started with a very small objective.

Initially, I only wanted to connect a BME280 environmental sensor to a Wemos D1 mini and display temperature inside a browser.

That seemed like a simple weekend experiment.

However, once the basic prototype was working, I immediately started asking myself additional questions.

Why should configuration require editing source code?

Why not allow the device to configure itself using a web interface?

Why should it remember only one wireless network?

What happens if the configured Wi-Fi disappears?

Could it automatically switch to another network?

Could the device itself become a small wireless router?

Would it be possible to build a clean administration dashboard instead of another minimalistic embedded configuration page?

Each answered question created two or three new ideas.

Within a relatively short time the project evolved from a basic sensor demonstration into a much larger embedded platform.

Wemos D1 mini ESP8266 development board used for OpenEnvRouter
The Wemos D1 mini provides the processing, flash storage, and Wi-Fi platform for OpenEnvRouter.

Project Goals

Before writing large amounts of firmware, I defined several principles that guided every design decision throughout the project.

The first objective was local-first operation.

Many IoT products immediately require cloud accounts, remote activation, proprietary mobile applications, or external services before they become useful.

I wanted the exact opposite.

After powering the device, users should immediately see a wireless network, open a browser, configure the device, and start using it.

No registration.

No cloud dependency.

No vendor lock-in.

The second objective was usability.

Embedded devices often expose configuration pages that feel outdated, difficult to navigate, and designed only for developers.

Since this project would become part of my professional portfolio, I wanted the interface to resemble a modern web application instead of a traditional firmware configuration page.

The third objective was modularity.

Rather than placing every feature inside one large source file, the firmware would be separated into reusable components responsible for networking, sensor management, configuration storage, and user interface generation.

That structure makes the code easier to maintain and significantly simplifies future expansion.

Finally, I wanted every external integration to use open standards.

Instead of proprietary communication protocols, the firmware exposes JSON over HTTP through a REST API. This decision allows virtually any programming language or monitoring platform to communicate with the device without requiring custom libraries.

Choosing the Hardware

One of the reasons I enjoy embedded development is the challenge of creating practical solutions on limited hardware.

Instead of selecting a modern ESP32 with abundant memory and processing power, I intentionally chose the older ESP8266 platform.

The ESP8266 remains one of the most widely available microcontrollers in the embedded ecosystem. It is inexpensive, well documented, supported by a mature Arduino framework, and still powerful enough to host surprisingly sophisticated applications.

For this project I selected the Wemos D1 mini, which provides USB connectivity, integrated Wi-Fi, sufficient flash memory, and a compact form factor.

The environmental measurements are performed using a Bosch BME280 sensor connected via the I²C bus.

The sensor measures:

  • Temperature
  • Relative humidity
  • Atmospheric pressure

From these measurements the firmware also estimates altitude using the standard atmospheric pressure formula.

Although altitude estimation is primarily included for completeness, the three environmental values provide useful information for home laboratories, workshops, server rooms, and other indoor monitoring scenarios.

BME280 temperature humidity and pressure sensor used with OpenEnvRouter
The BME280 measures temperature, relative humidity, and atmospheric pressure over I²C.

Designing for Expansion

One mistake I often see in hobby projects is designing only for the current feature set.

Adding one more capability later frequently requires rewriting significant parts of the firmware because the original architecture was never intended to grow.

I wanted to avoid that situation.

Even during the earliest development stages I planned for features that did not yet exist.

For example, support for multiple Wi-Fi profiles influenced how configuration data would be stored.

Future REST endpoints affected the structure of internal objects.

Plans for historical sensor collection influenced the API design long before the first Python collector was written.

Thinking ahead required more work initially, but it dramatically reduced the amount of refactoring later in the project.

From Prototype to Platform

At some point I realized I was no longer building a simple environmental sensor.

The firmware had gradually become a small embedded platform combining several independent systems:

  • Environmental monitoring
  • Multi-network Wi-Fi management
  • Embedded web administration
  • REST API
  • Persistent configuration storage
  • IPv4 NAPT routing
  • External data collection
  • MySQL integration

Each subsystem could have existed as an independent project.

Instead, the challenge became integrating them into a coherent application that still ran reliably on an ESP8266.

That engineering challenge ultimately became the most interesting aspect of OpenEnvRouter.

Environmental Monitoring, Sensor Integration, and Building a Modern Embedded Dashboard

OpenEnvRouter environment dashboard showing temperature humidity pressure and sensor status
The Environment dashboard presents live BME280 telemetry and sensor diagnostics.

Turning Raw Measurements into Useful Information

The environmental monitoring subsystem became the foundation of the entire project.

Reading values from a sensor is relatively straightforward. Hundreds of tutorials already demonstrate how to connect a BME280 to an ESP8266 and print temperature values to the serial console.

That was never my objective.

Instead, I wanted OpenEnvRouter to present environmental information in a way that was immediately useful for everyday operation.

Rather than exposing raw numbers, the firmware continuously validates every measurement, updates the internal sensor state, and makes the latest information simultaneously available to multiple components:

  • the web dashboard;
  • the REST API;
  • external monitoring software;
  • the Python collector.

This eliminates duplicated code while ensuring that every consumer receives the same validated data.

From an architectural perspective, the sensor becomes a service rather than a standalone peripheral.

Selecting the BME280

One of the earliest hardware decisions was choosing the environmental sensor.

Several inexpensive sensors are available for Arduino-compatible platforms, including the DHT11, DHT22, BMP280, AHT20, and SHT31.

After comparing their capabilities, I selected the Bosch BME280.

The decision was influenced by several factors.

First, the sensor combines three measurements within a single package:

  • air temperature;
  • relative humidity;
  • atmospheric pressure.

Second, communication occurs through the I²C bus, requiring only four wires and leaving most GPIO pins available for future extensions.

Finally, the BME280 has an excellent reputation for stability and repeatability, making it an ideal choice for long-running environmental monitoring.

Hardware Integration

The hardware itself is intentionally uncomplicated.

The BME280 is connected directly to the Wemos D1 mini using the I²C interface.

Wemos D1 miniBME280
3.3VVCC
GNDGND
D1SCL
D2SDA

Keeping the wiring simple was an important design goal.

One of the strengths of ESP8266-based development is that inexpensive hardware can still produce surprisingly capable systems.

Anyone with a Wemos D1 mini and a compatible BME280 module should be able to reproduce the prototype within minutes.

Continuous Sensor Monitoring

Unlike simple demonstration sketches that read the sensor only when a page is refreshed, OpenEnvRouter continuously updates the environmental state in the background.

The firmware periodically performs the following sequence:

  1. Read temperature.
  2. Read humidity.
  3. Read atmospheric pressure.
  4. Calculate estimated altitude.
  5. Validate the measurements.
  6. Update the shared sensor cache.

This approach provides several advantages.

The web interface never blocks while waiting for I²C communication.

REST API requests return immediately.

Multiple clients can access sensor data simultaneously without repeatedly querying the hardware.

The architecture also makes future extensions considerably easier because every subsystem works with cached values instead of communicating with the sensor directly.

Handling Sensor Errors

Reliability was another important consideration.

Embedded devices frequently operate unattended for weeks or months.

Temporary communication failures should therefore never cause the entire firmware to become unstable.

Instead of assuming that every measurement succeeds, OpenEnvRouter tracks the overall health of the sensor subsystem.

The dashboard reports:

  • initialization status;
  • measurement validity;
  • successful reads;
  • failed reads;
  • age of the latest measurement.

This additional information proved extremely useful while testing different sensors and verifying long-term stability.

It also provides immediate feedback whenever a wiring problem or hardware fault occurs.

OpenEnvRouter BME280 dashboard with successful and failed sensor sample counters
Sensor health information makes wiring and communication problems easier to diagnose.

Building a Modern Dashboard

Many embedded web interfaces still resemble applications from the early 2000s.

Although functional, they often present large tables of text, multiple configuration pages, and little visual hierarchy.

I wanted something completely different.

The administration interface should feel closer to a modern web application while remaining lightweight enough to run comfortably on an ESP8266.

Instead of generating large HTML documents, the firmware serves compact pages that use simple CSS and JavaScript to present information clearly.

No heavy frameworks.

No unnecessary animations.

No external dependencies.

Everything is delivered directly by the microcontroller.

Despite these limitations, the result feels responsive on desktops, tablets, and smartphones alike.

Presenting Environmental Data

The Environment page focuses on clarity rather than quantity.

Instead of overwhelming users with dozens of values, only the most relevant information is displayed prominently.

The dashboard includes:

  • Temperature
  • Relative Humidity
  • Atmospheric Pressure
  • Estimated Altitude

Below the primary measurements, additional diagnostic information is available for advanced users.

These values include sensor initialization status, measurement validity, sample age, and internal counters.

Although most users will rarely need these details, they become invaluable during troubleshooting and hardware validation.

Responsive Design

One objective that remained constant throughout development was creating an interface that behaved consistently across different screen sizes.

The administration dashboard was therefore designed using responsive layout principles.

Whether accessed from a desktop computer, tablet, or smartphone, the same information remains available without requiring a dedicated mobile application.

This also simplifies deployment.

Users only need a standard web browser.

No companion application needs to be installed.

No operating system restrictions exist.

The browser itself becomes the administration interface.

Preparing for Long-Term Monitoring

Displaying live sensor values is only one part of environmental monitoring.

Historical information often becomes even more valuable.

How has the room temperature changed throughout the day?

Did humidity increase overnight?

Has atmospheric pressure been falling for several hours?

Answering these questions requires persistent storage.

Rather than implementing a local database inside the ESP8266, I deliberately separated measurement collection from historical storage.

The firmware focuses exclusively on acquiring reliable sensor data.

External applications can then retrieve that information through the REST API and store it wherever appropriate.

This separation keeps the embedded firmware compact while making future integrations significantly easier.

Why This Architecture Matters

At first glance, continuously reading a sensor and displaying several numbers may appear relatively simple.

In reality, this subsystem became the foundation upon which the rest of OpenEnvRouter was built.

Every networking feature, every REST endpoint, every monitoring integration, and every future expansion depends on accurate, reliable environmental data.

By designing the sensor layer as an independent service rather than a collection of isolated function calls, the firmware gained a level of modularity that will continue to simplify future development.

What began as a small experiment with a temperature sensor gradually evolved into a reusable environmental monitoring platform.

Multi-Wi-Fi Management, Automatic Failover, and Transforming an ESP8266 into a Small IPv4 Router

OpenEnvRouter network dashboard showing upstream Wi-Fi Internet status and NAPT state
The Network dashboard prioritizes operational status, connectivity, and routing information.

Beyond a Simple Sensor

By the time the environmental monitoring subsystem was complete, the firmware was already capable of collecting measurements, displaying them through a responsive web interface, and exposing them via a REST API.

For many hobby projects, that would have been the natural endpoint.

I decided to continue.

During testing I noticed that embedded devices often become surprisingly fragile once networking is involved. A typical ESP8266 project stores a single Wi-Fi SSID and password in flash memory. Everything works well until the router is replaced, the password changes, or the wireless network becomes temporarily unavailable.

At that point, many devices simply disappear from the network and require manual reconfiguration.

I wanted OpenEnvRouter to behave differently.

Instead of assuming that only one wireless network exists, the firmware should be capable of remembering multiple networks, selecting the most appropriate one, and recovering automatically whenever connectivity changes.

That decision eventually transformed the networking subsystem into one of the largest components of the entire project.

Designing for Real Deployments

Real environments are rarely static.

A laboratory may contain several wireless networks.

A workshop may have both employee and guest Wi-Fi.

A home office can easily have multiple access points operating under different names.

During travel, the same device might connect to completely different networks every day.

Hardcoding a single SSID simply does not scale.

OpenEnvRouter therefore stores multiple Wi-Fi profiles, each containing its own configuration and priority.

Rather than treating wireless configuration as a one-time setup procedure, the firmware continuously manages available connections throughout its lifetime.

This seemingly small architectural decision dramatically improves reliability.

OpenEnvRouter saved Wi-Fi profiles and available wireless network scan
Saved profiles support priority-based selection and automatic failover.

Multi-Profile Wi-Fi Management

Every stored profile contains considerably more than just a network name and password.

Each profile maintains information such as:

  • SSID
  • Password
  • Priority
  • Enable/disable status
  • Optional BSSID and channel hints
  • Automatic-use status
  • Internal identifiers

Separating profiles into individual configuration objects simplifies future expansion.

The profile-based structure also provides a clear foundation for future per-network options without coupling them to the connection manager.

Although those features are not yet implemented, the architecture was intentionally designed with future growth in mind.

Automatic Network Selection

When the firmware starts, it does not blindly connect to the first stored network.

Instead, it performs a Wi-Fi scan and compares the detected access points against the stored profiles.

Several factors influence the final decision:

  • Profile priority
  • Availability
  • Signal strength
  • Configuration status

The objective is not simply to establish any wireless connection.

The objective is to establish the best available connection.

This distinction becomes increasingly important as more wireless networks become available.

Failover Instead of Failure

One of my favorite aspects of this project is the automatic failover mechanism.

Imagine the following situation.

The primary wireless router loses power.

Most embedded devices immediately become unreachable.

OpenEnvRouter behaves differently.

After detecting that the current network is no longer usable, the firmware automatically searches for another configured profile.

If another known network becomes available, the device reconnects without requiring any user interaction.

From the user’s perspective, the device simply continues operating.

This behaviour makes the firmware much more suitable for unattended installations.

Internet Connectivity Is Not the Same as Wi-Fi

One lesson I learned early during development is that successfully connecting to a Wi-Fi network does not necessarily mean the device has Internet access.

Several scenarios can produce this situation:

  • the router lost its WAN connection;
  • the upstream gateway stopped responding;
  • DNS resolution failed;
  • a captive portal blocked access;
  • the network exists but has no external connectivity.

Simply checking whether Wi-Fi is connected is therefore insufficient.

To solve this problem, OpenEnvRouter performs a lightweight connectivity verification after establishing a wireless connection.

Only after Internet availability has been confirmed is the network considered fully operational.

Otherwise, another stored profile can be evaluated.

Although the implementation is intentionally lightweight, it significantly improves overall reliability.

Wemos D1 mini ESP8266 board connected as part of the OpenEnvRouter prototype
The ESP8266 operates simultaneously as an upstream Wi-Fi station and a local access point.

Simultaneous Access Point and Station Mode

Perhaps the most technically interesting networking feature is the ability to operate simultaneously as both:

  • a Wi-Fi Access Point;
  • a Wi-Fi Station.

This configuration allows the device to connect to an existing wireless network while continuing to host its own local administration network.

During installation this proves extremely convenient.

Users can always connect directly to the device through its setup access point, even after the firmware joins another network.

At the same time, administrators working on the primary network can access the dashboard using the IP address assigned by the upstream router.

This dual-access architecture provides considerable flexibility without requiring additional hardware.

Building a Small Router

While exploring the ESP8266 SDK documentation, I discovered support for IPv4 Network Address Port Translation (NAPT).

Although rarely used in hobby projects, this capability immediately caught my attention.

I wanted to understand whether the ESP8266 could function as a small wireless router while simultaneously performing environmental monitoring.

After extensive experimentation, the answer turned out to be yes.

When NAPT is enabled, wireless clients connected to the device’s access point can forward their traffic through the active upstream Wi-Fi connection.

The ESP8266 effectively becomes a miniature wireless gateway.

This functionality is certainly not intended to replace dedicated networking hardware.

However, it demonstrates just how much capability can be extracted from inexpensive embedded platforms through careful software engineering.

Devices connected to the OpenEnvRouter ESP8266 access point
Connected SoftAP clients can be reviewed directly from the administration interface.

Monitoring Connected Clients

Once routing functionality became available, another logical feature followed naturally.

If the device behaves as a gateway, administrators should also be able to see which devices are currently connected.

The Network dashboard therefore displays information about connected wireless clients, including:

  • IP address
  • MAC address
  • Number of connected devices
  • Current network state

Although relatively simple, this feature significantly improves usability during deployment and troubleshooting.

It also illustrates an important design principle that guided the project from the beginning:

Every new capability should improve the overall experience rather than exist merely because it is technically possible.

A Dashboard Designed for Administrators

Throughout the networking implementation I continuously asked myself one question:

“If I installed this device inside a server room six months from now, what information would I want to see first?”

The resulting dashboard intentionally prioritises operational status over configuration.

Rather than immediately presenting forms and settings, the interface first answers the questions administrators usually care about:

  • Is the device online?
  • Which network is currently active?
  • Is Internet connectivity available?
  • Which profile is being used?
  • How many clients are connected?
  • Is NAPT currently enabled?
  • What is the current signal strength?

Only after providing this overview does the interface expose configuration options.

This workflow makes the dashboard useful not only during installation but throughout the entire lifetime of the device.

Networking as a Platform

Originally, networking was simply supposed to allow sensor data to reach a browser.

By the end of development, it had become one of the defining characteristics of OpenEnvRouter.

Instead of treating Wi-Fi as a supporting feature, the project evolved into a platform capable of intelligently managing multiple wireless environments, recovering from failures, routing traffic, and providing continuous administration through multiple interfaces.

In many ways, this networking subsystem became the bridge between traditional embedded firmware and modern infrastructure software.

It also became one of the most rewarding engineering challenges of the entire project.

Building a REST API, External Python Collector, and Designing a Long-Term Environmental Data Platform

OpenEnvRouter temperature humidity pressure and altitude readings stored in MySQL
The optional Python collector stores validated environmental readings in MySQL.

Beyond Real-Time Monitoring

Displaying environmental measurements in a browser is useful, but only for the current moment.

As I continued working on OpenEnvRouter, I realized that the most valuable information is often hidden in historical data.

A single temperature reading tells me almost nothing.

However, observing temperature changes over several days immediately reveals useful patterns.

How warm does the server room become during the afternoon?

Does humidity increase overnight?

Is atmospheric pressure changing before a storm?

These questions cannot be answered by live values alone.

The firmware therefore needed a way to export measurements without becoming responsible for long-term storage.

That requirement ultimately shaped one of the most important architectural decisions in the project.

Separating Responsibilities

One principle I consistently follow in software engineering is that every component should have a clearly defined responsibility.

The ESP8266 is excellent at:

  • reading sensors;
  • managing wireless networking;
  • serving a web interface;
  • responding to HTTP requests.

It is not an ideal platform for storing months of historical data.

Flash memory has a limited write lifetime.

Available storage is relatively small.

Database functionality would unnecessarily complicate the firmware.

Instead of forcing the microcontroller to solve every problem, I deliberately separated the architecture into independent layers.

The firmware focuses exclusively on collecting reliable measurements.

Everything related to historical storage happens outside the device.

This separation keeps the embedded software small, reliable, and significantly easier to maintain.

Designing the REST API

Once the responsibilities were clearly defined, communication between them became straightforward.

I intentionally avoided proprietary protocols.

No binary packets.

No custom message formats.

No vendor-specific libraries.

Instead, the firmware exposes a lightweight REST API returning standard JSON documents.

This immediately provides several advantages.

Any programming language capable of performing an HTTP request can retrieve environmental measurements.

Python.

PHP.

Go.

Rust.

JavaScript.

Shell scripts.

Monitoring systems.

Home automation platforms.

The API remains equally accessible to all of them.

OpenEnvRouter environmental monitoring interface used alongside the JSON API
The same validated measurements are available through both the dashboard and the JSON API.

A Human-Readable Interface

One design decision I particularly appreciate is making every API response easy to understand without additional documentation.

For example, requesting:

GET /api/sensor

returns a JSON document similar to:

{
  "initialized": true,
  "valid": true,
  "temperature_c": 27.4,
  "humidity_percent": 45.3,
  "pressure_hpa": 1022.4,
  "altitude_m": -75.8
}

Estimated altitude is derived from atmospheric pressure and depends on the configured sea-level reference pressure. It should be treated as an environmental estimate rather than a GPS elevation.

Nothing needs to be decoded.

Field names are descriptive.

Units are obvious.

The API can be tested directly from a browser or with a simple command such as:

curl http://<device-ip>/api/sensor

That simplicity dramatically reduces the effort required to integrate OpenEnvRouter into other systems.

Monitoring the Device Itself

Sensor measurements represent only part of the available information.

During development I quickly realized that operational status is equally important.

For this reason the firmware also exposes:

GET /api/status

This endpoint provides information such as:

  • current Wi-Fi state;
  • connected SSID;
  • assigned IP address;
  • Internet availability;
  • RSSI signal strength;
  • active profile;
  • number of connected clients;
  • NAPT status;
  • access point information.

This turns the REST API into much more than a sensor interface.

It becomes a complete operational status endpoint for the entire device.

Building the Python Collector

Once the REST API existed, creating external integrations became remarkably simple.

The first companion application I wrote was a lightweight Python collector.

Its responsibilities are intentionally limited.

Every minute it performs a single HTTP request.

The returned JSON is validated.

If the response is valid, the measurements are written into a MySQL database.

If the request fails, the collector logs the error and retries during the next scheduled interval.

This approach keeps both the firmware and the collector independent.

Neither component needs to understand how the other is internally implemented.

They communicate exclusively through a stable HTTP interface.

Why Python?

Several programming languages would have been suitable for the collector.

I chose Python primarily because it provides an excellent balance between readability, portability, and ecosystem support.

The entire application remains relatively small while benefiting from mature libraries for:

  • HTTP communication;
  • MySQL connectivity;
  • scheduling;
  • logging;
  • Linux integration.

More importantly, almost every Linux distribution already includes Python, making deployment straightforward on home servers, Raspberry Pi systems, virtual machines, and cloud instances.

Storing Data in MySQL

Selecting MySQL was another deliberate decision.

Although specialized time-series databases such as InfluxDB offer additional analytical capabilities, MySQL remains one of the most widely deployed database systems in the world.

It is reliable.

Well documented.

Easy to back up.

Supported virtually everywhere.

The database schema is intentionally minimal.

Every measurement stores:

  • timestamp;
  • temperature;
  • humidity;
  • atmospheric pressure;
  • calculated altitude.

Keeping the schema simple provides flexibility.

Historical charts, dashboards, exports, and external integrations can all be implemented without modifying the firmware.

Preparing for Future Integrations

One objective throughout development was avoiding architectural dead ends.

The current collector stores measurements in MySQL.

Tomorrow it could just as easily send them to:

  • InfluxDB;
  • PostgreSQL;
  • TimescaleDB;
  • MQTT;
  • Prometheus;
  • Home Assistant;
  • Grafana;
  • cloud storage.

The firmware itself would not require any modifications.

That separation between data acquisition and data processing makes OpenEnvRouter considerably more future-proof.

Designing for Reliability

Another important aspect of the collector is that failures remain isolated.

If the database server becomes unavailable, the firmware continues operating normally.

If the ESP8266 is temporarily offline, the Linux service simply retries later.

Neither component depends on the internal implementation of the other.

This loose coupling significantly improves maintainability while making future upgrades much easier.

Building a Small Monitoring Platform

At this stage the project had evolved well beyond its original purpose.

Instead of merely displaying temperature values, OpenEnvRouter had become a complete environmental monitoring platform consisting of multiple independent components working together:

  • embedded firmware;
  • responsive administration dashboard;
  • REST API;
  • Python collector;
  • MySQL database;
  • Linux service;
  • automated data collection.

Each subsystem can evolve independently while remaining connected through simple, open interfaces.

That architecture ultimately became one of the aspects of the project I am most satisfied with.

It demonstrates that even relatively inexpensive embedded hardware can serve as the foundation for reliable, maintainable, and extensible monitoring systems when software architecture receives the same attention as hardware design.

Engineering Challenges, Memory Optimization, Lessons Learned, and Why I Chose to Release OpenEnvRouter as Open Source

Engineering Begins Where Tutorials End

One of the most common misconceptions about embedded development is that connecting a sensor to a microcontroller is the difficult part.

In reality, that is usually the easiest stage of the project.

The real engineering work begins when individual demonstrations need to become a stable system.

Every feature introduced into OpenEnvRouter affected multiple other subsystems.

Adding multiple Wi-Fi profiles influenced configuration storage.

Introducing automatic failover changed the networking logic.

Adding NAPT affected memory usage.

The web interface required additional RAM.

The REST API increased the number of concurrent operations.

Every new capability created new dependencies.

The challenge was no longer writing individual features—it was making all of them coexist reliably on hardware with extremely limited resources.

That transition from “writing code” to “designing software architecture” became the most educational part of the entire project.

Working Within the Limits of the ESP8266

Modern software development often takes hardware resources for granted.

On desktop and server platforms, adding another library rarely becomes a serious concern.

The ESP8266 offers a very different development experience.

Every kilobyte matters.

Memory allocation must be considered carefully.

Long-running applications must avoid fragmentation.

Blocking operations quickly become noticeable.

Although the ESP8266 is remarkably capable for its price, it still requires disciplined engineering.

Throughout development I continuously monitored memory consumption after introducing each new feature.

The final firmware simultaneously operates:

  • an HTTP server;
  • a responsive web dashboard;
  • a REST API;
  • an Access Point;
  • a Wi-Fi Station;
  • BME280 sensor communication;
  • LittleFS configuration storage;
  • multiple Wi-Fi profiles;
  • Internet connectivity verification;
  • IPv4 NAPT routing.

Running all of these services together on an ESP8266 required significantly more planning than implementing them individually.

Building a Maintainable Codebase

As the project expanded, keeping the source code organized became increasingly important.

Instead of collecting every function inside a single Arduino sketch, I separated the firmware into independent modules.

Each component is responsible for one clearly defined task.

For example:

  • sensor management handles environmental measurements;
  • profile management stores Wi-Fi configurations;
  • networking manages wireless connectivity;
  • the web layer generates the administration interface;
  • the main firmware coordinates networking and exposes JSON HTTP endpoints.

This modular approach provides several advantages.

Features can be modified without affecting unrelated components.

Individual modules become easier to test.

Future extensions require less refactoring.

Most importantly, the project remains understandable months after the original code was written.

That long-term maintainability was one of my primary objectives from the beginning.

Designing the User Experience

Many embedded projects focus almost exclusively on technical functionality.

The administration interface often becomes an afterthought.

I deliberately approached the dashboard differently.

Whenever I added a new feature, I asked myself a simple question:

“If I had installed this device six months ago and forgotten about it, what information would I want to see first when opening the dashboard?”

That question shaped nearly every page.

The interface emphasizes current operational status before presenting configuration options.

Administrators immediately see:

  • whether the device is online;
  • which Wi-Fi network is active;
  • whether Internet connectivity is available;
  • how many clients are connected;
  • environmental measurements;
  • overall system status.

Only after this overview do configuration controls appear.

This workflow reflects how administrators typically interact with production systems.

Reliability Over Features

One lesson that repeatedly emerged during development was that adding new functionality is relatively easy.

Making that functionality reliable is considerably harder.

A feature that works only under ideal conditions has limited practical value.

Throughout development I therefore spent significant time testing situations such as:

  • Wi-Fi disconnections;
  • router restarts;
  • sensor initialization failures;
  • incorrect passwords;
  • unavailable Internet connectivity;
  • repeated reconnections;
  • simultaneous web requests.

Many improvements visible in the final firmware resulted directly from these practical tests rather than from initial design plans.

Reliability ultimately became a feature in its own right.

Why Local-First Matters

Modern IoT ecosystems frequently depend on cloud services.

Although convenient in some situations, they also introduce additional complexity.

Internet outages.

Account management.

Subscription services.

Privacy concerns.

Vendor lock-in.

For OpenEnvRouter I deliberately chose a different direction.

Everything required for normal operation is available locally.

The device creates its own wireless network during initial configuration.

The administration interface runs directly on the microcontroller.

The REST API is served locally.

Historical storage remains optional.

Users decide whether they want to integrate external services.

Nothing in the project requires a proprietary cloud platform.

That philosophy gives users complete ownership over their own data and infrastructure.

Lessons Learned

Every engineering project teaches something beyond its original objectives.

OpenEnvRouter was no exception.

Perhaps the most valuable lesson was that software architecture becomes increasingly important as projects grow.

Initially, spending additional time designing reusable components felt slower.

Later, those early architectural decisions dramatically accelerated development.

Another important lesson was the value of incremental progress.

The firmware did not begin with routing, failover, REST APIs, or database integration.

Each feature was implemented, tested, and refined before the next one was introduced.

This iterative approach reduced debugging complexity while making the project significantly easier to evolve.

Finally, I gained a renewed appreciation for constrained systems.

Developing software within strict hardware limits encourages better engineering discipline.

Optimizations become meaningful.

Architectural decisions become visible.

Good design becomes essential rather than optional.

Why I Released the Project as Open Source

Publishing the project under the MIT License was an easy decision.

Throughout my own career I have learned an enormous amount from open-source software.

Countless developers have invested time documenting their work, explaining difficult concepts, and publishing code that others could study.

OpenEnvRouter is my contribution back to that community.

I hope the project can be useful in several different ways.

Some developers may simply want a small environmental monitoring device.

Others may be interested in the networking implementation.

Some may study the REST API.

Others may extend the firmware for entirely different purposes.

Regardless of how it is used, I believe practical engineering projects become more valuable when they can be explored, improved, and shared openly.

Looking Ahead

Although version 1.0.0 already provides a complete monitoring platform, I intentionally designed the architecture with future expansion in mind.

Some of the features I would like to explore include:

  • ESP32 support;
  • HTTPS;
  • OTA firmware updates;
  • encrypted credential storage;
  • user authentication;
  • MQTT integration;
  • Home Assistant discovery;
  • Prometheus metrics;
  • Grafana dashboards;
  • Docker-based monitoring stack;
  • support for additional environmental sensors;
  • centralized management of multiple OpenEnvRouter devices.

Most importantly, these features should build upon the existing architecture rather than replace it.

A well-designed foundation allows software to evolve naturally over time.

Conclusion

OpenEnvRouter started as a small experiment with a Wemos D1 mini and a BME280 sensor.

It gradually evolved into a complete embedded platform that combines environmental monitoring, Wi-Fi management, REST APIs, routing, Linux integration, and long-term data collection within a single open-source project.

More importantly, the project reflects the way I enjoy building software.

I prefer understanding systems rather than simply using them.

I enjoy designing architectures that remain maintainable as they grow.

I believe software should solve practical problems while remaining approachable for other developers.

OpenEnvRouter is the result of those ideas.

OpenEnvRouter demonstrates that meaningful engineering is not defined by hardware price. An inexpensive ESP8266 can become a reliable embedded platform when software architecture, networking, monitoring, and usability are treated as equally important parts of the system.

I hope the project helps other developers build better embedded software, explore the less obvious networking capabilities of the ESP8266, and create their own local-first monitoring systems.

OpenEnvRouter is available under the MIT License. The repository contains the complete firmware, Python collector, MySQL schema, installation documentation, API reference, hardware photographs, and automated build workflows.

Support the Project

OpenEnvRouter is completely open source and released under the MIT License.

If you found this project useful, learned something from it, or would like to support future development, you can make a voluntary contribution via PayPal.

PayPal: [email protected]

Every contribution helps fund new hardware, testing, documentation, and future open-source releases.

Thank You for Reading

Thank you for taking the time to explore this project.

Whether you are an embedded developer, a system administrator, a student, or simply someone interested in building practical IoT solutions, I hope OpenEnvRouter provides useful ideas, inspiration, or a solid foundation for your own experiments.

This is only the beginning of the project, and I look forward to continuing its development in future releases.

Taggs: