Note

Bonjour et bienvenue dans la communauté des passionnés de SunFounder Raspberry Pi & Arduino & ESP32 sur Facebook ! Plongez plus profondément dans l’univers du Raspberry Pi, de l’Arduino et de l’ESP32 avec d’autres passionnés.

Pourquoi nous rejoindre ?

  • Support d’experts : Résolvez les problèmes après-vente et les défis techniques avec l’aide de notre communauté et de notre équipe.

  • Apprendre & Partager : Échangez des astuces et tutoriels pour améliorer vos compétences.

  • Aperçus exclusifs : Accédez en avant-première aux annonces de nouveaux produits et aperçus.

  • Réductions spéciales : Profitez de réductions exclusives sur nos derniers produits.

  • Promotions festives et concours : Participez à des concours et promotions spéciales pendant les fêtes.

👉 Prêt à explorer et créer avec nous ? Cliquez sur [Ici] et rejoignez-nous dès aujourd’hui !

3.1.4 Ventilateur intelligent (MCP3008)

Note

_images/mcp3008_and_adc0834.jpg

Selon la version de votre kit, identifiez si vous disposez de l’ADC0834 ou du MCP3008 et poursuivez avec la section correspondante.

Introduction

Dans ce projet, nous utiliserons des moteurs, des boutons et des thermistances pour créer un ventilateur intelligent, manuel + automatique, dont la vitesse est réglable.

Composants requis

Pour ce projet, nous avons besoin des composants suivants.

_images/list2_Smart_Fan.png

Schéma de câblage

Nom T-Board

physique

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

_images/schematic_3.1.4_smart_fan_mcp3008.png

Procédure expérimentale

Étape 1 : Construire le circuit.

_images/july24_3.1.4_smart_fan_mcp3008.png

Note

Le module d’alimentation peut utiliser une pile 9V avec le clip pour pile 9V fourni dans le kit. Insérez le cavalier du module d’alimentation dans les rails d’alimentation 5V de la plaque d’essai.

_images/image118.jpeg

Pour les utilisateurs du langage C

Étape 2 : Accédez au dossier du code.

cd ~/davinci-kit-for-raspberry-pi/c/3.1.4-2/

Étape 3 : Compiler.

gcc 3.1.4_SmartFan.c -o SmartFan -lwiringPi -lm

Étape 4 : Exécuter l’exécutable ci-dessus.

./SmartFan

Lorsque le code s’exécute, démarrez le ventilateur en appuyant sur le bouton. Chaque pression augmente ou diminue le niveau de vitesse d’une unité. Il y a 5 niveaux de vitesse : 0~4. Lorsque vous atteignez le 4ème niveau et appuyez à nouveau, le ventilateur s’arrête (0).

Une variation de température supérieure à ±2℃ entraîne automatiquement une augmentation ou diminution d’un niveau de vitesse.

Note

Si le programme ne fonctionne pas après exécution ou si un message d’erreur apparaît : « wiringPi.h: No such file or directory », veuillez vous référer à Installer et vérifier 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;                      // Bit de démarrage
    buffer[1] = (8 + channel) << 4;     // Mode à extrémité unique et canal
    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;  // Utiliser 3,3V comme Vref pour 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;
}

Explication du code

int read_ADC(int channel)
{
    if (channel < 0 || channel > 7) return -1;

    unsigned char buffer[3];
    buffer[0] = 1;                      // Bit de démarrage
    buffer[1] = (8 + channel) << 4;     // Mode unipolaire et canal
    buffer[2] = 0;

    wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);

    int result = ((buffer[1] & 3) << 8) | buffer[2];
    return result;
}

Cette fonction permet de lire une entrée analogique depuis le MCP3008 sur le canal spécifié. Elle envoie une commande SPI de 3 octets et retourne une valeur numérique 10 bits comprise entre 0 et 1023.

int temperture()
{
    int analogVal = read_ADC(0);
    double Vr = 3.3 * analogVal / 1023.0;  // Utiliser 3,3 V comme Vref pour le 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;
}

La fonction temperture() lit le signal analogique du thermistor via le MCP3008, calcule la tension et la résistance, puis convertit la valeur en Celsius et en Fahrenheit en utilisant la formule du thermistor (approximation Steinhart–Hart).

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;
}

La fonction motor() contrôle la vitesse du ventilateur via PWM. Le niveau varie de 0 à 4, où 0 arrête le ventilateur et chaque niveau augmente le cycle de service de 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);
}

La fonction setup() initialise WiringPi, configure l’interface SPI, configure le PWM et définit les broches GPIO nécessaires au contrôle du moteur et à l’entrée du bouton.

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;
}

