GPIO en C: Control automático de ventilador según temperatura

Anterior                        Índice                        Siguiente


En este entrada se explica cómo conectar un ventilador a un SBC (en nuestro caso una OPi), y cómo controlarlo de forma automática a través de un script capaz de determinar la temperatura del sistema (correspondiente al SoC) y actuar en consecuencia, controlando al ventilador y estableciendo velocidades de giro prefijadas, todo ello gracias al control PWM y la librería WiringOP


Notas:

  • Existen dos ramas diferentes para controlar y hacer uso de los pines GPIO: mediante el lenguaje C o mediante el lenguaje Python.
  • Esta entrada se corresponde con la rama basada en el lenguaje C (librería WiringOP).
  • Para conectar un ventilador a una Orange Pi hace falta de un circuito intermedio, como se ve en esta entrada.
  • Para controlar la velocidad de giro de un ventilador, se utiliza una señal PWM.
  • Existen ventiladores con 3 o más conexiones (cables), de las cuales una es utilizada como entrada PWM, en ellos, el circuito que hace uso de dicha señal de entrada y regula la velocidad se encuentra en el interior del propio ventilador. En nuestro caso, utilizaremos un ventilador de solo 2 conexiones, las de alimentación, por ello, el circuito para controlar su velocidad de giro será el que hará falta desarrollar.
  • Los componentes necesarios para realizar la conexión presentan hipervínculos a productos online donde poder comprarlos. No tengo ninguna relación con estas tiendas, y están puestos a modo de ejemplo para saber determinar exactamente el componente necesario, podéis adquirirlos en cualquier otro sitio.
  • Hay algunos aspectos en esta entrada enfocados a personas con mayor conocimiento técnico, sobre todo en el apartado de diseño electrónico, pues no quería dejarme nada en el tintero, no obstante, son elementos correspondientes a profundizar en el diseño, así como los cálculos y el porqué del circuito de control, por lo que, aunque no se entienda algo de esa parte, simplemente se puede hacer uso del circuito final diseñado.
  • Todo lo recopilado en esta entrada debería ser aplicable y utilizable en otros SBC (como por ejemplo la Raspberry Pi).

Conectar un ventilador (Diseño electrónico):

Cuando se pretende conectar algún elemento externo a través de los pines GPIO, a la Orange Pi, hay que tener presente si ese elemento es controlable dentro de los rangos de voltaje y corriente de operación usado por los pines del sistema.

Con lo anterior en mente, y enfocándonos en el ventilador como elemento a conectar, procederemos a analizar la forma segura y correcta de conectarlo para evitar que la OPi sufra daño alguno, ni a corto, ni a largo plazo.

Lo que se pretende es conectar un ventilador para tener un control completo sobre él, que permita apagarlo, encenderlo y gestionar la velocidad de giro, desde el propio sistema. Por ello, la conexión debe de hacerse a través de un pin GPIO que permita controlar y regular la velocidad con la que debe de girar.

¿Por qué no se puede conectar directamente el ventilador?

ventilador_conexion_directa

Primero, hay que dejar claro un error grave y muy común que se comete a la hora de conectar un ventilador, y es que, este no debe ser alimentado directamente por un pin GPIO (atención, por los pines de 5V y GND sí, ya que, aunque estén en la tira de pines GPIO, no son pines GPIO, son pines de alimentación, que se corresponden con el conector DC de la entrada de la fuente).

Como se ha dicho, para conectar un ventilador, tenemos que determinar si los pines GPIO de la OPi trabajan en el mismo rango de valores de voltaje y, para empezar, esto no es así, pues la lógica de los pines GPIO de la OPi (y el de la mayoría de SoCs y circuitos integrados modernos) es de 3.3V (es decir, el pin apagado daría 0V y activado 3.3V), mientras que el ventilador necesita 5V para funcionar como debe.

Que el voltaje que suministre un pin GPIO (3.3V) sea inferior al necesario por un ventilador (5V) solo implica que este no girará con la potencia que debiera (o no tendrá fuerza para arrancar/girar), sin embargo, hay algo mucho más preocupante y peligroso, que podría dañar a la OPi, y es la demanda de corriente.

Ventilador

Si miramos en diferentes tipos de ventiladores con estas características de pequeño tamaño y un voltaje de funcionamiento de 5V, tenemos que todos ellos necesitan cientos de mili-amperios para funcionar, por ejemplo, en el caso del ventilador de la imagen anterior, se requieren 200mA (hay otros ventiladores con estas características que necesitan 100mA y otros 300mA), es decir que el ventilador le va a exigir al pin GPIO que le suministre dicha cantidad de corriente.

