Nota

Ciao, benvenuto nella Community SunFounder Raspberry Pi & Arduino & ESP32 Enthusiasts su Facebook! Approfondisci Raspberry Pi, Arduino ed ESP32 insieme ad altri appassionati.

Perché unirti?

  • Supporto esperto: Risolvi problemi post-vendita e sfide tecniche con l’aiuto della nostra community e del nostro team.

  • Impara e condividi: Scambia consigli e tutorial per migliorare le tue competenze.

  • Anteprime esclusive: Ottieni accesso anticipato agli annunci di nuovi prodotti e anteprime.

  • Sconti speciali: Approfitta di sconti esclusivi sui nostri prodotti più recenti.

  • Promozioni festive e giveaway: Partecipa a giveaway e promozioni festive.

👉 Pronto a esplorare e creare con noi? Clicca [Qui] e unisciti oggi stesso!

3.1.4 Ventilatore Intelligente (MCP3008)

Nota

_images/mcp3008_and_adc0834.jpg

A seconda della versione del tuo kit, identifica se possiedi ADC0834 o MCP3008 e procedi con la sezione corrispondente.

Introduzione

In questo progetto utilizzeremo motori, pulsanti e termistori per realizzare un ventilatore intelligente manuale + automatico con velocità regolabile.

Componenti Necessari

In questo progetto abbiamo bisogno dei seguenti componenti.

_images/list2_Smart_Fan.png

Schema Elettrico

T-Board Name

fisico

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

Procedure Sperimentali

Passo 1: Monta il circuito.

_images/july24_3.1.4_smart_fan_mcp3008.png

Nota

Il modulo di alimentazione può utilizzare una batteria da 9V con il connettore per batteria 9V incluso nel kit. Inserisci il ponticello del modulo di alimentazione nella barra del bus 5V della breadboard.

_images/image118.jpeg

Per utenti del linguaggio C

Passo 2: Entra nella cartella del codice.

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

Passo 3: Compila.

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

Passo 4: Esegui il file eseguibile.

./SmartFan

Quando il codice è in esecuzione, avvia il ventilatore premendo il pulsante. Ogni volta che lo premi, il livello di velocità aumenta o diminuisce di 1 grado. Ci sono 5 livelli di velocità: 0~4. Quando è impostato al 4° livello e premi il pulsante, il ventilatore si ferma con velocità 0.

Quando la temperatura aumenta o diminuisce di oltre 2℃, la velocità aumenta o diminuisce automaticamente di un livello.

Nota

Se non funziona dopo l’esecuzione o viene visualizzato l’errore: "wiringPi.h: No such file or directory", fai riferimento a Installare e Verificare WiringPi.

Codice

#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 di avvio
    buffer[1] = (8 + channel) << 4;     // Modalità a estremità singola e canale
    buffer[2] = 0;

    wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);

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

int temperture()
{
    int analogVal = read_ADC(0);
    double Vr = 3.3 * analogVal / 1023.0;  // Usa 3.3V come Vref per 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 fallito!\n");
        return;
    }

    if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
        printf("SPI setup fallito!\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;
}

Spiegazione del Codice

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

    unsigned char buffer[3];
    buffer[0] = 1;                      // Bit di avvio
    buffer[1] = (8 + channel) << 4;     // Modalità a estremità singola e canale
    buffer[2] = 0;

    wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);

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

Questa funzione viene utilizzata per leggere l’ingresso analogico dall’MCP3008 sul canale specificato. Invia un comando SPI di 3 byte e restituisce un valore digitale a 10 bit compreso tra 0 e 1023.

int temperture()
{
    int analogVal = read_ADC(0);
    double Vr = 3.3 * analogVal / 1023.0;  // Usa 3.3V come Vref per 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 funzione temperture() legge il segnale analogico del termistore tramite MCP3008, calcola tensione e resistenza, quindi converte in gradi Celsius e Fahrenheit utilizzando la formula del termistore (approssimazione di 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 funzione motor() controlla la velocità della ventola tramite PWM. Il livello varia da 0 a 4, dove 0 spegne la ventola e ogni livello aumenta il duty cycle del 25%.

void setup()
{
    if (wiringPiSetup() == -1) {
        printf("wiringPi setup fallito!\n");
        return;
    }

    if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
        printf("SPI setup fallito!\n");
        return;
    }

    softPwmCreate(MotorPin1, 0, 100);
    softPwmCreate(MotorPin2, 0, 100);
    pinMode(MotorEnable, OUTPUT);
    pinMode(BtnPin, INPUT);
}

