Ir al contenido principal

Cómo leer el sensor de temperatura de la Raspberry Pi Pico

Leyendo las especificaciones de la Raspberry Pi Pico me llamó la atención que venía equipada con un sensor de temperatura en el propio chip, así que me puse a buscar información. En este artículo os quiero mostrar cómo acceder a él y de camino cómo utilizar el conversor analógico/digital (ADC).

Raspberry Pi Pico

El siguiente esquema muestra cómo está conectado el ADC a los GPIO y al sensor de temperatura interno.

Multiplexor ADC Raspberry Pi Pico

Es decir, tenemos un multiplexor de cuatro entradas que permite seleccionar la señal de entrada para el ADC. En nuestro caso, la que nos interesa es la 4, que es la fuente de tensión proveniente del sensor. Para seleccionar la entrada en el multiplexor usamos la siguiente función.

adc_select_input(4);

Para hacernos una idea, en la página 574 del datasheet del RP4020 nos dice que esta tensión es de 0.706V a 27 grados centígrados. La fórmula para aproximar la temperatura con respecto a esta tensión es

T = 27 - (ADC_voltage - 0.706)/0.001721

También hay que poner a 1 el registro TS_EN, para conectar el sensor. El SDK nos permite hacer esto fácilmente con la función

adc_set_temp_sensor_enabled(true);

Veamos pues cómo sería el proceso completo con un ejemplo en el que vamos a usar un display LCD 1602 para mostrar la temperatura. El manejo del LCD se hace por I2C y no voy a entrar en mucho detalle aquí, ya que hay bastante documentación al respecto. Además tenéis un ejemplo en la carpeta pico-examples/i2c/lcd_1602_i2c/ que acompaña al SDK.

Ya os conté en otro artículo los pasos para crear un proyecto para la Raspberry Pi Pico, así que ya sabemos que necesitamos como mínimo dos archivos: CMakeLists.txt y el código fuente, que en este caso lo he llamado termometro.c.