Y es aquí donde tenemos el problema, pues si accedemos al Datasheet del SoC Allwinner H3, y consultamos el apartado de valores máximos absolutos, del capítulo correspondiente a las características eléctricas (Página 607), podemos ver que la corriente máxima que podría suministrar o recibir cualquier pin, es de 40mA (ver la siguiente imagen), muy inferior a los 200mA que demandan los ventiladores.

OPi_Igpio

Por tanto, tenemos que un pin GPIO de la OPi puede entregar como máximo 40mA y el ventilador le pedirá que de 200mA, 5 veces más de lo que puede dar y esto podría provocar que el pin o todo el puerto (conjunto de pines del SoC que comparten el mismo circuito interno de entrada/salida) se dañase, lo que, en casos extremos (pero poco probables), podría incluso provocar algún cortocircuito interno que dañase e inutilizase a todo el SoC.

ventilador_conexion_directa - copia (2)

Como conclusión, decir que este es el motivo por el que no se debe conectar directamente un ventilador (o cualquier tipo de motor o sistema que requiera mucha corriente) a un pin GPIO (ni de la OPi, ni de la RPi, ni cualquier otro tipo de SoC o microcontrolador/microprocesador), ya que los pines GPIO no pueden entregar/recibir la cantidad de corriente que exige el ventilador.

Conexión correcta y segura del ventilador. El circuito de control

Como hemos visto, no se puede alimentar un ventilador directamente mediante un pin GPIO porque se necesita más corriente de la que el pin puede entregar y este se dañaría, por lo que, para alimentar al ventilador, se necesita de un elemento capaz de ofrecer un voltaje de 5V y de suministrar esos 200mA requeridos por el ventilador. Así que, el elemento más accesible y adecuado que tenemos para ello no es otro que la propia fuente de alimentación que se conecta a la OPi (volvemos a recordar que los pines de 5V y GND se corresponden con esta). No obstante, si conectásemos a los pines de alimentación, 5V y GND, el ventilador giraría a máxima velocidad, pero no tendríamos control sobre el mismo, para poder gestionar tanto la activación/desactivación, como la velocidad de giro, se necesita de un pin que podamos controlar, un pin GPIO.

La solución entonces pasa por el uso de «algo» que nos permita controlar la alimentación de 5V-GND del ventilador a través de un pin GPIO, y este es el papel fundamental del transistor, el cual actuará como un interruptor controlado por el pin GPIO (los transistores pueden trabajar de muchas otras formas, por ejemplo, como elementos de amplificación de señales analógicas, pero su uso más básico y simple es el de «interruptor/conmutador»). De este modo, el ventilador será controlado por el transistor y, a su vez, el transistor será controlador por el pin GPIO de la OPi, siendo la corriente requerida para la activación del transistor, apta para ser suministrada por el pin GPIO (mucho menor, como veremos, de los 40mA máximos que puede entregar el pin).

Cuando se utiliza un transistor como si fuera un interruptor, trabajando en los llamados modos de “Corte” y “Saturación”, al aplicarle una corriente en su terminal base (véase la siguiente imagen), se cerrará la conexión (transistor en Saturación) entre sus terminales colector y emisor; y al dejar de suministrar dicha corriente, la conexión quedará abierta (transistor en Corte).

transistorcortesat

Dicho esto, el circuito básico para controlar el ventilador sería de la siguiente forma:

CircuitoCtlBas

El transistor que vamos a utilizar será el 2n2222A (un transistor muy común y que cumple con lo que necesitamos, trabajar con corrientes superiores a los 200mA que necesita el ventilador, con este transistor se pueden manejar corrientes de hasta 600/800mA según el modelo), es un transistor BJT (bipolar) por lo que, para activar su base se necesita de una corriente (a diferencia de los transistores JFET o MOSFET que lo que se requiere es un voltaje). Por ello, necesitamos gestionar la corriente de salida del pin GPIO, limitándola a través del uso de una resistencia (R). Entonces, nos toca determinar que resistencia se ha de poner, he incluso qué corriente de base se requiere para activar al transistor, pues bien, ambas cosas están relacionadas con la corriente que necesita el elemento a controlar, en este caso el ventilador.

Para los transistores BJT, la corriente que queremos que fluya entre colector y emisor (correspondiente a la corriente del sistema a controlar, en este caso los 200mA del ventilador), va a estar relacionada con la corriente necesaria en base para su activación a través de lo que se conoce como la ganancia del transistor (β o hFE). Ya que la propiedad básica de los transistores es la amplificación de la señal, la ganancia toma un papel fundamental en la misma, comportándose como un parámetro de amplificación, de esta forma, la relación matemática establece que la corriente de colector será beta veces mayor a la de base:

eq1

Y dado que la corriente que fluirá por el colector es la que se necesita para activar el ventilador, tendremos que la corriente de colector será de unos 200mA:

