Temperature-Based Smart Fan

Mornings too cold, but gets too hot by noon? By hooking up a temperature sensor to the Omega, we can use the data it provides to modulate the speed of a fan - cooling us down only when we need it!

This kind of setup is used in many places: the cooling fans in your laptop or desktop computer operate in the same way. Other applications include home-brewing beer or wine kegs and anywhere temperature control is required.

Smart fan all set up!


Skill Level: Intermediate ~ Advanced

Time Required: 40 Minutes

There’s a lot of implementation details in this project that will change depend on the exact hardware you have access to. We used a D18B20 1Wire temperature sensor, for example. For the fan, we recommend a computer case fan, since those are quite easy to come by and works decently well.

We also cooked up a DC motor with a 3D printed rotor setup just like in the Omega2 Maker Kit since we had all of those handy.

To control the fan, we’ll be using a python script and the Onion PWM Expansion Python Module to control the fan speed. We also use a library to operate the 1Wire sensor, but your methods may vary depending on your exact sensor.

All the code we used is written for a case fan with a transistor switching it. It can be found in Onion’s iot-smart-fan repository on GitHub.



Follow these instructions to set this project up on your very own Omega!

1. Prepare

First let’s get the Omega ready to go. if you haven’t already, complete the First Time Setup Guide to connect your Omega to WiFi and update to the latest firmware.

Plug in the PWM Expansion to the Dock and grab all the components:

2. Install the Required Software

We need Python and the Onion PWM Expansion Python Module to make this work:

opkg update
opkg install python-light pyPwmExp

Everything else will be included in the GitHub repo.

3. Connect the Fan

Computer case fans are voltage driven, but we can cheat by using PWM with a transistor to switch the supply voltage.

If you have jumpers handy, we recommend using them as a bridge between the header of the fan and the PWM expansion.

First, we’ll have to set up the transistor. For our lab setup, we used an S9014 NPN transistor with a 2-wire PC case fan. If you use a different model, make sure to note which pin is the base/collector/emitter.

If you use a PNP transistor, your fan will automatically turn on unless you set the PWM output to 100%. This is because PNP transistors turn ‘on’ when the base draws current, when the PWM channel is at 0% duty, it draws a tiny bit of current - enough to turn on the transistor!

Most commonly, case fans have three pins/wires - one of which is a tachometer output. If you’re using one of these, make sure there’s no power being supplied to the output pin, this will cause damage to the fan.

The output pin sends the current speed of the fan, it can be used in your code to check if the fan is working as a bonus!

We connected the power supply to the PWM expansion for cleaner wiring.

  1. Connect the transistor to the breadboard across 3 empty rows.
  2. Connect the (-) (usually black) wire of the fan to the transistor’s collector pin (right pin when looking at the flat front).
  3. Connect the (+) (usually red) wire of the fan to an empty row a few spaces away.
  4. Connect the Vcc pin on the PWM Expansion’s S0 channel to the (+) pin of the fan using a M-F jumper wire.

  5. Connect one end of the 1kΩ resistor to the transistor’s base pin (middle).
  6. Connect the other end of the resistor to the SIGNAL pin on the PWM Expansion’s S0 channel using a M-F jumper.
  7. Connect the transistor’s emitter pin (left pin when looking at the flat front) to one of the Expansion Dock’s GND pins using a M-M jumper.

  8. Connect the capacitor across the fan’s (+) and (-) wires where they are connected to the breadboard.
    • If you have a polarized capacitor with the (-) or (+) side clearly marked, make sure to match the terminals with the fan’s ((-) to (-), (+) to (+))!

This circuit will now switch the Fan’s voltage based on the PWM signal from channel 0!

The capacitor acts as a simple low-pass filter to supply the fan with a smooth analog voltage.

4. Wire up the Temperature Sensor

This part is written assuming you’re working with the D18B20, if your sensor is different, you may have to find a guide elsewhere on wiring it properly.

The D18B20 has a pinout that looks like this:

D18B20 Temperature Sensor Pin Layout

NOTE: the second graphic is a bottom view, where the pins are pointing towards you (we may have fried a sensor by misreading this one).