cmake_minimum_required(VERSION 3.12)
#enable_language( C ASM )
include(~/tools/pico/pico-sdk/external/pico_sdk_import.cmake)
#include(pico_sdk_import.cmake)
project(termometro C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(termometro termometro.c)
target_link_libraries(termometro pico_stdlib hardware_i2c hardware_adc)
pico_add_extra_outputs(termometro)

Este es el contenido del archivo CMakeLists.txt, que es muy similar al que ya vimos en el anterior artículo. Se diferencia en la siguiente línea, que indica al compilador que debe añadir las librería para la gestión del bus I2C (para el LCD) y del ADC.

target_link_libraries(termometro pico_stdlib hardware_i2c hardware_adc)

Antes de poner el código fuente completo, voy a centrarme en los pasos para leer el sensor de temperatura. El código mínimo para leer el sensor tendría una forma como ésta.

#include "hardware/adc.h"
int main() {
    // 12 bits. Vref=3.3V
    const float conv_factor = 3.3f / (1<<12); 
	
    uint16_t lectura;
    float lect_conv, temperatura;
    adc_init();
    adc_set_temp_sensor_enabled(true);
    adc_select_input(4); // imput 4 - sensor temperatura
	    
    while(1) {
        // Consultar ADC
        lectura = adc_read();
        lect_conv = lectura * conv_factor;
        temperatura = 27-(lect_conv-0.706)/0.001721;
        // mostrar temperatura
		printf("%2.1f", temperatura);
		    
        sleep_ms(20000);
    }
}

A la lectura del ADC hay que aplicarle un factor de conversión. En este caso, la tensión máxima es de 3.3V y nuestro ADC es de 12 bits, por lo que este factor es 3.3f/(1<<12). Al resultado le aplicamos la fórmula que habíamos visto más arriba para aproximar la temperatura.

El programa completo es algo más complicado, ya que incluye el código para el manejo del LCD, y sería el siguiente.

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "hardware/adc.h"
#include "pico/binary_info.h"
/* panel LCD 16x2 con adaptador I2C PCF8574 
   GPIO 20 (pin 26)-> SDA
   GPIO 21 (pin 27)-> SCL
   VSYS (pin 39) -> VCC
   GND (pin 38)  -> GND
*/
// algunas definiciones para controlar el LCD
const int LCD_CLEARDISPLAY = 0x01;
const int LCD_RETURNHOME = 0x02;
const int LCD_ENTRYMODESET = 0x04;
const int LCD_DISPLAYCONTROL = 0x08;
const int LCD_CURSORSHIFT = 0x10;
const int LCD_FUNCTIONSET = 0x20;
const int LCD_SETCGRAMADDR = 0x40;
const int LCD_SETDDRAMADDR = 0x80;
const int LCD_ENTRYSHIFTINCREMENT = 0x01;
const int LCD_ENTRYLEFT = 0x02;
const int LCD_BLINKON = 0x01;
const int LCD_CURSORON = 0x02;
const int LCD_DISPLAYON = 0x04;
const int LCD_MOVERIGHT = 0x04;
const int LCD_DISPLAYMOVE = 0x08;
const int LCD_5x10DOTS = 0x04;
const int LCD_2LINE = 0x08;
const int LCD_8BITMODE = 0x10;
const int LCD_BACKLIGHT = 0x08;
const int LCD_ENABLE_BIT = 0x04;
// dirección del bus I2C
// podría cambiar según modelos
#define I2C_PORT_LCD i2c0
static int addr_led = 0x27;
#define LCD_CHARACTER  1
#define LCD_COMMAND    0
#define MAX_LINES      2
#define MAX_CHARS      16
// pines I2C en la placa
#define LCD_SDA 20
#define LCD_SCL 21
void i2c_write_byte(uint8_t val) {
    i2c_write_blocking(I2C_PORT_LCD, addr_led, &val, 1, false);
}
void lcd_toggle_enable(uint8_t val) {
#define DELAY_US 600
    sleep_us(DELAY_US);
    i2c_write_byte(val | LCD_ENABLE_BIT);
    sleep_us(DELAY_US);
    i2c_write_byte(val & ~LCD_ENABLE_BIT);
    sleep_us(DELAY_US);
}void lcd_send_byte(uint8_t val, int mode) {
    uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT;
    uint8_t low = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT;
    i2c_write_byte(high);
    lcd_toggle_enable(high);
    i2c_write_byte(low);
    lcd_toggle_enable(low);
}
void lcd_clear(void) {
    lcd_send_byte(LCD_CLEARDISPLAY, LCD_COMMAND);
}
void lcd_set_cursor(int line, int position) {
    int val = (line == 0) ? 0x80 + position : 0xC0 + position;
    lcd_send_byte(val, LCD_COMMAND);
}
static void inline lcd_char(char val) {
    lcd_send_byte(val, LCD_CHARACTER);
}
void lcd_string(const char *s) {
    while (*s) {
        lcd_char(*s++);
    }
}
void lcd_init() {
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x03, LCD_COMMAND);
    lcd_send_byte(0x02, LCD_COMMAND);
    lcd_send_byte(LCD_ENTRYMODESET | LCD_ENTRYLEFT, LCD_COMMAND);
    lcd_send_byte(LCD_FUNCTIONSET | LCD_2LINE, LCD_COMMAND);
    lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND);
    lcd_clear();
}
int main() {
	uint16_t lectura;
	float lect_conv, temperatura;
	char s[10];
	const float conv_factor = 3.3f / (1<<12); 
    i2c_init(I2C_PORT_LCD, 100 * 1000);
    gpio_set_function(LCD_SDA, GPIO_FUNC_I2C);
    gpio_set_function(LCD_SCL, GPIO_FUNC_I2C);
    gpio_pull_up(LCD_SDA);
    gpio_pull_up(LCD_SCL);
    bi_decl( bi_2pins_with_func(LCD_SDA, LCD_SCL, GPIO_FUNC_I2C));
	
    lcd_init();
    adc_init();
	adc_set_temp_sensor_enabled(true);
	adc_select_input(4); // imput 4 - sensor temperatura
	
	while(1) {
		// Consultar ADC
		lectura = adc_read();
		lect_conv = lectura * conv_factor;
		temperatura = 27-(lect_conv-0.706)/0.001721;
        // mostrar temperatura en el LCD
		sprintf(s, "%2.1f", temperatura);
		lcd_set_cursor(0,0);
		lcd_string("Temperatura:");
		lcd_set_cursor(1,0);
		lcd_string(s);
		sleep_ms(20000);
		lcd_clear();
	}
    return 0;
}