eq2

Por otro lado, consultando la hoja de datos (Datasheet) del transistor, tenemos que para la ganancia del mismo, con una corriente de colector cercana a los 200mA podríamos tomar, para el caso peor, el valor mínimo de 100:

2n2222Data1

eq3

Por lo que la corriente de base deberá de ser 100 veces inferior a la de colector. Es decir que la corriente de base del transistor tiene que ser 100 veces menor a la necesaria para activar el ventilador, en este caso, 2mA:

eq4

Y ahora que conocemos la corriente que se le debe de suministrar a la base del transistor a partir del pin GPIO para poder activar el ventilador, nos toca limitar dicha corriente calculando el valor de la resistencia R (si no se pusiera esta resistencia, la corriente que se le pediría al pin GPIO sería la correspondiente a la resistencia interna de la unión base-emisor del transistor, la cual es muy pequeña, y demandaría por tanto una gran cantidad de corriente, superior a la que podría suministrar el pin GPIO).

Internamente, la conexión base-emisor de un transistor se puede modelar como un diodo (una unión PN, pues estamos ante un transistor NPN), con una caída de tensión máxima que se corresponde con lo especificado por el fabricante en la hoja de datos como Vbe en saturación (estado activo de modo de trabajo del transistor como interruptor), y en este caso es de 1.2V (para el peor de los casos, el valor máximo).

2n2222Data2

Por tanto, para poder activar el ventilador, la resistencia máxima que podemos poner en la base del transistor vendrá dada por la llamada ley de ohm (relación básica existente entre el voltaje, la corriente y la resistencia):

eq5

Es decir, la resistencia máxima que podríamos poner en la base del transistor para asegurar que se manejen los 200mA, deberá ser igual o menor al valor teórico máximo de 1050 ohmios, el cual podemos disminuir (a menor resistencia más corriente) al valor comercial de 1k, para dejar un margen de seguridad, lo que nos asegura que la corriente de base será de 2mA o más. Hay que mencionar que el valor calculado depende tanto del transistor utilizado, como de la corriente necesaria por el ventilador (en caso de utilizar el mismo transistor 2n2222, pero en un ventilador que necesite 300mA, la corriente de base sería de 3mA y por tanto la resistencia debería ser menor o igual a 700 ohmios, es decir una resistencia comercial de 680 ohmios sería adecuada).

Así, el circuito básico de control con el valor de resistencia calculado quedaría como sigue:

CircuitoCtlBasValores

Con todo esto ya tenemos un circuito básico y seguro para controlar el ventilador mediante un pin GPIO, pero podemos encontrarnos con algún efecto no deseado.

Mientras el pin GPIO no esté bien configurado como salida digital, la base del transistor puede actuar de antena y tomar valores lógicos inadecuados, activando y desactivando el transistor según la contaminación electromagnética que haya en el entorno. Para solucionar esto, se recurre a fijar un nivel lógico en la línea para que actúe cuando el pin GPIO no esté configurado; esto se puede conseguir a través de una resistencia de pull-down, ya que por defecto el ventilador debe estar parado (una resistencia de pull-up, es decir fijar un valor HIGH en la base, haría que el transistor estuviera saturado y consecuentemente que el ventilador estuviera activo y esto no es lo que necesitamos). El cálculo de los límites de valores de esta nueva resistencia es mucho más complejo que el anterior y no entraremos en ello, ya que afectan otros factores a parte del voltaje y la corriente (también habría que analizar el circuito interno del pin GPIO y determinar los efectos capacitivos e inductivos de la propia linea de base del transistor, pues estará relacionado con la frecuencia máxima de conmutación del transistor y la velocidad máxima a la que se pueden realizar cambios de estados lógicos en la línea), no obstante, un valor adecuado para más del 90% de casos (entre ellos el nuestro) es el valor de resistencia de 10K, y será el que tomemos para nuestro circuito (recomiendo utilizar valores en el rango 10k a 47k).

Por otra parte, el uso del transistor evita que se dañe el pin GPIO por la demanda excesiva de corriente, pero hay un efecto que sucede con los motores (en nuestro caso el ventilador) por el cual, si este está funcionando y se desactiva su funcionamiento (cuando queremos que se detenga), los campos electromagnéticos generados por las bobinas internas, usadas en el motor para funcionar, comienzan a colapsar y auto-inducen una diferencia de potencial que puede generar un flujo de corriente capaz de, a largo plazo, dañar al transistor. Este efecto se conoce como Flyback y hace referencia a esa corriente de retroceso no deseada, sin embargo, una solución bastante sencilla de evitar que este efecto dañe al transistor se basa en la utilización de un diodo que redirija a la corriente y haga caer al voltaje (potencial) generado. De esta forma, el diodo que utilizaremos será el 1n4007 (por el hecho de ser muy común, aunque podrían utilizarse muchos otros, 1n4004, 1n4001…).

