Embedded web server for controlling Windmi heat pumps via Modbus TCP.
Disclaimer: This is a hobby project not affiliated with, endorsed by, or connected to Rotenso or any of its affiliates. This software is provided "as-is" without any warranty. Users bear full responsibility for the use of this program. The author is not liable for any damages, losses, or incorrect operation resulting from the use of this software.
This project provides a web-based interface for monitoring and controlling Rotenso Windmi heat pumps. The controller connects to the Waveshare Modbus gateway (or compatible Modbus TCP devices) and exposes a REST API for reading status and setting parameters.
- Web Interface: Built-in HTTP server with static HTML page for local control
- REST API: JSON-based endpoints for status, mode control, and temperature setpoints
- Modbus TCP: Direct TCP communication with Waveshare gateway (transparent mode)
- Modbus RTU over Serial (RS-485): Serial port communication for devices without TCP gateway
- Multi-threaded: Separate threads for HTTP server and Modbus operations
- Thread-safe: Lock-free SPSC queues for inter-thread communication
- Comprehensive Logging: Structured logging with levels, timestamps, and component tags
- Unit Tests: Google Test framework for verified functionality
- CMake Build System: Cross-platform build configuration
The application uses two threads communicating via lock-free SPSC queues:
-
Main Thread (HTTP Server)
- Mongoose HTTP server
- Static file serving
- REST API endpoints
- JSON request/response handling
-
Modbus Thread
- TCP connection to Modbus gateway
- Register polling (every 30 seconds)
- Control logic and state machine
- Write verification
┌─────────────────┐ ┌──────────────────┐
│ Main Thread │ │ Modbus Thread │
│ (HTTP Server) │ │ (Modbus I/O) │
├─────────────────┤ ├──────────────────┤
│ Mongoose │ │ Modbus Client │
│ WebServer │────▶│ ControlLoop │
│ REST API │ │ StatusMonitor │
└─────────────────┘ └──────────────────┘
.
├── CMakeLists.txt # Main build configuration
├── Makefile # Legacy Make build (backward compatibility)
├── include/ # Public headers
│ ├── core/ # Core controller logic
│ ├── modbus/ # Modbus client interfaces
│ ├── web/ # Web server interfaces
│ └── utils/ # Utility classes (Logger, Config, etc.)
├── src/ # Source code
│ ├── core/ # Core implementation (C++)
│ ├── modbus/ # Modbus implementation (C++)
│ ├── web/ # Web server implementation (C++)
│ └── utils/ # Utility implementation (C++)
├── tests/ # Unit tests
│ ├── core/
│ ├── modbus/
│ ├── web/
│ └── utils/
├── docs/ # Documentation
│ ├── doxygen/ # Doxygen configuration
│ └── *.md # Technical documentation
├── static/ # Web interface files
├── mongoose/ # Mongoose library (submodule)
└── googletest/ # Google Test (submodule)
- Build Tools: CMake 3.16+, GCC with C99/C++17 support, or Clang
- Dependencies:
- Mongoose (embedded web server) - included as submodule
- Google Test - included as submodule
- pthreads (standard on most systems)
# Clone the repository with submodules
git clone --recursive https://github.com/yourusername/wpomp.git
cd wpomp
# Create build directory
mkdir build && cd build
# Configure with CMake
cmake ..
# Build
make -j$(nproc)
# Or use the Makefile (legacy)
cd ..
make setup
make# Run with defaults (gateway 192.168.123.10:8899)
./build/windmi-control
# Run with custom gateway IP and port
./build/windmi-control --ip 192.168.1.50 --port 4196
# Run with custom web server port
./build/windmi-control --web 3000
# Enable demo mode (no hardware required)
./build/windmi-control --demo
# Run with debug logging
./build/windmi-control --log-level DEBUG
# Run self-test
./build/windmi-control --selftest
# Show all options
./build/windmi-control --helpFor devices connected via RS-485 without a TCP gateway, use the serial port mode. The controller uses 9600 8N1 as the default configuration (matching Windmi device defaults).
# Connect via serial device with default 9600 8N1
./build/windmi-control --serial /dev/ttyUSB0
# Connect with custom baud rate
./build/windmi-control --serial /dev/ttyUSB0 --baud 19200
# Connect with 8E1 configuration
./build/windmi-control --serial /dev/ttyUSB0 --parity E
# Connect with 8N2 configuration
./build/windmi-control --serial /dev/ttyUSB0 --stop-bits 2
# Enable RS-485 direction control
./build/windmi-control --serial /dev/ttyUSB0 --rs485Note: Serial mode (--serial) is mutually exclusive with TCP mode (--ip, --port). You cannot specify both.
Supported baud rates: 9600, 19200, 38400, 57600, 115200
Parity options: N (none/8N1), E (even/8E1), O (odd/8O1)
Stop bits: 1 or 2
| Option | Long Form | Description | Default |
|---|---|---|---|
-i <addr> |
--ip <addr> |
Modbus gateway IP address | 192.168.123.10 |
-p <port> |
--port <port> |
Modbus gateway TCP port | 8899 |
-w <port> |
--web <port> |
HTTP server port | 8080 |
-d |
--demo |
Demo mode (simulated device) | disabled |
-l <level> |
--log-level <level> |
Logging level (TRACE/DEBUG/INFO/WARN/ERROR/FATAL) | INFO |
-o <file> |
--log-file <file> |
Log file path (default: console only) | none |
-t |
--selftest |
Run self-test suite | disabled |
--serial <device> |
- | Serial device for Modbus RTU (e.g., /dev/ttyUSB0) | none |
--baud <rate> |
- | Baud rate | 9600 |
| `--parity <N | E | O>` | - |
| `--stop-bits <1 | 2>` | - | Stop bits: 1 or 2 |
--rs485 |
- | Enable RS-485 direction control | disabled |
-h |
--help |
Show help message | - |
Get current heat pump status.
{
"mode": 2,
"runningStatus": 2,
"dhwTemperature": 45.5,
"dhwTarget": 48.0,
"heatingTemperature": 38.2,
"heatingTarget": 38.0,
"outdoorTemperature": 12.3,
"dhwPriority": 1,
"errors": [],
"timestamp": "2026-06-01T14:32:05Z"
}Mode values:
0: OFF1: DHW only2: Heating only3: DHW + Heating
Running status values:
0: Off1: Cool2: Heat4: DHW7: Defrost20: Anti-freeze
Set working mode.
curl -X POST -H "Content-Type: application/json" \
-d '{"mode": 2}' http://localhost:8080/api/set-modeSet DHW target temperature.
curl -X POST -H "Content-Type: application/json" \
-d '{"target": 48.0}' http://localhost:8080/api/set-dhwSet heating target temperature.
curl -X POST -H "Content-Type: application/json" \
-d '{"target": 36.0}' http://localhost:8080/api/set-heatingSet DHW priority.
curl -X POST -H "Content-Type: application/json" \
-d '{"priority": 1}' http://localhost:8080/api/set-priorityThe controller exposes 4 UI working modes that map to the device's native capabilities:
| UI Mode | Mode Register | Heating Target | DHW Target | DHW Priority |
|---|---|---|---|---|
OFF (0) |
0 (Off) | - | - | - |
DHW only (1) |
2 (Heat+DHW) | 25°C (min) | user setpoint | 1 |
Heating only (2) |
2 (Heat+DHW) | user setpoint | 40°C (min) | 0 |
DHW+Heating (3) |
2 (Heat+DHW) | user setpoint | user setpoint | 1 |
Note: The device's mode register (0x002C) only supports "Off" and "Heat+DHW" natively. The controller emulates "DHW only" and "Heating only" by setting minimum target temperatures to suppress demand.
Edit include/config.h to change compiled-in defaults:
#define MODBUS_DEFAULT_IP "192.168.123.10"
#define MODBUS_DEFAULT_PORT 8899
#define WEB_SERVER_DEFAULT_PORT 8080
#define LOG_DEFAULT_LEVEL WINDMI_LOG_INFOUse command-line arguments (see above) to override defaults at runtime.
mkdir build && cd build
cmake -DWINDMI_BUILD_TESTS=ON ..
make -j$(nproc)
# Run tests
ctest --output-on-failuremkdir build && cd build
cmake -DWINDMI_BUILD_DOCS=ON ..
make docs
# View documentation
firefox docs/html/index.html- ControlLoop: State machine for working modes, target temperature management
- StatusMonitor: Thread-safe status data aggregation
- ModbusClient: Modbus TCP communication with gateway
- WebServer: HTTP server and REST API implementation
- Logger: Structured logging with levels, timestamps, and component tags
- Config: Application configuration management
- JsonHelpers: JSON serialization/deserialization
- SpscQueue: Single-producer single-consumer lock-free queue
- Create new source files in appropriate
src/subdirectory - Add public headers to
include/with proper namespace - Update
CMakeLists.txtto include new sources - Add unit tests in corresponding
tests/subdirectory - Update documentation as needed
- Core Tests: ControlLoop state machine, StatusMonitor thread safety
- Modbus Tests: Frame encoding/decoding, CRC calculation
- Web Tests: HTTP routing, JSON parsing
- Utils Tests: Logger filtering, Config loading, Queue operations
# All tests
ctest --output-on-failure
# Specific test suite
./tests/utils/test_utils --gtest_filter='LoggerTest.*'
# Specific test
./tests/modbus/test_modbus --gtest_filter='ModbusClientTest.ReadWrite'Connection refused to Modbus gateway:
- Verify gateway IP address with
--ipoption - Check gateway is reachable with
ping - Verify gateway port (default 8899)
Web server port already in use:
- Use
--webto specify alternative port - Stop conflicting service or use
sudofor port < 1024
No logs visible:
- Check log level with
--log-level DEBUG - Verify console output is not being redirected
Build errors:
- Ensure submodules are initialized:
git submodule update --init --recursive - Verify CMake version:
cmake --version - Check compiler supports C++17:
g++ --version
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow existing code style (C++17, C99)
- Add unit tests for new functionality
- Update documentation as needed
- Ensure all tests pass before submitting PR
This project is licensed under the GNU General Public License v2.0 - see the LICENSE file for details.
- Mongoose - Embedded web server library
- Google Test - Unit testing framework
- CONVERSION_PLAN.md - C++ conversion plan and architecture
- LOGGING_MIGRATION_PLAN.md - Structured logging implementation
- TEST_PLAN_AND_FIX_IMPLEMENTATION.md - Test coverage and fixes
- working-modes.md - Working mode strategy details
This project was developed for Windmi heat pump integration.
For technical support or questions, please open an issue on GitHub.