Skip to content

🌬 Yet another hardware/software (async) project developed in Micropython on an ESP8266 to allow controlling a fan based on temperature, button or REST-API/UI including MQTT integration

Notifications You must be signed in to change notification settings

engdan77/iot_fan_controller

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

83 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Internet-Of-Things Fan Controller

Table of contents

  1. The story and reflections behind this project
  2. Requirements
  3. Screenshots & video
  4. Installation
  5. How does it work?
  6. REST API
  7. Hardware design
  8. Install using pre-built image
  9. Build a frozen image from source and upload to ESP8266
    1. Manually build binary image without upload
    2. Flash and upload from within Container
    3. Software design
  10. Special thanks to

The story and reflections behind this project

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 πŸ˜πŸ‘πŸ»

Requirements

  • Hardware
    • ESP8266 (NodeMCUv3 recommend), now this should run on any MCU with support for GPIO such as big-brother ESP32
    • DHT22
    • Switch
    • A computer fan
    • HT7333 or a buck converter to convert high to low voltage
    • Soldering iron, solder and some wires
    • Willingness to build following the sketch below πŸ˜‰
  • A computer to upload the data from

Screenshots & video

You can also watch a short video here.

Installation

  1. Connect the compents according to hardware design

  2. Install esptool by this instruction if you haven't or have other software doing the same

  3. Clone the repository

git clone https://github.com/engdan77/iot_fan_controller.git
  1. 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

How does it work?

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.

REST API

Get status

URL : /status

Method : GET

Success Response

Code : 200 OK

Content examples

{
    "state": "off",
    "temp": "27.3",
}

Turn on/off fan

URL : /status?state=on

Method : GET

Success Response

Code : 200 OK

Content examples

{
    "state": "on",
    "temp": "25.2",
}

Hardware design

This is a sketch that show how you would solder if you're using an NodeMCU.

https://i.ibb.co/rkmCtnR/fan-control-board-schem.png

Install using pre-built image

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 a frozen image from source and upload to ESP8266

Manually build binary image without upload

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.

Flash and upload from within Container

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.

Software design

uncached image

Special thanks to

About

🌬 Yet another hardware/software (async) project developed in Micropython on an ESP8266 to allow controlling a fan based on temperature, button or REST-API/UI including MQTT integration

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published