Añadiendo estos dos simples componentes a nuestro circuito, la resistencia de 10k y el diodo 1n4007, corregiremos estos dos efectos no deseados y completaríamos nuestro circuito de control, el cual quedará de la siguiente forma:

CircuitoCtlCom

Diagrama de conexión

Una vez con el circuito de control completamente diseñado, veremos la conexión que hay que realizar entre este, y la Orange Pi:

GPIOFan

Cabe mencionar que el pin de control utilizado es el pin 7 (PA6), pero podríamos haber utilizado otro.

WiringPi_GPIOs_PWM

Como puede observarse, la conexión entre la OPi y el circuito de control se lleva a cabo a través de 3 pines (2 de alimentación y 1 de control), mientras que el ventilador se conecta al circuito de control mediante sus 2 pines, es decir, que, a la hora de construir el circuito de control, este presentará 3 conexiones de entrada y 2 de salida, como veremos a continuación.


Creando el circuito de control y conectando el ventilador (Desarrollo hadware):

Una vez que tenemos planteado los elementos que intervienen en la conexión, pasamos a la creación del circuito, pero antes, vamos a recopilar la lista de herramientas y componentes necesarios.

Elementos y componentes necesarios

Para empezar, necesitaremos dos cosas básicas:

Además, necesitaremos de ciertos componentes electrónicos que utilizar para hacer un circuito intermedio entre la OPi y el ventilador que permita la conexión segura entre ellos y el control de velocidad de giro:

Opcionalmente, para el caso de querer montar el circuito sobre una placa de prototipado y realizar las conexiones por cables, tal y como se realiza en esta entrada, necesitaremos:

Por último, se necesitarán de ciertas herramientas y elementos fungibles para realizar el circuito:

  • Soldador/Cautín para soldar los componentes.
  • Estaño para realizar la soldadura.

Creación de la placa de control, y conexión final

La parte de la creación y soldadura de los componentes de la placa de control queda libre a como quiera hacerlo cada uno, además de que es difícil de explicar en texto, no obstante, daré algunos apuntes al respecto para aquellos alejados al mundo de la electrónica.

Para empezar, algo que hay que conocer es el patillaje de los componentes, al menos en el caso del transistor, donde hay que saber localizar la base, el colector y el emisor, y del diodo, con su ánodo y su cátodo (la orientación de las resistencias es indiferente):

  • Para el diodo, basta con prestar atención a su encapsulado, en él, deberá estar marcado, con una franja blanca (en el caso del 1n4001), el terminal negativo (cátodo) de salida del diodo (el cual es representado igualmente en su símbolo, en el esquema del apartado anterior, con una línea):

DiodoEncap

  • Para el transistor, se puede determinar el patillaje si se posee alguna herramienta de medida (multimetro) capaz de ello, si no, también se puede determinar mirando la imagen correspondiente a su encapsulado en los Datasheets (hay 2 tipos de encapsulados para el transistor 2n2222, el TO-92 y el TO-18, y dependiendo del Datasheet se mostrará uno u otro):

2n2222encap

Por otro lado, hay que decir que la placa de control se ha realizado sobre una placa perforada de prototipado, aunque alguien con soltura y herramientas, podría crear una placa de circuito impreso o realizarlo de algún otro modo. En este caso, se han insertado los componentes en la placa perforada y se han soldado de acuerdo al diseño previo elaborado. Para las entradas y salidas del circuito se han utilizado pines machos que permitan conectarnos y desconectarnos a la placa a nuestro antojo, mediante cables Dupont.

 

 

Nota: en el circuito de las imágenes anteriores falta el diodo de protección del transistor, pero este debería de incluirse para evitar que el transistor se dañe a largo plazo.

Automatizando el control del ventilador según la temperatura (Desarrollo software):

Ahora que tenemos todo el hardware resuelto, podemos proceder al desarrollo del programa que controle automáticamente al ventilador según la temperatura del sistema, vayamos por partes.

Regulación de la velocidad de giro

Volviendo brevemente a la teoría, debemos explicar el cómo controlar la velocidad de giro de un motor (en este caso el ventilador).

