Table of contents
- The story and reflections behind this project
- Requirements
- Screenshots & video
- Installation
- How does it work?
- REST API
- Hardware design
- Install using pre-built image
- Build a frozen image from source and upload to ESP8266
- Special thanks to
I noticed that my Macbook Pro became rather warm β¨οΈ while working and thought I would drill a hole and place fan under the surface of the laptop in the desk, I also remembered I both had a few ESP8266 (NodeMCU v3, let's call it MCU) and DHT22 in box somewhere to be used in some context and this was a good oportunity get use of those allowing the temperature π‘ of the surface to control whether that fan should be on or off to avoid unnecesary noise. I first begun thinking I could spend a moment soldering the this together with a BC547 transistor to allow a 3.3v from GPIO of the MCU allow a higher voltage 12v to the fan βοΈ. Easy-peasy.. hmm, it might also be nice to have a button π to be able to force an on/off so let's put there as well to another GPIO... but before trying this on a breadboard, I got caught with some fundemental trap while verifying the transistor working, and thankfully to my electronic-genius to friend Erik helped me get it right.. Now next soldering .. and a lot of cursing π€¬ - honestly it must be difficult finding anyone worse than me soldering but finally those pieces in its place and now to the part I find most fun, programming Micropython to do the work for me. With a few lines of code l just doing a quick while-loop to determine the temperatur and supply voltage to a GPIO was extremely easy, but it made me think bigger .. wouldn't it be nice to have a RESTful-API to allow me to control this fan through the network ...? Well, that in itself is easily done .. now, the challenge here is concurrency - we do need to assure we maintain the state of the button, and the temperature at the same time as we need to assure that the MCU also responds to incoming TCP/IP packets the code needs to following asynchronous paradigm due to the fact that this is a single-core RISC-processor without support for threading. π€ Thankfully Micropython does support asyncio that makes this possible by async/await keywords. To save some efforts writing the web-framework I found this nice picoweb to the rescue built as async. Now for readability I developed classes for button-presses and temperature with builtin subroutines checking their states, now exposing these to the web-framework there was a need to subclass a couple of methods to allow this.... now almost there ... now I was able to use REST to also check status and also suppled methods to update configuration... hmm .. π‘ wouldn't it be even nicer to build a captive portal π² that many products does have to simplify configuration, I went into the rabbit-hole learning everything about captive portals that involves quite a lot of spoofing-mechanism not only involving HTTP, DHCP but also DNS-requests .. I found some projects built trying this out, but didn't find it stable enough so I left this (for the time beeing at least) with some better insight of this for the future. I did manage to supply code that allows the MCU to broadcast an essid "fan control" that allows one to access if the Wifi configured is not properly connected - this is allows me to alway configure the device if Wifi isn't connecting. The captive portal gave me the idea that it would also be nice to have some sort of a web-configuration-interface for the initial configuration instead of hard-code configuration or send as REST (JSON) .. found some boilerplate for that but now I dug deeper into the world of CSS to make it look nice, AJAX using JQuery to allow me to nicely have the web-interface with help of my REST-API in real-time (or near) present the state of the fan and what the current temperature is. With this interface you could also configure Wifi-settings ... now since I am happy user of the great Home Assistant (HASS) project where its heart of home-automation is an MQTT-broker, I also thought that I might as well add MQTT-client to allow this MCU to inform my HASS what the temperature currently is... That's cool, that way I can e.g. have the home inform me by shouting to me through the SONOS-speakers when my laptop burns in flames π₯ π .. Ok, cool .. now while squeezing more and more Python-code while the project grew successfully it finally more and more often got something like..
MemoryError: memory allocation failed, allocating 4084 bytes
Oh dear ... now what can one expect from a such small device with a cost equallent to a chocolate-cake .. I read and learned that NodeMCU v3 in particular has 4MB flash, which is the "permanent" storage which is quite a lot, but now it turns out that this MCU has roughly 50KB of usable RAM to use (in fact see like 21KB after REPL been started) .. Slightly less than Commode 64 π so quite impressive that you can really do this much with that little memory and considering what you get for the same cost as you would pay for a candybar π«. I've learnt that one could save memory by freezing βοΈ your modules into byte-code (as you may know Micropython does a compilation before you run the code), and you do this by adding those .py file into the "modules" folder before compiling a binary you can upload to the MCU, there was also another lesson learned that to allow Micropython to address all of those 4MB flash you would need to configre it to do so instead of those defaul 512KB flash that the even slimmer/cheaper models ESP-01 does have.. this was described in this thread. Now also thanks to docker-images such as this one made it easy to build your own image without the hassle of configuring the cross-compiler. Now since it is better being safe than sorry, even though since I've build a frozen code saving RAM and yet no memory allocation failures it turns out that one could easily implement a Watch Dog Timer π that make sure that assure that in case of a larger exception we have an interrupt that assures the MCU resets itself... and now if MQTT is being enabled one would easily get informed when this happens.
Now .. finally, with a binary-image of less than 610KB uploaded to the MCU it now supports πΎ .. took a few more hours than anticipated π€
- Fan control by
- Temperatures based on under/above threshold
- Button clicks
- REST-API
- Web-interface
- Temperatue readings by
- Web-interface
- Push to MQTT-queue (for future home automation)
- Configuration by
- Web-interface
- REST-API
- Watch Dog in case of unpredictable errors
All coded using async ... and now with the cost around a couple of β¬ .. personally thinking that is rather awesome πππ»
- Hardware
- A computer to upload the data from
You can also watch a short video here.
-
Connect the compents according to hardware design
-
Install esptool by this instruction if you haven't or have other software doing the same
-
Clone the repository
git clone https://github.com/engdan77/iot_fan_controller.git
- Upload the firmware, or you like to compile the firmware yourself follow the instruction further down
esptool.py --port /dev/cu.usbserial-1410 --baud 115200 write_flash --flash_size=detect 0 iot_fan_controller/dist/esp8266/firmware-fan-control.bin
The first 5 seconds you have an option to
- click button once - factory default the device
- click button twice - the device will start in WEBREPL mode using the IP/Wifi configured or default to 192.168.4.1 (essid: fan_control) where you can remotely access its repl/prompt or access its filesystem through using this tool.
- Do not nothing ... then the main loop will start by itself..
Unless you have configured the device you will find a essid fan_control, that you can connect to in which you can then go to http://192.168.4.1 and configure
- Wifi configuration
- MQTT, if you enable you need to configure broker etc
- Trigger temp - the fan will start when above this temp β
- Override time - this is the time in seconds the fan will either be off/on based on manual "button click" or through web-interface or REST-API
From this interface you can also read the current status of the fan (and also change) as well reading the current temperature.
URL : /status
Method : GET
Code : 200 OK
Content examples
{
"state": "off",
"temp": "27.3",
}
URL : /status?state=on
Method : GET
Code : 200 OK
Content examples
{
"state": "on",
"temp": "25.2",
}
This is a sketch that show how you would solder if you're using an NodeMCU.
You can basically download tool such as esptool to upload it using
esptool.py --port /dev/cu.usbserial-1410 --baud 115200 write_flash --flash_size=detect 0 ./firmware-fan-control.bin
And replace the serial port you are using, and if you like to reset current flash before you can use
esptool.py --port /dev/cu.usbserial-1410 --baud 115200 erase_flash
Build the docker image of the master branch. The custom Dockerfile will add src as frozen and update the entrypoint
docker build -t fancontrol-build . && docker create --name fancontrol-build-container fancontrol-build && docker cp fancontrol-build-container:/micropython/ports/esp8266/build-GENERIC/firmware-combined.bin firmware-combined.bin && docker stop fancontrol-build-container && docker rm fancontrol-build-container && docker rmi && fancontrol-build
To specify a particular version of micropython provide it through the build-arg
. Otherwise the HEAD of the master branch will be used.
docker build -t fancontrol-build --build-arg VERSION=v1.8.7 .
The firmware can then be uploaded with the esptool
esptool.py --port ${SERIAL_PORT} --baud 115200 write_flash --verify --flash_size=detect 0 firmware-combined.bin
Here ${SERIAL_PORT}
is the path to the serial device on which the board is connected.
If you have built the image directly on your host (Linux), you also can flash your ESP directly by running a container from the image. I prefereably erase flash memory of ESP8266 before starting flash a new firmware
docker run --rm -it --device ${SERIAL_PORT} --user root --workdir /micropython/ports/esp8266 fancontrol-build make PORT=${SERIAL_PORT} erase deploy
Here ${SERIAL_PORT}
is the path to the serial device on which the board is connected, generally is equal to /dev/ttyUSB0.
- Erik Wallebom for always helping with the electronic challenges
- Micropython comments in forums and good and tool like data_to_micropython by Peter Hinch
- picoweb project by Paul Sakolovsky
- Thanks to Paul Falstad for his Circuit simulator