Nota

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

Perché unirsi?

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

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

  • Anteprime esclusive: ottieni accesso anticipato a nuovi annunci di prodotto e anteprime.

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

  • Promozioni festive e giveaway: partecipa a giveaway e promozioni durante le festività.

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

3.1.8 Monitor di Surriscaldamento (MCP3008)

Nota

_images/mcp3008_and_adc0834.jpg

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

Introduzione

Potresti voler realizzare un dispositivo di monitoraggio del surriscaldamento applicabile a varie situazioni, ad esempio in fabbrica, quando vogliamo avere un allarme e lo spegnimento automatico tempestivo della macchina in caso di surriscaldamento di un circuito. In questo progetto utilizzeremo termistore, joystick, buzzer, LED e LCD per creare un dispositivo intelligente di monitoraggio della temperatura con soglia regolabile.

Componenti necessari

In questo progetto, abbiamo bisogno dei seguenti componenti.

_images/list2_Overheat_Monitor.png

Schema elettrico

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

Pin15

3

22

GPIO23

Pin16

4

23

GPIO24

Pin18

5

24

SDA1

Pin 3

SCL1

Pin 5

_images/Schematic_three_one8.png

Procedure sperimentali

Passo 1: Monta il circuito.

_images/july24_3.1.8_overheat_monitor_mcp3008.png

Per utenti C Language

Passo 2: Vai nella cartella del codice.

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

Passo 3: Compila il codice.

gcc 3.1.8_OverheatMonitor.c -lm -lwiringPi

Passo 4: Esegui il file compilato.

sudo ./a.out

Quando il codice è in esecuzione, la temperatura attuale e la soglia di alta temperatura 40 vengono visualizzate su I2C LCD1602. Se la temperatura attuale è maggiore della soglia, buzzer e LED si attivano per avvisarti.

Il joystick serve per regolare la soglia di temperatura alta. Spostandolo sugli assi X e Y puoi aumentare o diminuire la soglia. Premendo di nuovo il joystick la soglia si resetta al valore iniziale.

Nota

  • Se appare l’errore wiringPi.h: No such file or directory, fare riferimento a Installare e Verificare WiringPi.

  • Se appare Unable to open I2C device: No such file or directory, fare riferimento a Configurazione I²C per abilitare l’I2C e controllare il cablaggio.

  • Se il codice e il cablaggio sono corretti ma l’LCD non mostra nulla, regolare il potenziometro sul retro per aumentare il contrasto.

Code

#include <wiringPi.h>
#include <stdio.h>
#include <wiringPiI2C.h>
#include <wiringPiSPI.h>
#include <string.h>
#include <math.h>

typedef unsigned char uchar;
typedef unsigned int  uint;

#define Joy_BtnPin 3      // GPIO22 -> WiringPi 3
#define buzzPin    4      // GPIO23 -> WiringPi 4
#define LedPin     5      // GPIO24 -> WiringPi 5
#define SPI_CHANNEL 0
#define SPI_SPEED   1000000

int LCDAddr    = 0x27;
int BLEN       = 1;
int fd;
int upperTem   = 40;

// Global variable to store the last joystick change
int lastJoystickChange = 0;

int read_ADC(int channel) {
    if (channel < 0 || channel > 7) return -1;
    unsigned char buffer[3];
    buffer[0] = 1;
    buffer[1] = (8 + channel) << 4;
    buffer[2] = 0;
    wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);
    return ((buffer[1] & 0x03) << 8) | buffer[2];
}

void write_word(int data){
    int temp = data;
    if (BLEN)      temp |= 0x08;
    else           temp &= 0xF7;
    wiringPiI2CWrite(fd, temp);
}

void send_command(int comm){
    int buf = comm & 0xF0;
    buf |= 0x04; write_word(buf); delay(2); buf &= 0xFB; write_word(buf);
    buf = (comm & 0x0F) << 4;
    buf |= 0x04; write_word(buf); delay(2); buf &= 0xFB; write_word(buf);
}

void send_data(int data){
    int buf = data & 0xF0;
    buf |= 0x05; write_word(buf); delay(2); buf &= 0xFB; write_word(buf);
    buf = (data & 0x0F) << 4;
    buf |= 0x05; write_word(buf); delay(2); buf &= 0xFB; write_word(buf);
}

