It links to my Google Home installation and responds to voice commands.
https://us.store.tapo.com/products/tapo-h110-smart-ir-iot-hu...
Someone with too much time on their hands might benefit from the iroh solution....
There’s a lot that comes for free by adding all these libraries and crates and steps. But from what I can tell it comes down to:
let _ = if fan_on { fan.set_high() } else { fan.set_low() };
https://docs.iroh.computer/languages/javascript
Edit: actually, that's a Node.js-specific API. For browsers, it seems like they should have a platform-independent JavaScript/TypeScript API that includes a WebAssembly file (if needed) instead of expecting you to compile WebAssembly yourself.
Somebody wanted to do something, and they did it; it doesn't have to be any more complicated than that :-)
> Don't be curmudgeonly. Thoughtful criticism is fine, but please don't be rigidly or generically negative.
Sincerely, HN Guidelines Police :-)
Now I am not sure it is feasible but it would be interesting to have it available in esphome. I feel this is really where real adoption happens.
Sure, you could probably make this much smaller if you invented a specialized p2p fan control protocol, but that's a lot of work.
I really hope more people will play around with iroh and build stuff especially because in the last year some things have been renamed in the API to be more clear and other stuff has been simplified e.g. see this blog post https://www.iroh.computer/blog/iroh-0-94-0-the-endpoint-take...
For specific applications like transfer fans for a server room with a small amount of heat to react, you can buy an all-in-one thermostatically controlled fan: https://acinfinity.com/room-to-room-fan-8-with-temperature-c...
It's a demo of how easy it is to make even an ESP32 device available globally with iroh.
Anyway, why are you commenting here if you're not into this sort of thing? Feels like you're just trying to stir up an argument.
There's only a limited number of features that you can pack into a few buttons and a 7-segment display. If you want to sell outside the US and need to support the long-tail of non-English languages, preferably without per-country product variants, you can't even label the buttons any more, you have to rely on simple pictograms and icons.
If there's a $1 microcontroller in your device (and there often is), you're very tempted to implement lots of features which cost you almost nothing, but that kind of UI just doesn't really let you do so. Sure, you could add a proper touch screen with a localizable UI stack, with reflowable text and support for displaying Kanji and RTL languages, but that's often more expensive (and less practical) than slapping on a BLE or WiFi chip.
But that 10% is magic. A fan that switches on when air quality falls below a threshold? Not that useful in a living room, but in a workshop setting - especially a shared workshop setting? Awesome. Just awesome.
A well defined use case, in the right setting, and smart stuff can be genuinely very useful. Usually that’s not how they’re used - i know, because of the 15-20 smart things i have only one or two are genuinely useful.
Sure you can do everything manually, but I like networked things, especially those using open standards (matter over openthread) so I can connect them all together easily.
For example, every time I watch a movie the roller blinds automatically come down, the lights turn off and the fan turns down, I think thats just cool.
Yes, that's 'a C compiler', like gcc.
> before the real runtime does its work
Sort of, the program is 'the runtime', but this is backwards, languages that have 'a runtime' get the name from it running at runtime to compile/interpret source or byte code. In C what runs at runtime is just your program, whatever you compiled. (Maybe it's an interpreter though!)
What do you think ahead of time compilation is?
Pff, assuming that everyone have arms and hands much?
Also I don't see the point of a fan, I live right next to the ocean, if you want moving air, why don't you just open a window?! Talk about useless invention
Why wouldn't that be useful? People be surprised how poor their air quality generally are inside, unless they already measure it, making it better sounds useful in oh so many ways.
> i know, because of the 15-20 smart things i have only one or two are genuinely useful.
What are those things? I have about 70-80 "smart things" by now, but every single one is genuinely useful, otherwise I wouldn't install them in the first place. Lots of open/closed sensors, soil moisture, temperature+pm2.5 sensors, water taps and so on.
First because it’s the wrong solution to the problem. As I understand it bad air quality in a home is created by specific activities like cooking, vacuuming, or lighting candles/incense/smoking. So you solve the problem by turning on the fan when you cook, opening the windows when you vacuum, and by not using incense or smoking.
Second because you can have a dumb sensor and switch the fan on with your hand when it goes turns orange or red.
> I have about 70-80 "smart things" by now, but every single one is genuinely useful
We have different definitions of genuinely useful. I’m glad you find you setups useful tho - you do you!
If you live in Europe the northern hemisphere, you are probably suffering through a heat wave right now. Let's do something to bring back some chill, using iroh.
The previous ESP32 examples demonstrated echo protocols. But typically ESP32s are used for more than just echoing data; you use an ESP32 as a cheap means to read sensors and drive actuators.
So we are going to write a very simple end-to-end example using an ESP32 to measure temperature and control a fan. Unlike most IoT devices, there won't be any cloud component. Just a tiny website that you can use from anywhere in the world using any browser that supports WebAssembly.
As the base, we are going to use an ESP32-WROVER devkit with 4 MiB of PSRAM, so we have all of iroh's networking capabilities available, including a relay connection, and remote control it from anywhere in the world. You can also use a M5StickC-Plus2, but you will have to adapt the GPIO pins.
If you have another devkit such as an ESP32-S3 with PSRAM, you should be able to run the examples with small config tweaks.
If all you have is an ESP32 without PSRAM, you can still use iroh. But you need to disable the relay connection and tweak QUIC buffers so we don't run out of memory. Use an appropriate example from iroh-esp32-examples as base.
As the first step, we are going to copy over an echo example from iroh-esp32-examples. We will use server-esp32-psram for the ESP32 binary.
For the client side we just use client, it runs on a desktop PC and is as vanilla as it gets.
This is going to be a smart fan example, so we just rename server-esp32-psram to server-smart-fan, and client to smart-fan-cli.
Note that we need different toolchains and want to keep the option to use a patch of iroh for the ESP32 variant, so the two directories are completely separate Rust projects. We do not use a workspace.
Let's try it out once before we do modifications. cargo run on the server project will search for an ESP32 connected via USB and flash it. So we just connect our ESP32 with a USB-C cable.
The initial release build will take some time, since we are compiling not just iroh, but also the operating system to the xtensa architecture. Subsequent builds will be faster, since the compilation results are cached in the .embuild directory.
Flashing itself will never be really fast, because the data rate to the chip is very limited. We can make it go a bit faster by setting the ESPFLASH_BAUD environment variable. My chip supports 230400 baud, but YMMV. If it doesn't work just run without the environment variable set, then it will use safe defaults.
We need to tell the ESP32 how to connect to WLAN. In the example we just use another environment variable WIFI_CONFIG=SSID:PASSWORD. Set this to your local WLAN.
You can do a single export WIFI_CONFIG=SSID:PASSWORD so you don't have to pass it every single time.
As you can see from the flash output, we are pretty close to the limit of the flash size.
You might think that every single added line of code will get you over the limit, but that is not the case. Additional pure Rust dependencies such as irpc add very little size.
What we should have now is a simple echo server running on the ESP32.
First of all, how do we assign the endpoint id? We want the ability to assign an endpoint id, but even if we don't do so we want the endpoint id to be stable after reboots. So the ESP32 should not generate a random one on each startup.
Instead we generate and store the secret key in non volatile memory on first startup and reuse it on subsequent startups. Non volatile memory is not overwritten by flashing, so we will get the same endpoint id for the same device unless we explicitly delete non volatile memory.
On startup the device will try to connect to WiFi using the given credentials. If that doesn't work it will hang. This happens before any iroh endpoint setup.
For a real product you would want two alternative WiFi configs and some recovery option, but we are going to skip this for the example.
Once the endpoint on the ESP32 starts up, we get very familiar output:
The device has been assigned a local IP address 192.168.0.186:51831 by the DHCP of the router. It prints both a long and short ticket, but for now is only reachable locally using the long ticket that contains the IP address.
Next it tries figuring out its location in the world using QAD.
Assuming you are connected to the internet, after a short time it will figure out which relay is closest and set that as its home relay.
At this point it is reachable from anywhere in the world using the short ticket that contains just the endpoint id.
So now let's try it out using the client binary.
Locally you can use the long ticket and bypass the relay, but as soon as the endpoint has published its home relay it should be reachable globally.
The client has an option to disable relay. If you do that you will only be able to use the long ticket.
It also has options for mDNS, but we are not using mDNS for this project.
You might think that stopping the cargo run --release will stop the binary. But this is not the case. It just stops the connection to the device. The endpoint will happily continue to run as long as it has power.
You can even disconnect it and plug it into a separate USB-C power supply, and it will boot up again with the same endpoint id. This is the whole point. The ESP32 is a fully self-contained embedded computer. It just needs power.
If you really want to shut it down, unplug it or delete the flash using espflash erase-flash.
Now that we have confirmed that the example works, we can start making it actually do something.
Since we want to build a smart fan, the first thing we need is a temperature sensor. We are going to use a DHT22 temperature and humidity sensor.

A DHT22 sensor. Photo by L293D, CC BY-SA 4.0, via Wikimedia Commons.
Wiring up the sensor is very simple. It has three wires, two for +3.3V (do not use +5V!) and GND and one for data.
If you use the ESP32 dev kit with the extension board and a breadboard, it will power the breadboard rails with +3.3V and +5V from the USB port. You can pull only ~100 mA without an external power supply, but it is enough for the DHT22, which only takes 1.5 mA while measuring and even less when idle.
We need to connect the middle wire to one of the many GPIO ports of the ESP32. We will choose GPIO 26, but you can use almost all GPIOs for this. Some GPIOs have special functions during boot, but GPIO26 does not.
Just to test the sensor, we will print out the sensor readings using tracing.
Troubleshooting:
Make sure you have +3.3V and GND wired up the right way. If not you will notice the sensor getting hot and have a few seconds to react before smoke comes out.
The DHT22 should work with the signal wire directly connected to GPIO 26. But if you get frequent timeouts you can try adding a pull up resistor of 3.3 kΩ that connects the GPIO to the +3.3V rail. Do not connect to the +5V rail!
Don't be afraid to get things wrong. Both the DHT22 and the ESP32 are pretty robust and forgiving for wiring mistakes if you correct them quickly!
Commit that adds sensor reading
At this point we have an iroh endpoint that supports our echo protocol, and local sensor readings. Obviously we want to read the sensor remotely as well.
To do that we are going to use irpc. If all we wanted to do is to read a single sensor, this would be overkill. But using irpc will make it easier to extend the protocol in the future.
We will define the protocol in a separate crate smart-fan-proto, since it will be used by both the client and the ESP32 itself.
The first rpc call will be just reading the current sensor values. For now this is just a temperature and humidity, but in the future there might be more. So we are going to use a sensor state struct.
Here is the complete protocol definition:
On the server side we have a struct that carries the current sensor state in a mutex:
We will add more in the future, but for now the client side just does a single reading.
Maybe we still want a simple way to check that the endpoint is up. We could of course add a dummy endpoint to the irpc protocol, but we can also just keep supporting the echo protocol.
When using the router, you can combine as many protocols as you want!
Commit that adds separate protocol crate
The CLI tool is nice for debugging, but what we really want is a GUI to show the temperature and, eventually, to control the fan. We could write a native GUI using dioxus that works on all major platforms. But who wants to install an app for this? So let's do a WASM GUI that runs in the browser.
I am not a javascript developer, so the WASM GUI is vibe coded. I just briefly checked it.
This first version is just a remote thermometer: paste a ticket from your device to see its temperature and humidity live over the relay.
To run the GUI, go into smart-fan-wasm and run npm run build; npm run serve, and then open the GUI on http://localhost:8080. But you don't have to! The GUI in this blog post is live, you can just paste your ticket and try it out.
Commit that adds WebAssembly GUI
At this point we have a remote accessible temperature and humidity meter. But we want a smart fan. So let's add an output. We are going to use a 5V desktop computer fan like, for example, the Noctua NF-A14-5V. This connects to the +5V and GND rail and has a single PWM control wire to switch or throttle the fan.
If you don't have such a fan, you can also just wire up a LED with a ~330 Ω resistor, or wire up a relay to control a household fan. It's more fun with a real fan though!
The actuator will be controlled using a simple logic: if the temperature is above some value, switch on the fan. We add a tiny bit of stickiness so the fan doesn't constantly toggle on and off.
Then to set the actual pin:
So far, so good.
Commit that adds output switching
At this point the major components are in place, and I stopped keeping the commit history clean.
During development, you could just change the protocol at will and make sure both client and server are up to date.
For a new production deployment, we would just change the ALPN to make it clear that this is a new protocol.
But what if we wanted the old remote thermometer GUI to still work? In that case we have to be careful to only add new methods to the RPC protocol, and leave the current methods in the same place in the enum with the same structures. Irpc is using postcard, and unlike json or protobuf, postcard makes zero attempts to be self-describing. Trying to read a different struct than what was written will just fail or produce weird results.
We can still evolve the protocol without changing ALPNs by adding new RPC methods at the end of the protocol enum. Then we can keep the ALPN, and old GUI versions will continue to work.
We have a much more principled approach for schema evolution, irpc-schema, but that will be the subject of another blog post.
So here is our new compatible schema enum:
The read-only GUI uses only GetStatus: it shows temperature, humidity, and whether the fan is running — but can't change anything, so it's safe to share publicly. It's the same page as the thermometer above, just talking to the newer protocol:
We now want the ability to set the threshold above which the fan starts to work. But we still want the ability to have the smart fan GUI hosted on a public website. We could rely on the endpoint id being secret, but that is not a good idea. If we use discovery, data is published for the endpoint id on dns.iroh.link. Also, we might want to retain the ability to share a read-only view of the fan state.
So let's extend the RPC protocol with a call to set the threshold, but add some authentication. We will use a simple secret that is baked into the code, then use it only in the new RPC call.
For the new GUI, we just add a slider that shows the current threshold and can be used to change it.
Here it is, running live in the browser as WebAssembly. Paste a ticket from your device to connect, and optionally enter its FAN_API_SECRET to unlock the threshold slider:
So far we got a breadboard with an ESP32, a LED or fan, and a DHT22 sensor wired up. In many cases this is where it ends for such projects - you confirm that it works, then have it sit on the desk for a few days, then disassemble it because you need the parts for the next fun project or because you want to clean up.
In this case I wanted to assemble the parts into an usable widget. So I designed an enclosure to hold the fan and the breadboard.
The enclosure in Bambu Studio. Download the STL.
I didn't bother with using a soldered prototype board or even a custom PCB. Here is the end result:

The finished fan: a Noctua NF-A14 in the printed housing, with an opening for the DHT22.

The electronics — ESP32 on a breadboard, DHT22 sensor and fan connected — glued into the base.
I used an ESP32-S3 because I had a spare lying around, and also changed the GPIO pins to simplify wiring. With the ESP32-S3 you only get one side of the breadboard accessible.
I put a QR code on the outside that opens the readonly web UI, and a QR code inside that opens the web UI with the secret for controlling the temperature threshold.
The QR codes are blurred, but if you manage to recover the original image with some fancy deconvolution algorithm, you can control my fan until I change the endpoint id.
The complete code for this blog post can be found in iroh-smart-fan. The 3D-printable enclosure is available as case.stl.
There are a wide variety of input and output devices available that work out of the box with an ESP32. For input, there is everything from simple temperature and light sensors to precise CO2 and VOC sensors, accelerometers, GPS, microphones, even various cameras. The ESP32 can configure the GPIOs as analog-to-digital converters, so you can easily use simple analog sensors such as potentiometers, photoresistors or switches.
The ESP32 also comes with a powerful built-in sensor suite - since you can get a lot of details about the WiFi signal. WiFi scanners or even complete pose estimation can be done without any external sensors.
For output, there is everything from LEDs to displays. One nice trick is to drive a normal RC servo directly from the +5V bus and a GPIO configured as PWM output. If you want to drive larger loads, you can either use a transistor or mosfet for amplifying the GPIO output signal, or use a PWM compatible solid state relay to drive any household appliance (at your own risk!).
I was never much of an electronics expert, but wiring stuff up to an ESP32 is like lego, it is very simple, and the ESP32 is also very forgiving of small wiring mistakes.
If you use the long tickets and the local LAN, then yes. If you use existing relays, you do have a small component in the cloud. But it is a completely application independent, open source, commodity component. You can use our public relays to play around, our hosted relays for a production deployment, or even self host if you don't mind the complexity of operating relays in multiple regions and don't dread the 3AM phone call.
If you have a special requirement for a commercial project, talk to us.
Iroh is a dial-any-device networking library that just works. Compose from an ecosystem of ready-made protocols to get the features you need, or go fully custom on a clean abstraction over dumb pipes. Iroh is open source, and already running in production on hundreds of thousands of devices.
To get started, take a look at our docs, dive directly into the code, or chat with us in our discord channel.