En esencia, según lo que hemos visto hasta ahora, tenemos que el circuito de control, que contiene un transistor actuando a modo de interruptor, nos permite activar o desactivar al ventilador, podría pensarse que esto no nos valdrá, o que hará falta incluir algún otro elemento hardware, pero la cosa es mucho más sencilla, para controlar la velocidad de giro de un motor basta con controlar la velocidad a la que se activa y se desactiva (concretando, la duración en la que está activo y la duración que está desactivado), de modo que activándolo y desactivándolo continuamente en un periodo de tiempo pequeño (mili-segundos), se controlará la velocidad a la que gire, así por ejemplo, cuanto más tiempo pase encendido y menos apagado, más rápido girará; este comportamiento se puede conseguir a través de un pin GPIO configurado para que genere lo que se denomina una señal de modulación por ancho de pulso, una señal PWM, la cual controla la duración de su estado de encendido (3.3V) y apagado (0V) según lo que se conoce por ciclo de trabajo de la señal.

Dicho esto, hay que recordar que existe una entrada específica donde se explica más sobre el PWM y el cómo utilizarla mediante un pin GPIO.

La lógica del programa de control

Puestos manos a la obra con la programación, vamos a resumir primero el cómo actuará el sistema para determinar el control a realizar sobre el ventilador según la temperatura interna del sistema, lo que sería la inteligencia del programa.

Al principio, el programa deberá de inicializar la librería de control GPIO (WiringOP) y configurar al pin número 7 (PA6) como salida PWM, una vez hecho esto, deberá de consultar la temperatura del sistema periódicamente (cada 5 segundos, para evitar sobre-cargar al sistema realizando consultas constantemente) y, si esta ha variado (2 o más grados, para evitar un cambio continuo entre un valor exacto), respecto a la temperatura consultada con anterioridad, configurará la señal PWM según la temperatura a la que se encuentre el sistema; trás esto, se vuelve a realizar la espera (de esos 5 segundos) y comienza nuevamente el proceso de consulta de temperatura y gestión de ventilador.

Un aspecto a tener en cuenta sobre la consulta de la temperatura del sistema es que, este valor es accesible a través del sistema de archivos (gracias al denominado Sysfs), mediante la consulta del archivo de sistema localizado en la ruta «/sys/class/thermal/thermal_zone1/temp», el contenido de este archivo mantiene el valor de temperatura actual, y en nuestro código, leemos su contenido para determinarla. Para la lectura de este archivo mediante el lenguaje de programación utilizado (lenguaje C), se ha desarrollado una función específica (int readTemp(const char* file_path)) que en esencia,  lo que hace es abrir el archivo «file_path« que se le pase como argumento, y una vez abierto, lee el primer número entero que haya en su interior (lo he hecho así porque sé que el archivo solo contiene ese valor numérico de temperatura, y solo se lee su valor entero, porque no me interesa el valor decimal de la temperatura, además de evitar problemas con caracteres especiales de fin de línea y de archivo, si hiciera la lectura de caracteres, en vez de números), cierra el fichero y retorna el valor de temperatura leído.

Las configuraciones de la señal PWM (con valores entre 0 y 1023) se han prefijado a 3 valores según el rango de temperatura en el que se encuentre el sistema, correspondiéndose con los siguientes estados de ventilador: apagado (valor PWM de 0), 50% de velocidad (valor PWM de 512) y 100% de velocidad de giro del ventilador (valor PWM de 1023), de modo que:

  • Si la temperatura actual se encuentra en un valor inferior a una temperatura umbral baja (55ºC), no hará falta el uso de ventilador, y por tanto, deberá estar apagado.
  • Si la temperatura se encuentra entre ese valor umbral bajo y un valor umbral alto de temperatura (entre 55ºC y 68ºC), deberá girar al 50% de su velocidad.
  • Si se supera el umbral alto de temperatura (68ºC), deberá ponerse al 100%.

Temperatura

Hay que mencionar que los valores de estos umbrales de temperatura, así como la diferencia mínima de temperatura, el tiempo de espera y el pin PWM a utilizar, pueden modificarse fácilmente en el código que se suministra a continuación, tan solo deben de tocarse las líneas que comienzan por «#define» del principio del código.

Se han desarrollado dos códigos distintos, el primero y más básico (tempfan.c), está enfocado exclusivamente al control automático del ventilador según la temperatura, siendo un código más simple e instructivo, mientras que el segundo código (tempfanadv.c), es una versión avanzada del primero, que aparte de controlar el ventilador, lleva un registro de forma segura, mediante un archivo de LOG, de los eventos relacionados con el ventilador (activación/desactivación, velocidad de giro y la temperatura y hora en la que sucede dicho evento). Así, se recomienda utilizar este segundo código, no obstante, se ha dejado el primero de forma que sirva para entender la forma más básica el control del ventilador.

Código del programa simple

/*******************************************************************/
/* Nombre: tempfan.c                                               */
/* Descripcion: Control automático de ventilador segun temperatura */
/* Autor: JRios                                                    */
/* Fecha: 20/06/2017                                               */
/* Version: 1.0.2                                                  */
/*******************************************************************/