void lcd_init(){
    send_command(0x33); delay(5);
    send_command(0x32); delay(5);
    send_command(0x28); delay(5);
    send_command(0x0C); delay(5);
    send_command(0x01); wiringPiI2CWrite(fd, 0x08);
}

void lcd_clear(){
    send_command(0x01);
}

void write_lcd(int x, int y, const char data[]){
    int addr = 0x80 + 0x40 * y + x;
    send_command(addr);
    for (int i = 0; i < (int)strlen(data); i++)
        send_data(data[i]);
}

int get_joystick_value(){
    int x = read_ADC(1);
    int y = read_ADC(2);

    // Dead-band filtering to reduce small fluctuations
    if (x > 900)      return  1;   //    else if (x < 100) return -1;   //     else if (y > 900) return -10;  //     else if (y < 100) return  10;  //    else              return   0;
}

void upper_tem_setting(){
    write_lcd(0,0, "Upper Adjust:");

    int change = get_joystick_value();

    // Only respond to actual direction change
    if (change != 0 && change != lastJoystickChange) {
        upperTem += change;
        lastJoystickChange = change;
    }
    else if (change == 0) {
        // Allow next change after returning to center
        lastJoystickChange = 0;
    }

    // Display current upperTem
    char str[6];
    snprintf(str, sizeof(str), "%d", upperTem);
    write_lcd(0,1, str);
    // Clear remaining LCD characters
    write_lcd(strlen(str),1, "            ");

    delay(100);
}

double temperature(){
    int raw = read_ADC(0);
    double Vr = 3.3 * ((double)raw / 1023.0);
    double Rt = 10000.0 * Vr / (3.3 - Vr);
    double tempK = 1.0 / ((log(Rt/10000.0)/3950.0) + 1.0/(273.15+25.0));
    return tempK - 273.15;
}

void monitoring_temp(){
    char str[6];
    double cel = temperature();
    snprintf(str, sizeof(str), "%.2f", cel);
    write_lcd(0,0, "Temp: ");
    write_lcd(6,0, str);

    snprintf(str, sizeof(str), "%d", upperTem);
    write_lcd(0,1, "Upper: ");
    write_lcd(7,1, str);
    delay(100);

    if (cel >= upperTem) {
        digitalWrite(buzzPin, HIGH);
        digitalWrite(LedPin,  HIGH);
    } else {
        digitalWrite(buzzPin, LOW);
        digitalWrite(LedPin,  LOW);
    }
}

void setup_all(){
    fd = wiringPiI2CSetup(LCDAddr);
    lcd_init();
    if (wiringPiSetup() == -1 ||
        wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
        printf("Setup failed!\n");
        return;
    }
    pinMode(Joy_BtnPin, INPUT);
    pullUpDnControl(Joy_BtnPin, PUD_UP);
    pinMode(buzzPin, OUTPUT);
    pinMode(LedPin,  OUTPUT);
}

int main(void){
    setup_all();

    int lastBtnState = HIGH;
    int stage = 0;

    while (1) {
        int curBtn = digitalRead(Joy_BtnPin);
        // Switch mode when button changes from LOW to HIGH (button released)
        if (curBtn == HIGH && lastBtnState == LOW) {
            stage = (stage + 1) % 2;
            lastJoystickChange = 0;  // Clear debounce status
            delay(100);
            lcd_clear();
        }
        lastBtnState = curBtn;

        if (stage == 1)
            upper_tem_setting();
        else
            monitoring_temp();
    }

    return 0;
}

Spiegazione del codice

int read_ADC(int channel) {
    if (channel < 0 || channel > 7) return -1;
    unsigned char buffer[3];
    buffer[0] = 1;
    buffer[1] = (8 + channel) << 4;
    buffer[2] = 0;
    wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);
    return ((buffer[1] & 0x03) << 8) | buffer[2];
}

Legge un valore analogico a 10 bit dal canale MCP3008 (CH0–CH7) tramite SPI e restituisce un intero da 0 a 1023.

int get_joystick_value() {
    int x = read_ADC(1);
    int y = read_ADC(2);

    if (x > 900)      return 1;   // Destra
    else if (x < 100) return -1;  // Sinistra
    else if (y > 900) return -10; // Su
    else if (y < 100) return 10;  // Giù
    else              return 0;
}

