tdse-tf_3-02

Controlador PID para derretidor de miel con control local via botones y remoto via WiFi
Index Commits Files Refs Submodules README
commit 723730f8908ed85a4e87095b403f7784d32e6dce
parent 5aa7e731b713b401dd6fc60d476e9da5be46cb7d
Author: Martin Klöckner <mjkloeckner@gmail.com>
Date:   Fri, 24 Jul 2026 00:12:29 -0300

Merge pull request #4 from mjkloeckner/memoria

Agregar memoria del proyecto
Diffstat:
AMEMORIA.md | 1222+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mfirmware-nucleo/App/Inc/logger.h | 2+-
Mfirmware-nucleo/App/Src/task_menu.c | 2+-
Aimg/3d_print_heater.png | 0
Aimg/button_statechart.png | 0
Aimg/buzzer_statechart.png | 0
Aimg/conexion_placas_perforadas_TA134.png | 0
Aimg/diagrama_de_bloques_firmware_nucleo_TA134.png | 0
Aimg/display_views.png | 0
Aimg/ds18b20.png | 0
Aimg/ds18b20_views.png | 0
Aimg/ds18b20_wiring.png | 0
Aimg/esp01s-pinout.jpg | 0
Aimg/esp01s.jpg | 0
Aimg/esp01s_views.png | 0
Aimg/idc.png | 0
Aimg/lcd/lcd_clock_set.png | 0
Aimg/lcd/lcd_off.png | 0
Aimg/lcd/lcd_off_prog.png | 0
Aimg/lcd/lcd_on.png | 0
Aimg/lcd/lcd_temp_set.png | 0
Aimg/lcd/lcd_timer_set.png | 0
Aimg/lcd/lcd_timer_set_end.png | 0
Aimg/lcd/lcd_timer_set_start.png | 0
Aimg/led_statechart.png | 0
Aimg/main_perf_board.png | 0
Aimg/npn_inv_mosfet_driver.png | 0
Aimg/npn_inv_mosfet_driver_plot.png | 0
Aimg/npn_led_driver.png | 0
Aimg/nucleo_f103rb.png | 0
Aimg/nucleo_f103rb_axo.png | 0
Aimg/nucleo_f103rb_views.png | 0
Aimg/pagina_web_dark.png | 0
Aimg/pagina_web_light.png | 0
Aimg/stm32_nucleo_expansion_board_cropped.png | 0
Aimg/stm32_pinout_view.png | 0
Aimg/system_statechart.png | 0
Aimg/temp_ctrl_views.png | 0
Aimg/to220.png | 0
Aimg/vista_inf_placa_sin_modulos.jpg | 0
Aimg/vista_sup_placa_con_modulos.jpg | 0
Aimg/vista_sup_placa_sin_modulos.jpg | 0
42 files changed, 1224 insertions(+), 2 deletions(-)
diff --git a/MEMORIA.md b/MEMORIA.md
@@ -0,0 +1,1222 @@
+<div align="center">
+
+<img src="img/logofiuba.png" width="100%">
+
+**UNIVERSIDAD DE BUENOS AIRES**  
+**Facultad de Ingeniería**  
+**TA134 - Taller de Sistemas Embebidos**
+
+# Controlador de derretidor de miel
+
+**Martin Klöckner** - [mklockner@fi.uba.ar](mailto:mklockner@fi.uba.ar)
+
+**Fecha:** 31 de mayo de 2026  
+**Cuatrimestre de cursada:** Segundo cuatrimestre de 2025
+
+*Trabajo realizado entre marzo y mayo de 2026.*
+
+</div>
+
+<br><br>
+
+En el presente trabajo se diseña e implementa un sistema para el control de un
+derretidor eléctrico de miel, reemplazando el sistema original basado en un
+termostato de bulbo capilar por un sistema digital. Las principales
+características del sistema diseñado son:
+
+- Control de temperatura mediante PID con salida PWM sobre MOSFET.
+- Sensor digital de temperatura DS18B20.
+- Interfaz local con display, botones, luces indicadores y buzzer.
+- Control remoto vía Wi-Fi con interfaz web.
+- Reloj de tiempo real con batería de respaldo y temporizador programable.
+- Persistencia de parámetros incluso si se pierde la alimentación.
+
+Los resultados demuestran la conveniencia de utilizar un controlador digital por
+sobre uno analógico de los equipos comerciales existentes. En las siguientes
+secciones se describe el diseño, la implementación y los resultados de las
+pruebas realizadas.
+
+<br>
+
+# Índice
+
+- [1. Introducción general](#1-introducción-general)
+  - [1.1. Alcance](#11-alcance)
+  - [1.2. Trabajos similares y alternativas](#12-trabajos-similares-y-alternativas)
+- [2. Introducción específica](#2-introducción-específica)
+  - [2.1. Requisitos](#21-requisitos)
+  - [2.2. Casos de uso](#22-casos-de-uso)
+  - [2.3. Descripción de módulos principales](#23-descripción-de-módulos-principales)
+    - [2.3.1. Control principal](#231-control-principal)
+    - [2.3.2. Módulo remoto](#232-módulo-remoto)
+    - [2.3.3. Sensor de temperatura](#233-sensor-de-temperatura)
+    - [2.3.4. Control de temperatura](#234-control-de-temperatura)
+    - [2.3.5. Display y adaptador paralelo a serie I2C](#235-display-y-adaptador-paralelo-a-serie-i2c)
+    - [2.3.6. Cable y conector IDC](#234-cable-y-conector-idc)
+- [3. Diseño e implementación](#3-diseño-e-implementación)
+  - [3.1. Hardware del sistema](#31-hardware-del-sistema)
+    - [3.1.1. Placa NUCLEO](#311-placa-nucleo)
+    - [3.1.2. Sensor DS18B20](#312-sensor-ds18b20)
+    - [3.1.3. Módulo ESP-01S](#313-modulo-esp-01s)
+    - [3.1.4. Display LCD 16x2 I2C](#314-display-lcd-16x2-i2c)
+    - [3.1.5. Control de temperatura](#315-control-de-temperatura)
+    - [3.1.6. Luces indicadoras](#316-luces-indicadoras)
+    - [3.1.7. Buzzer](#317-buzzer)
+    - [3.1.8. Botones](#318-botones)
+  - [3.2. Firmware del sistema](#32-firmware-del-sistema)
+    - [3.2.1. Ejecutor cíclico](#321-ejecutor-cíclico)
+    - [3.2.2. Tarea central](#322-tarea-central)
+    - [3.2.3. Tarea para botones](#323-tarea-para-botones)
+    - [3.2.4. Tarea para el buzzer](#324-tarea-para-el-buzzer)
+    - [3.2.5. Tarea para luces indicadoras](#325-tarea-para-luces-indicadoras)
+    - [3.2.6. Tarea para el menu](#326-tarea-para-el-menu)
+    - [3.2.7. Tarea para control de temperatura](#327-tarea-para-control-de-temperatura)
+    - [3.2.8. Tarea para sensor de temperatura](#328-tarea-para-sensor-de-temperatura)
+    - [3.2.9. Tarea para módulo remoto](#329-tarea-para-modulo-remoto)
+    - [3.2.10. Medición de tiempo de ejecución](#3210-medición-de-tiempo-de-ejecución)
+- [4. Ensayos y resultados](#4-ensayos-y-resultados)
+  - [4.1. Pruebas funcionales del hardware](#41-pruebas-funcionales-del-hardware)
+  - [4.2. Pruebas funcionales del firmware](#42-pruebas-funcionales-del-firmware)
+  - [4.3. Pruebas de integración](#43-pruebas-de-integración)
+  - [4.4. Cumplimiento de requisitos](#44-cumplimiento-de-requisitos)
+  - [4.5. Comparación con trabajos similares](#45-comparación-con-trabajos-similares)
+  - [4.6. Documentación del desarrollo realizado](#46-documentación-del-desarrollo-realizado)
+    - [4.6.1. Consumo de corriente](#461-consumo-de-corriente)
+    - [4.6.2. Tiempo de ejecución](#462-tiempo-de-ejecución)
+    - [4.6.3. Factor de uso de CPU](#463-consumo-de-corriente)
+    - [4.6.4. Uso de memorias](#464-uso-de-memorias)
+- [5. Conclusiones](#5-conclusiones)
+  - [5.1. Resultados obtenidos](#51-resultados-obtenidos)
+  - [5.2. Próximos pasos](#52-próximos-pasos)
+- [6. Referencias](#6-referencias)
+
+# 1. Introducción general
+
+## 1.1. Alcance
+
+Un derretidor eléctrico de miel es un equipo diseñado para elevar la temperatura
+de la miel de forma controlada, con el fin de reducir su viscosidad y facilitar
+su manipulación en procesos como envasado, filtrado y bombeo. En la figura 1.1
+se muestra un ejemplo de un derretidor eléctrico de miel convencional.
+
+<div align="center">
+    <img src="img/derretidor_convencional.png" style="padding: 10px" width="600">
+    <p align="center"><em>Figura 1.1: Derretidor de miel convencional.</em></p>
+</div>
+
+Los equipos comerciales actuales utilizan un sistema de control basado en un
+termostato de bulbo capilar, los cuales ofrecen un control rudimentario, sin
+capacidad de programación, monitoreo remoto o registro de datos. Es por esto que
+el objetivo del trabajo es diseñar e implementar un controlador digital para un
+derretidor eléctrico de miel para tambores, reemplazando el sistema original
+basado en un termostato por un sistema digital con mayor control, que permita:
+
+- Control preciso de temperatura.
+- Interfaz de usuario local con display y botones.
+- Control y monitoreo remoto vía Wi-Fi.
+- Programación de ciclos de derretido con temporizador.
+- Persistencia de parámetros ante cortes de energía.
+- Indicación luminosa y sonora de estados.
+
+El alcance de este proyecto está delimitado al desarrollo, implementación y
+validación de un Producto Mínimo Viable (MVP) que demuestre la factibilidad
+técnica del controlador digital. Quedan fuera del alcance el diseño de una placa
+PCB definitiva, el ensayo en un entorno de producción real con miel, y la
+validación térmica exhaustiva del conjunto completo. Estos aspectos se
+identificaron como un trabajo futuro para una posterior etapa de
+industrialización y comercialización del prototipo.
+
+## 1.2. Trabajos similares y alternativas
+
+No se encontraron productos comerciales que integren todas las funcionalidades
+propuestas a un costo accesible en el mercado nacional. Los derretidores de miel
+comerciales existentes se limitan a sistemas basados en termostatos, con control
+on/off y sin display, conectividad y capacidad de programación.
+
+En el ámbito académico, se identificaron trabajos similares en cuanto al
+control de temperatura, como un [horno eléctrico para gastronomía](https://docs.google.com/document/d/1ypnhOz-Gx_ZRbwiI6vEn4dvr7_YqNVnpy5sfgalQAoA/edit)
+y un [horno para componentes SMD](https://docs.google.com/document/d/1rQf3P1D7NSb7egpnlkKMTvL_rzy9PuMcMBPcu7NxmQw/edit),
+ambos de la misma materia (Sistemas Embebidos, FIUBA). Sin embargo, ninguno
+aborda la combinación de control PID, conectividad Wi-Fi con interfaz web, y
+temporizador programable en el contexto de derretido de miel.
+
+# 2. Introducción específica
+
+Se desarrolló un sistema de control de temperatura basado en el microcontrolador
+STM32F103RB de la placa NUCLEO-F103RB y el sensor de temperatura digital
+DS18B20. El sistema lee la temperatura de la miel a traves del sensor y controla
+automáticamente el dispositivo de calefacción del derretidor. El calefactor del
+derretidor se modela como una resistencia de 12 V y 48 W.
+
+El usuario puede interactuar con el sistema a traves de dos interfaces: una
+local, mediante botones, un display, luces indicadoras y un buzzer; y de manera
+remota, mediante un navegador web de un dispositivo dentro de la red local del
+sistema, accediendo a la pagina proporcionada por el modulo remoto ESP-01S del
+sistema.
+
+## 2.1. Requisitos
+
+En la tabla 2.1 se listan los requisitos alcanzados del trabajo, junto con su
+descripción correspondiente. En esta tabla no se incluyen requisitos no
+alcanzados, los cuales fueron considerados inicialmente durante la definición
+del proyecto pero que por restricciones de tiempo no se pudieron alcanzar.
+
+| ID | Requisito                  | Descripción                                                                   |
+| :- | :------------------------- | :---------------------------------------------------------------------------- |
+|  1 | Control por botones        | El usuario puede establecer y modificar parámetros mediante 4 botones físicos |
+|  2 | Control remoto             | El usuario puede controlar el dispositivo de manera remota vía Wi-Fi          |
+|  3 | Programación de ciclos     | El usuario puede programar ciclos con hora de inicio y fin                    |
+|  4 | Display                    | El sistema muestra el estado actual en un display LCD 16x2                    |
+|  5 | LEDs indicadores           | El sistema tiene LEDs para indicar estado de operación                        |
+|  6 | Alarma sonora              | El sistema tiene un buzzer para eventos y alarmas                             |
+|  7 | Monitoreo en tiempo real   | El sistema muestra temperatura, set point y duty cycle                        |
+|  8 | Conectividad Wi-Fi         | El sistema se conecta a redes Wi-Fi para comunicación remota                  |
+|  9 | Persistencia de parámetros | El sistema recuerda la última configuración al encender                       |
+| 10 | Reloj de tiempo real       | El sistema mantiene la hora incluso sin alimentación                          |
+
+<p align="center"><em>Tabla 2.1: Requisitos del trabajo</em></p>
+
+En la tabla 2.2 se muestran requisitos que quedaron fuera del alcance final del
+trabajo debido a restricciones de tiempo y complejidad. Se consideran requisitos
+futuros o deseables para una futura iteración del trabajo. 
+
+| ID | Requisito               | Descripción                                        |
+| :- | :---------------------- | :------------------------------------------------- |
+| 11 | Curvas de temperatura   | El usuario puede definir perfiles de calentamiento |
+| 12 | Registro de temperatura | El sistema almacena registro de temperatura        |
+| 13 | Límite de temperatura   | El sistema tiene un límite máximo de temperatura   |
+
+<p align="center"><em>Tabla 2.2: Requisitos futuros del trabajo</em></p>
+
+## 2.2. Casos de uso
+
+En las tablas 2.3, 2.4 y 2.5 se ejemplifican algunos casos de uso del derretidor
+de miel para cada uno de los modos de operación del sistema.
+
+| Elemento            | Definición                                                                                                                                                                |
+| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| Disparador          | Se quiere detener un ciclo de derretido de miel de manera remota                                                                                                          |
+| Precondiciones      | El sistema está en modo normal operando el calefactor                                                                                                                     |
+| Flujo principal     | El usuario interactúa con la interfaz remota seleccionando la opción detener calefactor, se detiene el calefactor y se cambia a modo de operación en reposo               |
+| Flujos alternativos | Se pierde la conexión con el sistema, en caso de ocurrir previo a seleccionar la opción el sistema sigue operando en modo normal, se reintenta la conexión con el usuario |
+
+<p align="center"><em>Tabla 2.3: Caso de uso modo normal</em></p>
+
+| Elemento            | Definición                                                                                                                                                                                                                                                                                                                                           |
+| :------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Disparador          | Se desea programar un ciclo de derretido para una hora y fecha especifica                                                                                                                                                                                                                                                                            |
+| Precondiciones      | El sistema está en modo reposo                                                                                                                                                                                                                                                                                                                       |
+| Flujo principal     | El usuario interactúa con la interfaz remota seleccionando la opción programar ciclo, elije entre las opciones disponibles y guarda la configuración. Llegado el momento definido por el usuario se cambia a modo de operación normal, enciendo el calefactor                                                                                        |
+| Flujos alternativos | En caso de que se pierda la energía previo a iniciar el ciclo, luego de que se vuelva a energizar el sistema y vuelva a modo reposo (si no hay fallas) el ciclo configurado permanece guardado porque los parámetros se guardan en memoria no volátil, llegado el momento configurado por el ciclo se cambia a modo normal encendiendo el calefactor |
+
+<p align="center"><em>Tabla 2.4: Caso de uso modo reposo</em></p>
+
+| Elemento            | Definición                                                                                                                                                                                                    |
+| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| Disparador          | El sistema se energiza                                                                                                                                                                                        |
+| Precondiciones      | El sistema se encontraba apagado, desenergizado                                                                                                                                                               |
+| Flujo principal     | El sistema inicializa todos los bloques, se comprueba que no haya ciclos de derretido predefinidos para el momento actual, en caso de que los haya se pasa a modo normal, si no los hay se pasa a modo reposo |
+| Flujos alternativos | Si hubo un error durante la inicialización de alguna tarea el sistema no se inicializa                                                                                                                        |
+
+<p align="center"><em>Tabla 2.5: Caso de uso modo transición</em></p>
+
+## 2.3. Descripción de módulos principales
+
+### 2.3.1. Control principal
+
+El control principal se implementa en la placa NUCLEO-F103RB, la cual se puede
+ver en la figura 2.3.1. Esta placa utiliza un microcontrolador ARM Cortex-M3 de
+32 bits, el STM32F103RBT6. En la placa se ejecuta el firmware principal del
+sistema, compuesto por un ejecutor cíclico con una pauta establecida de que un
+tick nunca supere 1 ms. Este ejecutar cíclico tiene asociado 8 tareas, las
+cuales controlan diferentes partes del sistema: 'system', 'buttons', 'buzzer',
+'leds', 'menu', 'temp_ctrl', 'temp_sensor' y 'remote'. La comunicación entre
+tareas se realiza mediante eventos y una estructura de datos compartida.
+
+<div align="center">
+    <img src="img/nucleo_f103rb_views.png" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 2.1: Placa NUCLEO F103RB</em></p>
+</div>
+
+Esta placa NUCLEO se utilizó exhaustivamente en la materia a lo largo de la
+cursada y fue por esto principalmente que se eligió para el desarrollo del
+trabajo. 
+
+### 2.3.2. Módulo remoto
+
+Para el control remoto se utilizó el módulo Wi-Fi ESP-01S basado en un
+microcontrolador Espressif ESP8266. Este se comunica a través de UART con la
+placa NUCLEO para intercambiar comandos con un formato especifico. La elección
+se basó en su bajo costo, disponibilidad en el mercado local y capacidad para
+actuar como servidor web. En la figura 2.2 se pueden ver imágenes de la placa,
+así como también las conexiones que ofrece la misma.
+
+<div align="center">
+    <img src="img/esp01s_views.png" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 2.2: Modulo ESP-01S</em></p>
+</div>
+
+El firmware utilizado en la placa ESP-01S fue desarrollado utilizando librerías
+de Arduino a través de PlatformIO. El firmware implementa funciones como la
+conexión a una red Wi-Fi fija, un servidor HTTP en el puerto 80 para servir la
+interfaz web estática desde el sistema de archivos, un servidor WebSocket en el
+puerto 81 para comunicación bidireccional en tiempo real y la comunicación UART
+con la placa NUCLEO.
+
+### 2.3.3. Sensor de temperatura
+
+Para medir la temperatura real del liquido, se utilizó el sensor DS18B20 de
+Maxim Integrated el cual es un termómetro digital con interfaz OneWire que
+ofrece una precisión de 0.5 °C en el rango de -10 °C a 85 °C. Se seleccionó por
+su bajo costo, disponibilidad en el mercado local y por tener la precisión
+adecuada para la aplicación.
+
+<div align="center">
+    <img src="img/ds18b20_views.png" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 2.3: Sensor de temperatura DS18B20</em></p>
+</div>
+
+La placa NUCLEO no es compatible con el protocolo OneWire por defecto es por
+esto que se implementó un "emulador" del protocolo utilizando el periférico UART
+de la NUCLEO cambiando dinámicamente la tasa de baudios (9600 baudios para
+reset, 96500 baudios para transferencia de datos). Y dado que la NUCLEO lo
+permite, se utilizo DMA para la comunicación con el sensor, disminuyendo así el
+tiempo de bloqueo del ejecutor cíclico.
+
+### 2.3.4. Control de temperatura
+
+Para controlar la temperatura, se modeló el derretidor eléctrico de miel
+utilizando un resistor de 12 V y 48 W, y el control de temperatura mediante un
+transistor MOSFET, controlado por modulación de ancho de pulsos (Pulse Width
+Modulation, PWM). El sistema regula la temperatura mediante un controlador
+Proporcional-Integral-Derivativo (PID) implementado en el firmware, siendo la
+señal de referencia a la temperatura establecida por el usuario y el valor leído
+de temperatura aquel obtenido mediante el sensor de temperatura.
+
+En la figura 2.4 se muestra, a la izquierda, el encapsulado TO-220 del MOSFET, y
+a la derecha, el resistor utilizado para modelar el calefactor del derretidor
+eléctrico de miel.
+
+<div align="center">
+    <img src="img/temp_ctrl_views.png" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 2.4: Dispositivos utilizados para el control de temperatura</em></p>
+</div>
+
+El MOSFET es controlado por la señal PWM generada por un timer de la placa
+NUCLEO. La frecuencia de conmutación de la señal se estableció en
+aproximadamente 500 Hz y el duty cycle se calcula mediante el algoritmo PID
+implementado en la tarea 'temp_ctrl'.
+
+### 2.3.5. Display y adaptador paralelo a serie I2C
+
+Para el control local se utilizó un display LCD con interfaz paralelo de 16x2
+caracteres, y para disminuir el número de pines usados de la NUCLEO, se utilizó
+una adaptador de interfaz paralelo del display a serie I2C para la NUCLEO,
+reduciendo el numero de pines necesarios de la NUCLEO de 6 a 2.
+
+En la figura 2.5 se muestra a la izquierda el display LCD de 16x2 caracteres
+utilizado y a la derecha el adaptador de interfaz paralela a serie I2C.
+
+<div align="center">
+    <img src="img/display_views.png" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 2.5: Display LCD y adaptador paralelo a serie I2C</em></p>
+</div>
+
+### 2.3.6. Cable y conector IDC
+
+Durante el diseño de hardware que se detallará en la sección siguiente, se
+separó el sistema en dos placas perforadas separadas. Por un lado la placa una
+placa donde se pueda conectar la NUCLEO y por otro una placa con todos los
+módulos y demás bloques que componen al sistema. Para interconectar estas dos
+placas perforadas se utilizaron conectores IDC de 20 pines (2 hileras de 10
+pines cada una). 
+
+En la figura 2.6 se muestran imágenes de los conectores utilizados, a la
+izquierda el conector hembra junto con el cable y a la derecha el conector macho
+soldado en ambas placas perforadas.
+
+<div align="center">
+    <img src="img/idc.png" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 2.6: Display LCD y adaptador paralelo a serie I2C</em></p>
+</div>
+
+# 3. Diseño e implementación
+
+En la figura 3.1 se muestra el diagrama en bloques con los principales
+módulos y conexiones del sistema. Se puede ver que la placa NUCLEO es el
+módulo central y luego todos los demás módulos se conectan a este.
+
+<div align="center">
+    <img style="padding: 10px" align=center src="img/diagrama_de_bloques.png" width="600">
+    <p><em>Figura 3.1: Diagrama en bloques del sistema.</em></p>
+</div>
+
+## 3.1. Hardware del sistema
+
+El trabajo se divide en dos placas perforadas experimentales. Estas placas
+perforadas se utilizan para unir los módulos, evitando utilizar cables. Las
+placas perforadas se conectan entre si a través de un cable IDC de 20 hilos, tal
+como se ve en la figura 3.2.
+
+<div align="center">
+    <img src="img/conexion_placas_perforadas_TA134.png" style="padding: 10px;" width="700">
+    <p align="center"><em>Figura 3.2: Conexión placas perforadas.</em></p>
+</div>
+
+La primer placa perforada se muestra en la figura 3.3 y hace de adaptador de los
+pines de la placa NUCLEO a cable con conector IDC de 2x10.
+
+<div align="center">
+    <img src="img/stm32_nucleo_expansion_board_cropped.png" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 3.3: Placa perforada adaptador NUCLEO a conector IDC 2x10.</em></p>
+</div>
+
+La segunda placa perforada, considerada la placa principal, se utilizó para unir
+todos los módulos restantes del sistema, como el modulo de acceso remoto, la
+bornera para el sensor de temperatura, las luces indicadoras, los botones, el
+portapilas para almacenar la pila CR2032, entre otros. En la figura 3.3 se
+muestra una vista de como se conectan los módulos dentro de la placa y al
+conector IDC.
+
+<div align="center">
+    <img src="img/main_perf_board.png" style="padding: 10px" align=center width="400px">
+    <p><em>Figura 3.4: Placa perforada principal.</em></p>
+</div>
+
+En las figuras 3.5, 3.6 y 3.7 se muestran imágenes de las placas armadas en
+placa perforada experimental, como se indicó previamente en las figuras 3.3 y
+3.4.
+
+<div align="center">
+    <img src="img/vista_sup_placa_con_modulos.jpg" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 3.5: Vista superior de placas con modulos en los zócalos.</em></p>
+</div>
+
+<div align="center">
+    <img src="img/vista_sup_placa_sin_modulos.jpg" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 3.6: Vista superior de placas sin modulos en los zócalos.</em></p>
+</div>
+
+<div align="center">
+    <img src="img/vista_inf_placa_sin_modulos.jpg" style="padding: 10px;" width="600">
+    <p align="center"><em>Figura 3.7: Vista inferior de placas sin modulos en los zócalos.</em></p>
+</div>
+
+### 3.1.1. Placa NUCLEO
+
+Como bien se mencionó, el trabajo se desarrolló utilizando la placa NUCLEO, esta
+placa utiliza el procesador STM32F103RBT6 configurado para operar a 64 MHz, ya
+que esta frecuencia es la que mejor se adapta a los periféricos utilizados. En
+particular los periféricos que se utilizaron son los siguientes:
+
+- I2C1: Conexion con display LCD 16x2.
+- USART1: Sensor DS18B20 (modo half-duplex, emulacion de protocolo OneWire).
+- USART3: Módulo ESP-01S.
+- TIM1_CH1: Salida PWM para control del MOSFET (PA8).
+- TIM3_CH1: Salida PWM para buzzer (PA6).
+- RTC: Reloj de tiempo real.
+- GPIO: Botones (PC13, PA10, PB5, PB4), LEDs (PA5, PB10).
+- DMA: Acceso directo a memoria para reducir uso de CPU.
+
+En la figura 3.8 se muestra una imagen de los pines utilizados del
+microcontrolador. Luego en la tabla 3.1 se muestran los nombres asignados y las
+funciones para los que fueron utilizados.
+
+<div align="center">
+    <img src="img/stm32_pinout_view.png" style="padding: 10px" align=center width="500px">
+    <p><em>Figura 3.8: Pines utilizados de microcontrolador STM32F103RBT6.</em></p>
+</div>
+
+| Pin  | Etiqueta  | Función                        |
+| :--- | :-------- | :----------------------------- |
+| PA5  | LD2       | LED verde de la placa NUCLEO   |
+| PA6  | TIM3_CH1  | Salida PWM buzzer              |
+| PA8  | TIM1_CH1  | Salida PWM control temperatura |
+| PA9  | USART1_TX | DS18B20                        |
+| PC13 | BTN_A     | Botón A                        |
+| PA10 | BTN_B     | Botón B                        |
+| PB4  | BTN_D     | Botón D                        |
+| PB5  | BTN_C     | Botón C                        |
+| PB6  | I2C1_SCL  | Linea SCL bus I2C              |
+| PB7  | I2C1_SDA  | Linea SDA bus I2C              |
+| PB10 | LD3       | LED azul                       |
+| PC10 | USART3_TX | ESP-01S Linea TX               |
+| PC11 | USART3_RX | ESP-01S Linea RX               |
+
+<p align="center"><em>Tabla 3.1: Pinout del sistema</em></p>
+
+La placa NUCLEO se puede alimentar a través del USB (ST-Link) o por el pin de 5
+V externo. Los 5 V externos provienen de un regulador LM7505 ubicado sobre la
+placa perforada principal, este regulador recibe alimentación de una fuente de
+12 V externa. Para seleccionar que tipo de alimentación se desea utilizar se
+debe ajustar la posición del jumper JP5 en la placa NUCLEO, tal como lo indica
+la Tabla 8 del manual de usuario de la placa.[1]
+
+### 3.1.2. Sensor DS18B20
+
+El sensor DS18B20 se conectó al USART1 de la placa NUCLEO en modo half-duplex,
+es decir, utilizando una sola linea del par TX/RX. De esta forma se puede
+aprovechar el periférico emulando el protocolo OneWire. Este protocolo requiere
+temporización y niveles específicos, tal como se indica en el manual del sensor
+DS18B20.[2]
+
+Para emular el protocolo OneWire utilizando el periférico USART1, se cambian
+dinámicamente la tasa en baudios (o baud rate, en inglés) de transmisión. Para
+enviar una señal de reset se requiere de como mínimo 480 μs, por lo que enviando
+un byte a 9600 baudios de tasa de transmisión, con los respectivos bits de
+inicio y fin (resultando en total 10 bits) se tiene un período de
+aproximadamente 1.04 ms:
+
+$$T_{bit} = \frac{1}{9600\ baudios} \approx 104\ \mu s \Rightarrow \boxed{T_{byte}
+\approx 1.04\ ms}$$
+
+En cambio tomando 96000 baudios de tasa de transmisión, se tiene un
+período de bit, y de byte, de aproximadamente $10.4\ \mu s$ y $104\ \mu s$,
+respectivamente.
+
+$$T_{bit} = \frac{1}{96000\ baudios} \approx 10.4\ \mu s \Rightarrow
+\boxed{T_{byte} \approx 104\ \mu s}$$
+
+Teniendo estas dos tasas de transmisión, enviando el byte hexadecimal 0xF0
+(equivalente al numero binario 11110000) a 9600 baudios, se logra un pulso lo
+suficientemente parecido al reset de OneWire.
+
+Para transmitir datos, se utiliza la tasa más rápida, de 96000 baudios, y se
+envía cada bit de datos en un byte, es decir si se desea enviar un byte de
+datos, se deben enviar 8 bytes en la transmisión, un byte por cada dato. Se
+transmite el byte 0xFF para representar un bit lógico 1 y el byte 0x00 para
+representar un bit lógico 0. Al transmitir el byte 0xFF la línea permanece en
+nivel alto durante casi toda la trama, salvo el bit de inicio que es siempre
+bajo, esto genera un pulso bajo breve, compatible con la escritura de un bit
+lógico 1 del protocolo OneWire. Por otro lado, al transmitir el byte 0x00, la
+línea permanece en nivel bajo durante casi toda la trama, a excepción del bit de
+fin que es siempre 1, esto produce un pulso bajo de mayor duración, similar al
+requerido para transmitir un bit lógico 0.
+
+Por lo tanto, se utiliza la tasa de 9600 baudios únicamente para aproximar el
+pulso de reset, el cual posee una duración considerablemente mayor y es
+necesario para comenzar al comunicación con el sensor, y la tasa de 96000
+baudios para transmitir datos, ya que se generan intervalos temporales
+compatibles con los slots de tiempo definidos por el protocolo OneWire.
+
+Y aprovechando aún más el periférico USART1, se realizaron las operaciones de
+forma no bloqueante mediante acceso directo a memoria (DMA) de esta forma se
+reduce aun más el tiempo de bloqueo del ejecutor cíclico y de uso de la CPU.
+
+### 3.1.3. Módulo ESP-01S
+
+El módulo Wi-Fi ESP-01S se conecta con la placa NUCLEO a través del periférico
+USART3 a una tasa de transmisión de 115200 baudios.
+
+Dado que el modulo ESP-01S trabaja con Wi-Fi y requiere, en promedio, entre 150
+mA y 250 mA, con picos de corriente durante la transmisión Wi-Fi de hasta 430 mA
+a 3,3 V, y dado que la corriente maxima suministrada por el regulador de la
+placa NUCLEO es de 500 mA, se decidió utilizar un regulador externo de 3,3 V, el
+AIC1117-33PT, montado sobre la misma placa perforada principal, suministrada por
+los 12 V de la fuente de tensión externa.
+
+El desarrollo del firmware del modulo ESP-01S no esta dentro del alcance de la
+materia, pero se desarrolló de todas formas utilizando PlatformIO y el framework
+Arduino. El firmware se desarrolló para que cumpla las siguientes funciones:
+
+- Conexión a una red Wi-Fi fija.
+- Servidor HTTP en el puerto 80, sirviendo la página web estática desde el
+  sistema de archivos.
+- Servidor WebSocket en el puerto 81 para evitar refrescar la pagina web
+  constantemente para actualizar la información.
+- Sistema de archivos LittleFS.
+
+### 3.1.4. Display LCD 16x2 I2C
+
+El display LCD se conecta al bus I2C1 a través del adaptador paralelo a serie
+mencionado previamente. Si bien es posible conectar el display directamente a la
+placa NUCLEO sin adaptador, esto requiere del uso de más pines de la placa, es
+por esto que se optó por el uso del adaptador I2C. Además al utilizar el
+periférico I2C se puede utilizar DMA, reduciendo el tiempo de bloqueo del
+ejecutor cíclico y for consiguiente el uso de la CPU.
+
+### 3.1.5. Control de temperatura
+
+La etapa de control de temperatura consiste en un MOSFET IRFZ44N de canal N, el
+IRFZ44N, controlado por la señal PWM del TIM1_CH1 (PA8). Dado que el MOSFET es
+un dispositivo controlador por tensión, se observó que utilizando directamente
+la salida GPIO de la placa NUCLEO para controlar el MOSFET esté elevaba su
+temperatura considerablemente. Para solucionar esto se implementó un driver para
+controlar el MOSFET con una señal PWM de mayor tensión, en particular 12 V de la
+fuente externa. El driver utiliza un transistor TBJ 2N3904 actuando como llave
+controlador por la señal PWM de la placa NUCLEO, como se puede ver en el
+esquemático de la figura 3.9.
+
+<div align="center">
+    <img src="img/npn_inv_mosfet_driver.png" style="padding: 10px" align=center width="700px">
+    <p><em>Figura 3.9: Driver para el MOSFET que controla el calefactor.</em></p>
+</div>
+
+Este circuito es simple de implementar, pero invierte la señal PWM como se
+observa en la figura 3.10. Existe la posibilidad de agregar una etapa adicional
+inversora, implementado con otro transistor, pero en este caso no es necesario
+ya que se puede invertir la polaridad de la señal PWM desde el firmware de la
+placa NUCLEO.
+
+<div align="center">
+    <img src="img/npn_inv_mosfet_driver_plot.png" style="padding: 10px" align=center width="700px">
+    <p><em>Figura 3.10: Simulación del driver para el MOSFET que controla el calefactor.</em></p>
+</div>
+
+El calefactor del derretidor de miel se modeló como una carga resistiva pura,
+por lo que se utilizó un resistor de alta potencia de 12 V y 40 W para las
+pruebas de laboratorio.
+
+### 3.1.6. Luces indicadoras
+
+Para la luz indicadora, se utilizó un diodo emisor de luz (Light Emitting Diode,
+LED) de 5 mm de color verde. Este tipo de LEDs requieren de aproximadamente 20
+mA. Se observó que para 20 mA de corriente la luz emitida por el LED es tenue y
+que se obtienen mejores resultados con entre 30 mA y 45 mA de corriente.
+
+Para suministrar esta corriente y evitar sobrecargar el regulador de la placa
+NUCLEO, se implementó un circuito driver para alimentar al LED con la fuente de
+tensión de 12 V externa, controlando solo el estado del led mediante una pequeña
+corriente subintrada por la placa NUCLEO. El circuito resultante se muestra en
+la figura 3.11.
+
+<div align="center">
+    <img src="img/npn_led_driver.png" style="padding: 10px" align=center width="550px">
+    <p><em>Figura 3.11: Driver para las luces indicadoras.</em></p>
+</div>
+
+El resistor RC se elige de manera tal que la corriente sobre el LED sea de
+aproximadamente 45 mA, como se muestra a continuación.
+
+$$RC=\frac{12\text{ V} - 2.2\text{ V}}{45\text{ mA}} \approx 220\ \Omega$$
+
+<!-- El LED 2 LEDs (PA5, PB10) con modos ON, OFF, BLINK (500 ms) y PULSE (250 ms). -->
+
+### 3.1.7. Buzzer
+
+Para el buzzer o parlante se utilizó un zumbador piezoeléctrico pasivo de 12 V.
+El termino pasivo implica que se debe suministrar una señal PWM para que este
+emita un sonido. Teniendo esto en cuenta, y dado que se requieren de 12 V de
+tensión, se replicó el circuito de la figura 3.6 utilizado para controlar el
+MOSFET.
+
+<!-- (TIM3_CH1, 1 kHz) con eventos de beep corto (35 ms) y largo (500 ms). -->
+
+### 3.1.8. Botones
+
+Para los botones, se utilizaron 3 tact switch en la placa perforada principal
+con los resistores de pull-up internos de la placa NUCLEO.
+
+<!--
+4 pulsadores tact switch con pull-up externo. Antirebote por máquina
+de estados en firmware (50 ms de filtro).
+-->
+
+## 3.2. Firmware del sistema
+
+El firmware de la placa NUCLEO se compone de una aplicación que implementa un
+ejecutor cíclico con 8 tareas asociadas. Estas tareas se organizan de acuerdo al
+rol que cumplen: actuador, sensor o lógica del sistema.
+
+En la figura 3.12 se muestra un diagrama de bloques de las tareas del sistema y
+como interactúan entre ellas. La interacción puede ser a través de eventos o
+mediante la estructura global compartida `shared_data`.
+
+<div align="center">
+    <img src="img/diagrama_de_bloques_firmware_nucleo_TA134.png" style="padding: 10px" align=center width="650px">
+    <p><em>Figura 3.12: Diagrama de bloques firmware del sistema.</em></p>
+</div>
+
+<!--
+Esta aplicacion tiene como principal caracteristica ser no bloqueante.
+-->
+
+### 3.2.1. Ejecutor cíclico
+
+El ejecutor cíclico se implementa en el archivo `app.c`. Este ejecutor ciclico
+posee un tick de 1 ms generado por el timer SysTick propio del microcontrolador.
+En cada tick, la función `app_update()` recorre secuencialmente las ocho tareas
+asociadas, ejecutando tantas iteraciones como ticks estén pendientes. En
+operación normal, se cumple que la función `app_update()` no se bloquea por más
+de 1 ms, por lo que en cada tick no debería haber más de una operación pendiente
+en cada tarea.
+
+En particular del archivo `app.c` se destaca a continuación la lista de tareas
+que se asocian a la aplicación. Las tareas son: `task_system`, `task_buttons`,
+`task_buzzer`, `task_leds`, `task_menu`, `task_temp_ctrl`, `task_temp_sensor` y
+`task_remote`. Cada tarea tiene asociada una función de inicialización y una
+función de actualización, denotadas por el sufijo `_init` y `_update`,
+respectivamente.
+
+```c
+const task_cfg_t task_cfg_list[] = {
+    {task_system_init,      task_system_update,      &shared_data},
+    {task_buttons_init,     task_buttons_update,     NULL},
+    {task_buzzer_init,      task_buzzer_update,      NULL},
+    {task_leds_init,        task_leds_update,        NULL},
+    {task_menu_init,        task_menu_update,        &shared_data},
+    {task_temp_ctrl_init,   task_temp_ctrl_update,   &shared_data},
+    {task_temp_sensor_init, task_temp_sensor_update, &shared_data},
+    {task_remote_init,      task_remote_update,      &shared_data},
+};
+```
+
+La función de actualización de cada tarea sigue el mismo patrón: consulta su
+contador de ticks (incrementado en cada tick de `SysTick`) y si hay ticks
+pendientes, se decrementa el contadora en una unidad y se ejecuta su máquina de
+estados o lógica correspondiente, si no hay ticks pendientes no hace nada.
+
+Las tareas se comunican entre sí mediante dos mecanismos: eventos, a través de
+buffers circulares con una interfaz dedicada para cada tarea; o mediante la
+estructura global compartida `shared_data`. Que mecanismo utiliza cada tarea se
+puede observar en la figura 3.12, en la cual las flechas con lineas punteadas
+indican el mecanismo de acceso a la variable compartida y las flechas con lineas
+continuas indican el mecanismo de eventos.
+
+### 3.2.2. Tarea central
+
+La tarea central, `task_system`, gestiona la lógica del sistema y la máquina de
+estados principal. En la figura 3.13 se muestra un diagrama de estados de la
+maquina que implementa la tarea. Este diagrama es una versión simplificada de la
+lógica que se implementa en la tarea, ya que no se muestran los eventos que se
+envían a otras tareas, pero sirve para mostrar como se cambia la funcionalidad
+de los botones de acuerdo al estado.
+
+<div align="center">
+    <img src="img/system_statechart.png" style="padding: 10px" align=center width="800px">
+    <p><em>Figura 3.13: Diagrama de estados de tarea central. Version simplificada.</em></p>
+</div>
+
+Se tienen los siguientes estados:
+
+- `ST_SYS_IDLE`: Estado de reposo, se muestran varios parámetros en el display,
+  tal como el estado del calefactor, la temperatura actual, la temperatura
+  deseada y si hay un timer programado. Este estado también contempla que el
+  calefactor esté activo, lo que puede resultar confuso. Un mejor nombre quizás
+  hubiese sido `ST_SYS_MAIN`.
+- `ST_SYS_CLOCK_SET`: Configuración de hora del reloj de tiempo real.
+- `ST_SYS_TEMP_SET`: Ajuste de temperatura deseada.
+- `ST_SYS_TIMER_SET_STATUS`: Habilitación/deshabilitación del timer.
+- `ST_SYS_TIMER_SET_START`: Configuración de hora de inicio del timer.
+- `ST_SYS_TIMER_SET_END`: Configuración de hora de fin del timer.
+
+Además en esta tarea se gestiona:
+
+- Persistencia en flash: Utiliza una página completa (1024 bytes) en la
+  dirección `0x0801FC00` como emulación EEPROM. Los valores se escriben en
+  direcciones contiguas para reducir el desgaste, borrando la página solo cuando
+  está llena (512 escrituras).
+- Temporizador programable: Si el timer está activo, en cada segundo se compara
+  la hora actual del RTC con los límites configurados. Si `end_hour > start_hour`
+  (mismo día) o `end_hour < start_hour` (cruce de medianoche) se activa o
+  desactiva el calefactor automáticamente.
+
+### 3.2.3. Tarea para botones
+
+En la tarea `task_buttons` se implementa una máquina de estados de anti-rebote
+para cada uno de los botones. En la figura 3.14 se muestra un diagrama de
+estados de la maquina.
+
+<div align="center">
+    <img src="img/button_statechart.png" style="padding: 10px" align=center width="700px">
+    <p><em>Figura 3.14: Diagrama de estados para botones.</em></p>
+</div>
+
+Al detectar una pulsación válida (flanco ascendente o descendente confirmado) se
+envía un evento a `task_system` y, aunque no se muestra en la figura 3.14, se
+envían eventos directamente a `task_buzzer` para que inmediatamente luego de
+presionar el botón se active el buzzer, sin interactuar con la tarea central
+`task_system`. Esto se observa también en el diagrama de bloques del firmware de
+la figura 3.12.
+
+### 3.2.4. Tarea para el buzzer
+
+En la tarea `task_buzzer` se controla el buzzer generando una señal PWM de 1
+kHz. Esta tarea recibe eventos para generar beeps cortos (de 35 ms para
+pulsación de botón) o largos (de 500 ms para activación/desactivación del
+calefactor).
+
+Para controlar el buzzer se implementa una maquina de estados con los estados
+`ST_BUZ_XX_OFF` y `ST_BUZ_XX_ON` con auto-apagado por timeout, tal como se
+observa en la figura 3.15.
+
+<div align="center">
+    <img src="img/buzzer_statechart.png" style="padding: 10px" align=center width="600px">
+    <p><em>Figura 3.15: Diagrama de estados de buzzer.</em></p>
+</div>
+
+### 3.2.5. Tarea para luces indicadoras
+
+En la tarea `task_leds` se controlan las luces indicadoras o LEDs (LD2, LD3)
+permitiendo los modos: encendido, apagado, intermitente y un único pulso. Para
+esto se implementa la maquina de estados cuyo diagrama de estados se muestra en
+la figura 3.16, con los siguientes estados:
+
+- `ST_LED_XX_OFF`: LED apagado.
+- `ST_LED_XX_ON`: LED encendido fijo.
+- `ST_LED_XX_BLINK_ON` / `ST_LED_XX_BLINK_OFF`: Parpadeo cada 500 ms.
+- `ST_LED_XX_PULSE`: Encendido por 250 ms y luego apagado.
+
+<div align="center">
+    <img src="img/led_statechart.png" style="padding: 10px" align=center width="800px">
+    <p><em>Figura 3.16: Diagrama de estados de luces indicadoras.</em></p>
+</div>
+
+El modo intermitente se utiliza para cuando el calefactor está activo, indicando
+visualmente el estado de operación. Cuando el calefactor esta inactivo las luces
+permaneces apagadas.
+
+### 3.2.6. Tarea para el menú
+
+La tarea `task_menu`, gestiona la actualización del display LCD 16x2 según el
+estado actual del sistema. Se implementa también una maquina de estados similar
+a la implementada para el sistema, con estados similares, de manera tal que cada
+estado del sistema tiene su correspondiente estado del menú y cada estado del
+menú define su contenido del display. En la figura 3.17 se muestran diferentes
+contenidos del display para diferentes estados.
+
+Por ejemplo en estado reposo (estado del sistema `ST_SYS_IDLE`) con el
+calefactor apagado y un timer programado, como se muestra en la figura 3.17(b),
+en la primer linea se muestra el estado del calefactor ("Off"), y en la segunda
+linea el estado del timer ("[P]" si activo, "   ", si no), la temperatura
+actual, la temperatura deseada (entre paréntesis) y la unidad de la temperatura.
+
+<div align="center">
+
+<table>
+
+<tr>
+<td align="center">
+<img src="img/lcd/lcd_off.png" width="350px"><br>
+<em>(a) Calefactor apagado.</em>
+</td>
+
+<td align="center">
+<img src="img/lcd/lcd_off_prog.png" width="350px"><br>
+<em>(b) Calefactor apagado con timer programado.</em>
+</td>
+</tr>
+
+<tr>
+<td align="center">
+<img src="img/lcd/lcd_on.png" width="350px"><br>
+<em>(c) Calefactor encendido con timer programado.</em>
+</td>
+
+<td align="center">
+<img src="img/lcd/lcd_clock_set.png" width="350px"><br>
+<em>(d) Configuración de reloj.</em>
+</td>
+</tr>
+
+<tr>
+<td align="center">
+<img src="img/lcd/lcd_temp_set.png" width="350px"><br>
+<em>(e) Configuracion de temperatura deseada.</em>
+</td>
+
+<td align="center">
+<img src="img/lcd/lcd_timer_set.png" width="350px"><br>
+<em>(f) Configuracion de encendido/apagado de timer.</em>
+</td>
+</tr>
+
+<tr>
+<td align="center">
+<img src="img/lcd/lcd_timer_set_start.png" width="350px"><br>
+<em>(g) Configuracion de inicio de timer.</em>
+</td>
+
+<td align="center">
+<img src="img/lcd/lcd_timer_set_end.png" width="350px"><br>
+<em>(h) Configuracion de fin de timer.</em>
+</td>
+</tr>
+
+</table>
+
+<em>Figura 3.17: Interfaces mostradas en el display LCD 16x2.</em>
+</div>
+
+La comunicación con el LCD es asíncrona: las operaciones se encolan y se
+procesan en cada tick de la tarea mediante `display_i2c_async_tick()`.
+
+### 3.2.7. Tarea para control de temperatura
+
+En la tarea `task_temp_ctrl` se implementa un controlador proporcional, integral
+y derivativo (PID) para regular la temperatura. Se toma como señal de referencia
+el punto de temperatura deseada, y como señal de error la diferencia entre la
+señal de referencia y la temperatura actual leída por el sensor.
+
+En particular, la sección de código que implementa el controlador PID se muestra
+a continuación.
+
+```c
+p_pid->error = temp_set_point - temp_sensor;
+p_pid->pid_p = error * Kp;
+p_pid->pid_i = (pid_i + (error * dt)) * Ki;
+p_pid->pid_d = ((error - prev_error) / dt) * Kd;
+output = clampf(pid_p + pid_i + pid_d, 0.0f, 100.0f);
+```
+
+Siendo las constantes proporcionales, integrales y derivativas, `Kp`, `Ki` y
+`Kd`, respectivamente, las que se muestran a continuación:
+
+- Kp = 40,0
+- Ki = 0,75
+- Kd = 0,01
+- dt = 250 ms
+
+La salida del controladora (0 % a 100 %) se asigna directamente al ciclo de
+trabajo del PWM que controla el calefactor a través de la variable compartida
+por las tareas `shared_data`. Si el calefactor está deshabilitado, se fuerza el
+ciclo de trabajo a 0 %.
+
+### 3.2.8. Tarea para sensor de temperatura
+
+La tarea `task_temp_sensor` gestiona la lectura asíncrona del sensor DS18B20.
+Cada 1 segundo inicia una secuencia de comandos:
+
+1. Reset (cambio a 9600 baudios + pulso de presencia).
+2. `SKIP_ROM` + `CONVERT_T` (inicia conversión).
+3. Espera 100 ms (tiempo de conversión aproximado para resolución de 9 bits).
+4. Reset + `SKIP_ROM` + `READ_SCRATCHPAD`.
+5. Lectura de bytes LSB y MSB.
+6. Conversion de los bytes leídos a temperatura real y almacenamiento en
+   estructura compartida `shared_data`.
+
+Todas las operaciones se encolan y ejecutan mediante DMA, con callbacks que
+indican la finalización de transmisión/recepción.
+
+### 3.2.9. Tarea para módulo remoto
+
+En la tarea `task_remote` se administra la comunicación con el módulo ESP-01S a
+través de USART3. Se implementa un protocolo propio basado en comandos de un
+byte (caracteres 'A' a 'F') con parámetros adicionales según el comando.
+
+Comandos recibidos desde el ESP-01S:
+
+| Comando | Función                                    | Respuesta                                                                               |
+| :------ | :----------------------------------------- | :-------------------------------------------------------------------------------------- |
+| 'A'     | Solicitar estado completo                  | 'A' + main_output + system_time + timer_status + timer_params(4B) + temp_now + temp_set |
+| 'B'     | Toggle calefactor                          | 'B'                                                                                     |
+| 'C'     | Toggle timer                               | 'C'                                                                                     |
+| 'D'     | Set timer (start_h, start_m, end_h, end_m) | 'D'                                                                                     |
+| 'E'     | Solicitar temperatura                      | 'E' + temp_now + temp_set                                                               |
+| 'F'     | Set temperatura deseada                    | 'F' + temp_set_point                                                                    |
+
+Comandos enviados al ESP-01S periódicamente cada 1 segundo:
+
+| Comando | Función                | Datos                     |
+| :------ | :--------------------- | :------------------------ |
+| 'E'     | Actualizar temperatura | 'E' + temp_now + temp_set |
+
+La recepción se realiza por interrupción (`HAL_UART_RxCpltCallback`) con
+encolado en un buffer circular. Cada vez que se recibe un byte se encola
+automáticamente, para luego ser procesado.
+
+La transmisión utiliza DMA para no bloquear el ejecutor cíclico, para esto se
+implementa un buffer circular similar al de recepción. En este buffer circular
+se encolan los bytes que se desean enviar y mientras la cola no esté vacía, y
+siempre que DMA haya terminado, se enviando los bytes.
+
+<div align="center">
+<table>
+
+<tr>
+<td align="center">
+<img src="img/pagina_web_dark.png" width="400px"><br>
+<em>(a) Esquema de colores oscuro.</em>
+</td>
+
+<td align="center">
+<img src="img/pagina_web_light.png" width="400px"><br>
+<em>(b) Esquema de colores claro.</em>
+</td>
+
+</tr>
+
+</table>
+
+<p><em>Figura 3.18: Pagina web del modulo remoto. Esquema de colores adaptado al
+dispositivo.</em></p>
+
+</div>
+
+### 3.2.10. Medición de tiempo de ejecución
+
+En el ejecutor cíclico se mide el tiempo de ejecución de cada tarea y del ciclo
+completo mediante el contador de ciclos del DWT. Por cada tarea se mide:
+
+- `WCET`: Peor tiempo de ejecución desde que se inicializa el sistema.
+- `WCET_interval`: Peor tiempo de ejecución en un intervalo de 10 segundos.
+- `runtime_us`: Tiempo total del ciclo actual.
+- `runtime_us_mean`: Promedio sobre 1000 muestras (1 segundo).
+
+Los resultados se pueden consultar mediante la consola de semi-hosting, lo que
+permite caracterizar temporalmente el sistema sin afectar los tiempos de
+ejecución.
+
+# 4. Ensayos y resultados
+
+En esta sección se evaluará el sistema desarrollado, tanto el firmware como el
+hardware, en aspectos como consumo de corriente, memoria, factor de uso de CPU,
+entres otras cosas. De esta forma, se verificará si las decisiones tomadas para
+el diseño fueron correctas y si se cumplen los requisitos. En caso de que se
+cumplan los requisitos, se comparará el sistema desarrollado con proyectos
+similares.
+
+## 4.1. Pruebas funcionales del hardware
+
+| Ensayo                          | Resultado                            | Estado   |
+| :------------------------------ | :----------------------------------- | :------: |
+| Alimentación y regulación 3,3 V | Tensión estable en placa NUCLEO      | Correcto |
+| Comunicación I2C con LCD        | Inicialización y escritura correctas | Correcto |
+| Lectura DS18B20                 | Detección y lectura de temperatura   | Correcto |
+| Comunicación UART con ESP-01S   | Intercambio de datos bidireccional   | Correcto |
+| Control PWM sobre MOSFET        | Variación de ciclo de trabajo        | Correcto |
+| Buzzer PWM                      | Generación de tono a 1 kHz           | Correcto |
+| LEDs                            | Estados ON/OFF/BLINK correctos       | Correcto |
+| Botones con antirebote          | Eventos limpios sin rebotes          | Correcto |
+
+<p align="center"><em>Tabla 4.1: Pruebas funcionales de hardware.</em></p>
+
+## 4.2. Pruebas funcionales del firmware
+
+| Ensayo                        | Resultado                                   | Estado   |
+| :---------------------------- | :------------------------------------------ | :------: |
+| Ejecutor cíclico (1 ms tick)  | Tiempo de ejecucion menor a 1 ms            | Correcto |
+| Máquina de estados de sistema | Transiciones correctas en todos los estados | Correcto |
+| Algoritmo PID                 | Cálculo con valores esperados               | Correcto |
+| Lectura DS18B20 asíncrona     | Actualización cada 1 segundo sin bloqueo    | Correcto |
+| Comunicación UART remota      | Envío/recepción de comandos A-F             | Correcto |
+| Persistencia en flash         | Lectura/escritura en página dedicada        | Correcto |
+| RTC con respaldo              | Mantenimiento de hora sin alimentación      | Correcto |
+| Antirebote de botones         | Filtro de 50 ms sin falsos positivos        | Correcto |
+
+<p align="center"><em>Tabla 4.2: Pruebas funcionales de firmware.</em></p>
+
+## 4.3. Pruebas de integración
+
+Se validó la interacción completa del sistema:
+
+1. Ciclo completo local: Botón enciende calefactor, PID regula temperatura,
+   display muestra valores, LEDs parpadean, buzzer suena.
+2. Ciclo completo remoto: Web solicita status, ESP-01S envía comando 'A', NUCLEO
+   responde con estado, WebSocket actualiza interfaz web.
+3. Temporizador: Configuración de timer, cambio automático de estado al alcanzar
+   la hora programada.
+4. Persistencia de parámetros: Configuración de la temperatura deseada, corte de
+   energía, restauración correcta al encender.
+
+En el siguiente [video](https://www.youtube.com/watch?v=ZpNlU7789YU) se presenta
+el sistema y se muestran las pruebas funcionales. En primer lugar, se realiza la
+configuración de parámetros de manera local y remota. Luego, se verifica la
+persistencia de parámetros al perder la alimentación. Por último, se hace una
+prueba del sistema calentando agua desde 19 °C a 30 °C en un vaso.
+
+Como parte de las pruebas de integración, se utilizó un analizador lógico
+(Saleae) para verificar la comunicación mediante el protocolo UART entre la
+placa NUCLEO y el módulo ESP-01S, así como también la comunicación mediante el
+protocolo OneWire entre la placa NUCLEO y el sensor de temperatura DS18B20.
+
+## 4.4. Cumplimiento de requisitos
+
+| ID   | Requisito                  | Estado          |
+| :--- | :------------------------- | :-------------- |
+| 1    | Control por botones        | Implementado    |
+| 2    | Control remoto             | Implementado    |
+| 3    | Programación de ciclos     | Implementado    |
+| 4    | Curvas de temperatura      | No implementado |
+| 5    | Display                    | Implementado    |
+| 6    | LEDs indicadores           | Implementado    |
+| 7    | Alarma sonora              | Implementado    |
+| 8    | Monitoreo en tiempo real   | Implementado    |
+| 9    | Conectividad Wi-Fi         | Implementado    |
+| 10   | Registro de temperatura    | No implementado |
+| 11   | Límite de temperatura      | Implementado    |
+| 12   | Persistencia de parámetros | Implementado    |
+| 13   | Reloj de tiempo real       | Implementado    |
+
+<p align="center"><em>Tabla 4.6: Estado de cumplimiento de requisitos propuestos
+inicialmente.</em></p>
+
+Notas sobre requisitos no implementados:
+
+- Requisito 4 (curvas de temperatura): No implementado por restricciones de
+  tiempo. Requeriría una interfaz de usuario más compleja y un algoritmo PID más
+  sofisticado que el actual.
+- Requisito 10 (registro de temperatura): No implementado por restricciones de
+  tiempo.
+
+## 4.5. Comparación con trabajos similares
+
+En la tabla 4.7 se presentan las principales diferencias entre el sistema
+desarrollado y los productos y proyectos académicos mencionados previamente.
+
+| Característica         | Derretidor comercial  | Horno gastronomía | Horno SMD   | Este trabajo |
+| :--------------------- | :-------------------- | :---------------- | :---------- | :----------- |
+| Control de temperatura | On/off con termocupla | PID digital       | PID digital | PID digital  |
+| Control remoto         | No                    | No                | No          | Si           |
+| Display                | No                    | Sí                | Sí          | Si           |
+| Programación de ciclos | No                    | No                | No          | Sí           |
+| Persistencia           | No                    | No                | No          | Sí           |
+| RTC                    | No                    | No                | No          | Sí           |
+
+<p align="center"><em>Tabla 4.7: Comparación con sistemas similares.</em></p>
+
+## 4.6. Documentación del desarrollo realizado
+
+En esta sección se presentan los resultados de las mediciones de consumo de
+corriente, tiempos de ejecución, factor de uso de CPU y uso de memoria.
+
+### 4.6.1. Consumo de corriente
+
+Las mediciones de consumo se realizaron en primer lugar sobre el conjunto NUCLEO
+y periféricos, luego sobre el calefactor.
+
+| Componente                  | Tension [V]  | Corriente [mA]  |
+| :-------------------------- | -----------: | --------------: |
+| NUCLEO                      | 5            | 31              |
+| ESP-01S (conectado, sin TX) | 3,3          | 68              |
+| ESP-01S (transmitiendo)     | 3,3          | 210             |
+| Buzzer                      | 12           | 34              |
+| Luces indicadoras           | 12           | 52              |
+| Calefactor                  | 12           | 2600            |
+
+<p align="center"><em>Tabla 4.3: Consumo de corriente del sistema.</em></p>
+
+La placa NUCLEO consume menos de 100 mA a 5 V en operación normal, lo cual es
+menor al límite de 500 mA de un puerto USB estándar.
+
+### 4.6.2. Tiempo de ejecución
+
+Se midió el tiempo de ejecución de cada tarea utilizando el DWT. Mientras se
+realizaban las mediciones se presionaron multiples botones, navegando por
+diferentes interfaces, de manera de obtener el peor tiempo de ejecucion posible.
+En la tabla 4.4 se muestran los tiempos de ejecucion obtenidos de cada tarea y
+el total para el ejecutor ciclico. Denotando $M_{i}$ al peor tiempo de ejecucion
+en un intervalo de 10 segundos y $C_{i}$ al peor tiempo de ejecución total,
+desde que se inicializa el sistema.
+
+| Tarea              | $M_{i}\ [\mu s]$ | $C_{i}\ [\mu s]$ |
+| :----------------- | ---------------: | -------------: |
+| `task_system`      | 5                | 5              |
+| `task_buttons`     | 13               | 13             |
+| `task_buzzer`      | 4                | 219            |
+| `task_leds`        | 6                | 6              |
+| `task_menu`        | 6                | 140            |
+| `task_temp_ctrl`   | 4                | 5              |
+| `task_temp_sensor` | 33               | 33             |
+| `task_remote`      | 4                | 94             |
+| **Total**          | **75**           | **515**        |
+
+<p align="center"><em>Tabla 4.4: Tiempo de ejecución aproximado por tarea.</em></p>
+
+<!--
+En la figura 4.1 se muestra la evolución del WCET del sistema a lo largo del tiempo.
+
+<div align="center">
+    <img src="img/g_app_WCET_without_DMA.png" width="80%">
+    <p><em>Figura 4.1: WCET del sistema sin contar transfers DMA</em></p>
+</div>
+
+<div align="center">
+    <img src="img/g_app_WCET_DMA.png" width="80%">
+    <p><em>Figura 4.2: WCET del sistema con transfers DMA</em></p>
+</div>
+
+<div align="center">
+    <img src="img/g_app_runtime_us_mean.png" width="80%">
+    <p><em>Figura 4.3: Tiempo de ejecución promedio (1000 muestras)</em></p>
+</div>
+-->
+
+### 4.6.3. Factor de uso de CPU
+
+Utilizando la expresión clásica de sistemas en tiempo real, se define el factor
+de uso $U$ como la sumatoria del peor tiempo de ejecucion de cada tarea $C_{i}$
+dividido el tiempo de un tick del ejecutor ciclico $T$ (1 ms).
+
+$$U = \sum_{i=1}^{n} \frac{C_i}{T} = \frac{515\,\mu\text{s}}{1000\,\mu\text{s}} = 51,5\\%$$
+
+El factor de uso resultante de 51,5\% se mide en el peor caso posible.
+Tomando el tiempo de ejecucion en un intervalo de 10 segunddos se tine un factor
+de uso de CPU aproximado de 7,5\%.
+
+<!--
+| Tarea            | $C_i$ [us] | $T$ [us] | $C_i/T$ |
+| :--------------- | ---------: | -------: | ------: |
+| Todas (conjunto) |        515 |     1000 |   0.368 |
+
+<p align="center"><em>Tabla 4.5: Factor de uso de CPU</em></p>
+
+En ambos casos se tiene un amplio margen para futuras extensiones, por lo que se
+confirma que el microcontrolador STM32F103RBT6 es adecuado para este proyecto.
+-->
+
+### 4.6.4. Uso de memorias
+
+El uso de memoria del firmare se reporta al momento de realizar la compilación.
+La salida del programa se muestra a continuación.
+
+```
+   text   data    bss     dec     hex   filename
+  64648    276   5616   70540   1138c   tdse-tf_3-02.elf
+```
+
+Por lo tanto se tiene:
+
+- Flash: 64648 bytes + 276 bytes = 64924 bytes (~50,7 % de 128 KB).
+- RAM: 5616 bytes + 276 bytes = 5892 bytes (~29,4 % de 20 KB).
+
+# 5. Conclusiones
+
+En esta sección se presentan las conclusiones generales del proyecto de acuerdo
+con las etapas de desarrollo de firmware y hardware, y los resultados obtenidos.
+
+Se condensan los hallazgos más significativos del Producto Mínimo Viable (MVP) y
+se determina en qué medida el trabajo implementado responde al problema de
+ingeniería inicialmente planteado, abriendo camino para las líneas de trabajo
+futuro.
+
+## 5.1. Resultados obtenidos
+
+Los principales aportes del trabajo realizado son:
+
+1. Diseño completo de un sistema embebido funcional para control de temperatura
+   con conectividad Wi-Fi y múltiples interfaces de usuario.
+2. Implementación de un ejecutor cíclico con ocho tareas, máquinas de estado y
+   medición de WCET, demostrando la factibilidad temporal del diseño (U < 52 %
+   en el peor caso).
+3. Comunicación remota funcional mediante ESP-01S con WebSocket, permitiendo
+   control y monitoreo desde cualquier dispositivo con navegador web.
+4. Control PID de temperatura implementado con éxito, con parámetros ajustables
+   y salida PWM sobre MOSFET.
+5. Persistencia de configuración en flash con manejo de desgaste por escritura
+   en direcciones contiguas.
+
+El sistema cumple 11 de los 13 requisitos inicialmente planteados, con los dos
+restantes identificados como mejoras para versiones futuras.
+
+## 5.2. Próximos pasos
+
+- Implementar perfiles de temperatura programables (curvas multi-paso).
+- Agregar registro histórico de temperatura en memoria externa.
+- Migrar a una placa PCB dedicada en lugar de NUCLEO + protoboard.
+- Agregar modos de calibración para el sensor DS18B20.
+- Unificar las implementaciones de colas en un módulo genérico.
+- Implementar exclusión mutua entre interfaz local y remota, es decir, que no se
+  puedan utilizar al mismo tiempo.
+- Permitir un único usuario por vez en la interfaz remota.
+- Agregar modo de bajo consumo (Sleep/Stop) para ahorro energético.
+## 6. Referencias
+
+1. STMicroelectronics, "UM1724 User manual - STM32 Nucleo-64 boards (MB1136)"
+    Rev. 17, 2025. [En linea]. Disponible en:
+    [https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf](https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf)
+
+2. Analog Devices, "DS18B20 - Programmable Resolution 1-Wire Digital
+   Thermometer," Rev. 6, ago. 2019. [En linea]. Disponible en:
+   [https://www.analog.com/media/en/technical-documentation/data-sheets/ds18b20.pdf](https://www.analog.com/media/en/technical-documentation/data-sheets/ds18b20.pdf)
+
diff --git a/firmware-nucleo/App/Inc/logger.h b/firmware-nucleo/App/Inc/logger.h
@@ -19,7 +19,7 @@ extern "C" {
 
 #define LOGGER_CONFIG_ENABLE                    (1)
 #define LOGGER_CONFIG_MAXLEN                    (64)
-#define LOGGER_CONFIG_USE_SEMIHOSTING           (1)
+#define LOGGER_CONFIG_USE_SEMIHOSTING           (0)
 
 #if 1 == LOGGER_CONFIG_ENABLE
 #define LOGGER_LOG(...)\
diff --git a/firmware-nucleo/App/Src/task_menu.c b/firmware-nucleo/App/Src/task_menu.c
@@ -224,7 +224,7 @@ static void task_menu_statechart(void *parameters)
                 }
                 else
                 {
-                    sprintf(menu_row_str, "Idle"); 
+                    sprintf(menu_row_str, "Off");
                     display_i2c_async_write_string(menu_row_str);
                 }
 
diff --git a/img/3d_print_heater.png b/img/3d_print_heater.png
Binary files differ.
diff --git a/img/button_statechart.png b/img/button_statechart.png
Binary files differ.
diff --git a/img/buzzer_statechart.png b/img/buzzer_statechart.png
Binary files differ.
diff --git a/img/conexion_placas_perforadas_TA134.png b/img/conexion_placas_perforadas_TA134.png
Binary files differ.
diff --git a/img/diagrama_de_bloques_firmware_nucleo_TA134.png b/img/diagrama_de_bloques_firmware_nucleo_TA134.png
Binary files differ.
diff --git a/img/display_views.png b/img/display_views.png
Binary files differ.
diff --git a/img/ds18b20.png b/img/ds18b20.png
Binary files differ.
diff --git a/img/ds18b20_views.png b/img/ds18b20_views.png
Binary files differ.
diff --git a/img/ds18b20_wiring.png b/img/ds18b20_wiring.png
Binary files differ.
diff --git a/img/esp01s-pinout.jpg b/img/esp01s-pinout.jpg
Binary files differ.
diff --git a/img/esp01s.jpg b/img/esp01s.jpg
Binary files differ.
diff --git a/img/esp01s_views.png b/img/esp01s_views.png
Binary files differ.
diff --git a/img/idc.png b/img/idc.png
Binary files differ.
diff --git a/img/lcd/lcd_clock_set.png b/img/lcd/lcd_clock_set.png
Binary files differ.
diff --git a/img/lcd/lcd_off.png b/img/lcd/lcd_off.png
Binary files differ.
diff --git a/img/lcd/lcd_off_prog.png b/img/lcd/lcd_off_prog.png
Binary files differ.
diff --git a/img/lcd/lcd_on.png b/img/lcd/lcd_on.png
Binary files differ.
diff --git a/img/lcd/lcd_temp_set.png b/img/lcd/lcd_temp_set.png
Binary files differ.
diff --git a/img/lcd/lcd_timer_set.png b/img/lcd/lcd_timer_set.png
Binary files differ.
diff --git a/img/lcd/lcd_timer_set_end.png b/img/lcd/lcd_timer_set_end.png
Binary files differ.
diff --git a/img/lcd/lcd_timer_set_start.png b/img/lcd/lcd_timer_set_start.png
Binary files differ.
diff --git a/img/led_statechart.png b/img/led_statechart.png
Binary files differ.
diff --git a/img/main_perf_board.png b/img/main_perf_board.png
Binary files differ.
diff --git a/img/npn_inv_mosfet_driver.png b/img/npn_inv_mosfet_driver.png
Binary files differ.
diff --git a/img/npn_inv_mosfet_driver_plot.png b/img/npn_inv_mosfet_driver_plot.png
Binary files differ.
diff --git a/img/npn_led_driver.png b/img/npn_led_driver.png
Binary files differ.
diff --git a/img/nucleo_f103rb.png b/img/nucleo_f103rb.png
Binary files differ.
diff --git a/img/nucleo_f103rb_axo.png b/img/nucleo_f103rb_axo.png
Binary files differ.
diff --git a/img/nucleo_f103rb_views.png b/img/nucleo_f103rb_views.png
Binary files differ.
diff --git a/img/pagina_web_dark.png b/img/pagina_web_dark.png
Binary files differ.
diff --git a/img/pagina_web_light.png b/img/pagina_web_light.png
Binary files differ.
diff --git a/img/stm32_nucleo_expansion_board_cropped.png b/img/stm32_nucleo_expansion_board_cropped.png
Binary files differ.
diff --git a/img/stm32_pinout_view.png b/img/stm32_pinout_view.png
Binary files differ.
diff --git a/img/system_statechart.png b/img/system_statechart.png
Binary files differ.
diff --git a/img/temp_ctrl_views.png b/img/temp_ctrl_views.png
Binary files differ.
diff --git a/img/to220.png b/img/to220.png
Binary files differ.
diff --git a/img/vista_inf_placa_sin_modulos.jpg b/img/vista_inf_placa_sin_modulos.jpg
Binary files differ.
diff --git a/img/vista_sup_placa_con_modulos.jpg b/img/vista_sup_placa_con_modulos.jpg
Binary files differ.
diff --git a/img/vista_sup_placa_sin_modulos.jpg b/img/vista_sup_placa_sin_modulos.jpg
Binary files differ.