Fundamentos de IoT y Sistemas Embebidos: Sensores, Microcontroladores, Raspberry Pi, Arduino y Edge Computing
Este artículo es una introducción exhaustiva a los fundamentos de IoT y sistemas embebidos, que incluye sensores, microcontroladores, Raspberry Pi, Arduino y Edge Computing con ejemplos prácticos.
Resumen rápido
IoT y los sistemas embebidos son ordenadores especializados conectados en red que vinculan dispositivos físicos con Internet y procesan datos en tiempo real.
Descripción técnica compacta
IoT (Internet de las Cosas) es una red de dispositivos físicos conectados que recopilan datos a través de sensores, los procesan e intercambian información a través de Internet.
Componentes clave:
Sistemas embebidos
- Microcontroladores: ARM Cortex-M, AVR, PIC
- Microprocesadores: ARM Cortex-A, x86, RISC-V
- Sistemas operativos: Embedded Linux, RTOS, FreeRTOS
- Memoria: Flash, EEPROM, RAM, tarjetas SD
Sensores y actuadores
- Sensores ambientales: Temperatura, humedad, presión
- Sensores de movimiento: Aceleración, giroscopio, magnetómetro
- Sensores ópticos: Cámara, infrarrojo, ultrasonidos
- Actuadores: Motores, servos, relés, LEDs
Protocolos de comunicación
- Inalámbricos: WiFi, Bluetooth, LoRaWAN, Zigbee
- Alámbricos: I2C, SPI, UART, CAN, Ethernet
- Protocolos IoT: MQTT, CoAP, HTTP/REST, WebSocket
- Protocolos Edge: gRPC, Protocol Buffers
Edge Computing
- Procesamiento de datos: Procesamiento local en lugar de nube
- Optimización de latencia: Procesamiento en tiempo real
- Eficiencia de ancho de banda: Transferencia de datos reducida
- Funcionamiento sin conexión: Operación sin conectividad a Internet
Puntos clave para el examen
- IoT: Internet de las Cosas, dispositivos físicos conectados
- Sistemas embebidos: Sistemas informáticos especializados
- Sensores: Temperatura, movimiento, sensores ópticos
- Microcontroladores: ARM Cortex-M, AVR, PIC
- Raspberry Pi: Ordenador de placa única con Linux
- Arduino: Plataforma de microcontrolador de código abierto
- Edge Computing: Procesamiento de datos local
- RTOS: Sistema operativo de tiempo real
- MQTT: Transporte de telemetría de cola de mensajes
- Relevancia IHK: Desarrollo moderno de sistemas embebidos e IoT
Componentes principales
- Hardware: Microcontroladores, procesadores, sensores
- Firmware: Software de bajo nivel, controladores, bootloader
- Sistemas operativos: Embedded Linux, RTOS, FreeRTOS
- Comunicación: Protocolos, redes, seguridad
- Edge Computing: Procesamiento local, análisis
- Integración con nube: Transferencia de datos, almacenamiento
- Gestión de energía: Eficiencia energética, gestión de batería
- Seguridad: Cifrado, autenticación, actualizaciones
Ejemplos prácticos
1. Nodo sensor Arduino IoT con MQTT
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <ArduinoJson.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
// WiFi configuration
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// MQTT configuration
const char* mqtt_server = "mqtt.example.com";
const int mqtt_port = 1883;
const char* mqtt_username = "your_username";
const char* mqtt_password = "your_password";
const char* client_id = "arduino_sensor_01";
// Sensor configuration
#define DHT_PIN 4
#define DHT_TYPE DHT22
#define LIGHT_PIN A0
#define MOTION_PIN 2
#define BUZZER_PIN 5
#define LED_PIN 13
// Topics
const char* temp_topic = "iot/sensors/temperature";
const char* humidity_topic = "iot/sensors/humidity";
const char* light_topic = "iot/sensors/light";
const char* motion_topic = "iot/sensors/motion";
const char* status_topic = "iot/sensors/status";
const char* control_topic = "iot/sensors/control";
// Timing
unsigned long lastSensorRead = 0;
const long sensorInterval = 5000; // 5 seconds
unsigned long lastStatusUpdate = 0;
const long statusInterval = 60000; // 1 minute
// Global objects
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
DHT dht(DHT_PIN, DHT_TYPE);
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org");
// Sensor data structure
struct SensorData {
float temperature;
float humidity;
int lightLevel;
bool motionDetected;
unsigned long timestamp;
};
// Device status
struct DeviceStatus {
bool wifiConnected;
bool mqttConnected;
float batteryLevel;
int uptime;
String firmwareVersion;
};
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println("IoT Sensor Node starting...");
// Initialize pins
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(MOTION_PIN, INPUT_PULLUP);
// Initialize sensors
dht.begin();
// Initialize NTP client
timeClient.begin();
timeClient.setTimeOffset(3600); // UTC+1
// Connect to WiFi
connectWiFi();
// Connect to MQTT
connectMQTT();
// Initialize time
timeClient.update();
Serial.println("IoT Sensor Node ready!");
blinkLED(3, 200);
}
void loop() {
// Handle WiFi connection
if (WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
// Handle MQTT connection
if (!mqttClient.connected()) {
connectMQTT();
}
// Update NTP time
timeClient.update();
// Read sensors periodically
if (millis() - lastSensorRead >= sensorInterval) {
readAndPublishSensors();
lastSensorRead = millis();
}
// Update status periodically
if (millis() - lastStatusUpdate >= statusInterval) {
publishStatus();
lastStatusUpdate = millis();
}
// Handle MQTT messages
mqttClient.loop();
// Check for motion interrupt
checkMotion();
delay(100);
}
void connectWiFi() {
Serial.println("Connecting to WiFi...");
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
digitalWrite(LED_PIN, HIGH);
} else {
Serial.println("\nFailed to connect to WiFi");
digitalWrite(LED_PIN, LOW);
}
}
void connectMQTT() {
Serial.println("Connecting to MQTT...");
mqttClient.setServer(mqtt_server, mqtt_port);
mqttClient.setCallback(mqttCallback);
int attempts = 0;
while (!mqttClient.connected() && attempts < 10) {
Serial.print("Attempting MQTT connection...");
if (mqttClient.connect(client_id, mqtt_username, mqtt_password)) {
Serial.println("connected!");
// Subscribe to control topic
mqttClient.subscribe(control_topic);
// Publish connection message
publishConnectionStatus(true);
} else {
Serial.print("failed, rc=");
Serial.print(mqttClient.state());
Serial.println(" try again in 5 seconds");
delay(5000);
attempts++;
}
}
if (!mqttClient.connected()) {
Serial.println("Failed to connect to MQTT");
}
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
String message = "";
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message);
// Parse JSON message
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, message);
if (error) {
Serial.println("Failed to parse JSON");
return;
}
// Handle control commands
if (String(topic) == control_topic) {
handleControlCommand(doc);
}
}
void handleControlCommand(JsonDocument& command) {
String cmd = command["command"];
if (cmd == "buzz") {
int duration = command["duration"] | 1000;
buzz(duration);
} else if (cmd == "led") {
bool state = command["state"];
digitalWrite(LED_PIN, state ? HIGH : LOW);
} else if (cmd == "restart") {
Serial.println("Restarting device...");
ESP.restart();
} else if (cmd == "sleep") {
int duration = command["duration"] | 10;
Serial.println("Going to sleep for " + String(duration) + " seconds");
ESP.deepSleep(duration * 1000000);
}
}
void readAndPublishSensors() {
SensorData data;
// Read temperature and humidity
data.temperature = dht.readTemperature();
data.humidity = dht.readHumidity();
// Read light level
data.lightLevel = analogRead(LIGHT_PIN);
// Read motion sensor
data.motionDetected = digitalRead(MOTION_PIN) == HIGH;
// Get timestamp
data.timestamp = timeClient.getEpochTime();
// Check for valid readings
if (isnan(data.temperature) || isnan(data.humidity)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
// Publish sensor data
publishSensorData(data);
// Print sensor data
Serial.println("Sensor Data:");
Serial.print("Temperature: ");
Serial.print(data.temperature);
Serial.println("°C");
Serial.print("Humidity: ");
Serial.print(data.humidity);
Serial.println("%");
Serial.print("Light: ");
Serial.println(data.lightLevel);
Serial.print("Motion: ");
Serial.println(data.motionDetected ? "Yes" : "No");
Serial.print("Timestamp: ");
Serial.println(data.timestamp);
}
void publishSensorData(SensorData& data) {
// Create JSON document
DynamicJsonDocument doc(1024);
doc["device_id"] = client_id;
doc["timestamp"] = data.timestamp;
doc["temperature"] = data.temperature;
doc["humidity"] = data.humidity;
doc["light_level"] = data.lightLevel;
doc["motion_detected"] = data.motionDetected;
// Serialize JSON
String jsonStr;
serializeJson(doc, jsonStr);
// Publish to topics
mqttClient.publish(temp_topic, String(data.temperature).c_str());
mqttClient.publish(humidity_topic, String(data.humidity).c_str());
mqttClient.publish(light_topic, String(data.lightLevel).c_str());
mqttClient.publish(motion_topic, String(data.motionDetected).c_str());
// Publish combined data
mqttClient.publish("iot/sensors/combined", jsonStr.c_str());
}
void publishStatus() {
DeviceStatus status;
status.wifiConnected = (WiFi.status() == WL_CONNECTED);
status.mqttConnected = mqttClient.connected();
status.batteryLevel = getBatteryLevel();
status.uptime = millis() / 1000;
status.firmwareVersion = "1.0.0";
// Create JSON document
DynamicJsonDocument doc(1024);
doc["device_id"] = client_id;
doc["timestamp"] = timeClient.getEpochTime();
doc["wifi_connected"] = status.wifiConnected;
doc["mqtt_connected"] = status.mqttConnected;
doc["battery_level"] = status.batteryLevel;
doc["uptime"] = status.uptime;
doc["firmware_version"] = status.firmwareVersion;
doc["free_heap"] = ESP.getFreeHeap();
doc["cpu_freq"] = ESP.getCpuFreqMHz();
// Serialize JSON
String jsonStr;
serializeJson(doc, jsonStr);
// Publish status
mqttClient.publish(status_topic, jsonStr.c_str());
}
void publishConnectionStatus(bool connected) {
DynamicJsonDocument doc(512);
doc["device_id"] = client_id;
doc["timestamp"] = timeClient.getEpochTime();
doc["connected"] = connected;
doc["ip_address"] = WiFi.localIP().toString();
doc["mac_address"] = WiFi.macAddress();
String jsonStr;
serializeJson(doc, jsonStr);
mqttClient.publish("iot/sensors/connection", jsonStr.c_str());
}
void checkMotion() {
static bool lastMotionState = false;
bool currentMotionState = digitalRead(MOTION_PIN) == HIGH;
if (currentMotionState && !lastMotionState) {
// Motion detected
Serial.println("Motion detected!");
// Immediate motion notification
DynamicJsonDocument doc(256);
doc["device_id"] = client_id;
doc["timestamp"] = timeClient.getEpochTime();
doc["motion_detected"] = true;
doc["alert_type"] = "motion";
String jsonStr;
serializeJson(doc, jsonStr);
mqttClient.publish("iot/sensors/alert", jsonStr.c_str());
// Alert buzzer
buzz(200);
}
lastMotionState = currentMotionState;
}
float getBatteryLevel() {
// Read battery voltage (assuming voltage divider)
int rawValue = analogRead(A0);
float voltage = rawValue * (3.3 / 4096.0) * 2.0; // Voltage divider factor
// Convert to percentage (assuming 3.0V to 4.2V range)
float percentage = ((voltage - 3.0) / 1.2) * 100.0;
percentage = constrain(percentage, 0.0, 100.0);
return percentage;
}
void buzz(int duration) {
tone(BUZZER_PIN, 1000, duration);
}
void blinkLED(int times, int delayMs) {
for (int i = 0; i < times; i++) {
digitalWrite(LED_PIN, HIGH);
delay(delayMs);
digitalWrite(LED_PIN, LOW);
delay(delayMs);
}
}
// OTA Update functionality
void checkForOTAUpdate() {
// This would implement OTA update checking
// For simplicity, just print status
Serial.println("Checking for OTA updates...");
}
// Power management
void enterDeepSleep(int seconds) {
Serial.println("Entering deep sleep...");
ESP.deepSleep(seconds * 1000000);
}
void wakeFromDeepSleep() {
Serial.println("Waking from deep sleep...");
// Reinitialize peripherals after wake
}
2. Edge Computing en Raspberry Pi con Python
import time
import json
import threading
import queue
import subprocess
import os
import signal
import sys
from datetime import datetime
import logging
import sqlite3
import paho.mqtt.client as mqtt
import picamera
import RPi.GPIO as GPIO
import Adafruit_DHT
import board
import busio
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
import numpy as np
import cv2
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
import tensorflow as tf
class IoTEdgeGateway:
def __init__(self, config_file='config.json'):
"""Initialize IoT Edge Gateway"""
self.config = self.load_config(config_file)
self.setup_logging()
self.setup_gpio()
self.setup_sensors()
self.setup_camera()
self.setup_mqtt()
self.setup_database()
self.setup_ml_models()
# Data queues
self.sensor_queue = queue.Queue()
self.image_queue = queue.Queue()
self.control_queue = queue.Queue()
# Runtime state
self.running = True
self.threads = []
# Register signal handlers
signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, self.signal_handler)
self.logger.info("IoT Edge Gateway initialized")
def load_config(self, config_file):
"""Load configuration from JSON file"""
try:
with open(config_file, 'r') as f:
return json.load(f)
except FileNotFoundError:
self.logger.error(f"Config file {config_file} not found")
return self.get_default_config()
def get_default_config(self):
"""Get default configuration"""
return {
"mqtt": {
"broker": "localhost",
"port": 1883,
"username": "",
"password": "",
"client_id": "edge_gateway_01"
},
"sensors": {
"dht_pin": 4,
"light_pin": 18,
"motion_pin": 24,
"ads1115_address": 0x48
},
"camera": {
"resolution": (640, 480),
"framerate": 30,
"rotation": 0
},
"database": {
"path": "iot_data.db"
},
"ml_models": {
"object_detection": "models/object_detection.h5",
"anomaly_detection": "models/anomaly_detection.h5"
},
"processing": {
"sensor_interval": 5,
"image_interval": 10,
"batch_size": 32
}
}
def setup_logging(self):
"""Setup logging configuration"""
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('edge_gateway.log'),
logging.StreamHandler(sys.stdout)
]
)
self.logger = logging.getLogger(__name__)
def setup_gpio(self):
"""Setup GPIO pins"""
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# Setup pins
self.dht_pin = self.config['sensors']['dht_pin']
self.light_pin = self.config['sensors']['light_pin']
self.motion_pin = self.config['sensors']['motion_pin']
# Setup motion pin as input with pull-up
GPIO.setup(self.motion_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# Add interrupt for motion detection
GPIO.add_event_detect(self.motion_pin, GPIO.RISING,
callback=self.motion_detected, bouncetime=300)
self.logger.info("GPIO setup completed")
def setup_sensors(self):
"""Setup sensor interfaces"""
# DHT22 sensor
self.dht_sensor = Adafruit_DHT.DHT22
# ADS1115 ADC for analog sensors
i2c = busio.I2C(board.SCL, board.SDA)
self.ads = ADS.ADS1115(i2c)
self.ads.gain = 1 # +/- 4.096V
# Analog channels
self.light_channel = AnalogIn(self.ads, ADS.P0)
self.temp_channel = AnalogIn(self.ads, ADS.P1)
self.humidity_channel = AnalogIn(self.ads, ADS.P2)
self.logger.info("Sensors setup completed")
def setup_camera(self):
"""Setup camera interface"""
try:
self.camera = picamera.PiCamera()
self.camera.resolution = tuple(self.config['camera']['resolution'])
self.camera.framerate = self.config['camera']['framerate']
self.camera.rotation = self.config['camera']['rotation']
# Camera warm-up
time.sleep(2)
self.logger.info("Camera setup completed")
except Exception as e:
self.logger.error(f"Camera setup failed: {e}")
self.camera = None
def setup_mqtt(self):
"""Setup MQTT client"""
self.mqtt_client = mqtt.Client(self.config['mqtt']['client_id'])
# Set callbacks
self.mqtt_client.on_connect = self.mqtt_on_connect
self.mqtt_client.on_message = self.mqtt_on_message
self.mqtt_client.on_disconnect = self.mqtt_on_disconnect
# Set credentials if provided
if self.config['mqtt']['username']:
self.mqtt_client.username_pw_set(
self.config['mqtt']['username'],
self.config['mqtt']['password']
)
# Connect to broker
try:
self.mqtt_client.connect(
self.config['mqtt']['broker'],
self.config['mqtt']['port'],
60
)
self.mqtt_client.loop_start()
self.logger.info("MQTT client connected")
except Exception as e:
self.logger.error(f"MQTT connection failed: {e}")
def setup_database(self):
"""Setup SQLite database"""
self.db_path = self.config['database']['path']
self.init_database()
self.logger.info("Database setup completed")
def init_database(self):
"""Initialize database tables"""
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
# Sensor data table
cursor.execute('''
CREATE TABLE IF NOT EXISTS sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
sensor_type TEXT,
sensor_id TEXT,
value REAL,
unit TEXT,
processed BOOLEAN DEFAULT FALSE
)
''')
# Image data table
cursor.execute('''
CREATE TABLE IF NOT EXISTS image_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
filename TEXT,
objects_detected TEXT,
confidence REAL,
processed BOOLEAN DEFAULT FALSE
)
''')
# System status table
cursor.execute('''
CREATE TABLE IF NOT EXISTS system_status (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
cpu_usage REAL,
memory_usage REAL,
disk_usage REAL,
temperature REAL,
uptime INTEGER
)
''')
conn.commit()
conn.close()
def setup_ml_models(self):
"""Setup machine learning models"""
self.models = {}
try:
# Load object detection model
if os.path.exists(self.config['ml_models']['object_detection']):
self.models['object_detection'] = load_model(
self.config['ml_models']['object_detection']
)
self.logger.info("Object detection model loaded")
# Load anomaly detection model
if os.path.exists(self.config['ml_models']['anomaly_detection']):
self.models['anomaly_detection'] = load_model(
self.config['ml_models']['anomaly_detection']
)
self.logger.info("Anomaly detection model loaded")
except Exception as e:
self.logger.error(f"ML model loading failed: {e}")
def mqtt_on_connect(self, client, userdata, flags, rc):
"""MQTT connection callback"""
if rc == 0:
self.logger.info("Connected to MQTT broker")
# Subscribe to control topics
client.subscribe("edge/gateway/control")
client.subscribe("edge/sensors/+/control")
else:
self.logger.error(f"MQTT connection failed with code {rc}")
def mqtt_on_message(self, client, userdata, msg):
"""MQTT message callback"""
try:
topic = msg.topic
payload = json.loads(msg.payload.decode())
self.logger.info(f"Received message on {topic}: {payload}")
# Handle control messages
if "control" in topic:
self.control_queue.put((topic, payload))
except Exception as e:
self.logger.error(f"MQTT message processing failed: {e}")
def mqtt_on_disconnect(self, client, userdata, rc):
"""MQTT disconnection callback"""
self.logger.warning(f"MQTT disconnected with code {rc}")
def motion_detected(self, channel):
"""Motion detection interrupt handler"""
timestamp = datetime.now().isoformat()
# Publish motion alert
alert = {
"device_id": self.config['mqtt']['client_id'],
"timestamp": timestamp,
"alert_type": "motion",
"location": "entrance"
}
self.mqtt_client.publish("edge/alerts/motion", json.dumps(alert))
# Trigger image capture
if self.camera:
self.image_queue.put(("motion", timestamp))
self.logger.info("Motion detected and alert published")
def read_dht_sensor(self):
"""Read DHT22 sensor data"""
humidity, temperature = Adafruit_DHT.read_retry(
self.dht_sensor, self.dht_pin
)
if humidity is not None and temperature is not None:
return {
"temperature": temperature,
"humidity": humidity,
"timestamp": datetime.now().isoformat()
}
else:
self.logger.error("Failed to read DHT sensor")
return None
def read_analog_sensors(self):
"""Read analog sensor data"""
try:
# Read light sensor
light_voltage = self.light_channel.voltage
light_level = (light_voltage / 3.3) * 100 # Convert to percentage
# Read temperature sensor (if using analog temp sensor)
temp_voltage = self.temp_channel.voltage
# Convert voltage to temperature (example conversion)
analog_temp = (temp_voltage - 0.5) * 100 # LM35 conversion
# Read humidity sensor (if using analog humidity sensor)
humidity_voltage = self.humidity_channel.voltage
analog_humidity = (humidity_voltage / 3.3) * 100
return {
"light_level": light_level,
"analog_temperature": analog_temp,
"analog_humidity": analog_humidity,
"timestamp": datetime.now().isoformat()
}
except Exception as e:
self.logger.error(f"Analog sensor reading failed: {e}")
return None
def capture_image(self, trigger="scheduled"):
"""Capture image from camera"""
if not self.camera:
return None
try:
timestamp = datetime.now().isoformat()
filename = f"images/capture_{timestamp.replace(':', '-')}.jpg"
# Capture image
self.camera.capture(filename)
# Process image with ML models
processed_image = self.process_image(filename)
return {
"filename": filename,
"timestamp": timestamp,
"trigger": trigger,
"processed": processed_image
}
except Exception as e:
self.logger.error(f"Image capture failed: {e}")
return None
def process_image(self, image_path):
"""Process image with ML models"""
try:
# Load and preprocess image
img = image.load_img(image_path, target_size=(224, 224))
img_array = image.img_to_array(img)
img_array = np.expand_dims(img_array, axis=0)
img_array = img_array / 255.0
result = {}
# Object detection
if 'object_detection' in self.models:
predictions = self.models['object_detection'].predict(img_array)
result['objects'] = self.decode_predictions(predictions)
# Anomaly detection
if 'anomaly_detection' in self.models:
anomaly_score = self.models['anomaly_detection'].predict(img_array)[0][0]
result['anomaly_score'] = float(anomaly_score)
result['is_anomaly'] = anomaly_score > 0.5
return result
except Exception as e:
self.logger.error(f"Image processing failed: {e}")
return None
def decode_predictions(self, predictions):
"""Decode ML model predictions"""
# This would depend on the specific model
# Example implementation
class_names = ['person', 'car', 'bicycle', 'dog', 'cat']
# Get top predictions
top_indices = np.argsort(predictions[0])[-5:][::-1]
results = []
for i, index in enumerate(top_indices):
results.append({
"class": class_names[index],
"confidence": float(predictions[0][index]),
"rank": i + 1
})
return results
def store_sensor_data(self, sensor_data):
"""Store sensor data in database"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
for sensor_type, value in sensor_data.items():
if sensor_type == "timestamp":
continue
cursor.execute('''
INSERT INTO sensor_data (sensor_type, sensor_id, value, unit)
VALUES (?, ?, ?, ?)
''', (sensor_type, "gateway_01", value, self.get_unit(sensor_type)))
conn.commit()
conn.close()
except Exception as e:
self.logger.error(f"Database storage failed: {e}")
def get_unit(self, sensor_type):
"""Get unit for sensor type"""
units = {
"temperature": "°C",
"humidity": "%",
"light_level": "%",
"analog_temperature": "°C",
"analog_humidity": "%"
}
return units.get(sensor_type, "")
def get_system_status(self):
"""Get system status information"""
try:
# CPU usage
cpu_usage = psutil.cpu_percent()
# Memory usage
memory = psutil.virtual_memory()
memory_usage = memory.percent
# Disk usage
disk = psutil.disk_usage('/')
disk_usage = disk.percent
# CPU temperature
try:
temp = subprocess.check_output(['vcgencmd', 'measure_temp'])
cpu_temp = float(temp.decode().split('=')[1].split("'")[0])
except:
cpu_temp = 0.0
# Uptime
uptime = time.time() - psutil.boot_time()
return {
"cpu_usage": cpu_usage,
"memory_usage": memory_usage,
"disk_usage": disk_usage,
"temperature": cpu_temp,
"uptime": uptime,
"timestamp": datetime.now().isoformat()
}
except Exception as e:
self.logger.error(f"System status check failed: {e}")
return None
def sensor_worker(self):
"""Worker thread for sensor data collection"""
self.logger.info("Sensor worker started")
while self.running:
try:
# Read DHT sensor
dht_data = self.read_dht_sensor()
if dht_data:
self.sensor_queue.put(("dht", dht_data))
# Read analog sensors
analog_data = self.read_analog_sensors()
if analog_data:
self.sensor_queue.put(("analog", analog_data))
# Get system status
system_status = self.get_system_status()
if system_status:
self.sensor_queue.put(("system", system_status))
# Sleep for interval
time.sleep(self.config['processing']['sensor_interval'])
except Exception as e:
self.logger.error(f"Sensor worker error: {e}")
time.sleep(1)
def image_worker(self):
"""Worker thread for image processing"""
self.logger.info("Image worker started")
while self.running:
try:
# Get image capture request
if not self.image_queue.empty():
trigger, timestamp = self.image_queue.get(timeout=1)
# Capture image
image_data = self.capture_image(trigger)
if image_data:
# Publish image data
self.mqtt_client.publish(
"edge/gateway/images",
json.dumps(image_data)
)
# Store in database
self.store_image_data(image_data)
# Periodic image capture
else:
image_data = self.capture_image("scheduled")
if image_data:
self.mqtt_client.publish(
"edge/gateway/images",
json.dumps(image_data)
)
self.store_image_data(image_data)
# Sleep for interval
time.sleep(self.config['processing']['image_interval'])
except queue.Empty:
continue
except Exception as e:
self.logger.error(f"Image worker error: {e}")
time.sleep(1)
def control_worker(self):
"""Worker thread for control commands"""
self.logger.info("Control worker started")
while self.running:
try:
# Get control command
topic, command = self.control_queue.get(timeout=1)
self.logger.info(f"Processing control command: {command}")
# Handle commands
if command.get("command") == "restart":
self.restart_system()
elif command.get("command") == "shutdown":
self.shutdown_system()
elif command.get("command") == "capture_image":
self.image_queue.put(("manual", datetime.now().isoformat()))
elif command.get("command") == "update_config":
self.update_config(command.get("config", {}))
except queue.Empty:
continue
except Exception as e:
self.logger.error(f"Control worker error: {e}")
time.sleep(1)
def data_processor(self):
"""Worker thread for data processing and publishing"""
self.logger.info("Data processor started")
while self.running:
try:
# Process sensor data
if not self.sensor_queue.empty():
sensor_type, data = self.sensor_queue.get(timeout=1)
# Store in database
self.store_sensor_data(data)
# Publish to MQTT
topic = f"edge/sensors/{sensor_type}"
self.mqtt_client.publish(topic, json.dumps(data))
# Check for anomalies
if sensor_type == "system":
self.check_system_anomalies(data)
time.sleep(0.1)
except queue.Empty:
continue
except Exception as e:
self.logger.error(f"Data processor error: {e}")
time.sleep(1)
def check_system_anomalies(self, system_data):
"""Check for system anomalies"""
anomalies = []
# Check CPU usage
if system_data.get("cpu_usage", 0) > 80:
anomalies.append("high_cpu_usage")
# Check memory usage
if system_data.get("memory_usage", 0) > 85:
anomalies.append("high_memory_usage")
# Check disk usage
if system_data.get("disk_usage", 0) > 90:
anomalies.append("high_disk_usage")
# Check temperature
if system_data.get("temperature", 0) > 70:
anomalies.append("high_temperature")
# Publish anomalies if any
if anomalies:
alert = {
"device_id": self.config['mqtt']['client_id'],
"timestamp": datetime.now().isoformat(),
"alert_type": "system_anomaly",
"anomalies": anomalies,
"system_data": system_data
}
self.mqtt_client.publish("edge/alerts/system", json.dumps(alert))
self.logger.warning(f"System anomalies detected: {anomalies}")
def store_image_data(self, image_data):
"""Store image data in database"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO image_data (filename, objects_detected, confidence, processed)
VALUES (?, ?, ?, ?)
''', (
image_data["filename"],
json.dumps(image_data.get("processed", {})),
0.0, # Default confidence
image_data.get("processed") is not None
))
conn.commit()
conn.close()
except Exception as e:
self.logger.error(f"Image data storage failed: {e}")
def restart_system(self):
"""Restart the system"""
self.logger.info("System restart requested")
self.running = False
# Restart after cleanup
subprocess.run(['sudo', 'reboot'])
def shutdown_system(self):
"""Shutdown the system"""
self.logger.info("System shutdown requested")
self.running = False
# Shutdown after cleanup
subprocess.run(['sudo', 'shutdown', '-h', 'now'])
def update_config(self, new_config):
"""Update configuration"""
self.config.update(new_config)
# Save new config
with open('config.json', 'w') as f:
json.dump(self.config, f, indent=2)
self.logger.info("Configuration updated")
def start(self):
"""Start the edge gateway"""
self.logger.info("Starting IoT Edge Gateway")
# Start worker threads
self.threads = [
threading.Thread(target=self.sensor_worker, daemon=True),
threading.Thread(target=self.image_worker, daemon=True),
threading.Thread(target=self.control_worker, daemon=True),
threading.Thread(target=self.data_processor, daemon=True)
]
for thread in self.threads:
thread.start()
self.logger.info("All worker threads started")
def stop(self):
"""Stop the edge gateway"""
self.logger.info("Stopping IoT Edge Gateway")
self.running = False
# Stop MQTT client
self.mqtt_client.loop_stop()
self.mqtt_client.disconnect()
# Cleanup GPIO
GPIO.cleanup()
# Wait for threads to finish
for thread in self.threads:
thread.join(timeout=5)
self.logger.info("IoT Edge Gateway stopped")
def signal_handler(self, signum, frame):
"""Handle system signals"""
self.logger.info(f"Received signal {signum}")
self.stop()
sys.exit(0)
def run(self):
"""Main run loop"""
try:
self.start()
# Keep main thread alive
while self.running:
time.sleep(1)
except KeyboardInterrupt:
self.logger.info("Keyboard interrupt received")
except Exception as e:
self.logger.error(f"Unexpected error: {e}")
finally:
self.stop()
if __name__ == "__main__":
# Import psutil for system monitoring
import psutil
# Create and run edge gateway
gateway = IoTEdgeGateway()
gateway.run()
Este ejemplo implementa una puerta de enlace IoT completa ejecutándose en Raspberry Pi. La clase IoTEdgeGateway gestiona múltiples sensores, captura imágenes, procesa datos con modelos ML y se comunica a través de MQTT.
La arquitectura utiliza threads de trabajo para ejecutar diferentes tareas de forma concurrente: recopilación de sensores, procesamiento de imágenes, manejo de comandos de control y procesamiento de datos. Los datos se almacenan en una base de datos SQLite local y se publican en un broker MQTT para análisis posteriores.
La clase maneja la detección de movimiento mediante interrupciones GPIO, captura imágenes bajo demanda o de forma programada, y ejecuta modelos de detección de objetos y detección de anomalías. También monitorea el estado del sistema, detectando condiciones anómalas como uso elevado de CPU, memoria o temperatura.
El sistema está diseñado para ser robusto, con manejo de errores, registros detallados y limpieza ordenada al recibir señales del sistema. Las colas aseguran que el procesamiento de datos se realice de forma segura entre threads, y la configuración JSON permite personalizar fácilmente los parámetros sin modificar el código.
3. C++ Embedded para microcontroladores
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <RTClib.h>
#include <DHT.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#include <FreeRTOS.h>
#include <task.h>
// Configuration constants
#define WIFI_SSID "YOUR_WIFI_SSID"
#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD"
#define MQTT_SERVER "mqtt.example.com"
#define MQTT_PORT 1883
#define MQTT_CLIENT_ID "embedded_device_01"
#define MQTT_USERNAME "your_username"
#define MQTT_PASSWORD "your_password"
// Pin definitions
#define DHT_PIN 4
#define BMP_SDA_PIN 21
#define BMP_SCL_PIN 22
#define SD_CS_PIN 5
#define LED_PIN 2
#define BUZZER_PIN 12
#define RELAY_PIN 13
#define BUTTON_PIN 0
// Sensor types
#define DHT_TYPE DHT22
// Task priorities
#define SENSOR_TASK_PRIORITY 2
#define COMM_TASK_PRIORITY 3
#define CONTROL_TASK_PRIORITY 1
#define DATA_LOGGER_TASK_PRIORITY 1
// Task stack sizes
#define SENSOR_TASK_STACK_SIZE 2048
#define COMM_TASK_STACK_SIZE 4096
#define CONTROL_TASK_STACK_SIZE 2048
#define DATA_LOGGER_TASK_STACK_SIZE 2048
// Data structures
struct SensorData {
float temperature;
float humidity;
float pressure;
float altitude;
unsigned long timestamp;
bool valid;
};
struct SystemStatus {
bool wifiConnected;
bool mqttConnected;
bool sdCardPresent;
float batteryLevel;
int uptime;
int freeHeap;
float cpuTemperature;
unsigned long lastSensorRead;
unsigned long lastMQTTMessage;
};
struct ControlCommand {
String command;
String parameters;
unsigned long timestamp;
bool processed;
};
// Global objects
DHT dhtSensor(DHT_PIN, DHT_TYPE);
Adafruit_BMP280 bmp280;
RTC_DS3231 rtc;
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
// Data queues
QueueHandle_t sensorDataQueue;
QueueHandle_t controlCommandQueue;
QueueHandle_t logDataQueue;
// Semaphores
SemaphoreHandle_t wifiMutex;
SemaphoreHandle_t mqttMutex;
SemaphoreHandle_t sensorMutex;
SemaphoreHandle_t sdMutex;
// Global variables
SystemStatus systemStatus;
TaskHandle_t sensorTaskHandle;
TaskHandle_t commTaskHandle;
TaskHandle_t controlTaskHandle;
TaskHandle_t dataLoggerTaskHandle;
// Function prototypes
void sensorTask(void *pvParameters);
void communicationTask(void *pvParameters);
void controlTask(void *pvParameters);
void dataLoggerTask(void *pvParameters);
void connectWiFi();
void connectMQTT();
void mqttCallback(char* topic, byte* payload, unsigned int length);
void publishSensorData(SensorData& data);
void publishSystemStatus();
void handleControlCommand(ControlCommand& command);
void logData(String message);
void setupSensors();
void setupSDCard();
void setupRTC();
void buttonISR();
void alertUser(int beeps, int duration);
void enterDeepSleep(int seconds);
void wakeFromDeepSleep();
void setup() {
Serial.begin(115200);
Serial.println("Embedded IoT Device starting...");
// Initialize GPIO
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Setup interrupts
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
// Initialize semaphores
wifiMutex = xSemaphoreCreateMutex();
mqttMutex = xSemaphoreCreateMutex();
sensorMutex = xSemaphoreCreateMutex();
sdMutex = xSemaphoreCreateMutex();
// Initialize queues
sensorDataQueue = xQueueCreate(10, sizeof(SensorData));
controlCommandQueue = xQueueCreate(5, sizeof(ControlCommand));
logDataQueue = xQueueCreate(20, sizeof(String[50]));
// Setup hardware
setupSensors();
setupSDCard();
setupRTC();
// Initialize system status
systemStatus = {
.wifiConnected = false,
.mqttConnected = false,
.sdCardPresent = false,
.batteryLevel = 0.0,
.uptime = 0,
.freeHeap = ESP.getFreeHeap(),
.cpuTemperature = 0.0,
.lastSensorRead = 0,
.lastMQTTMessage = 0
};
// Create FreeRTOS tasks
xTaskCreate(
sensorTask,
"SensorTask",
SENSOR_TASK_STACK_SIZE,
NULL,
SENSOR_TASK_PRIORITY,
&sensorTaskHandle
);
xTaskCreate(
communicationTask,
"CommTask",
COMM_TASK_STACK_SIZE,
NULL,
COMM_TASK_PRIORITY,
&commTaskHandle
);
xTaskCreate(
controlTask,
"ControlTask",
CONTROL_TASK_STACK_SIZE,
NULL,
CONTROL_TASK_PRIORITY,
&controlTaskHandle
);
xTaskCreate(
dataLoggerTask,
"DataLoggerTask",
DATA_LOGGER_TASK_STACK_SIZE,
NULL,
DATA_LOGGER_TASK_PRIORITY,
&dataLoggerTaskHandle
);
// Alert user that system is ready
alertUser(3, 200);
Serial.println("Embedded IoT Device ready!");
}
void loop() {
// Main loop is handled by FreeRTOS tasks
vTaskDelay(pdMS_TO_TICKS(1000));
}
void sensorTask(void *pvParameters) {
Serial.println("Sensor task started");
SensorData sensorData;
const TickType_t xDelay = pdMS_TO_TICKS(5000); // 5 seconds
while (1) {
// Take sensor mutex
if (xSemaphoreTake(sensorMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
// Read DHT sensor
float humidity = dhtSensor.readHumidity();
float temperature = dhtSensor.readTemperature();
// Read BMP280 sensor
float pressure = bmp280.readPressure();
float altitude = bmp280.readAltitude(1013.25); // Sea level pressure
// Check if readings are valid
if (!isnan(humidity) && !isnan(temperature) && pressure > 0) {
sensorData.temperature = temperature;
sensorData.humidity = humidity;
sensorData.pressure = pressure / 100.0; // Convert to hPa
sensorData.altitude = altitude;
sensorData.timestamp = millis();
sensorData.valid = true;
// Update system status
systemStatus.lastSensorRead = millis();
systemStatus.freeHeap = ESP.getFreeHeap();
// Send data to queue
if (xQueueSend(sensorDataQueue, &sensorData, pdMS_TO_TICKS(100)) != pdTRUE) {
Serial.println("Failed to send sensor data to queue");
}
// Log sensor data
String logMessage = "Sensor: T=" + String(temperature) +
"C, H=" + String(humidity) +
"%, P=" + String(sensorData.pressure) + "hPa";
logData(logMessage);
} else {
Serial.println("Invalid sensor readings");
sensorData.valid = false;
// Log error
logData("ERROR: Invalid sensor readings");
}
xSemaphoreGive(sensorMutex);
} else {
Serial.println("Failed to take sensor mutex");
}
vTaskDelay(xDelay);
}
}
void communicationTask(void *pvParameters) {
Serial.println("Communication task started");
const TickType_t xDelay = pdMS_TO_TICKS(1000); // 1 second
while (1) {
// Handle WiFi connection
if (xSemaphoreTake(wifiMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
if (WiFi.status() != WL_CONNECTED) {
connectWiFi();
}
systemStatus.wifiConnected = (WiFi.status() == WL_CONNECTED);
xSemaphoreGive(wifiMutex);
}
// Handle MQTT connection
if (systemStatus.wifiConnected) {
if (xSemaphoreTake(mqttMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
if (!mqttClient.connected()) {
connectMQTT();
}
systemStatus.mqttConnected = mqttClient.connected();
// Process MQTT messages
mqttClient.loop();
xSemaphoreGive(mqttMutex);
}
}
// Publish sensor data
SensorData sensorData;
if (xQueueReceive(sensorDataQueue, &sensorData, pdMS_TO_TICKS(100)) == pdTRUE) {
if (sensorData.valid && systemStatus.mqttConnected) {
publishSensorData(sensorData);
systemStatus.lastMQTTMessage = millis();
}
}
// Publish system status periodically
static unsigned long lastStatusPublish = 0;
if (millis() - lastStatusPublish > 60000) { // Every minute
if (systemStatus.mqttConnected) {
publishSystemStatus();
lastStatusPublish = millis();
}
}
vTaskDelay(xDelay);
}
}
void controlTask(void *pvParameters) {
Serial.println("Control task started");
ControlCommand command;
const TickType_t xDelay = pdMS_TO_TICKS(500); // 0.5 seconds
while (1) {
// Process control commands
if (xQueueReceive(controlCommandQueue, &command, pdMS_TO_TICKS(100)) == pdTRUE) {
handleControlCommand(command);
}
// Monitor system health
static unsigned long lastHealthCheck = 0;
if (millis() - lastHealthCheck > 30000) { // Every 30 seconds
// Check free heap
if (ESP.getFreeHeap() < 1000) {
logData("WARNING: Low memory");
alertUser(2, 100);
}
// Check uptime
systemStatus.uptime = millis() / 1000;
// Check battery level (if implemented)
systemStatus.batteryLevel = getBatteryLevel();
lastHealthCheck = millis();
}
vTaskDelay(xDelay);
}
}
void dataLoggerTask(void *pvParameters) {
Serial.println("Data logger task started");
String logMessage;
const TickType_t xDelay = pdMS_TO_TICKS(1000); // 1 second
while (1) {
// Process log messages
if (xQueueReceive(logDataQueue, &logMessage, pdMS_TO_TICKS(100)) == pdTRUE) {
// Print to serial
Serial.println(logMessage);
// Write to SD card if available
if (systemStatus.sdCardPresent) {
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
writeLogToSD(logMessage);
xSemaphoreGive(sdMutex);
}
}
}
vTaskDelay(xDelay);
}
}
void connectWiFi() {
Serial.println("Connecting to WiFi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
vTaskDelay(pdMS_TO_TICKS(500));
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
digitalWrite(LED_PIN, HIGH);
logData("WiFi connected: " + WiFi.localIP().toString());
} else {
Serial.println("\nFailed to connect to WiFi");
digitalWrite(LED_PIN, LOW);
logData("ERROR: WiFi connection failed");
}
}
void connectMQTT() {
Serial.println("Connecting to MQTT...");
mqttClient.setServer(MQTT_SERVER, MQTT_PORT);
mqttClient.setCallback(mqttCallback);
int attempts = 0;
while (!mqttClient.connected() && attempts < 10) {
Serial.print("Attempting MQTT connection...");
if (mqttClient.connect(MQTT_CLIENT_ID, MQTT_USERNAME, MQTT_PASSWORD)) {
Serial.println("connected!");
// Subscribe to control topics
mqttClient.subscribe("embedded/control");
mqttClient.subscribe("embedded/" + String(MQTT_CLIENT_ID) + "/control");
logData("MQTT connected");
} else {
Serial.print("failed, rc=");
Serial.print(mqttClient.state());
Serial.println(" try again in 5 seconds");
vTaskDelay(pdMS_TO_TICKS(5000));
attempts++;
}
}
if (!mqttClient.connected()) {
Serial.println("Failed to connect to MQTT");
logData("ERROR: MQTT connection failed");
}
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
String message = "";
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message);
// Parse JSON message
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, message);
if (error) {
Serial.println("Failed to parse JSON");
logData("ERROR: Failed to parse MQTT message");
return;
}
// Create control command
ControlCommand command;
command.command = doc["command"] | "";
command.parameters = doc["parameters"] | "";
command.timestamp = millis();
command.processed = false;
// Send command to control task
if (xQueueSend(controlCommandQueue, &command, pdMS_TO_TICKS(100)) != pdTRUE) {
Serial.println("Failed to send control command to queue");
}
}
void publishSensorData(SensorData& data) {
if (xSemaphoreTake(mqttMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
// Create JSON document
DynamicJsonDocument doc(1024);
doc["device_id"] = MQTT_CLIENT_ID;
doc["timestamp"] = data.timestamp;
doc["temperature"] = data.temperature;
doc["humidity"] = data.humidity;
doc["pressure"] = data.pressure;
doc["altitude"] = data.altitude;
// Serialize JSON
String jsonStr;
serializeJson(doc, jsonStr);
// Publish to topics
mqttClient.publish("embedded/sensors/temperature", String(data.temperature).c_str());
mqttClient.publish("embedded/sensors/humidity", String(data.humidity).c_str());
mqttClient.publish("embedded/sensors/pressure", String(data.pressure).c_str());
mqttClient.publish("embedded/sensors/combined", jsonStr.c_str());
xSemaphoreGive(mqttMutex);
}
}
void publishSystemStatus() {
if (xSemaphoreTake(mqttMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
// Create JSON document
DynamicJsonDocument doc(1024);
doc["device_id"] = MQTT_CLIENT_ID;
doc["timestamp"] = millis();
doc["wifi_connected"] = systemStatus.wifiConnected;
doc["mqtt_connected"] = systemStatus.mqttConnected;
doc["sd_card_present"] = systemStatus.sdCardPresent;
doc["battery_level"] = systemStatus.batteryLevel;
doc["uptime"] = systemStatus.uptime;
doc["free_heap"] = systemStatus.freeHeap;
doc["cpu_temperature"] = systemStatus.cpuTemperature;
doc["last_sensor_read"] = systemStatus.lastSensorRead;
doc["last_mqtt_message"] = systemStatus.lastMQTTMessage;
// Serialize JSON
String jsonStr;
serializeJson(doc, jsonStr);
// Publish status
mqttClient.publish("embedded/status", jsonStr.c_str());
xSemaphoreGive(mqttMutex);
}
}
void handleControlCommand(ControlCommand& command) {
Serial.println("Processing control command: " + command.command);
if (command.command == "buzz") {
int duration = command.parameters.toInt();
if (duration == 0) duration = 1000;
alertUser(1, duration);
} else if (command.command == "led") {
bool state = (command.parameters == "on");
digitalWrite(LED_PIN, state ? HIGH : LOW);
} else if (command.command == "relay") {
bool state = (command.parameters == "on");
digitalWrite(RELAY_PIN, state ? HIGH : LOW);
} else if (command.command == "restart") {
Serial.println("Restarting device...");
logData("System restart requested");
vTaskDelay(pdMS_TO_TICKS(1000));
ESP.restart();
} else if (command.command == "sleep") {
int seconds = command.parameters.toInt();
if (seconds == 0) seconds = 10;
Serial.println("Going to sleep for " + String(seconds) + " seconds");
logData("Entering deep sleep for " + String(seconds) + " seconds");
enterDeepSleep(seconds);
} else if (command.command == "get_status") {
publishSystemStatus();
} else {
Serial.println("Unknown command: " + command.command);
logData("WARNING: Unknown control command: " + command.command);
}
command.processed = true;
}
void logData(String message) {
String logEntry = String(millis()) + ": " + message;
// Send to logger queue
if (xQueueSend(logDataQueue, &logEntry, pdMS_TO_TICKS(100)) != pdTRUE) {
Serial.println("Failed to send log message to queue");
}
}
void setupSensors() {
Serial.println("Setting up sensors...");
// Initialize DHT sensor
dhtSensor.begin();
// Initialize BMP280
if (!bmp280.begin(BMP280_ADDRESS_ALT)) {
Serial.println("Could not find BMP280 sensor!");
logData("ERROR: BMP280 sensor not found");
} else {
Serial.println("BMP280 sensor initialized");
logData("BMP280 sensor initialized");
}
Serial.println("Sensors setup completed");
}
void setupSDCard() {
Serial.println("Setting up SD card...");
if (SD.begin(SD_CS_PIN)) {
Serial.println("SD card initialized");
systemStatus.sdCardPresent = true;
logData("SD card initialized");
// Create log file if it doesn't exist
File logFile = SD.open("/log.txt", FILE_WRITE);
if (logFile) {
logFile.println("\n=== System Restart ===");
logFile.close();
}
} else {
Serial.println("SD card initialization failed");
systemStatus.sdCardPresent = false;
logData("ERROR: SD card initialization failed");
}
}
void setupRTC() {
Serial.println("Setting up RTC...");
if (rtc.begin()) {
if (rtc.lostPower()) {
Serial.println("RTC lost power, setting to compile time");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
Serial.println("RTC initialized");
logData("RTC initialized");
} else {
Serial.println("Couldn't find RTC");
logData("ERROR: RTC not found");
}
}
void writeLogToSD(String message) {
File logFile = SD.open("/log.txt", FILE_WRITE);
if (logFile) {
DateTime now = rtc.now();
logFile.print(now.timestamp(DateTime::TIMESTAMP_FULL));
logFile.print(": ");
logFile.println(message);
logFile.close();
}
}
void buttonISR() {
// Button press interrupt
static unsigned long lastInterrupt = 0;
unsigned long interruptTime = millis();
if (interruptTime - lastInterrupt > 200) { // Debounce
// Create manual capture command
ControlCommand command;
command.command = "capture";
command.parameters = "manual";
command.timestamp = interruptTime;
command.processed = false;
// Send to control task
xQueueSendFromISR(controlCommandQueue, &command, NULL);
// Alert user
alertUser(1, 100);
}
lastInterrupt = interruptTime;
}
void alertUser(int beeps, int duration) {
for (int i = 0; i < beeps; i++) {
tone(BUZZER_PIN, 1000, duration);
vTaskDelay(pdMS_TO_TICKS(duration * 2));
}
}
float getBatteryLevel() {
// Read battery voltage (assuming voltage divider)
int rawValue = analogRead(A0);
float voltage = rawValue * (3.3 / 4096.0) * 2.0; // Voltage divider factor
// Convert to percentage (assuming 3.0V to 4.2V range)
float percentage = ((voltage - 3.0) / 1.2) * 100.0;
percentage = constrain(percentage, 0.0, 100.0);
return percentage;
}
void enterDeepSleep(int seconds) {
// Configure wake-up sources
esp_sleep_enable_timer_wakeup(seconds * 1000000); // Convert to microseconds
esp_sleep_enable_ext0_wakeup(GPIO_NUM_0, 0); // Wake on button press
// Enter deep sleep
esp_deep_sleep_start();
}
void wakeFromDeepSleep() {
// This function is called after waking from deep sleep
Serial.println("Waking from deep sleep...");
// Reinitialize peripherals
setupSensors();
setupSDCard();
// Check wake-up reason
esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
switch (wakeup_reason) {
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("Wakeup caused by timer");
break;
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("Wakeup caused by external signal (RTC GPIO)");
break;
default:
Serial.println("Wakeup was not caused by deep sleep");
break;
}
}
Este código implementa un dispositivo IoT embebido completo que integra múltiples sensores, conectividad WiFi y MQTT, almacenamiento en tarjeta SD, y procesamiento multitarea mediante FreeRTOS.
El proyecto demuestra patrones profesionales de programación embebida: sincronización con semáforos para evitar condiciones de carrera, colas de mensajes para comunicación entre tareas, y manejo robusto de periféricos con reintentos y validación de datos.
Las cuatro tareas FreeRTOS principales funcionan en paralelo, priorizando la lectura de sensores, comunicación de red, control de actuadores y registro de datos. El sistema incluye comandos MQTT para controlar remotamente componentes hardware como LEDs, relés y buzzer, además de monitorear el estado del sistema en tiempo real.
La capacidad de deep sleep permite optimizar el consumo energético, especialmente importante en aplicaciones con batería. El dispositivo puede despertar desde temporizadores o presión de botones, reinicializando solo los periféricos necesarios.
Arquitectura IoT
Modelo de capas
graph TD
A[Physical Layer] --> B[Sensor/Actuator Layer]
B --> C[Edge Computing Layer]
C --> D[Network Layer]
D --> E[Cloud Layer]
E --> F[Application Layer]
A1[Sensors, Actuators] --> A
B1[Microcontrollers, Gateways] --> B
C1[Edge Processing, Analytics] --> C
D1[WiFi, LoRaWAN, 5G] --> D
E1[Cloud Services, Storage] --> E
F1[Web Apps, Mobile Apps] --> F
Comparativa de sistemas embebidos
Microcontroladores vs. Microprocesadores
| Característica | Microcontrolador | Microprocesador |
|---|---|---|
| Integración | Alta (CPU + periféricos) | Baja (solo CPU) |
| Costo | Bajo | Medio a Alto |
| Consumo de energía | Muy bajo | Medio a Alto |
| Rendimiento | Limitado | Alto |
| Memoria | Interna, limitada | Externa, flexible |
| Aplicación | Embebido, IoT | Uso general |
Plataformas IoT
| Plataforma | Protocolos | Edge Computing | Integración Cloud | Licencia |
|---|---|---|---|---|
| Arduino | WiFi, Ethernet | Limitada | Cloud Services | Open Source |
| Raspberry Pi | WiFi, Ethernet | Sí | Integración completa | Open Source |
| ESP32 | WiFi, Bluetooth | Sí | MQTT, HTTP | Open Source |
| NVIDIA Jetson | WiFi, Ethernet | Sí | NVIDIA Cloud | Comercial |
Protocolos de comunicación
Protocolos inalámbricos
| Protocolo | Alcance | Tasa de datos | Consumo de energía | Aplicación |
|---|---|---|---|---|
| WiFi | 50-100m | 150-600 Mbps | Alto | Indoor, alta velocidad |
| Bluetooth | 10-100m | 1-3 Mbps | Medio | Área personal |
| LoRaWAN | 2-15km | 0.3-50 kbps | Muy bajo | Largo alcance |
| Zigbee | 10-100m | 250 kbps | Bajo | Redes de malla |
Protocolos IoT
| Protocolo | Tipo | Overhead | QoS | Aplicación |
|---|---|---|---|---|
| MQTT | Publish/Subscribe | Bajo | 3 niveles | Tiempo real |
| CoAP | Request/Response | Muy bajo | Ninguno | Dispositivos limitados |
| HTTP/REST | Request/Response | Alto | TCP | Integración web |
| WebSocket | Full-Duplex | Medio | TCP | Tiempo real |
Conceptos de Edge Computing
Edge vs. Cloud Processing
| Aspecto | Edge Computing | Cloud Computing |
|---|---|---|
| Latencia | Milisegundos | Segundos |
| Ancho de banda | Reducido | Alto |
| Offline | Posible | Imposible |
| Escalabilidad | Limitada | Ilimitada |
| Costo | Variable | Predecible |
Aplicaciones de Edge Computing
- Mantenimiento predictivo: análisis local de datos de sensores
- Video Analytics: detección de objetos en tiempo real
- Industrial IoT: control de líneas de producción
- Smart Cities: análisis y control del tráfico
Ventajas y desventajas
Ventajas de IoT y sistemas embebidos
- Automatización: reducción del trabajo manual
- Monitoreo en tiempo real: información inmediata del estado
- Eficiencia: optimización de procesos
- Escalabilidad: adaptación flexible a las necesidades
- Ahorro de costos: reducción de gastos operativos
Desventajas
- Riesgos de seguridad: superficie de ataque para hackers
- Complejidad: integración y mantenimiento
- Dependencia: fallos ante problemas de red
- Privacidad: recopilación de datos personales
- Compatibilidad: estándares dispares
Preguntas frecuentes de examen
-
¿Cuál es la diferencia entre un microcontrolador y un microprocesador? Los microcontroladores integran CPU, memoria y periféricos en un solo chip, mientras que los microprocesadores contienen solo la CPU y requieren componentes externos.
-
Explica qué es Edge Computing. Edge Computing procesa datos localmente en dispositivos cercanos a la fuente de datos en lugar de enviarlos a la nube, reduciendo latencia y ahorrando ancho de banda.
-
¿Cuándo se usa cada protocolo IoT? MQTT para comunicación en tiempo real, CoAP para dispositivos con recursos limitados, HTTP/REST para integración web, WebSocket para comunicación bidireccional.
-
¿Cuáles son los requisitos principales de un sistema embebido? Confiabilidad, capacidad de tiempo real, bajo consumo de energía, producción rentable, larga vida útil y mantenimiento mínimo.
Fuentes principales
- https://www.arduino.cc/
- https://www.raspberrypi.org/
- https://mqtt.org/
- https://www.espressif.com/en/products/socs/esp32