// Librerias
#include <wiringPi.h>
#include <softPwm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// Definiciones (modificables a antojo del usuario)
#define PIN_PWM   7                                       // Pin PWM numero 7 (PA6)
#define PWM_MIN   0                                       // Valor PWM minimo
#define PWM_MID   512                                     // Valor PWM intermedio [poner a 750 para Raspberry Pi]
#define PWM_MAX 1023                                      // Valor PWM maximo
#define DIFF_TEMP 2                                       // Diferencia de temperatura minima entre lecturas (2ºC)
#define TEMP_LOW  55                                      // Valor umbral de temperatura bajo (55ºC) [Poner a 50 para Raspberry Pi]
#define TEMP_HIGH 68                                      // Valor umbral de temperatura alto (68ºC) [Poner a 60/65 para Raspberry Pi]
#define T_READS   5                                       // Tiempo de espera entre lecturas de temperatura (5s)
#define FILE_TEMP "/sys/class/thermal/thermal_zone0/temp" // Ruta y nombre del archivo de acceso a la temperatura

/*******************************************************************/

// Tipo de dato para determinar los estados correspondiente a la velocidad del ventilador (0%, 50% y 100%)
typedef enum {OFF, SLOW, FAST} T_state;

// Prototipo de funcion de lectura de temperatura
int readTemp(const char* file_path);

/*******************************************************************/

// Funcion principal
int main(void)
{
	int temp_last, temp_now, diff_temp;
	T_state state_last, state_now;

	wiringPiSetup();
	softPwmCreate(PIN_PWM, PWM_MIN, PWM_MAX);

	softPwmWrite(PIN_PWM, PWM_MIN); // Ventilador apagado
	state_now = OFF;
	state_last = OFF;

	temp_now = readTemp((char*)FILE_TEMP); // Leer temperatura
	if((temp_now = TEMP_LOW) && (temp_now  TEMP_HIGH) && (state_last != FAST)) // Temperatura mayor que TEMP_HIGH (70ºC) y estado anterior distinto
	{
		softPwmWrite(PIN_PWM, PWM_MAX); // Ventilador al 100%
		state_now = FAST; // Estado actual: Ventilador al 100%
	}
	temp_last = temp_now; // Guardar la ultima lectura de temperatura (cuando se ha configurado el ventilador)
	state_last = state_now;

	while(1)
	{
		temp_now = readTemp((char*)FILE_TEMP); // Leer temperatura
		diff_temp = abs(temp_now - temp_last); // Calcular diferencia de temperatura

		if(diff_temp >= DIFF_TEMP) // Diferencia de temperatura mayor que DIFF_TEMP (2ºC)
		{
			if((temp_now = TEMP_LOW) && (temp_now  TEMP_HIGH) && (state_last != FAST)) // Temperatura mayor que TEMP_HIGH (70ºC) y estado anterior distinto
			{
				softPwmWrite(PIN_PWM, PWM_MAX); // Ventilador al 100%
				state_now = FAST; // Estado actual: Ventilador al 100%
			}

			if(state_now != state_last) // Estado actual distinto al anterior (ha cambiado de estado)
			{
				temp_last = temp_now; // Guardar la ultima lectura de temperatura (cuando se ha configurado el ventilador)
				state_last = state_now; // Actualiza la variable de estado anterior
			}
		}

		sleep(T_READS); // Esperamos, liberando a la CPU del proceso actual durante T_READS (5s)
    }

    return 0;
}

/*******************************************************************/

// Funcion de lectura de la temperatura
int readTemp(const char* file_path)
{
	FILE* file; // Puntero a tipo archivo (para manejar el archivo)
	int temp = 0; // Variable donde almacenar la temperatura leida y para devolverla al salir de la funcion

	file = fopen(file_path, "r"); // Abrimos el archivo
	if(file) // Comprobamos que la apertura ha sido exitosa (si no, no hace nada)
	{
		fscanf(file, "%d", &temp); // Almacenamos en la variable "temp" el primer valor entero (int) que haya en el archivo
		fclose(file); // Cerramos el archivo
	}

	// Si la temperatura tiene 4 o más digitos, dividimos entre 1000 (por ejemplo, en OPi Zero, se leen esos valores)
	if(temp >= 1000)
		temp = temp/1000;

	return temp; // Devolvemos el valor de temperatura leida
}

Instalación y utilización del programa

Aunque mostremos el código de la versión simple del programa en esta entrada, se recomienda utilizar la versión avanzada del mismo, y esto es lo que se va a realizar a continuación. El código puede ser descargado desde e repositorio Github que lo contiene, de forma que siempre estará en su última versión (en caso de que se actualice en el futuro). Así, mostramos el proceso básico de adquisición compilación y puesta en funcionamiento del script:

1 – Clonar en local (descargar) el programa de control del ventilador, desde su repositorio Github:

git clone https://github.com/J-Rios/OPi_FanTempControl

2 – Accedemos al directorio descargado:

cd OPi_FanTempControl

3 – Compilamos el código y generamos el archivo ejecutable:

gcc -o tempfan tempfanadv.c -lwiringPi -lpthread

4 – Ejecutamos el programa en segundo plano:

sudo ./tempfan &

5 – (Opcional) Para detener el programa:

PID=`sudo pidof tempfan`
sudo kill -9 $PID

Hacer que el programa se lanze al arrancar el sistema

La forma más sencilla, y quizás la más apta para utilizar en la mayoría de distros, es la de lanzar el programa a través del archivo rc.local (el cual se ejecuta al arrancar el sistema y una vez que todos los servicios iniciales estén inicializados):

1 – Copiamos nuestro programa de control compilado en el directorio de binarios del usuario (con esto se consigue poder llamar a nuestro programa desde cualquier ruta, pues el directorio /usr/bin, al igual que otros, pertenecen a la llamada variable de entorno PATH):

sudo cp -a tempfan /usr/bin/

2 – (Opcional) Una vez con el programa compilado y posicionado en /usr/bin, podemos borrar todo el directorio que contiene el archivo fuente:

cd ..
sudo rm -rf OPi_FanTempControl

3 – Adquirimos privilegios de administración:

sudo -i

4 – Añadimos al archivo rc.local el comando para lanzar a nuestro programa (se puede hacer editando el archivo, pero con los siguientes comandos nos aseguramos de que se hace de forma correcta, antes de la línea «exit 0»):

cp -a /etc/rc.local /etc/rc.local.back # Hacemos una copia de seguridad del archivo original
sed -i '/exit/d' /etc/rc.local # Eliminamos la linea exit 0
echo "tempfan" >> /etc/rc.local # Insertamos la linea tempfan al final del archivo
echo "exit 0" >> /etc/rc.local # Insertamos la linea exit 0 al final del archivo

Hecho lo anterior, se consigue que cada vez que arranque la OPi se ejecute el programa de control del ventilador, para probarlo, basta con reiniciar el sistema:

reboot

Anterior                        Índice                        Siguiente