Legge i valori analogici X e Y del joystick dai canali CH1 e CH2. Restituisce un intero che indica la direzione di movimento in base alle soglie.

void upper_tem_setting() {
    write_lcd(0,0, "Upper Adjust:");

    int change = get_joystick_value();

    if (change != 0 && change != lastJoystickChange) {
        upperTem += change;
        lastJoystickChange = change;
    }
    else if (change == 0) {
        lastJoystickChange = 0;
    }

    char str[6];
    snprintf(str, sizeof(str), "%d", upperTem);
    write_lcd(0,1, str);
    write_lcd(strlen(str),1, "            ");

    delay(100);
}

Permette all’utente di regolare la soglia di temperatura massima con il joystick, evitando modifiche ripetute se la direzione viene mantenuta.

double temperature() {
    int raw = read_ADC(0);
    double Vr = 3.3 * ((double)raw / 1023.0);
    double Rt = 10000.0 * Vr / (3.3 - Vr);
    double tempK = 1.0 / ((log(Rt/10000.0)/3950.0) + 1.0/(273.15+25.0));
    return tempK - 273.15;
}

Legge il valore analogico dal CH0 collegato al termistore. Usa l’equazione di Steinhart–Hart per calcolare la temperatura in gradi Celsius.

void monitoring_temp() {
    char str[6];
    double cel = temperature();
    snprintf(str, sizeof(str), "%.2f", cel);
    write_lcd(0,0, "Temp: ");
    write_lcd(6,0, str);

    snprintf(str, sizeof(str), "%d", upperTem);
    write_lcd(0,1, "Upper: ");
    write_lcd(7,1, str);
    delay(100);

    if (cel >= upperTem) {
        digitalWrite(buzzPin, HIGH);
        digitalWrite(LedPin,  HIGH);
    } else {
        digitalWrite(buzzPin, LOW);
        digitalWrite(LedPin,  LOW);
    }
}

Legge continuamente la temperatura attuale e la visualizza insieme alla soglia. Se la temperatura supera la soglia, il buzzer e il LED si attivano.

void setup_all() {
    fd = wiringPiI2CSetup(LCDAddr);
    lcd_init();
    if (wiringPiSetup() == -1 || wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
        printf("Setup failed!\n");
        return;
    }
    pinMode(Joy_BtnPin, INPUT);
    pullUpDnControl(Joy_BtnPin, PUD_UP);
    pinMode(buzzPin, OUTPUT);
    pinMode(LedPin,  OUTPUT);
}

Inizializza LCD, SPI, GPIO per pulsante del joystick, buzzer e LED. Imposta anche la resistenza di pull-up per il pulsante del joystick.

int main(void) {
    setup_all();

    int lastBtnState = HIGH;
    int stage = 0;

    while (1) {
        int curBtn = digitalRead(Joy_BtnPin);
        if (curBtn == HIGH && lastBtnState == LOW) {
            stage = (stage + 1) % 2;
            lastJoystickChange = 0;
            delay(100);
            lcd_clear();
        }
        lastBtnState = curBtn;

        if (stage == 1)
            upper_tem_setting();
        else
            monitoring_temp();
    }

    return 0;
}

Il ciclo principale alterna tra due modalità:

  1. Monitoraggio della temperatura.

  2. Regolazione del limite superiore tramite joystick.

La modalità cambia quando il pulsante del joystick viene rilasciato (trigger sul fronte di salita).

Per utenti Python Language

Passo 2: Configura l’interfaccia SPI e installa la libreria spidev (vedi Configurazione SPI per le istruzioni dettagliate). Se lo hai già fatto, puoi saltare questo passo.

Passo 3: Vai nella cartella del codice.

cd ~/raphael-kit/python

Passo 4: Esegui il file.

sudo python3 3.1.8-2_OverheatMonitor.py

Quando il codice è in esecuzione, la temperatura attuale e la soglia 40 vengono mostrate su I2C LCD1602. Se la temperatura è maggiore della soglia, buzzer e LED si attivano per avvisarti.

Il joystick permette di regolare la soglia di alta temperatura: spostandolo sugli assi X/Y puoi aumentare o diminuire la soglia; premendolo di nuovo, si resetta al valore iniziale.