La fonction main() contient la boucle principale du programme :

  1. Vérifie en continu l’état du bouton et lit la température actuelle.

  2. Lorsqu’on appuie sur le bouton, le niveau du ventilateur augmente (cycle de 0 à 4) et enregistre la température.

  3. Si la température change de ± 2 °C, la vitesse du ventilateur s’ajuste automatiquement.

  4. Appelle motor(level) pour mettre à jour la sortie PWM selon le niveau.

Pour les utilisateurs du langage Python

Étape 2 : Configurez l’interface SPI et installez la bibliothèque spidev (voir Configuration SPI pour les instructions détaillées). Si vous avez déjà effectué ces étapes, vous pouvez les ignorer.

Étape 3 : Accédez au dossier contenant le code.

cd ~/davinci-kit-for-raspberry-pi/python

Étape 4 : Exécutez.

sudo python3 3.1.4-2_SmartFan.py

Lorsque le code s’exécute, démarrez le ventilateur en appuyant sur le bouton. Chaque pression change la vitesse d’un niveau. Il y a 5 niveaux possibles : 0 à 4. À la 4ème vitesse, une pression supplémentaire arrête le ventilateur (niveau 0).

Dès que la température augmente ou diminue de plus de 2 °C, la vitesse s’ajuste automatiquement d’un niveau.

Code

Note

Vous pouvez Modifier/Réinitialiser/Copier/Exécuter/Arrêter le code ci-dessous. Avant cela, assurez-vous d’être dans le chemin du code source comme davinci-kit-for-raspberry-pi/python. Après modification, vous pouvez l’exécuter directement pour voir l’effet.

#!/usr/bin/env python3

import RPi.GPIO as GPIO
import spidev
import time
import math

# Configuration des broches
BTN_PIN = 22            # Bouton GPIO (broche physique 15)
MOTOR_IN1 = 5           # Moteur avant
MOTOR_IN2 = 6           # Moteur arrière
MOTOR_EN = 13           # Broche d’activation PWM

# Configuration GPIO
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)

# Configuration PWM pour le contrôle de la vitesse du moteur
pwm = GPIO.PWM(MOTOR_EN, 1000)  # Fréquence de 1 kHz
pwm.start(0)

# Initialisation SPI pour MCP3008
spi = spidev.SpiDev()
spi.open(0, 0)  # Bus 0, CE0
spi.max_speed_hz = 1000000  # 1 MHz

# Variables globales
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)  # Mapper le niveau (1–4) à 25 %–100 %
    return level

def changeLevel(channel):
    global level, currentTemp, markTemp
    print("Bouton pressé")
    level = (level + 1) % 5
    markTemp = currentTemp

# Détection d’événement pour le bouton
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()

Explication du code

  1. Importer les modules nécessaires :

    • RPi.GPIO pour le contrôle GPIO (bouton et moteur)

    • spidev pour la communication avec l’ADC MCP3008

    • time pour les temporisations

    • math pour les calculs logarithmiques de température

    #!/usr/bin/env python3
    
    import RPi.GPIO as GPIO
    import spidev
    import time
    import math
    
  2. Configurer les broches GPIO :

    • Bouton sur GPIO22 (avec résistance de tirage interne)

    • Contrôle moteur : GPIO5 (avant), GPIO6 (arrière), GPIO13 (activation PWM)

    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)
    
  3. Initialiser la communication SPI avec MCP3008 (Bus 0, CE0) à 1 MHz

    spi = spidev.SpiDev()
    spi.open(0, 0)
    spi.max_speed_hz = 1000000
    
  4. Définir ``read_adc()`` : lit une valeur analogique 10 bits (0–1023) sur le canal MCP3008

    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
    
  5. Définir ``temperature()`` : convertit la tension analogique en résistance, applique la formule du thermistor et retourne la température en 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
    
  6. Définir ``motor_run()`` :

    • Niveau 0 : arrêt du moteur

    • Niveaux 1–4 : PWM de 25 % à 100 % (moteur avant)

    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
    
  7. Définir ``changeLevel()`` : fonction de rappel pour le bouton qui incrémente le niveau du moteur et enregistre la température de référence

    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)
    
  8. Boucle ``main()`` :

    • Surveille les variations de température par rapport à la référence

    • Ajuste automatiquement la vitesse si ± 2 °C

    • Met à jour le moteur toutes les 0,2 s

    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)
    
  9. Nettoyage à l’arrêt (Ctrl + C) : arrêt PWM, nettoyage GPIO, fermeture SPI

    try:
        main()
    except KeyboardInterrupt:
        pass
    finally:
        pwm.stop()
        GPIO.cleanup()
        spi.close()