Note
Hello, welcome to the SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts Community on Facebook! Dive deeper into Raspberry Pi, Arduino, and ESP32 with fellow enthusiasts.
Why Join?
Expert Support: Solve post-sale issues and technical challenges with help from our community and team.
Learn & Share: Exchange tips and tutorials to enhance your skills.
Exclusive Previews: Get early access to new product announcements and sneak peeks.
Special Discounts: Enjoy exclusive discounts on our newest products.
Festive Promotions and Giveaways: Take part in giveaways and holiday promotions.
👉 Ready to explore and create with us? Click [here] and join today!
3.1.4 Smart Fan(MCP3008)
Note
Depending on your kit version, please identify whether you have ADC0834 or MCP3008 and proceed with the matching section.
Introduction
In this project, we will use motors, buttons and thermistors to make a manual + automatic smart fan whose wind speed is adjustable.
Required Components
In this project, we need the following components.
Schematic Diagram
T-Board Name |
physical |
wiringPi |
BCM |
SPICE0 |
Pin 24 |
10 |
8 |
SPIMOSI |
Pin 19 |
12 |
10 |
SPIMISO |
Pin 21 |
13 |
9 |
SPISCLK |
Pin 23 |
14 |
11 |
GPIO22 |
Pin 15 |
3 |
22 |
GPIO5 |
Pin 29 |
21 |
5 |
GPIO6 |
Pin 31 |
22 |
6 |
GPIO13 |
Pin 33 |
23 |
13 |
Experimental Procedures
Step 1: Build the circuit.
Note
The power module can apply a 9V battery with the 9V Battery Buckle in the kit. Insert the jumper cap of the power module into the 5V bus strips of the breadboard.
For C Language Users
Step 2: Get into the folder of the code.
cd ~/davinci-kit-for-raspberry-pi/c/3.1.4-2/
Step 3: Compile.
gcc 3.1.4_SmartFan.c -o SmartFan -lwiringPi -lm
Step 4: Run the executable file above.
./SmartFan
As the code runs, start the fan by pressing the button. Every time you press, 1 speed grade is adjusted up or down. There are 5 kinds of speed grades: 0~4. When set to the 4th speed grade and you press the button, the fan stops working with a 0 wind speed.
Once the temperature goes up or down for more than 2℃, the speed automatically gets 1-grade faster or slower.
Note
If it does not work after running, or there is an error prompt: "wiringPi.h: No such file or directory", please refer to Install and Check the WiringPi.
Code
#include <wiringPi.h>
#include <wiringPiSPI.h>
#include <stdio.h>
#include <softPwm.h>
#include <math.h>
#define SPI_CHANNEL 0
#define SPI_SPEED 1000000
#define MotorPin1 21
#define MotorPin2 22
#define MotorEnable 23
#define BtnPin 3
int read_ADC(int channel)
{
if (channel < 0 || channel > 7) return -1;
unsigned char buffer[3];
buffer[0] = 1; // Start bit
buffer[1] = (8 + channel) << 4; // Single-ended mode and channel
buffer[2] = 0;
wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);
int result = ((buffer[1] & 3) << 8) | buffer[2];
return result;
}
int temperture()
{
int analogVal = read_ADC(0);
double Vr = 3.3 * analogVal / 1023.0; // Use 3.3V as Vref for MCP3008
double Rt = 10000.0 * Vr / (3.3 - Vr);
double temp = 1 / (((log(Rt / 10000.0)) / 3950.0) + (1 / (273.15 + 25.0)));
double cel = temp - 273.15;
double Fah = cel * 1.8 + 32;
printf("Celsius: %.2f C Fahrenheit: %.2f F\n", cel, Fah);
return (int)cel;
}
int motor(int level)
{
if (level == 0) {
digitalWrite(MotorEnable, LOW);
return 0;
}
if (level >= 4) {
level = 4;
}
digitalWrite(MotorEnable, HIGH);
softPwmWrite(MotorPin1, level * 25);
return level;
}
void setup()
{
if (wiringPiSetup() == -1) {
printf("wiringPi setup failed!\n");
return;
}
if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
printf("SPI setup failed!\n");
return;
}
softPwmCreate(MotorPin1, 0, 100);
softPwmCreate(MotorPin2, 0, 100);
pinMode(MotorEnable, OUTPUT);
pinMode(BtnPin, INPUT);
}
int main(void)
{
setup();
int currentState, lastState = 0;
int level = 0;
int currentTemp, markTemp = 0;
while (1) {
currentState = digitalRead(BtnPin);
currentTemp = temperture();
if (currentTemp <= 0) continue;
if (currentState == 1 && lastState == 0) {
level = (level + 1) % 5;
markTemp = currentTemp;
delay(500);
}
lastState = currentState;
if (level != 0) {
if (currentTemp - markTemp <= -2) {
level = level - 1;
markTemp = currentTemp;
}
if (currentTemp - markTemp >= 2) {
level = level + 1;
markTemp = currentTemp;
}
}
level = motor(level);
}
return 0;
}
Code Explanation
int read_ADC(int channel)
{
if (channel < 0 || channel > 7) return -1;
unsigned char buffer[3];
buffer[0] = 1; // Start bit
buffer[1] = (8 + channel) << 4; // Single-ended mode and channel
buffer[2] = 0;
wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);
int result = ((buffer[1] & 3) << 8) | buffer[2];
return result;
}
This function is used to read analog input from MCP3008 on the specified channel. It sends a 3-byte SPI command and returns a 10-bit digital value between 0–1023.
int temperture()
{
int analogVal = read_ADC(0);
double Vr = 3.3 * analogVal / 1023.0; // Use 3.3V as Vref for MCP3008
double Rt = 10000.0 * Vr / (3.3 - Vr);
double temp = 1 / (((log(Rt / 10000.0)) / 3950.0) + (1 / (273.15 + 25.0)));
double cel = temp - 273.15;
double Fah = cel * 1.8 + 32;
printf("Celsius: %.2f C Fahrenheit: %.2f F\n", cel, Fah);
return (int)cel;
}
The temperture() function reads the thermistor analog signal via MCP3008,
calculates voltage, resistance, then converts to Celsius and Fahrenheit using
the thermistor formula (Steinhart–Hart approximation).
int motor(int level)
{
if (level == 0) {
digitalWrite(MotorEnable, LOW);
return 0;
}
if (level >= 4) {
level = 4;
}
digitalWrite(MotorEnable, HIGH);
softPwmWrite(MotorPin1, level * 25);
return level;
}
This motor() function controls fan speed via PWM.
Level ranges from 0–4, where 0 turns the fan off and each level increases the duty cycle by 25%.
void setup()
{
if (wiringPiSetup() == -1) {
printf("wiringPi setup failed!\n");
return;
}
if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
printf("SPI setup failed!\n");
return;
}
softPwmCreate(MotorPin1, 0, 100);
softPwmCreate(MotorPin2, 0, 100);
pinMode(MotorEnable, OUTPUT);
pinMode(BtnPin, INPUT);
}
The setup() function initializes WiringPi, sets up SPI, configures PWM
and GPIO pins needed for motor control and button input.
int main(void)
{
setup();
int currentState, lastState = 0;
int level = 0;
int currentTemp, markTemp = 0;
while (1) {
currentState = digitalRead(BtnPin);
currentTemp = temperture();
if (currentTemp <= 0) continue;
if (currentState == 1 && lastState == 0) {
level = (level + 1) % 5;
markTemp = currentTemp;
delay(500);
}
lastState = currentState;
if (level != 0) {
if (currentTemp - markTemp <= -2) {
level = level - 1;
markTemp = currentTemp;
}
if (currentTemp - markTemp >= 2) {
level = level + 1;
markTemp = currentTemp;
}
}
level = motor(level);
}
return 0;
}
The main() function contains the program loop:
Constantly checks button state and reads current temperature.
On button press, fan level increases (cycles 0–4) and saves temperature.
If temperature changes by ±2°C, it auto-adjusts fan speed accordingly.
Calls
motor(level)to update PWM output based on current level.
For Python Language Users
Step 2: Set up the SPI interface and install the spidev library (see SPI Configuration for detailed instructions). If you have already completed these steps, you can skip this.
Step 3: Get into the folder of the code.
cd ~/davinci-kit-for-raspberry-pi/python
Step 4: Run.
sudo python3 3.1.4-2_SmartFan.py
As the code runs, start the fan by pressing the button. Every time you press, 1 speed grade is adjusted up or down. There are 5 kinds of speed grades: 0~4. When set to the 4th speed grade and you press the button, the fan stops working with a 0 wind speed.
Once the temperature goes up or down for more than 2℃, the speed automatically gets 1-grade faster or slower.
Code
Note
You can Modify/Reset/Copy/Run/Stop the code below. But before that, you need to go to source code path like davinci-kit-for-raspberry-pi/python. After modifying the code, you can run it directly to see the effect.
#!/usr/bin/env python3
import RPi.GPIO as GPIO
import spidev
import time
import math
# Pin configuration
BTN_PIN = 22 # Button GPIO (physical pin 15)
MOTOR_IN1 = 5 # Motor forward
MOTOR_IN2 = 6 # Motor backward
MOTOR_EN = 13 # PWM enable pin
# GPIO setup
GPIO.setmode(GPIO.BCM)
GPIO.setup(BTN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(MOTOR_IN1, GPIO.OUT)
GPIO.setup(MOTOR_IN2, GPIO.OUT)
GPIO.setup(MOTOR_EN, GPIO.OUT)
# PWM setup for motor speed control
pwm = GPIO.PWM(MOTOR_EN, 1000) # 1kHz frequency
pwm.start(0)
# Initialize SPI for MCP3008
spi = spidev.SpiDev()
spi.open(0, 0) # Bus 0, CE0
spi.max_speed_hz = 1000000 # 1 MHz
# Global variables
level = 0
currentTemp = 0
markTemp = 0
def read_adc(channel):
if channel < 0 or channel > 7:
return -1
adc = spi.xfer2([1, (8 + channel) << 4, 0])
value = ((adc[1] & 0x03) << 8) | adc[2]
return value
def temperature():
analogVal = read_adc(0)
Vr = 3.3 * analogVal / 1023.0
Rt = 10000.0 * Vr / (3.3 - Vr)
tempK = 1.0 / (((math.log(Rt / 10000.0)) / 3950.0) + (1.0 / (273.15 + 25.0)))
Cel = tempK - 273.15
return Cel
def motor_run(level):
if level == 0:
GPIO.output(MOTOR_IN1, GPIO.LOW)
GPIO.output(MOTOR_IN2, GPIO.LOW)
pwm.ChangeDutyCycle(0)
return 0
if level >= 4:
level = 4
GPIO.output(MOTOR_IN1, GPIO.HIGH)
GPIO.output(MOTOR_IN2, GPIO.LOW)
pwm.ChangeDutyCycle(level * 25) # Map level (1–4) to 25%–100%
return level
def changeLevel(channel):
global level, currentTemp, markTemp
print("Button pressed")
level = (level + 1) % 5
markTemp = currentTemp
# Add event detection for button press
GPIO.add_event_detect(BTN_PIN, GPIO.FALLING, callback=changeLevel, bouncetime=300)
def main():
global level, currentTemp, markTemp
markTemp = temperature()
while True:
currentTemp = temperature()
if level != 0:
if currentTemp - markTemp <= -2:
level -= 1
markTemp = currentTemp
elif currentTemp - markTemp >= 2:
if level < 4:
level += 1
markTemp = currentTemp
level = motor_run(level)
time.sleep(0.2)
try:
main()
except KeyboardInterrupt:
pass
finally:
pwm.stop()
GPIO.cleanup()
spi.close()
Code Explanation
Import required modules:
RPi.GPIOfor GPIO control (button and motor),spidevfor communicating with MCP3008 ADC,timefor delays,mathfor temperature calculation using logarithmic functions.
#!/usr/bin/env python3 import RPi.GPIO as GPIO import spidev import time import math
Set up GPIO pins:
Button on GPIO22 (with internal pull-up),
Motor control using GPIO5 (forward), GPIO6 (backward), and GPIO13 (PWM enable).
BTN_PIN = 22 MOTOR_IN1 = 5 MOTOR_IN2 = 6 MOTOR_EN = 13 GPIO.setmode(GPIO.BCM) GPIO.setup(BTN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup(MOTOR_IN1, GPIO.OUT) GPIO.setup(MOTOR_IN2, GPIO.OUT) GPIO.setup(MOTOR_EN, GPIO.OUT) pwm = GPIO.PWM(MOTOR_EN, 1000) pwm.start(0)
Initialize SPI communication to the MCP3008 (Bus 0, Chip Enable 0) at 1 MHz.
spi = spidev.SpiDev() spi.open(0, 0) spi.max_speed_hz = 1000000
Define
read_adc()function to read a 10-bit analog value (0–1023) from the specified MCP3008 channel (0–7).def read_adc(channel): if channel < 0 or channel > 7: return -1 adc = spi.xfer2([1, (8 + channel) << 4, 0]) value = ((adc[1] & 0x03) << 8) | adc[2] return value
Define
temperature()function to:Convert analog voltage to resistance,
Apply the Steinhart–Hart equation to compute temperature in Celsius.
def temperature(): analogVal = read_adc(0) Vr = 3.3 * analogVal / 1023.0 Rt = 10000.0 * Vr / (3.3 - Vr) tempK = 1.0 / (((math.log(Rt / 10000.0)) / 3950.0) + (1.0 / (273.15 + 25.0))) Cel = tempK - 273.15 return Cel
Define
motor_run()to:Stop motor at level 0,
Run motor forward at increasing speed based on level 1–4, with PWM duty cycle of 25% to 100%.
def motor_run(level): if level == 0: GPIO.output(MOTOR_IN1, GPIO.LOW) GPIO.output(MOTOR_IN2, GPIO.LOW) pwm.ChangeDutyCycle(0) return 0 if level >= 4: level = 4 GPIO.output(MOTOR_IN1, GPIO.HIGH) GPIO.output(MOTOR_IN2, GPIO.LOW) pwm.ChangeDutyCycle(level * 25) return level
Define
changeLevel()callback for button press to:Increase the motor level cyclically (0 to 4),
Record the current temperature as the new baseline.
def changeLevel(channel): global level, currentTemp, markTemp print("Button pressed") level = (level + 1) % 5 markTemp = currentTemp GPIO.add_event_detect(BTN_PIN, GPIO.FALLING, callback=changeLevel, bouncetime=300)
Define
main()loop to:Monitor temperature change relative to the marked temperature,
Decrease level if temperature drops by 2°C or more,
Increase level if it rises by 2°C or more,
Adjust motor speed accordingly every 0.2 seconds.
def main(): global level, currentTemp, markTemp markTemp = temperature() while True: currentTemp = temperature() if level != 0: if currentTemp - markTemp <= -2: level -= 1 markTemp = currentTemp elif currentTemp - markTemp >= 2: if level < 4: level += 1 markTemp = currentTemp level = motor_run(level) time.sleep(0.2)
Run the main function and ensure proper cleanup on Ctrl+C (stop motor, cleanup GPIO, close SPI).
try: main() except KeyboardInterrupt: pass finally: pwm.stop() GPIO.cleanup() spi.close()