.. note::
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 [|link_sf_facebook|] e unisciti oggi stesso!
.. _3.1.4_c_mcp3008:
3.1.4 Ventilatore Intelligente (MCP3008)
========================================
.. note::
.. image:: img/mcp3008_and_adc0834.jpg
:width: 25%
:align: left
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.
.. image:: img/list2_Smart_Fan.png
:align: center
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
============ ======== ======== ===
.. image:: img/schematic_3.1.4_smart_fan_mcp3008.png
:align: center
Procedure Sperimentali
----------------------
**Passo 1:** Monta il circuito.
.. image:: img/july24_3.1.4_smart_fan_mcp3008.png
:align: center
.. note::
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.
.. image:: img/image118.jpeg
:align: center
**Per utenti del linguaggio C**
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Passo 2**: Entra nella cartella del codice.
.. raw:: html
.. code-block::
cd ~/davinci-kit-for-raspberry-pi/c/3.1.4-2/
**Passo 3**: Compila.
.. raw:: html
.. code-block::
gcc 3.1.4_SmartFan.c -o SmartFan -lwiringPi -lm
**Passo 4**: Esegui il file eseguibile.
.. raw:: html
.. code-block::
./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\ :sup:`°` 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.
.. note::
Se non funziona dopo l'esecuzione o viene visualizzato l'errore: \"wiringPi.h: No such file or directory\", fai riferimento a :ref:`install_wiringpi`.
Codice
------
.. code-block:: c
#include
#include
#include
#include
#include
#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
----------------------
.. code-block:: c
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.
.. code-block:: c
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).
.. code-block:: c
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%.
.. code-block:: c
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.
.. code-block:: c
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 :ref:`spi_configuration` per le istruzioni dettagliate). Se hai già completato questi passaggi, puoi saltare oltre.
**Passo 3:** Vai nella cartella del codice.
.. raw:: html
.. code-block::
cd ~/davinci-kit-for-raspberry-pi/python
**Passo 4:** Esegui.
.. raw:: html
.. code-block::
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\ :sup:`°` 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
--------
.. note::
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.
.. raw:: html
.. code-block:: python
#!/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
----------------------
#. 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.
.. code-block:: python
import RPi.GPIO as GPIO
import spidev
import time
import math
#. Configura i pin GPIO:
- Pulsante su GPIO22 (con pull-up interno),
- Controllo motore su GPIO5 (avanti), GPIO6 (indietro) e GPIO13 (PWM).
.. code-block:: python
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)
#. Inizializza la comunicazione SPI verso MCP3008 (Bus 0, CE0) a 1 MHz.
.. code-block:: python
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1000000
#. Definisce ``read_adc()`` per leggere un valore analogico a 10 bit (0–1023) dal canale MCP3008 (0–7).
.. code-block:: python
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
#. Definisce ``temperature()`` per:
- Convertire la tensione analogica in resistenza,
- Applicare l’equazione di Steinhart–Hart per ottenere la temperatura in °C.
.. code-block:: python
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
#. 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%.
.. code-block:: python
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
#. Definisce ``changeLevel()`` come callback del pulsante per:
- Incrementare ciclicamente il livello del motore (0–4),
- Registrare la temperatura attuale come nuova temperatura di riferimento.
.. code-block:: python
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)
#. 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.
.. code-block:: python
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)
#. Avvia la funzione principale e assicura la corretta pulizia delle risorse con Ctrl+C (ferma motore, pulisce GPIO, chiude SPI).
.. code-block:: python
try:
main()
except KeyboardInterrupt:
pass
finally:
pwm.stop()
GPIO.cleanup()
spi.close()