Is this another silly idea / tribute to insanity, by needlessly bolting the internet and a subscription service to a device, which usually degrades performance and adds built-in obsolescence (not to mention a 22second boot-time)?
Well maybe, or maybe it's just a way of providing people with alternatives, but either way it involves Adafruit IO as an alternative to Home-Assistant / Node-RED or the official IKEA smart-hub + smart devices, and Adafruit's open-source IOT firmware called WipperSnapper as an alternative to ESPHome.
It's possible to use on the free-plan on Adafruit IO (you do NOT need IO+), or offline with a little coding, so I don't feel bad about suggesting it...
Here's a demo video of it in action: (Adafruit IO Dashboard on left, WipperSnapper device page on right, Speed dial of Air Purifier + Wind Sock on Picture-in-Picture video)
This project was a personal desire to put Adafruit IO through it's paces, and see how far I could go in recreating a project designed for ESPHome + Home Assistant. Traditionally I would have assumed it was too complex for the Actions and Dashboard of Adafruit IO if I used a Wippersnapper device, instead relying on Node-RED to string things together, but now that I work on the WipperSnapper project I was curious how far it had come / could go.
The basic idea was to take a £60 "dumb" air purifier with a 3-speed dial (£70 with carbon filter) and add a WiFi micro-controller, so that it can be switched on and the speed controlled according to the values sent from a separate Air Quality sensor (Particulate Matter <2.5µm), and a user-adjustable control panel hosted as an Adafruit IO Dashboard.
There are of course more expensive versions of the same family of air purifiers that include wifi support for another £85 (£145 total for less effective "smart" units plus separate additional £60 smart-hub required). And it would be unfair of me to not mention that IKEA also have cheaper "dumb" version (less effective), but more interestingly a version built into a table which is at least a good idea as far as use of space goes...
Original Guide / Project - Converting to Adafruit IO
The original forum post on the home-assistant forum is very helpful, including high resolution photos, and recipes for ESPHome/HomeAssistant. Please read it, that will save me repeating everything. This is what we will be basing our guide off of, but it's worth mentioning now that it limits our speed adjusting ability unless we follow a second guide relating to direct driving of the fan control signal.
What I liked about the idea, was it felt relatively simple in "making" terms, and used only basic inputs and outputs (although inverted). The challenges would be the initial speed setting based on dial position, then getting control to also be affected by a slider on a Dashboard, and finally overriding control if the air quality is too dirty. An extension project is to replace the PCB with our own variable frequency control signal (prototyped as a Buzzer which supports varying Frequency in the IO interface) for the fan motor (and tachometer input to verify fan speed).
For ease of recreation I decided to name everything as the home-assistant article does where they use a Wemos LOLIN D1 device with pins named D3 etc (so mine are labelled Wemos D1 pin D3) even though I'm using a different boards and pin numbers. That way anyone following both guides can see what's been changed / copied as this screenshot of the WipperSnapper device page shows below (notice the real pins in grey boxes at the end of each row):
Exported JSON for WipperSnapper Device Configuration
You can use this JSON below, save it to a file on your local computer (call it something.json), import it to a WipperSnapper device, and it will recreate the setup above. Just click the Auto Config button on the WipperSnapper device page (next to New Component button at the top), then choose Import Config and upload the JSON file you saved. If you use another device that doesn't match the pinout, then you'll need to edit each component (Cog icon) and update the pin to the one on your board.
{
"exportVersion": "1.0.0",
"exportedBy": "tyeth_staging_admin",
"exportedAt": "2024-01-02T15:03:17.355Z",
"exportedFromDevice": {
"board": "qtpy-esp32s2",
"firmwareVersion": "1.0.0-beta.74"
},
"components": [
{
"name": "Dial Position Speed0️⃣ (Off) Wemos D1 pin D1 (inverted=true)",
"pinName": "D17",
"type": "push_button",
"mode": "DIGITAL",
"direction": "INPUT",
"period": 0,
"pull": "UP",
"isPin": true
},
{
"name": "Dial Position Speed1️⃣ Wemos D1 pin D5 (inverted=true)",
"pinName": "D9",
"type": "push_button",
"mode": "DIGITAL",
"direction": "INPUT",
"period": 0,
"pull": "UP",
"isPin": true
},
{
"name": "Dial Position Speed2️⃣ Wemos D1 pin D6 (inverted=true)",
"pinName": "D8",
"type": "push_button",
"mode": "DIGITAL",
"direction": "INPUT",
"period": 0,
"isPin": true,
"pull": "UP"
},
{
"name": "Dial Position Speed3️⃣ Wemos D1 pin D7 (inverted=true)",
"pinName": "D5",
"type": "push_button",
"mode": "DIGITAL",
"direction": "INPUT",
"period": 0,
"pull": "UP",
"isPin": true
},
{
"name": "🌬️Output Speed1️⃣Wemos D1 pin D4 (inverted=true)",
"pinName": "D36",
"type": "non_latching_relay",
"mode": "DIGITAL",
"direction": "OUTPUT",
"isPin": true
},
{
"name": "🌬️Output Speed2️⃣Wemos D1 pin D3 (inverted=true)",
"pinName": "D37",
"type": "non_latching_relay",
"mode": "DIGITAL",
"direction": "OUTPUT",
"isPin": true
},
{
"name": "🌬️Output Speed3️⃣Wemos D1 pin D2 (inverted=true)",
"pinName": "D35",
"type": "non_latching_relay",
"mode": "DIGITAL",
"direction": "OUTPUT",
"isPin": true
}
]
}
My original intentions were to ensure I could undo anything that I did to the Air Purifier, even considering if I could do no damage to the device at all. That led me to make the decision to sacrifice one Air Purifier in the name of science / education (I have a second that will remain pristine/undamaged), and cut the rotary switch connections like the Home-Assistant guide indicated. To that end even though I cut the metal contact/wires from the rotary switch, I still chose to stick JST headers onto the PCB instead of soldering new wires directly. That way I could link the two JST connectors together to remove my modification and leave the device "dumb" again.
In hindsight, I should have launched straight into V2, which actually just replaces the main board and rotary switch, leaving the device repairable quite easily. I think I was afraid of just sending clock signals and reading RPM straight from the 24V fan without having the chance to do it in stages. I've also since starting this guide found a nearly identical switch as a replacement or to avoid damaging the original https://www.lcsc.com/product-detail/Rotary-Switches_ALPSALPINE-SRBV141201_C470372.html
I also used a small screwdriver to bend the edge of the pins further out from the switch to allow my tool to get in and snip. The edge ones either side were easy, the others I did half a pin at a time and you'll possibly notice the twisted look of each one, due to the screwdriver usage when levering the pins out further from the switch, and later when bending them up a bit both to make soldering easier and ensure good isolation / separation.
Take your JST 5pin connector and bend the pins down a bit (I went from 90degrees to about 75degrees as mine started half-way because they are vertical through-hole style) to reach the lower switch connectors that are cut. I included a male/female combination to give the socket pins alignments and reinforcement during the bend. I also trimmed the pins at a slight angle to allow pointing the JST connector away from the black inductor on the PCB (you can see the lower 3 pins are not as long in the photo below).
Use something to temporarily stick in place or find something permenant. I did one with double sided sticky tape and then later added super-glue (cyano-acrylate). Solder in place, but before you do - make sure you can fit your plug / dupont cables in at that angle without clashing with the black inductor on the PCB.
Repeat with the other side. The home assistant guide recommends removing the pins, but we have safely severed and separated them so effectively the same thing. By attaching a connector at the bottom we can not only send our signals that we read from the switch or internet, but also connecting a cable between both JST sockets would repair the board.
Plug in your connector or dupont jumper wires (they fit in the JST socket) before soldering, to ensure the other protruding pins on the board aren't in the way. (I found the dupont jumper wires fit perfectly between the protruding pins.)
Powering our microcontroller (QT-PY ESP32-S2 or anything that fits in the space)
One advantage of using the QT-PY series is they have solder pads for battery terminals on the back tolerating 3-6V DC, with built in protection diode, and no battery charging circuitry. If using something else and directly connecting to the 5V pin of a board then use some kind of protection diode.
I've gone with an MP1584EN breakout board, which takes 28v max and outputs an adjustable 0.8-20v.
I got a ten pack for about $10, here was my exact product and here is an identical alternative.
I wired those with both JST connectors, and a panel mount USB-to-wires adapter (for easy swapping of MCU mainboard while prototyping). While connected to the IKEA 24volt power supply, I attached a multi-meter (measures voltage) to the output connection and adjusted the screw until approximately 5volts was reached.
Speaking of Wiring, tell us more...
Well I'd love to, but I haven't created a fritzing diagram yet due to lacking the switch part, but maybe I'll come back and remedy the situation in the future. For now you can see a video that shows the positioning of components and wiring layout as described above.
It feels like being generous to leave a note here also indicating that I brought out all 5 wires from the JST sockets on both sides of the PCB, so I could more easily prototype, and like the reddit article shows only 3 of the wires are needed for the output side of the PCB(blue), so I joined the other two together (ground) after prototyping like the article shows (Green wire joined to Brown wire via a white wire that was knotted to reduce length and hanging down lowest in the video)
I can't deny, it's not a quick two click operation, more like a 5click sequence repeated twelve times...which felt more like one hundred. This was partly why I was testing the idea, to see how possible and how bad it would be to recreate, but more importantly how much easier it will be once Actions supports the new Blockly interface - and further more when that supports multiple comparisons and maths functions.
The problem was that having the existing physical speed dial affect things takes quite a few actions just to flip the 3 output lines which are inverted. Then there's the issue of adding a user-control element which can override the dial state, which requires adding an additional new "communal" feed to publish new fan-speed instructions (instead of driving the output feeds directly), updating the actions for the dial changes to publish there, and the actions that output new speeds to subscribe to the new fan-speed feed. Then we can create a dashboard with a slider that subscribes and publishes to the new fan-speed feed.
The last element is to get a device with a particle sensor publishing to a feed (adafruit sell a couple, but I've got my favourite Sensirion SEN54 producing data to a PM2.5 feed so will use that), and then we need actions to respond to the particle levels. I also wanted those levels to be adjustable so there are "settings" sliders too which point to related feeds like speed_3_ppm
.
This creates a couple of problems, Actions currently (*Dec 2023) only support one comparison per action, so we can't have more than one threshold for Particulate Matter or they would conflict, which makes my 4 threshold sliders useless except for one or two of them (speed0_ppm, speed1_ppm, etc).
e.g. we can only have one action for if PM2.5 >= 100(speed3_ppm) then fan_speed =3
and a second action using the same or a separate threshold like if PM2.5 < 100(speed3_ppm) then fan_speed = 2
The second problem is which signal drives the speed? The answer is the latest one, which works well if you change the sensor polling time to a few minutes (maybe even 15), so that the user can override the speed with the physical dial or dashboard slider if it's currently too high for (up to) a 15minute period without the PM2.5 sensor cranking it back to max 30seconds later (if the PM2.5 poll time was 30secs instead of 15mins).
Show us the Actions:
I should probably do a truth table or something for this, but for now this page / Playground-Note is just a scratchpad before V2, so here are the actions that I've got setup if you're trying to follow along and recreate it.
Long story short: switch off the others when another signal is activated, or all off if off (but everything is inverted so 0 means activated and 1 means off)
Also make sure to UNTICK the checkbox for "Notify on reset" when editing each action or you'll face unexpected behaviour.
In the future I'll have some JSON to recreate them automatically too (and hopefully the dashboard)
Does it work? Yes!
Yes, as previously mentioned it can take a little while to boot (up to 22seconds depending on distance to WiFi router), but then immediately returns to last fan level. The particle sensor connection works well, and manually changing the physical speed dial on the air purifier updates the real fan speed (over the internet and back before the fan gets updated) in a fraction of a second. Using the adjustable speed slider on the dashboard is in theory even quicker.
I've also tested using an IFTTT connection to trigger the fans via Google Assistant (publishing to adafruit IO fan-speed feed), using a couple of static phrases, "Google activate the air scrubbers" and "Fans to full power". I can't show you those as they don't work on the adafruit IO staging server and I haven't remade the latest version of the project in my other account on the production server.
I did have to remap the pins and feeds once when switching from a feather to the QTPY, although the feather would fit it was painfully tight, and the only one of that board I had which I still need for work, hence swapping to the QTPY. Using Blockly to edit the Actions and select the feeds was a breeze, and I look forward to more people getting to use it, and more so the future possibilities it will unlock.
Having used it for a fortnight to great success, I'm trying to find the right "Wind-Sock" to illustrate the devices greatness, but alas my maker skills have so far ended at some poor paper based wind indicator resembling some kite-like wind-sock with wings held up by an old bit of 3d printed PETG (single line+layer) "string". Imagine the idea of something better that you could possibly imagine, that's what I want, but if you're reading this then I probably just whipped this existing monstrosity into a Video and called it done instead...
Squeezing it back in the case:
Tiny dab of hot glue here and there tamed the beast and it was almost too subtle afterwards, so I added the sticker.
Good as new ;)