¿Alguna vez te ha sucedido que tu microcontrolador ejecuta las tareas como te lo esperas, pero de un cierto tiempo a otro deja de funcionar inexplicablemente?
En el siguiente post explicaré la importacia y uso de una de las herramientas para realizar debugging en C más importante que todo programador de sistemas embebidos deberia conocer. Desde mi punto de vista no hubo mejor forma de aprender que haciendo, es por ello que les contaré desde la perspectiva de un usuario “beginner” en el entorno de desarrollo C de STM32CubeIDE.
Hace algún tiempo atrás estaba en tareas de realizar un proyecto en donde requería de las funciones de almacenar grandes cantidades de datos recolectados por ADCs en memorias ROM, para este caso me decante por la tecnología flash que proveían las memorias SD que se podían integrar facilmente a través del middleware FatFS. Luego, para poder extraer los datos almacenados de la tarjeta SD se me ocurrió trabajar con TCP/IP usando el protocolo FTP (‘File Transfer Protocol’) ya saben, para darle la tipica etiqueta de IoT 🙂 . El esquema general del proyecto era parecido al de a continuación:

Componentes del proyecto:
- Microcontrolador STM32G431KB en plataforma Nucleo32 STM32G4.
- Ethernet Shield W5500 – Comprada en amazon.
- Modulo MicroSD Catalex v1.0 – Comprado en Amazon.
- IDE de desarrollo: STM32CubeIDE v1.9.0 (2022)
- Libreria FatFS
- Libreria io_library de Wiznet
Como vieron el proyecto era algo sencillo, abarcaba las necesidades propuestas y estaba al alzance para el desarrollo en poco tiempo. Los detalles de este proyecto lo podran esperar en otro Post, de momento retomaremos el hilo de lo que nos llevo a utilzar watchdogs. D:
En un principio el proyecto programado y compilado no dio problema alguno, era la version de IDE 1.7.0 de STM32CubeIDE y estaba confiado en que el proyecto estaba encaminado (mas lejos de la realidad no pude estar). Con el pasar del tiempo ST lanzó las nuevas versiones de su IDE y sorpresa! de la noche a la mañana lo que pareció un programa estable y confiable ya no lo era mas, la ejecución del servidor FTP se detenía justo al momento de ingresar usuario y contraseña:

En ese momento me di cuenta que no se trataba de un error común ya que se detenía completamente el proceso de ejecución del microcontrolador. La pregunta rondaba; ¿Qué era lo que detenía la ejecución del proceso? realizando más investigaciones me di cuenta que el proceso se detenia justo despues de ingresar el comando ‘USER’

Realizando un debug de las instrucciones uno a uno, surgieron mas respuestas. En el momento en que el microcontrolador ejecutaba la instrucción para validar el usuario ingresado con respecto al almacenado en memoria por la variable ‘ftp.user’ esta no contenia ningun dato, a pesar de en instrucciones previas haber almacenado el usuario correctamente al iniciar el FTP server correctamente. Era literalmente como si la variable global se limpiara sin razon aparente.
/* Execute specific command */
switch (cmdp – commands)
{
case USER_CMD :
#if defined(_FTP_DEBUG_)
printf(“USER_CMD : %s\r\n”, arg);
printf(“Save_user3 %s\r\n”, ftp.user);
#endif
if(strcmp(arg,ftp.user)==0) //———Comprobacion de la variable ftp.user con el argumento pasado como usuario
{
slen = sprintf(sendbuf, “331 Enter PASS command\r\n”);
send(ftp.ctrl_sock, (uint8_t *)sendbuf, slen);
}
else
{
slen = sprintf(sendbuf, “530 Not logged in\r\n”);
send(ftp.ctrl_sock, (uint8_t *)sendbuf, slen);
}
break;
Entrando en materia…
En palabras sencillas: un Watchpoint o muchas veces también llamado data breakpoint es una utilidad que poseen los entornos de desarrollo; para este caso C, que nos permite retornar al debugging durante la ejecución de un programa en dependencia de si la variable observada ha sido escrita o leida por alguna instruccion del programa. Para mas detalles vease aquí
Con esta herramienta bajo el arsenal de troubleshooting me di a la tarea de analizar en que punto de la ejecucion la variable ‘ftp.user’ era sobre escrita, para crear un Watchpoint en STM32CubeIDE debemos entrar en el modo debugging de nuestro IDE:

Si es nuestra primera vez ó no aparece la vista de Breakpoints deberemos añadirla a nuestro panel:

Se nos abrirá una ventana del lado derecho de nuestro IDE (o en dependencia de tu vista configurada) desde donde podremos crear nuestros Breakpoints:

Creamos un nuevo Breakpoint dede los 3 puntos verticales ubicados del extremo derecho dentro de la ventana ‘Breakpoints’

Configuramos nuestra opciones, para el caso de ‘Expression to watch’ escribimos exactamente el nombre de la variable a observar. En caso de que sea requerido podemos activar la opción ‘Read’ tanto como ‘Write’ (ambos hacen referencia a lectura o escritura de la variable en observación).

Aplicamos y cerramos, luego veremos como se nos ha creado un nuevo Breakpoint con la variable: ‘ftp.user’, con esto el programa sabrá cuando detenerse si observa que alguna instrucción esta accediendo al espacio en memoria de la variable en observación. Podemos ejecutar ‘Reanudar’ desde nuestro modo debugging para observar en que momento o cuales de las instrucciones esta accediendo a la variable. Ejemplo:

Como se observa en la imagen anterior, el Breakpoint nos detiene en la ejecución de la función: strcpy() la que copia el nombre del usuario para el FTP server desde la variable ‘user’ hacia la variable global ‘ftp.user’, en un claro ejemplo que la variable ‘ftp.user’ ha sido escrita por la función.
Si reanudamos la ejecución, los redireccionará a la siguiente instrucción:

BINGO! En este segundo Breakpoint nos encontramos con una funcion memset() comunmente usada para limpiar un buffer de tamaño ‘x’ y asignarle ‘0’ o elemento NULL. Como se observa en la imagen el memset realiza esta operación 512 veces sobre el mapa de memoria del microcontrolador, posiblemente para cuando se ejecuta esta funcion el espacio de memoria en donde se almacenaba ‘ftp.user’ se limpiaba (debido a que la alocación de las variables globales, estan son del tipo estáticas durante la ejecución del programa), nos damos cuenta rapido que es un bug de la librería debido a que no esta explícitamente la participación de la variable ‘ftp.user’ en la ejecución del programa.
Para este caso el uso de los breakpoints dentro del debug del programa fue la clave para determinar la causa Raiz del problema, de aqui en adelante las tareas de repación del codigo consistían en si era útil o no la limpieza de tan gran espacio en memoria.
Puedes ver el repositorio en github de este proyecto completo, en el siguiente Link.
Con esto concluimos nuestro repaso por el uso de Watchpoint, si tienen algun comentario o sugerencia son bienvenidos. Espero que les haya gustado y esperen mas publicaciones sobre este proyecto pronto! 😀 Saludes troubleshooters!
Buen artículo Gerardo, gracias por compartir ✅🔝
Muy buen articulo.
Gracias