La funzione setup() inizializza WiringPi, configura l’SPI, imposta il PWM e i pin GPIO necessari per il controllo del motore e l’ingresso del pulsante.

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 funzione main() contiene il ciclo principale del programma:

  1. Controlla costantemente lo stato del pulsante e legge la temperatura corrente.

  2. Alla pressione del pulsante, il livello della ventola aumenta (ciclicamente 0–4) e salva la temperatura.

  3. Se la temperatura cambia di ±2°C, regola automaticamente la velocità della ventola.

  4. Chiama motor(level) per aggiornare l’uscita PWM in base al livello corrente.

Per gli Utenti del Linguaggio Python

Passo 2: Configura l’interfaccia SPI e installa la libreria spidev (vedi Configurazione SPI per le istruzioni dettagliate). Se hai già completato questi passaggi, puoi saltare oltre.

Passo 3: Vai nella cartella del codice.

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

Passo 4: Esegui.

sudo python3 3.1.4-2_SmartFan.py

Quando il codice è in esecuzione, avvia la ventola premendo il pulsante. Ogni volta che lo premi, la velocità viene aumentata o diminuita di 1 livello. Ci sono 5 livelli di velocità: 0~4. Quando è impostato sul 4° livello e premi il pulsante, la ventola si ferma con velocità 0.

Quando la temperatura sale o scende di oltre 2℃, la velocità aumenta o diminuisce automaticamente di 1 livello.

Codice

Nota

Puoi Modificare/Reimpostare/Copiare/Eseguire/Interrompere il codice qui sotto. Ma prima di tutto, devi posizionarti nel percorso del codice sorgente, ad esempio davinci-kit-for-raspberry-pi/python. Dopo aver modificato il codice, puoi eseguirlo direttamente per vedere l’effetto.

#!/usr/bin/env python3

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

# Configurazione dei pin
BTN_PIN = 22            # Pulsante GPIO (pin fisico 15)
MOTOR_IN1 = 5           # Avanti
MOTOR_IN2 = 6           # Indietro
MOTOR_EN = 13           # Pin PWM

# Impostazione 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)

# Impostazione PWM per il controllo della velocità
pwm = GPIO.PWM(MOTOR_EN, 1000)  # Frequenza 1 kHz
pwm.start(0)

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

# Variabili globali
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)  # Mappa livello (1–4) a 25%–100%
    return level

def changeLevel(channel):
    global level, currentTemp, markTemp
    print("Pulsante premuto")
    level = (level + 1) % 5
    markTemp = currentTemp

# Rilevamento evento pressione pulsante
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()

Spiegazione del Codice

  1. Importa i moduli necessari:

    • RPi.GPIO per il controllo dei GPIO (pulsante e motore),

    • spidev per comunicare con l’ADC MCP3008,

    • time per gestire i ritardi,

    • math per il calcolo della temperatura con funzioni logaritmiche.

    import RPi.GPIO as GPIO
    import spidev
    import time
    import math
    
  2. Configura i pin GPIO:

    • Pulsante su GPIO22 (con pull-up interno),

    • Controllo motore su GPIO5 (avanti), GPIO6 (indietro) e GPIO13 (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. Inizializza la comunicazione SPI verso MCP3008 (Bus 0, CE0) a 1 MHz.

    spi = spidev.SpiDev()
    spi.open(0, 0)
    spi.max_speed_hz = 1000000
    
  4. Definisce read_adc() per leggere un valore analogico a 10 bit (0–1023) dal canale MCP3008 (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
    
  5. Definisce temperature() per:

    • Convertire la tensione analogica in resistenza,

    • Applicare l’equazione di Steinhart–Hart per ottenere la temperatura in °C.

    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. Definisce motor_run() per:

    • Fermare il motore a livello 0,

    • Far girare il motore in avanti con velocità crescente (livelli 1–4) mappati su duty cycle PWM dal 25% al 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
    
  7. Definisce changeLevel() come callback del pulsante per:

    • Incrementare ciclicamente il livello del motore (0–4),

    • Registrare la temperatura attuale come nuova temperatura di riferimento.

    def changeLevel(channel):
        global level, currentTemp, markTemp
        print("Pulsante premuto")
        level = (level + 1) % 5
        markTemp = currentTemp
    
    GPIO.add_event_detect(BTN_PIN, GPIO.FALLING, callback=changeLevel, bouncetime=300)
    
  8. Definisce il ciclo main() per:

    • Monitorare la variazione di temperatura rispetto al riferimento,

    • Diminuire il livello se la temperatura scende di 2°C o più,

    • Aumentare il livello se la temperatura sale di 2°C o più,

    • Regolare la velocità del motore ogni 0,2 secondi.

    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)
    
  9. Avvia la funzione principale e assicura la corretta pulizia delle risorse con Ctrl+C (ferma motore, pulisce GPIO, chiude SPI).

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