Nota

  • Se appare l’errore FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1', abilita l’I2C come spiegato in Configurazione I²C.

  • Se appare ModuleNotFoundError: No module named 'smbus2', esegui sudo apt install python3-smbus2.

  • Se appare OSError: [Errno 121] Remote I/O error, significa che il modulo è cablato male o difettoso.

  • Se l’LCD non mostra nulla, regola il potenziometro sul retro.

Avvertimento

Se appare l’errore RuntimeError: Cannot determine SOC peripheral base address, vedere Se gpiozero non funziona.

Codice

Nota

Puoi Modificare/Reimpostare/Copiare/Eseguire/Arrestare il codice qui sotto. Prima di farlo, però, devi accedere al percorso del codice sorgente, ad esempio davinci-kit-for-raspberry-pi/python.

#!/usr/bin/env python3

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

# GPIO pin definitions
JOY_BTN_PIN = 22  # Button pin
BUZZER_PIN = 23   # Buzzer pin
LED_PIN = 24      # LED pin

# Initialize GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(JOY_BTN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(BUZZER_PIN, GPIO.OUT)
GPIO.setup(LED_PIN, GPIO.OUT)

# Set initial upper temperature threshold
upperTem = 40

# Initialize SPI for MCP3008
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1000000  # 1 MHz

# Initialize LCD1602
LCD1602.init(0x27, 1)

def read_adc(channel):
    """
    Read analog value from MCP3008 (0–7)
    """
    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 get_joystick_value():
    """
    Reads the joystick values and returns a change value based on the joystick's position.
    """
    x_val = read_adc(1)
    y_val = read_adc(2)
    if x_val > 800:
        return 1
    elif x_val < 200:
        return -1
    elif y_val > 800:
        return -10
    elif y_val < 200:
        return 10
    else:
        return 0

def upper_tem_setting():
    """
    Adjusts and displays the upper temperature threshold on the LCD.
    """
    global upperTem
    LCD1602.write(0, 0, 'Upper Adjust: ')
    change = int(get_joystick_value())
    upperTem += change
    strUpperTem = str(upperTem)
    LCD1602.write(0, 1, strUpperTem)
    LCD1602.write(len(strUpperTem), 1, '              ')
    time.sleep(0.1)

def temperature():
    """
    Reads the current temperature from the sensor and returns it in Celsius.
    """
    analogVal = read_adc(0)
    Vr = 3.3 * analogVal / 1023.0
    if Vr == 0:
        return 0
    Rt = 10000.0 * (3.3 - Vr) / Vr
    tempK = 1.0 / (((math.log(Rt / 10000.0)) / 3950.0) + (1.0 / (273.15 + 25.0)))
    Cel = tempK - 273.15
    return round(Cel, 2)

def monitoring_temp():
    """
    Monitors and displays the current temperature and upper temperature threshold.
    Activates buzzer and LED if the temperature exceeds the upper limit.
    """
    global upperTem
    Cel = temperature()
    LCD1602.write(0, 0, 'Temp: ')
    LCD1602.write(0, 1, 'Upper: ')
    LCD1602.write(6, 0, str(Cel))
    LCD1602.write(7, 1, str(upperTem))
    time.sleep(0.1)
    if Cel >= upperTem:
        GPIO.output(BUZZER_PIN, GPIO.HIGH)
        GPIO.output(LED_PIN, GPIO.HIGH)
    else:
        GPIO.output(BUZZER_PIN, GPIO.LOW)
        GPIO.output(LED_PIN, GPIO.LOW)

# Main loop
try:
    lastState = GPIO.input(JOY_BTN_PIN)
    stage = 0
    while True:
        currentState = GPIO.input(JOY_BTN_PIN)
        if currentState == GPIO.HIGH and lastState == GPIO.LOW:
            stage = (stage + 1) % 2
            time.sleep(0.1)
            LCD1602.clear()
        lastState = currentState

        if stage == 1:
            upper_tem_setting()
        else:
            monitoring_temp()

except KeyboardInterrupt:
    pass

finally:
    LCD1602.clear()
    GPIO.cleanup()
    spi.close()

