Sistema inteligente de monitorización de temperatura para escuelas: preciso, en tiempo real y seguro
Componentes y materiales
| Componente | Cantidad |
|---|---|
| Pycom SiPy (Sigfox + WiFi/BLE) | 1 |
| Raspberry Pi 3 Model B | 1 |
| Arduino Nano R3 | 1 |
| Conversor de nivel lógico bidireccional | 1 |
| Módulo RFID RC522 (NFC) | 1 |
| Sensor distancia Sharp GP2Y0A41SK0F (4-30 cm) | 1 |
| Sensor temperatura IR MLX90614 (I2C) | 1 |
| Pantalla LCD I2C 16x2 (DFRobot) | 1 |
| LED genérico + resistencia 330Ω | 1 |
| Buzzer | 1 |
| Resistencias 330Ω (x2) | 2 |
Aplicaciones y servicios
- Arduino IDE
- Visual Studio Code + Pymakr (para SiPy)
- ThingSpeak API
- IFTTT (alertas email)
- PuTTY / IDLE (Python en Raspberry Pi)
Sobre el proyecto

Planteamiento del problema
En Asia, por clima cálido y brotes como SARS (2003), los escolares deben medirse la temperatura cada trimestre. Cada alumno debe traer su termómetro digital, pero a menudo lo olvidan, pierden o rompen. No pueden compartirlos por higiene y algunos no pueden comprar uno nuevo. Además, el profesor debe introducir manualmente 40 temperaturas en base de datos: tarea tediosa y lenta.
La solución

Sistema automático que mide temperatura a usuarios válidos (tarjeta RFID), almacena datos localmente (MySQL en Raspberry Pi) y en la nube (ThingSpeak vía Sigfox), y envía alerta por email si hay fiebre (>38°C). Cumple Objetivo de Desarrollo Sostenible: Salud y bienestar.
Funcionamiento: Raspberry Pi + Arduino
- Raspberry Pi ejecuta Python + MySQL. Al arrancar, crea BD y tablas si no existen.
- Espera datos por puerto serie del Arduino (handshaking).
- Al detectar tarjeta RFID, Arduino envía UID a Raspberry Pi.
- Raspberry Pi valida UID en BD. Si válido → responde con iniciales y userID; Arduino muestra "Welcome, [iniciales]" en LCD. Si no → "Error! Invalid User!".
- Arduino pide tomar temperatura. Sensor de distancia (Sharp) detecta presencia.
- Sensor IR MLX90614 mide temperatura frontal. Si está en rango 28-42°C, muestra en LCD y envía a Raspberry Pi (MySQL) y a SiPy (Sigfox → ThingSpeak).
- Handshaking bidireccional: ambas partes envían "READY"/"RECEIVED" para sincronizarse.


Funcionamiento: Sigfox (SiPy)
SiPy recibe por UART (pines P3/P4) el userID y temperatura (parte entera y decimal). Envía 3 bytes a backend Sigfox (RCZ4 Asia). El backend reenvía a ThingSpeak mediante callback HTTP.
Funcionamiento: Nube (ThingSpeak + IFTTT)
- Canal privado (SIGFOX) TEMPERATURE PRIVATE VIEWING recibe bytes crudos (userID, temp_entera, temp_decimal).
- MATLAB Analysis (disparado por React al insertar datos) combina parte entera+decimal, escribe en canal público (SIGFOX) TEMPERATURE PUBLIC VIEWING (userID, temperatura combinada).
- Si temperatura ≥ 38°C, MATLAB lanza Webhook IFTTT → email a profesores.
- Canal público muestra tabla autoactualizable (estilo Excel) para administración.

