Smart controller WiFi firmware
The VanTurle smart controller is an ESP32-C3 powered upgrade kit, compatible with your Maxxfan®, that exposes much more flexibility than the IR remote or button panel. It can turn the fan off and on, adjust the speed, reverse the direction of rotation, and even measure the current power consumption of the fan.
This manual covers the WiFi firmware stack, which enables a web app, REST API, websocket, MQTT client and Tuya integration. A separate Bluetooth firmware build exists for the Home app on iOS and and a companion app on Android. Only one of these can be on the device at a time, as they use the same hardware resources. Select the one you want during checkout.
Connecting to the controller
When first booted, the controller is in AP mode and hosts a “hotspot” WiFi network that you can connect to for initial setup. Once configured, it switches to STA mode and connects to your local WiFi network, where it can be accessed from any device on the same network. The hotspot network configuration is as follows:
| SSID | VanTurtle Fan |
|---|---|
| Password | Something like: 0000-0000 |
| Address | 192.168.4.1 |
Connect to this network from a phone or laptop. Most devices will automatically prompt you to open the captive portal, which opens the controller’s web interface directly. If that prompt does not appear, open a browser and navigate to http://192.168.4.1 manually. Take care to go to the http url, and not https.
Once you have the web app open, you should see the slider controls for the fan and be able to set the speed. If all works as it should, you can proceed to the settings (cog icon in the top right) and select the WiFi settings.
Fill in your vans WiFi network name (SSID) and password, then save the settings. The controller should reboot and attempt to connect to your WiFi. If the credentials were correct and the connection is successful, the controller will switch to STA mode. If there was an error connecting to WiFi, it will fall back to AP mode so you can try again.
If your network supports mDNS (most home routers do), the controller will be accessible at http://vanturtle-fan.local. Otherwise you will need to find the IP address assigned to the controller by your router (check its DHCP table) and use that to access the web app.
Using the web app
The main screen shows a vertical slider. Drag it up or down to set the fan speed, or tap a position on the track to jump to that speed. The slider has 10 steps plus an off position at the bottom.
Three buttons sit below the slider:
| Auto temp | Toggles automatic temperature hold mode. |
|---|---|
| Open / close | Opens or closes the fan lid, even if off. |
| Reverse airflow | Toggles the fan direction between forward and reverse. |
When the browser cannot reach the controller the entire remote fades out to indicate the disconnected state. The app reconnects automatically every few seconds. The remote becomes interactive again as soon as the connection is re-established.
When on a mobile device, you can add the web app to your home screen for a more native app-like experience. In Safari, tap the share icon and select “Add to Home Screen”. In Chrome, open the menu and select “Add to Home Screen”. The exact steps may vary depending on your device and browser.
MQTT
The controller can connect to an MQTT broker on your local network (or remote if you really want to) and publish fan state changes to configurable topics, as well as accept commands by subscribing to those same topics. This makes it straightforward to integrate with many systems, including Home Assistant, Node-RED (like on the Victron Cerbo GX), or any other system that speaks MQTT.
MQTT is disabled by default. To enable it, open the settings, go to the MQTT section, and fill in at least the broker address. The controller will connect on next save. Leave the broker address blank to disable MQTT again.
All topics are freely configurable and none are set by default. Only topics with a value entered in the settings are subscribed to or published on. Each topic carries a plain-text value.
| Topic | Direction | Values |
|---|---|---|
| Speed | Publish & subscribe | Integer 1–10 |
| Direction | Publish & subscribe | 0 = maybe forward, 1 = maybe reverse |
| Auto temp | Publish & subscribe | 0 = off, 1 = on |
| On/off | Publish & subscribe | 0 = off, 1 = on |
| Open/close | Subscribe only | Any value triggers the action |
| Voltage | Publish only | Floating point, e.g. 12.345 (volts) |
| Current | Publish only | Floating point, e.g. 1.234 (amps) |
| Power | Publish only | Floating point, e.g. 15.678 (watts) |
| Keepalive | Publish only | Publishes 1 every 60 seconds |
All publish topics use QoS 1. The controller publishes the full fan state immediately after connecting to the broker, so the broker’s retained values are always up to date from the first connection.
When a broker is configured to retain messages, the controller will receive its own last-published values back when it reconnects. Without suppression this can cause the controller to act on its own state reports as if they were new commands. The Echo suppress setting specifies a window in milliseconds during which incoming messages are ignored after the controller publishes. Setting this to around 500 is enough to filter out most self-echoes without delaying legitimate external commands. Set it to 0 to disable.
REST API
The controller exposes a REST API over HTTP on port 80. This section covers the /state endpoint, which reads and controls the fan. To GET the current state:
GET http://vanturtle-fan.local/state
Example response:
{
"active": true,
"speed": 6,
"direction": 0,
"auto_hold": true,
"voltage": 12.341,
"current": 0.874,
"power": 10.786,
"has_calibration": true,
"is_calibrating": false
}
| active | Whether the fan is running. |
|---|---|
| speed | Current speed level, 1-10. |
| direction | A guess on the airflow direction, don’t trust it. |
| auto_hold | Whether auto-hold is enabled. |
| voltage | Voltage at the fan terminals in volts. |
| current | Current draw in amps. |
| power | Power in watts as reported by the INA226. |
| has_calibration | Whether the controller has been calibrated. |
| is_calibrating | Whether a calibration is currently in progress. |
Use the POST endpoint to update one or more fan state fields. Send a JSON body with any subset of the writable fields. Unrecognised fields are ignored. The response is the full updated state, identical in format to the GET response.
POST http://vanturtle-fan.local/state
Content-Type: application/json
{"active": true, "speed": 8}
| active | Boolean or 0/1. Turns the fan on or off. |
|---|---|
| speed | Integer 1–10. Sets the fan speed. Values outside this range are ignored. |
| direction | Toggles the fan direction. The value does not matter. |
| auto_hold | Boolean or 0/1. Enables or disables auto-hold. |
State changes made through the REST API are reflected in the web app and published over MQTT and Tuya in the same way as changes made from any other source.
WebSocket
The controller exposes a WebSocket endpoint at /ws. It carries two things: real-time fan state updates pushed from the controller to all connected clients, and the raw UART log output. The websocket is preferable over polling the REST API for real-time updates, and is required for receiving the log output.
State updates are sent on connect and on every state change. The format is identical to the GET /state response:
{"active":true,"speed":6,"direction":0,"auto_hold":true,"voltage":12.341,"current":0.874,"power":10.786,"has_calibration":true,"is_calibrating":false}
In addition to state, the controller forwards its internal log output to all connected clients. Log frames carry a type field to distinguish them from state updates:
{"type":"log","raw":"I (12345) MQTT: Connected to broker"}
It’s also possible to send commands to the controller over the websocket. Send a JSON text frame with a command field and an optional value. The controller applies the command and broadcasts an updated state to all clients.
| Command | Value | Effect |
|---|---|---|
active |
Boolean, 0/1, or 2 |
Turns the fan on or off. Value 2 toggles. |
speed |
Integer 1–10, or "+" / "-" |
Sets the speed. The string forms increment or decrement by one step. |
direction |
Any (ignored) | Toggles the fan direction. |
hold |
Boolean or 0/1 |
Enables or disables auto-hold. |
openclose |
N/A | Triggers a momentary open/close. |
get |
N/A | Replies with the current state to this client only, without broadcasting. |
calibrate |
N/A | Starts an INA226 recalibration. Has no effect if one is already running. |
reset |
N/A | Triggers a factory reset, erasing all stored settings. |
For instance, set speed to 7:
{"command":"speed","value":7}