Las conexiones entre el LCD y la Raspberry Pi Pico son las siguientes.

   GPIO 20 (pin 26)-> SDA en LCD
   GPIO 21 (pin 27)-> SCL en LCD
   VSYS (pin 39) -> VCC en LCD
   GND (pin 38)  -> GND en LCD

Ya vimos que para poder compilar el programa tendríamos que invocar a cmake para que se genere el makefile correspondiente.

cmake .

Y finalmente compilamos con make. El resultado en el siguiente video, donde se observa cómo sube la temperatura al tapar el microcontrolador con el dedo.

Comentarios

Entradas populares de este blog

Creando firmas de virus para ClamAV

ClamAv es un antivirus opensource y multiplataforma creado por Tomasz Kojm muy utilizado en los servidores de correo Linux. Este antivirus es desarrollado por la comunidad, y su utilidad práctica depende de que su base de datos de firmas sea lo suficientemente grande y actualizado. Para ello es necesario que voluntarios contribuyan activamente aportando firmas. El presente artículo pretende describir de manera sencilla cómo crear firmas de virus para ClamAV y contribuir con ellas a la comunidad.

Ingeniería inversa de un parche de Microsoft Windows

A estas alturas ya hemos asumido que la seguridad total en Internet es, cuanto menos, un mito. Ningún software está libre de vulnerabilidades, y por mucha auditoría, test de intrusión o pruebas de fuzzing que nos empeñemos en hacer, ninguna metodología puede demostrar sin lugar a dudas que un software es 100% seguro. Ante este panorama, hay dos tendencias mayoritarias a la hora de hacer públicas las vulnerabilidades. Por un lado, están los que piensan que cuando se descubre un fallo ha de hacerse público de forma inmediata. En este escenario, la publicación de una vulnerabilidad es el pistoletazo de salida para una carrera entre el desarrollador para sacar el parche y los "malos" para lograr explotarla. Esta política es conocida como full disclosure . Por otro lado, tenemos a los siguen una política responsible disclosure , que abogan por mantener la vulnerabilidad en secreto hasta la salida del parche por parte del desarrollador. Sin entrar en cuestiones filosóficas que no...

Manejo de grafos con NetworkX en Python

El aprendizaje computacional es un área de investigación que en los últimos años ha tenido un auge importante, sobre todo gracias al aprendizaje profundo (Deep Learning). Pero no todo son redes neuronales. Paralelamente a estas técnicas, más bien basadas en el aprendizaje de patrones, también hay un auge de otras técnicas, digamos, más basadas en el aprendizaje simbólico. Si echamos la vista algunos años atrás, podemos considerar que quizá, la promesa de la web semántica como gran base de conocimiento ha fracasado, pero no es tan así. Ha ido transmutándose y evolucionando hacia bases de conocimiento basadas en ontologías a partir de las cuales es posible obtener nuevo conocimiento. Es lo que llamamos razonamiento automático y empresas como Google ya lo utilizan para ofrecerte información adicional sobre tus búsquedas. Ellos lo llaman Grafos de Conocimiento o Knowledge Graphs . Gracias a estos grafos de conocimiento, Google puede ofrecerte información adicional sobre tu búsqueda, ad...