Arquitectura Embebida Limpia: Evitando el Firmware
El software no se desgasta, pero el hardware queda obsoleto — una Capa de Abstracción de Hardware evita que la lógica de negocio se convierta en firmware atrapado en un procesador específico.
Por qué importa
Robert Martin traza una distinción clara entre firmware y software. El firmware es código tan profundamente acoplado al hardware que no puede ejecutarse en ningún otro lugar. El software puede portarse. La tendencia preocupante es que desarrolladores que no escriben sistemas embebidos están escribiendo firmware de todas formas — acoplan su lógica de negocio a una base de datos, framework o runtime específico hasta que nunca puede extraerse.
La Capa de Abstracción de Hardware (HAL) se sitúa entre la lógica de negocio y el código específico del hardware. La lógica de negocio importa solo la interfaz abstracta de la HAL; la implementación concreta de la HAL maneja las llamadas reales al hardware. La OSAL (Capa de Abstracción del Sistema Operativo) hace lo mismo para las llamadas al RTOS.
La prueba clave: ¿puede tu lógica de negocio ejecutarse en tu laptop de desarrollo sin hardware especial? Si la respuesta es no, tienes firmware. El patrón HAL rompe la dependencia para que tus reglas de negocio puedan probarse con una HAL simulada en cualquier máquina, y el adaptador específico de hardware es reemplazable cuando cambia la plataforma de hardware.
✗El problema
Business logic directly calls hardware drivers. The motor control logic is welded to a specific board. Impossible to unit test without real hardware. Impossible to reuse on a different platform.
Bad
import RPi.GPIO as GPIO # hardware-specific import
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)
class ConveyorBelt:
def start(self, duration_seconds: float) -> None:
GPIO.output(18, GPIO.HIGH) # RPi-specific hardware call
time.sleep(duration_seconds)
GPIO.output(18, GPIO.LOW) # RPi-specific hardware call
def emergency_stop(self) -> None:
GPIO.output(18, GPIO.LOW) # RPi-specific hardware call
# To run tests: need a physical Raspberry Pi connected to a motor.
# To port to STM32 or Arduino: rewrite ConveyorBelt from scratch.
# The business logic has become firmware.
import { gpio } from "rpi-gpio"; // hardware-specific library
export class ConveyorBelt {
private readonly PIN = 18;
async start(durationMs: number): Promise {
await gpio.setup(this.PIN, gpio.DIR_OUT);
await gpio.write(this.PIN, true); // hardware-specific call
await new Promise(r => setTimeout(r, durationMs));
await gpio.write(this.PIN, false); // hardware-specific call
}
async emergencyStop(): Promise {
await gpio.write(this.PIN, false); // hardware-specific call
}
}
// To run tests: need a physical device with GPIO pins.
// To port to a different board: rewrite ConveyorBelt from scratch.
// The business logic has become firmware.
✓La solución
A MotorController HAL interface separates the business logic from hardware calls. MockMotorController enables unit tests on any laptop. Switching boards means writing a new adapter — business logic is untouched.
Good
from abc import ABC, abstractmethod
import time
# hal/motor_controller.py ← Hardware Abstraction Layer (stable interface)
class MotorController(ABC):
@abstractmethod
def turn_on(self) -> None: ...
@abstractmethod
def turn_off(self) -> None: ...
# hal/rpi_motor_controller.py ← RPi adapter (volatile, hardware-specific)
import RPi.GPIO as GPIO
class RaspberryPiMotorController(MotorController):
PIN = 18
def __init__(self):
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.PIN, GPIO.OUT)
def turn_on(self) -> None: GPIO.output(self.PIN, GPIO.HIGH)
def turn_off(self) -> None: GPIO.output(self.PIN, GPIO.LOW)
# hal/mock_motor_controller.py ← test double (runs on any machine)
class MockMotorController(MotorController):
def __init__(self): self.is_on = False
def turn_on(self) -> None: self.is_on = True
def turn_off(self) -> None: self.is_on = False
# domain/conveyor_belt.py ← pure business logic, no hardware import
class ConveyorBelt:
def __init__(self, motor: MotorController):
self._motor = motor
def start(self, duration_seconds: float) -> None:
self._motor.turn_on()
time.sleep(duration_seconds)
self._motor.turn_off()
def emergency_stop(self) -> None:
self._motor.turn_off()
# Tests run on any laptop — no hardware needed.
# Port to STM32: write STM32MotorController. ConveyorBelt unchanged.
// hal/MotorController.ts ← HAL interface (stable)
export interface MotorController {
turnOn(): Promise;
turnOff(): Promise;
}
// hal/RpiMotorController.ts ← RPi adapter (volatile, hardware-specific)
import { gpio } from "rpi-gpio";
import { MotorController } from "./MotorController";
export class RpiMotorController implements MotorController {
private readonly PIN = 18;
async turnOn(): Promise {
await gpio.setup(this.PIN, gpio.DIR_OUT);
await gpio.write(this.PIN, true);
}
async turnOff(): Promise { await gpio.write(this.PIN, false); }
}
// hal/MockMotorController.ts ← test double (runs on any machine)
import { MotorController } from "./MotorController";
export class MockMotorController implements MotorController {
public isOn = false;
async turnOn(): Promise { this.isOn = true; }
async turnOff(): Promise { this.isOn = false; }
}
// domain/ConveyorBelt.ts ← pure business logic, no hardware import
import { MotorController } from "../hal/MotorController";
export class ConveyorBelt {
constructor(private readonly motor: MotorController) {}
async start(durationMs: number): Promise {
await this.motor.turnOn();
await new Promise(r => setTimeout(r, durationMs));
await this.motor.turnOff();
}
async emergencyStop(): Promise { await this.motor.turnOff(); }
}
// Tests run on any laptop — no hardware needed.
// Port to new board: write new adapter. ConveyorBelt unchanged.
💡Conclusión clave
Si tu lógica de negocio no puede ejecutarse en tu laptop de desarrollo sin hardware especial, se ha convertido en firmware — no en software. La Capa de Abstracción de Hardware es el mismo patrón que Puertos y Adaptadores aplicado a dispositivos físicos: define una interfaz abstracta en el dominio, impleméntala una vez por objetivo de hardware, y prueba la lógica de negocio con una implementación simulada que funcione en cualquier lugar.
🔧 Algunos ejercicios pueden tener errores. Si algo parece incorrecto, usa el botón Feedback (abajo a la derecha) para reportarlo — nos ayuda a corregirlo rápido.
Pista: Si tu lógica de negocio no puede ejecutarse en tu laptop de desarrollo sin hardware especial, se ha convertido en firmware — no en software.
✗ Tu versión