1. Conexiones Arduino Nano
| Módulo | Pines Arduino |
|---|---|
| RFID RC522 | SDA→D10, SCK→D13, MOSI→D11, MISO→D12, RST→D9, 3.3V, GND |
| LCD I2C | SDA→A4, SCL→A5, 5V, GND |
| Sensor distancia Sharp | VCC→5V, GND, OUT→A0 |
| Buzzer | GND, OUT→D6 |
| MLX90614 | SDA→A4, SCL→A5, 5V, GND |
| LED | Cátodo→GND, Ánodo→D7 |
2. Configuración Raspberry Pi
- Instalar Raspbian (flashear imagen con balenaEtcher).
- Habilitar SSH/VNC en Raspberry Pi Configuration.
- (Opcional) IP estática eth0 editando
/etc/dhcpcd.conf.
3. Base de datos MySQL (MariaDB)
sudo apt update && sudo apt upgrade
sudo apt install apache2
sudo apt install php php-mbstring
sudo apt install mariadb-server php-mysql
sudo mysql --user=root
# En consola MySQL:
DROP USER 'root'@'localhost';
CREATE USER 'root'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;
exit;
mysql --user=root --password=yourpassword
sudo apt install phpmyadmin
sudo phpenmod mysqli
sudo /etc/init.d/apache2 restart
Acceso a phpMyAdmin: https://IP_RASPBERRY/phpmyadmin
4. SiPy Expansion Board 2.0
- Insertar SiPy con botón de reset hacia USB.
- Actualizar firmware (herramienta Pycom) eligiendo región RCZ correcta.
- Instalar VS Code + extensión Pymakr + Node.js.
- Crear carpeta proyecto, archivo
main.py, subir/ejecutar. - Obtener Device ID y PAC con script:
from network import Sigfox
import binascii
sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=Sigfox.RCZ4)
print(binascii.hexlify(sigfox.id()))
print(binascii.hexlify(sigfox.pac()))
- Registrar en Sigfox Backend (elegir país, ingresar ID/PAC, crear cuenta).
- Probar envío:
s.send(bytes([1,2,3,4,5,6,7,8,9,10,11,12])).
5. Configuración en la nube (ThingSpeak + IFTTT)
- Crear applet IFTTT: Webhooks (evento "fever") → Email (incluir {{Value1}}, {{Value2}}).
- Crear dos canales ThingSpeak: privado (recibe de Sigfox) y público (visualización).
- Configurar React en canal privado: al insertar datos en field3 (userID ≠ 0) → dispara MATLAB Analysis.
- MATLAB Analysis combina temperatura, escribe en canal público y si ≥38°C llama a Webhook IFTTT.
- Tabla HTML en canal público (solo vista privada) para análisis.
Colaboradores
- Aden: Raspberry Pi + Arduino + módulos + ThingSpeak (MATLAB/setup).
- Reginald: Sigfox + recopilación datos ThingSpeak + esquemáticos.
- Bo Sheng: Configuración IFTTT.
Código
[C] ARDUINO NANO
/* 90% Aden (https://www.astero.me), 10% librerías open source */
#include <SPI.h>
#include <MFRC522.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <Adafruit_MLX90614.h>
#define RST_PIN 9
#define SS_PIN 10
#define BUZZER_PIN 7
#define LED_PIN 6
#define dist_sensePin A0
MFRC522 mfrc522(SS_PIN, RST_PIN);
LiquidCrystal_I2C lcd(0x27, 16, 2);
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
SoftwareSerial unoSerial(2, 3); // RX, TX para SiPy
boolean tapped = true;
boolean confirmation = false;
String GetUID() {
String uid = "", uid_ns = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
uid.concat(String(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "));
uid.concat(String(mfrc522.uid.uidByte[i], HEX));
}
uid_ns.concat(uid.charAt(1)); uid_ns.concat(uid.charAt(2));
uid_ns.concat(uid.charAt(4)); uid_ns.concat(uid.charAt(5));
uid_ns.concat(uid.charAt(7)); uid_ns.concat(uid.charAt(8));
uid_ns.concat(uid.charAt(10)); uid_ns.concat(uid.charAt(11));
return uid_ns;
}
void setup() {
Serial.begin(9600);
while (!confirmation) { Serial.println("ARDUINO"); confirmation = Serial.readString() == "RAPI"; delay(1000); }
SPI.begin(); mfrc522.PCD_Init(); mlx.begin(); unoSerial.begin(9600);
pinMode(BUZZER_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT);
lcd.init(); lcd.backlight();
}
void loop() {
boolean readySignal = Serial.readString() == "READY";
if (readySignal) { tapped = true; Serial.println("RECEIVED"); }
if (tapped) {
digitalWrite(BUZZER_PIN, 0); digitalWrite(LED_PIN, 0); lcd.clear();
lcd.setCursor(0,0); lcd.print(" Please tap");
lcd.setCursor(0,1); lcd.print(" your card.");
delay(500); tapped = false;
} else {
if (!mfrc522.PICC_IsNewCardPresent()) return;
if (!mfrc522.PICC_ReadCardSerial()) return;
String card_UID = GetUID(); card_UID.toUpperCase();
Serial.print(card_UID); Serial.print("\n");
String rawData = Serial.readString();
int commaPos = rawData.indexOf(',');
String userInitial = rawData.substring(0, commaPos);
int userID = rawData.substring(commaPos + 1).toInt();
lcd.clear();
if (userID != 0 && userInitial != "0") {
lcd.setCursor(0,0); lcd.print("Welcome, ");
lcd.setCursor(0,1); lcd.print(userInitial);
int dist_senseValue = analogRead(dist_sensePin);
byte temp_Hbyte = 0, temp_Lbyte = 0;
float meas_temp = 0.0;
delay(2000);
while ((dist_senseValue < 170) || (dist_senseValue > 200)) {
dist_senseValue = analogRead(dist_sensePin);
lcd.setCursor(0,0); lcd.print("Please take");
lcd.setCursor(0,1); lcd.print("your temperature.");
}
digitalWrite(BUZZER_PIN, 1); digitalWrite(LED_PIN, 1); lcd.clear();
meas_temp = (float)mlx.readObjectTempC();
temp_Hbyte = floor(meas_temp);
temp_Lbyte = (meas_temp - temp_Hbyte) * 100;
String sMeasTemp = ""; sMeasTemp += userID; sMeasTemp += ","; sMeasTemp += temp_Hbyte; sMeasTemp += ","; sMeasTemp += temp_Lbyte;
Serial.println("TEMP"); Serial.println(meas_temp);
if (meas_temp >= 28 && meas_temp <= 42) {
unoSerial.print(sMeasTemp);
lcd.setCursor(0,0); lcd.print("Temperature: ");
lcd.setCursor(0,1); lcd.print(" " + String(meas_temp));
}
} else {
lcd.setCursor(0,0); lcd.print("Error!");
lcd.setCursor(0,1); lcd.print("Invalid User.");
}
rawData = Serial.readString();
commaPos = rawData.indexOf(',');
userInitial = rawData.substring(0, commaPos);
if (userInitial == "0") {
lcd.clear(); lcd.setCursor(0,0); lcd.print("Error!");
lcd.setCursor(0,1); lcd.print("Invalid Temperature.");
}
Serial.println("STANDBY"); delay(1000);
mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1();
}
}
[PYTHON] Raspberry Pi
''' Coded by Aden for Hackster.io Sigfox competition. https://www.astero.me '''
import mysql.connector, serial, os, time
def queryConnection():
cursor = conn.cursor(buffered=True)
cursor.execute("USE `" + databaseName + "`")
return cursor
host = "localhost"; user = "root"; password = "password"; databaseName = "smartTemp"
conn = mysql.connector.connect(host=host, user=user, passwd=password)
comPort = 'ttyUSB0'; baudRate = 9600
ser = serial.Serial('/dev/' + comPort, baudRate, timeout=0)
print("Confirming USB Serial Port now, please wait")
connected = False
for x in range(2):
time.sleep(2); ser.write("RAPI")
if not connected:
serialConfirmation = ser.readline().strip() == "ARDUINO"
print(serialConfirmation)
if not serialConfirmation: comPort = 'ttyUSB1'
connected = True
else: comPort = 'ttyUSB0'; connected = True
print(comPort)
ser = serial.Serial('/dev/' + comPort, baudRate)
print("COM PORT confirmed, using " + comPort)
countFile = open("count.txt", "a++")
tapRFID = False
cursor = queryConnection()
if os.stat("count.txt").st_size == 0:
print("Count has not been set, setting to 1 now."); count = 1
else:
for c in countFile: count = int(c)
print("Count restored: " + str(count))
try:
print("Creating " + databaseName + " database now..")
cursor.execute("CREATE DATABASE " + databaseName)
print("Database created.")
except mysql.connector.errors.DatabaseError:
print(databaseName + " database already exists.")
cursor.execute("CREATE TABLE IF NOT EXISTS `userInfo` (id int, cardID VARCHAR(9), userID VARCHAR(4), userInitial VARCHAR(8))")
cursor.execute("CREATE TABLE IF NOT EXISTS `tempData` (id int, userID VARCHAR(4), dateMeas date, timeMeas time, tempMeasure decimal(5,2))")
while True:
cursor = queryConnection()
cardID = str(ser.readline().strip())
cursor.execute("SELECT * FROM userInfo WHERE cardID = '" + cardID + "'")
items = cursor.fetchone()
if items:
print(cardID)
userID = str(items[2])
userDetails = str(items[3] + ", " + items[2])
currentDate = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())[0:10]
currentTime = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())[11:]
print(items)
ser.write(b'' + userDetails)
countFile.close(); countFile = open("count.txt", 'w')
tempSignal = False
while not tempSignal:
print("Getting Temperature")
tempSignal = ser.readline().strip() == "TEMP"
tempData = float(ser.readline().strip())
print(tempData)
if 28 <= tempData <= 42:
cursor.execute("INSERT INTO `tempData`(`id`, `userID`, `dateMeas`, `timeMeas`, `tempMeasure`) VALUES (" + str(count) + ",'" + userID + "','" + currentDate + "','" + currentTime + "','" + str(tempData) + "')")
conn.commit(); count += 1; countFile.write(str(count))
else:
ser.write(b'' + "0, 0")
else:
ser.write(b'' + ", 0"); print("Nothing found")
readySignal = ser.readline().strip() == "STANDBY"
while readySignal:
ser.write(b'' + "READY"); print("sending")
receivedSignal = ser.readline().strip() == "RECEIVED"
if receivedSignal: readySignal = False; print("received")
cursor.close(); conn.close()
[MicroPython] Sigfox (Pycom SiPy)
''' 100% Reginald '''
from machine import UART
from network import Sigfox
import binascii, socket, time, pycom, sys
pycom.heartbeat(False)
sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=Sigfox.RCZ4)
uart1 = UART(1, baudrate=9600)
uart1.init(9600, bits=8, parity=None, stop=1, pins=('P3','P4'), timeout_chars=20)
while True:
try:
recv = uart1.readline()
if recv:
print(binascii.hexlify(sigfox.id()))
print(binascii.hexlify(sigfox.pac()))
sk1 = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW)
sk1.setblocking(False)
sk1.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False)
dataList = recv.decode("utf-8")
print("dataList : %s" % dataList)
split_val = dataList.split(",")
userID = int(split_val[0])
temp_H = int(split_val[1])
temp_L = int(split_val[2])
print("userID : %d temp_H : %d temp_L : %d" % (userID, temp_H, temp_L))
bData = [userID, temp_H, temp_L]
meas_temp = temp_H + (temp_L * 0.01)
print("measure temperature : %.2f" % meas_temp)
sk1.send(bytes(bData))
sk1.close()
time.sleep(5)
except KeyboardInterrupt:
sys.exit()
[ThingSpeak] MATLAB SCRIPT
%% Aden - Hackster.io Sigfox Competition
readChannelID = 870479;
webhookTrigger = 'https://maker.ifttt.com/trigger/student_fever/with/key/h10MdSGwjSPtZQ43wH-AgoiKI0pwaljBNnGUEu4Yecn';
readAPIKey = 'QGHINBPNJQKULBH2';
writeChannelID = 870482;
writeAPIKey = 'R6NJIM8NT5A42R9N';
wholeNumber = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields', 1);
decimalNumber = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields', 2)/100;
userID = thingSpeakRead(readChannelID, 'ReadKey', readAPIKey, 'Fields', 3);
display(wholeNumber, 'BEFORE')
analyzedData = wholeNumber + decimalNumber;
display(analyzedData, 'AFTER')
display(userID, 'USERID')
thingSpeakWrite(writeChannelID, 'Fields', [1,2], 'Values', {analyzedData, userID}, 'WriteKey', writeAPIKey)
if (analyzedData >= 38)
webwrite(webhookTrigger, 'value1', analyzedData, 'value2', userID);
end
Esquemáticos







Proceso de manufactura
- Monitorización profesional de temperatura con Raspberry Pi y DS18B20
- Monitorización precisa de temperatura en armario de servidores con Raspberry Pi
- Control de temperatura casero para cerveza: monitorizar y regular con Arduino
- Sensor K30 CO₂: Monitorización en tiempo real de la calidad del aire interior
- Persianas Inteligentes: Control Automático de Luz con Arduino y Energía Solar
- Puerta de control de temperatura sin contacto para detección rápida y precisa
- Arduino UNO + Shield WiFi: Visualización de Temperatura en Tiempo Real en Web vía Serial
- Librería Eficiente de Puerto I/O de 8 Bits para Arduino – Expande el Control Digital
- Guante Inteligente de Gestos a Texto para Lenguaje de Signos
- Health Band: Asistente Inteligente Vestible para Personas Mayores
- Agricultura Inteligente: Monitorización Integral IoT para Futuro Agrícola