.. 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()