10 comentarios en “GPIO en C: Control automático de ventilador según temperatura

  1. Pingback: RETRO CONSOLA POR 25€. | caracolgeek's Blog

  2. Hola J. Rios. Lo primero, muchas gracias por tu post, era justo lo que iba buscando.
    Sin embargo, no logro que me vaya 100% bien. Cuando ejecuto el programa, el ventilador hace un ruido tipo máquina de coser. Entiendo que se está encendiendo y apagando con mucha rapidez. He probado todo lo que se me ha ocurrido (cambiar el T_READS, los valores al PWM, las temperaturas…) pero sin resultado: el ruido persiste. Cuando salgo del programa, el ventilador se queda cogido en alguno de los estados y funciona sin ruidos (ya sea al 100%, 66% o 33% – he introducido un valor nuevo), por lo que pienso que no es a causa del PWM.
    Lo cierto es que lo estoy ejecutando en una R-Pi 2. ¿Se te ocurre a qué podría deberse?

    Me gusta

  3. Hola, me alegra que sea útil incluso para poseedores de Raspberry :p

    Pues la verdad que no te sabría decir con exactitud, no creo que el problema sea software, ni debido a que utilices Raspberry (aunque tampoco se puede descartar al 100%, pero pondría esta posibilidad en último lugar). Tiendo más a pensar que el ruido podría venir dado tanto por la señal PWM, como por el hardware y/o el propio ventilador en cuestión que estes utilizando…

    Cosas que se me ocurren:

    – Tienes alguna variación en el hardware (distintos componentes), configuración o conexión erronea?

    – Que tipo de ventilador usas? De cuanto voltaje es y cuanta corriente necesita? Es un ventilador con más de un cable (los ventiladores de 3 o más cables llevan circuitería interna y es posible que una variación directa de los pines de alimentación mediante PWM no sea adecuada y haga resonar al ventilador)?

    – Has probado con distintos pines GPIO como salida PWM (por si existiera en la Raspberry alguna limitación del PWM en algún pin)?

    Realmente lo último que dices: «Cuando salgo del programa, el ventilador se queda cogido en alguno de los estados y funciona sin ruidos…» me inclina a pensar claramente que el ventilador lleva algún circuito interno para gestionar su funcionamiento, ya que da la impresión que al salir del programa (cuando deja de enviarse la señal PWM), aún así, el ventilador sigue funcionando a esa velocidad constante que no tiene por qué ser 100%? (no sé como cerraras el programa pero independientemente, al liberar el pin, este debería quedarse a un nivel lógico fijo y por tanto, a 0V o 5V)…

    Saludo.

    Me gusta

  4. Hola, josrios:
    Lo primero, muchas gracias por tu respuesta. 🙂
    En cuanto al problema, después de hacer unas cuantas simulaciones y darle varias vueltas, llegué a la conclusión de que era cuestión de la generación de PWM mediante software. Con un programa sencillo puse el ventilador a funcionar con distintos pulsos y observé que sólo funcionaba fluido cuando el PWM era del ~90% o superior. Tiré del hilo y vi que en la R-Pi no se recomienda usar el PWM software para motores porque la frecuencia es demasiado baja, en torno a 10Hz (ver aquí: https://raspberrypi.stackexchange.com/questions/298/can-i-use-the-gpio-for-pulse-width-modulation-pwm#304).
    Al final lo que he hecho es una modificación del software para poner sólo dos estados: encendido (100%) o apagado, usando el pin como salida digital. Observando las temperaturas de funcionamiento y para evitar múltiples encendidos/apagados, parece óptimo encender a 55ºC y apagar a 52ºC.
    Existe en la R-Pi un pin PWM real (GPIO18), aunque está ‘linkado’ con la salida de audio, por lo que me decanté por no usarlo.
    Un saludo.

    Me gusta

  5. Olvidé contestar a tus preguntas: el ventilador es de dos hilos, motor ‘brushless’, de las mismas características del que tú usas en el manual (5V, 0,2A). Sobre el circuito, no hice ninguna modificación, así que si no he soldado mal (que no creo), está todo ok.
    El por qué es fluido el funcionamiento cuando de ‘mata’ el proceso del programa me resulta un misterio (porque juraría que no funcionaba al 100%, aunque sí fluido, cuando mataba el hilo). Podría ser que con el hilo muerto, la generación del PWM no se viera interferida por la llamada al hilo desde la pila. Como dice en el enlace anterior, ‘the Raspberry Pi is not suitable for any serious software PWM as Linux is not a real-time operating system’. Aunque la verdad es que no tengo ni idea de cómo funciona eso en el SO.

    Me gusta

  6. Hola,

    Algunas preguntas…

    1.- La solución sería aplicable tal cual en Raspberry PI 3 B ??
    2.- Caso de serlo. Si…#define PIN_PWM 7 // Pin PWM numero6$
    Cual correspondería al GPIO18 …. el 19??
    3.- La generación del PWM en ese port es por Software o Hardware ??

    Gracias por tu ayuda.

    Me gusta

  7. 1 – Como puedes ver en los comentarios anteriores, nezah determinó que las Raspberry tienen una limitación en la generación de PWM por software y por tanto, no permite que el ciclo de trabajo sea bajo (pueda ir lento el ventilador). Así que sólo te podría decir que lo pruebes, por salir de dudas de sí tu modelo concreto de Raspberry fuera diferente o la variación de los elementos hardware pudieran darte mejores resultados (aunque sea, poder ampliar más el rango de velocidades que soporte el ventilador).

    2 – Si, una vez instalado Wiringpi, ejecutas el comando «gpio readall», te saldrá una tabla con cada uno de los pines de la raspberry y su número correspondiente para wiringpi (columna «wPi»), el cul será el que debas usar en el código. Si no me equivoco, el pin 7 sería el GPIO-7 de Raspberry (pin 4 en la nomenclatura del SoC Broadcom que lleva la Raspberry).

    3 – Por software (puede verse claramente en el código que todo lo relacionado con el PWM es del estilo «SoftPWM…»).

    Me gusta

  8. Pingback: Orange Pi Zero y control de ventilador – Una ventana soleada

  9. Buenas!!
    Me ha encantado y quiero ponerlo en practica, pero tengo un problema.
    En el ventilador que tengo no marca los A que requiere, dada la poca experiencia en electronica que tengo, hay alguna manera de saberlo? con un tester o algo similar?

    Un saludo y gracias!!

    Me gusta

  10. Buenas!!
    Lo he puesto en practica pero parece ser que estoy haciendo algo mal, lo estoy utilizando en una raspberry pi 4B de 2GB de RAM.
    He seguido los consejos de NEZAH y he cambiado para que solo se encienda al 100% o apagado, la verdad que no se si he hecho algo mal, porque uno de los temas que esperaba es que cuando apagase en modo seguro la placa, al quedar en STAND BY, el ventilador se quedara apagado pero no es así, entonces preguntar ya que NEZAH lo ha hecho anteriormente, si el ventilador se desactiva cuando está en «STAND BY o SUSPENSION» la placa o si no, pero en caso de que sea así, si el o alguien podría enviarme la modificación del archivo porque no encuentro mi error. Gracias a todos!!

    Me gusta

Deja un comentario