.. note::
¡Hola, bienvenido a la comunidad de entusiastas de SunFounder Raspberry Pi & Arduino & ESP32 en Facebook! Sumérgete más en Raspberry Pi, Arduino y ESP32 junto con otros entusiastas.
**¿Por qué unirse?**
- **Soporte experto**: Resuelve problemas postventa y desafíos técnicos con la ayuda de nuestra comunidad y equipo.
- **Aprender y compartir**: Intercambia consejos y tutoriales para mejorar tus habilidades.
- **Avances exclusivos**: Obtén acceso anticipado a nuevos anuncios de productos y adelantos.
- **Descuentos especiales**: Disfruta de descuentos exclusivos en nuestros productos más recientes.
- **Promociones y sorteos festivos**: Participa en sorteos y promociones especiales en épocas festivas.
👉 ¿Listo para explorar y crear con nosotros? Haz clic en [|link_sf_facebook|] y únete hoy.
.. _3.1.4_c_mcp3008:
3.1.4 Ventilador Inteligente (MCP3008)
==========================================
.. note::
.. image:: img/mcp3008_and_adc0834.jpg
:width: 25%
:align: left
Dependiendo de la versión de tu kit, identifica si tienes **ADC0834** o **MCP3008** y continúa con la sección correspondiente.
Introducción
-----------------
En este proyecto, usaremos motores, botones y termistores para hacer un
ventilador inteligente manual + automático cuya velocidad de viento sea ajustable.
Componentes requeridos
------------------------------
En este proyecto, necesitamos los siguientes componentes.
.. image:: img/list2_Smart_Fan.png
:align: center
Diagrama esquemático
------------------------
============ ======== ======== ===
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 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
Procedimientos experimentales
------------------------------
**Paso 1:** Montar el circuito.
.. image:: img/july24_3.1.4_smart_fan_mcp3008.png
:align: center
.. note::
El módulo de alimentación puede usar una batería de 9V con el conector de batería incluido en el kit. Inserta el jumper del módulo de alimentación en las líneas de alimentación de 5V de la protoboard.
.. image:: img/image118.jpeg
:align: center
**Para usuarios de lenguaje C**
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Paso 2**: Entra en la carpeta del código.
.. raw:: html
.. code-block::
cd ~/davinci-kit-for-raspberry-pi/c/3.1.4-2/
**Paso 3**: Compilar.
.. raw:: html
.. code-block::
gcc 3.1.4_SmartFan.c -o SmartFan -lwiringPi -lm
**Paso 4**: Ejecutar el archivo compilado.
.. raw:: html
.. code-block::
./SmartFan
Al ejecutar el código, enciende el ventilador presionando el botón. Cada vez que lo presiones, la velocidad aumentará o disminuirá un nivel. Hay **5** niveles de velocidad: **0~4**. Cuando está en el nivel 4\ :sup:`to` y presionas el botón, el ventilador se detendrá con una velocidad **0**.
Cuando la temperatura sube o baja más de 2℃, la velocidad se ajusta automáticamente un nivel hacia arriba o hacia abajo.
.. note::
Si no funciona después de ejecutarlo, o aparece el error: \"wiringPi.h: No such file or directory\", por favor revisa :ref:`install_wiringpi`.
Código
--------
.. 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 de inicio
buffer[1] = (8 + channel) << 4; // Modo de entrada simple y 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; // Usar 3.3V como Vref para 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("¡Fallo en la configuración de wiringPi!\n");
return;
}
if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
printf("¡Fallo en la configuración de SPI!\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;
}
Explicación del código
----------------------
.. code-block:: c
int read_ADC(int channel)
{
if (channel < 0 || channel > 7) return -1;
unsigned char buffer[3];
buffer[0] = 1; // Bit de inicio
buffer[1] = (8 + channel) << 4; // Modo de entrada simple y canal
buffer[2] = 0;
wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);
int result = ((buffer[1] & 3) << 8) | buffer[2];
return result;
}
Esta función se usa para leer la entrada analógica del MCP3008 en el canal especificado. Envía un comando SPI de 3 bytes y devuelve un valor digital de 10 bits entre 0–1023.
.. code-block:: c
int temperture()
{
int analogVal = read_ADC(0);
double Vr = 3.3 * analogVal / 1023.0; // Usar 3.3V como Vref para 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 función ``temperture()`` lee la señal analógica del termistor a través del MCP3008, calcula el voltaje, la resistencia y luego la convierte a grados Celsius y Fahrenheit usando la fórmula del termistor (aproximación 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 función ``motor()`` controla la velocidad del ventilador mediante PWM. El nivel va de 0–4, donde 0 apaga el ventilador y cada nivel incrementa el ciclo de trabajo un 25%.
.. code-block:: c
void setup()
{
if (wiringPiSetup() == -1) {
printf("¡Fallo en la configuración de wiringPi!\n");
return;
}
if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
printf("¡Fallo en la configuración de SPI!\n");
return;
}
softPwmCreate(MotorPin1, 0, 100);
softPwmCreate(MotorPin2, 0, 100);
pinMode(MotorEnable, OUTPUT);
pinMode(BtnPin, INPUT);
}
La función ``setup()`` inicializa WiringPi, configura SPI, establece PWM y los pines GPIO necesarios para el control del motor y la lectura del botón.
.. 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 función ``main()`` contiene el bucle del programa:
1. Verifica constantemente el estado del botón y lee la temperatura actual.
2. Al presionar el botón, el nivel del ventilador aumenta (ciclo 0–4) y guarda la temperatura.
3. Si la temperatura cambia ±2°C, ajusta automáticamente la velocidad del ventilador.
4. Llama a ``motor(level)`` para actualizar el PWM según el nivel actual.
**Para usuarios de lenguaje Python**
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Paso 2:** Configura la interfaz SPI e instala la biblioteca ``spidev`` (consulta :ref:`spi_configuration` para instrucciones detalladas). Si ya has completado estos pasos, puedes omitirlos.
**Paso 3**: Entra en la carpeta del código.
.. raw:: html
.. code-block::
cd ~/davinci-kit-for-raspberry-pi/python
**Paso 4**: Ejecutar.
.. raw:: html
.. code-block::
sudo python3 3.1.4-2_SmartFan.py
Al ejecutar el código, enciende el ventilador presionando el botón. Cada vez que lo presiones, la velocidad aumentará o disminuirá un nivel. Hay **5** niveles de velocidad: **0~4**. Cuando está en el nivel 4\ :sup:`to` y presionas el botón, el ventilador se detiene con velocidad **0**.
Cuando la temperatura sube o baja más de 2℃, la velocidad se ajusta automáticamente un nivel hacia arriba o hacia abajo.
Código
--------
.. note::
Puedes **Modificar/Restablecer/Copiar/Ejecutar/Detener** el código siguiente. Pero antes, debes ir a la ruta del código fuente como ``davinci-kit-for-raspberry-pi/python``. Después de modificarlo, puedes ejecutarlo directamente para ver el efecto.
.. raw:: html
.. code-block:: python
#!/usr/bin/env python3
import RPi.GPIO as GPIO
import spidev
import time
import math
# Configuración de pines
BTN_PIN = 22 # Botón GPIO (pin físico 15)
MOTOR_IN1 = 5 # Motor adelante
MOTOR_IN2 = 6 # Motor atrás
MOTOR_EN = 13 # Pin PWM habilitación
# Configuración de 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)
# Configuración de PWM para control de velocidad del motor
pwm = GPIO.PWM(MOTOR_EN, 1000) # Frecuencia 1 kHz
pwm.start(0)
# Inicializar SPI para 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) # Mapear nivel (1–4) a 25%–100%
return level
def changeLevel(channel):
global level, currentTemp, markTemp
print("Botón presionado")
level = (level + 1) % 5
markTemp = currentTemp
# Agregar detección de evento para pulsación de botón
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()
Explicación del código
----------------------
#. Importar los módulos requeridos:
- ``RPi.GPIO`` para control de GPIO (botón y motor),
- ``spidev`` para comunicarse con el ADC MCP3008,
- ``time`` para retardos,
- ``math`` para cálculos de temperatura con funciones logarítmicas.
.. code-block:: python
#!/usr/bin/env python3
import RPi.GPIO as GPIO
import spidev
import time
import math
#. Configurar pines GPIO:
- Botón en GPIO22 (con resistencia de pull-up interna),
- Control del motor usando GPIO5 (adelante), GPIO6 (atrás) y GPIO13 (PWM habilitación).
.. 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)
#. Inicializar comunicación SPI con el MCP3008 (Bus 0, Chip Enable 0) a 1 MHz.
.. code-block:: python
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1000000
#. Definir la función ``read_adc()`` para leer un valor analógico de 10 bits (0–1023) del canal MCP3008 especificado (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
#. Definir la función ``temperature()`` para:
- Convertir el voltaje analógico a resistencia,
- Aplicar la ecuación de Steinhart–Hart para calcular la temperatura en Celsius.
.. 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
#. Definir ``motor_run()`` para:
- Detener el motor en nivel 0,
- Hacer funcionar el motor hacia adelante con velocidad creciente según el nivel 1–4, con un ciclo de trabajo PWM del 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
#. Definir la función de devolución de llamada ``changeLevel()`` para pulsación de botón, que:
- Incrementa el nivel del motor cíclicamente (0 a 4),
- Registra la temperatura actual como nueva referencia.
.. code-block:: python
def changeLevel(channel):
global level, currentTemp, markTemp
print("Botón presionado")
level = (level + 1) % 5
markTemp = currentTemp
GPIO.add_event_detect(BTN_PIN, GPIO.FALLING, callback=changeLevel, bouncetime=300)
#. Definir el bucle ``main()`` para:
- Supervisar el cambio de temperatura respecto a la temperatura de referencia,
- Disminuir el nivel si la temperatura baja 2°C o más,
- Aumentar el nivel si la temperatura sube 2°C o más,
- Ajustar la velocidad del motor cada 0,2 segundos.
.. 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)
#. Ejecutar la función principal y asegurar la limpieza correcta al presionar Ctrl+C (detener motor, limpiar GPIO, cerrar SPI).
.. code-block:: python
try:
main()
except KeyboardInterrupt:
pass
finally:
pwm.stop()
GPIO.cleanup()
spi.close()