Spiegazione del codice

  1. Importazione delle librerie

    Questa sezione carica le librerie necessarie per GPIO, SPI, display LCD, ritardi temporali e operazioni matematiche.

    #!/usr/bin/env python3
    import RPi.GPIO as GPIO
    import spidev
    import time
    import math
    import LCD1602
    
  2. Configurazione dei GPIO e dei dispositivi

    Definisce i numeri dei pin GPIO per il pulsante del joystick, il buzzer e il LED, e configura le modalità GPIO.

    JOY_BTN_PIN = 22  # Pin pulsante
    BUZZER_PIN = 23   # Pin buzzer
    LED_PIN = 24      # Pin LED
    
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(JOY_BTN_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
    GPIO.setup(BUZZER_PIN, GPIO.OUT)
    GPIO.setup(LED_PIN, GPIO.OUT)
    
  3. Inizializzazione di SPI e LCD

    Avvia l’interfaccia SPI per MCP3008 e inizializza il display LCD1602 con indirizzo I2C 0x27.

    upperTem = 40
    spi = spidev.SpiDev()
    spi.open(0, 0)
    spi.max_speed_hz = 1000000
    
    LCD1602.init(0x27, 1)
    
  4. Lettura del valore ADC

    Legge i dati analogici dall’ADC MCP3008 tramite SPI. Il canale deve essere compreso tra 0 e 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. Rilevamento del movimento del joystick

    Controlla i valori degli assi X/Y del joystick e restituisce di quanto modificare la soglia.

    def get_joystick_value():
        x_val = read_adc(1)
        y_val = read_adc(2)
        if x_val > 800:
            return 1
        elif x_val < 200:
            return -1
        elif y_val > 800:
            return -10
        elif y_val < 200:
            return 10
        else:
            return 0
    
  6. Regolazione della soglia di temperatura massima

    Visualizza “Upper Adjust” sull’LCD e modifica la soglia utilizzando l’input del joystick.

    def upper_tem_setting():
        global upperTem
        LCD1602.write(0, 0, 'Upper Adjust: ')
        change = int(get_joystick_value())
        upperTem += change
        strUpperTem = str(upperTem)
        LCD1602.write(0, 1, strUpperTem)
        LCD1602.write(len(strUpperTem), 1, '              ')
        time.sleep(0.1)
    
  7. Calcolo della temperatura

    Converte la lettura analogica del sensore in tensione, poi in resistenza e infine in temperatura (°C) utilizzando l’approssimazione di Steinhart–Hart.

    def temperature():
        analogVal = read_adc(0)
        Vr = 3.3 * analogVal / 1023.0
        if Vr == 0:
            return 0
        Rt = 10000.0 * (3.3 - Vr) / Vr
        tempK = 1.0 / (((math.log(Rt / 10000.0)) / 3950.0) + (1.0 / (273.15 + 25.0)))
        Cel = tempK - 273.15
        return round(Cel, 2)
    
  8. Monitoraggio della temperatura

    Controlla e visualizza continuamente la temperatura e il limite massimo. Accende buzzer e LED se la temperatura supera la soglia.

    def monitoring_temp():
        global upperTem
        Cel = temperature()
        LCD1602.write(0, 0, 'Temp: ')
        LCD1602.write(0, 1, 'Upper: ')
        LCD1602.write(6, 0, str(Cel))
        LCD1602.write(7, 1, str(upperTem))
        time.sleep(0.1)
        if Cel >= upperTem:
            GPIO.output(BUZZER_PIN, GPIO.HIGH)
            GPIO.output(LED_PIN, GPIO.HIGH)
        else:
            GPIO.output(BUZZER_PIN, GPIO.LOW)
            GPIO.output(LED_PIN, GPIO.LOW)
    
  9. Logica principale del programma

    Alterna tra il monitoraggio della temperatura e la regolazione della soglia quando viene premuto il pulsante del joystick.

    try:
        lastState = GPIO.input(JOY_BTN_PIN)
        stage = 0
        while True:
            currentState = GPIO.input(JOY_BTN_PIN)
            if currentState == GPIO.HIGH and lastState == GPIO.LOW:
                stage = (stage + 1) % 2
                time.sleep(0.1)
                LCD1602.clear()
            lastState = currentState
    
            if stage == 1:
                upper_tem_setting()
            else:
                monitoring_temp()
    
  10. Pulizia in uscita

    Garantisce lo spegnimento corretto delle risorse GPIO e SPI quando si preme Ctrl+C.

    except KeyboardInterrupt:
        pass
    finally:
        LCD1602.clear()
        GPIO.cleanup()
        spi.close()