Now we can connect the sensor to the Expansion Headers.

  1. First, connect the temperature sensor to the breadboard across another three empty rows.
    • Leave some space from the transistor so you can easily interact with it!
  2. Connect the GND pin of the sensor to a GND pin on the Expansion Header using a M-M jumper wire.
  3. Next, connect the middle pin (DQ) to GPIO1 on the Expansion Header using a M-M jumper.
  4. Connect the VDD pin to a 3.3V pin on the Expansion Header using a M-M jumper.
  5. Finally, connect the 5.1kΩ resistor across the sensor’s VDD and DQ pins (right and middle respectively).

Your setup is now complete!

5. Get the Project Code

The code for this project is all done and can be found in Onion’s iot-smart-fan repo on GitHub. Use git to download the code to your Omega: navigate to the /root directory, and clone the GitHub repo:

cd /root
git clone https://github.com/OnionIoT/iot-smart-fan.git

5.5. Using a Different Sensor

There’s a good bit of setup for the temperature sensor - initialization, communicating, and parsing.

If you have a different sensor than the the one we’re using, you’ll have to modify the project code. The code that sets up the D18B20 1-wire sensor can be found in the lines between #~~~ SENSOR SETUP BEGIN and #~~~ SENSOR SETUP END.

Additionally, you’d probably need to change the function used to get the sensor data:

        temp = sensor.readValue()

One important thing to note is that the values assigned to the temp variable must be integer or float.

6. Calibrate and Customize

You can edit the config.json to change the possible speed range of the fan and restrict the temperature range to which the fan reacts:

    "tempMax" : "40",
    "tempMin" : "18",
    "dutyMin" : "60",
    "dutyMax" : "100",
    "frequency" : "1000",
    "fanType" : "case"

The dutyMin and dutyMax parameters control the minimum and maximum duty cycle of the signal being sent to the fan, thereby controlling the fan speed. The tempMin and tempMax parameters specify the temperature range in which to enable the fan. The fan speed has a linear relationship with the temperature when it is between the min and max temperature.

If you find that the fan does not spin when current is applied, you may have to increase the dutyMin to overcome the static friction in the fan’s shaft bearing. Once it gets up to speed, you can then lower the duty and the fan will still be able to spin.

Using A Different Fan

If you would rather use the H-Bridge and DC Motor setup, you’ll have to make some changes to the code. Namely, you’ll have to swap out the OmegaPwm class with the hBridgeMotor class from omegaMotors.py. Check the pin-outs that we’ve put in by default in iotSmartFan.py to make sure you’re correctly connecting the H-Bridge to the Servo Expansion.

For a detailed guide on how to set this up, check out the wiring instructions in the Maker Kit DC Motor experiment.

To change up the code, open up iotSmartFan.py and change this line:

    fan = OmegaPwm(FAN_PWM_CHANNEL)

To this:


And this line:


To this:


Code Highlight

Two of the key components in this project are the temperature sensor and the motor drivers, found in temperatureSensor.py and omegaMotors.py.

The output from the 1-Wire temperature sensor contains a lot of unnecessary information such as the device address, connection acknowledgements, and other fields. The __readOneWire() internal method of the TemperatureSensor class extracts the temperature value and converts it to degrees Celsius:

def __readOneWire(self):
        # device typically prints 2 lines, the 2nd line has the temperature sensor at the end
        # eg. a6 01 4b 46 7f ff 0c 10 5c t=26375
        rawValue = self.driver.readDevice()

        # grab the 2nd line, then read the last entry in the line, then get everything after the "=" sign
        value = rawValue[1].split()[-1].split("=")[1]

        # convert value from string to number
        value = int(value)

        # DS18B20 outputs in 1/1000ths of a degree C, so convert to standard units
        value /= 1000.0
        return value

The method to set the duty cycle for a servo fan, setDutyCycle() uses the Onion pwmExp class to easily control it:

def setDutyCycle(self, duty):
        """Set duty cycle for pwm channel"""
        ret     = pwmExp.setupDriver(self.channel, duty, 0)
        if (ret != 0):
            print 'ERROR: pwm-exp setupDriver not successful!'

        return ret