Maxxfan dbus-driver - dbus-maxxfan

dbus-maxxfan — a MaxxFan Deluxe on the Venus OS switch pane, over infrared

dbus-maxxfan — a MaxxFan Deluxe on the Venus OS switch pane, over infrared

The MaxxAir MaxxFan Deluxe is in a lot of camper vans, and it has no data
connection whatsoever — no VE.Direct, no Bluetooth, no wires beyond power. The
only way in is the infrared remote.

So that is the way in. An Arduino with an infrared LED hangs on a USB port of the
GX device and transmits; a driver publishes the fan on the D-Bus as
com.victronenergy.switch. The result is a proper card in the switch pane, on
the GX display, in the remote console and in VRM.

Repository: GitHub - mcgyver78/dbus-maxxfan · GitHub (MIT)

[screenshot: docs/switch-pane.png from the repository]

What you get

One card named MaxxFan with eight controls:

Control Type What it does
Fan toggle on / off
Speed slider 10 … 100 %, in steps of ten
Direction dropdown Intake / Exhaust
Lid toggle open / closed
Mode dropdown Manual / Auto (thermostat)
Auto setpoint temperature setpoint −2 … 37 °C
Resend button transmit the current state again
Beep button make the fan beep twice

Everything is also on /SwitchableOutput/<name>/…, so Node-RED and MQTT can read
and write the same values. Running the fan from a temperature sensor of your own,
or from PV surplus, is a small flow — the driver deliberately does not try to be
a thermostat itself.

Hardware

An ATmega328P board — classic Nano, Uno or Pro Mini — with an infrared LED on
pin 9 through a 180…220 Ω resistor, cathode to ground. That is the entire
circuit:

   D9 (OC1A) --[ 180 ... 220 R ]--|>|-- GND
                                 IR LED

Pin 9 is not a free choice: the 38 kHz carrier is generated by timer 1 in
hardware. That also means the sketch does not have to disable interrupts while
transmitting — a packet occupies the LED for about 150 ms, and sketches that
bit-bang the carrier lose every serial byte that arrives in that window.

The LED belongs next to the fan’s own receiver window; at ~20 mA straight off the
pin it carries a metre or two, which is plenty from there.

Installation

Install SetupHelper, then Settings →
Package manager → Inactive packages → new
:

Field Value
Package name dbus-maxxfan
GitHub user mcgyver78
GitHub branch or tag latest

Flash arduino/maxxfan_tx onto the board (no libraries needed), plug it into the
GX device, done. The driver finds the port through /dev/serial/by-id/ and asks
each candidate to identify itself before sending it anything, so it will never
talk to your VE.Direct cables by mistake.

Requires Venus OS v3.70 or newer with the new user interface — the switch pane is
a gui-v2 feature.

Infrared only goes one way

Worth being explicit about, because it shapes everything else: the fan never
answers.
What the display shows is the state the driver last transmitted, not a
reading. Use the hand held remote and the two drift apart until the next command
from the GX — which immediately puts them back in step, because every packet
carries the complete state.

Two consequences:

  • The driver does not transmit at startup. Re-sending the stored state on
    every GX reboot would start a fan somebody deliberately switched off by hand.
  • A periodic refresh every 15 minutes re-sends the current state, so a fan
    operated by hand comes back under GX control on its own. There is also a
    Resend button.

About the protocol

The packet layout was published by
skypeachblue and
wingspinner, and implemented for ESPHome by
brown-studios. This
driver builds the packet from the requested state rather than replaying recorded
pulse trains — which is what makes the thermostat setpoint reachable at all, and
incidentally shrinks the sketch from 88 % of the flash to 10 %.

Checking the encoder against the 99 signals captured from an original remote
turned up two things that differ from the published implementations. Both are
documented and the comparison script ships in tools/:

  • The symbol period is 834 µs, not the 800 µs usually quoted.
  • The setpoint is stored in Fahrenheit, and the remote’s conversion from the
    Celsius it displays truncates towards zero: degF = trunc(degC × 1.8) + 32.
    A rounded conversion is off by one degree on 20 of the 38 setpoints — 21 °C
    becomes 70 °F instead of the 69 °F the remote actually sends.

Both are tolerated by the fan, but matching the original leaves the widest margin
on a weak or off-axis path.

Why the switch service

Venus OS has no service class for a ventilation fan. com.victronenergy.switch
is the one the GUI scans for /SwitchableOutput/x/…, and it offers exactly what
this device needs: toggles, a value slider, dropdowns with labels, a temperature
setpoint and momentary buttons. The same API carries the GX IO extender, Node-RED
virtual switches and third party digital switches, so the fan sits on the switch
pane next to them with no GUI modification at all.

One thing to know if you build something similar: the stepped switch (type 4)
sends the number of the position it sits on
, not the value, and ignores the
minimum, maximum and step size the driver publishes. It cost me an hour of
watching the fan beep at every step without changing speed. Use the basic slider
(7) or the dimmer (2) for anything with a real range.

Status

Running here on Venus OS v3.79 against a MaxxFan Deluxe. Not implemented yet, but
cheap to add: an infrared receiver (TSOP38238 or similar) on D2 so the driver can
see what the hand held remote sends and stay in step with it. The decoder already
exists in the verification tool; it only needs porting to C.

Feedback and pull requests welcome.

3 Likes