{"id":10499,"date":"2026-05-08T16:34:53","date_gmt":"2026-05-08T14:34:53","guid":{"rendered":"https:\/\/blogs.etsii.urjc.es\/dseytr\/?p=10499"},"modified":"2026-05-08T16:34:56","modified_gmt":"2026-05-08T14:34:56","slug":"asistente-personal-ia-bmo","status":"publish","type":"post","link":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/","title":{"rendered":"ASISTENTE PERSONAL-IA BMO"},"content":{"rendered":"\n<p>Proyecto seytrma2526g10 \u00b7 URJC Autores Diego Garc\u00eda Garc\u00eda, David Garc\u00eda Rodr\u00edguez, \u00c1lvaro Monge P\u00e9rez e Iv\u00e1n Navarro \u00c1lvarez.<\/p>\n\n\n\n<p><a id=\"_Toc229070170\">Resumen Ejecutivo<\/a><\/p>\n\n\n\n<p>Este documento recoge el dise\u00f1o, desarrollo e implementaci\u00f3n de BMO, un asistente rob\u00f3tico de escritorio de bajo coste capaz de mantener conversaciones naturales en lenguaje hablado. El sistema integra un modelo de lenguaje de \u00faltima generaci\u00f3n (Google Gemini 1.5 Flash) con una interfaz f\u00edsica dotada de expresividad visual mediante una pantalla LCD gr\u00e1fica.<\/p>\n\n\n\n<p>El reto t\u00e9cnico principal abordado consiste en superar las limitaciones de procesamiento y memoria de un microcontrolador b\u00e1sico (Arduino Uno) sin renunciar a las capacidades de un asistente conversacional moderno. La soluci\u00f3n adoptada es una arquitectura distribuida de tipo Serial Bridge que separa la capa cognitiva (ejecutada en el ordenador) de la capa sensorial y de actuaci\u00f3n (ejecutada en el microcontrolador), comunicadas mediante protocolo serie sobre USB.<\/p>\n\n\n\n<p>El presupuesto total del prototipo se mantiene por debajo de los 15 \u20ac, demostrando que es posible democratizar el acceso a interfaces hombre-m\u00e1quina avanzadas mediante la combinaci\u00f3n inteligente de hardware modular de bajo coste y servicios de IA en la nube.<\/p>\n\n\n\n<p><strong>Palabras clave: <\/strong>Inteligencia Artificial conversacional, Procesamiento de Lenguaje Natural, Arduino, Python, Gemini 1.5 Flash, arquitectura Serial Bridge, rob\u00f3tica educativa, interfaz hombre-m\u00e1quina.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><a id=\"_Toc229070172\">1. Introducci\u00f3n y Visi\u00f3n General<\/a><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070173\">1.1. Contexto y motivaci\u00f3n<\/a><\/h2>\n\n\n\n<p>La popularizaci\u00f3n de los modelos de lenguaje de gran tama\u00f1o (LLM) ha transformado la concepci\u00f3n de los asistentes virtuales: hoy es posible mantener conversaciones contextualmente coherentes con un nivel de naturalidad inalcanzable hace pocos a\u00f1os. Sin embargo, la mayor parte de estos asistentes residen exclusivamente en pantallas (m\u00f3viles, ordenadores o altavoces inteligentes), lo que limita su car\u00e1cter emp\u00e1tico y su integraci\u00f3n en el entorno f\u00edsico del usuario.<\/p>\n\n\n\n<p>BMO surge como respuesta a esa carencia: un asistente f\u00edsico, expresivo y de bajo coste que combina la potencia cognitiva de un LLM con un cuerpo capaz de escuchar, mirar y responder mediante voz y expresiones faciales animadas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070174\">1.2. Objetivos del proyecto<\/a><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Objetivo principal: <\/strong>dise\u00f1ar e implementar un robot de escritorio capaz de mantener conversaciones naturales mediante una arquitectura distribuida que combine hardware embebido de bajo coste con servicios de IA en la nube.<\/li>\n\n\n\n<li>Desarrollar una interfaz f\u00edsica emp\u00e1tica que reaccione visualmente al di\u00e1logo mediante expresiones faciales sincronizadas con la voz.<\/li>\n\n\n\n<li>Demostrar la viabilidad t\u00e9cnica y econ\u00f3mica de un asistente conversacional f\u00edsico con un presupuesto inferior a 15 \u20ac.<\/li>\n\n\n\n<li>Implementar una arquitectura modular y escalable que permita la sustituci\u00f3n del modelo de IA o de los componentes f\u00edsicos sin reescribir el sistema.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070175\">1.3. Alcance y enfoque t\u00e9cnico<\/a><\/h2>\n\n\n\n<p>Para superar las estrictas limitaciones de memoria SRAM y capacidad de c\u00f3mputo del microcontrolador, el proyecto adopta una arquitectura denominada Serial Bridge (Puente Serial). Esta t\u00e9cnica delega el procesamiento intensivo (reconocimiento de voz, inferencia del modelo y s\u00edntesis de habla) a un ordenador central, reservando al hardware rob\u00f3tico las funciones de captaci\u00f3n sensorial y representaci\u00f3n visual y sonora.<\/p>\n\n\n\n<p>Esta separaci\u00f3n de responsabilidades permite mantener el coste del prototipo f\u00edsico al m\u00ednimo y, simult\u00e1neamente, beneficiarse de la potencia de c\u00e1lculo y los servicios cloud de \u00faltima generaci\u00f3n.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><a id=\"_Toc229070176\">2. Arquitectura del Sistema<\/a><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070177\">2.1. Visi\u00f3n general<\/a><\/h2>\n\n\n\n<p>El ecosistema BMO se articula en dos bloques funcionales que operan de forma as\u00edncrona y se sincronizan continuamente mediante un canal serie a 9.600 baudios sobre USB:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Cuerpo (Frontend): <\/strong>una placa Arduino Uno responsable de la captaci\u00f3n de eventos ac\u00fasticos y de la representaci\u00f3n visual de las expresiones faciales en la pantalla LCD.<\/li>\n\n\n\n<li><strong>Cerebro (Backend): <\/strong>un programa Python ejecutado en un ordenador convencional que realiza el reconocimiento del habla, consulta al modelo de lenguaje, sintetiza la respuesta hablada y orquesta las animaciones del cuerpo.<\/li>\n<\/ul>\n\n\n\n<p>Esta segregaci\u00f3n funcional sigue el patr\u00f3n cl\u00e1sico cliente-servidor adaptado al \u00e1mbito embebido, y garantiza que los componentes de bajo nivel (sens\u00f3rica y actuaci\u00f3n) permanezcan desacoplados de la capa cognitiva.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070178\">2.2. El Cuerpo: Frontend en Arduino (C++)<\/a><\/h2>\n\n\n\n<p>El microcontrolador ejecuta una m\u00e1quina de estados finita que coordina dos rutinas principales:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Monitorizaci\u00f3n activa: <\/strong>en estado de reposo, el sistema muestrea el micr\u00f3fono de manera continua. Cuando detecta un pico de presi\u00f3n sonora superior a un umbral configurable, env\u00eda la se\u00f1al RUIDO_DETECTADO al backend para activar el ciclo de conversaci\u00f3n.<\/li>\n\n\n\n<li><strong>Reacci\u00f3n visual: <\/strong>tras la activaci\u00f3n, el Arduino queda a la espera de los c\u00f3digos de comando enviados por el PC (&#8216;I&#8217; inactivo, &#8216;L&#8217; escuchando, &#8216;T&#8217; hablando, &#8216;S&#8217; silencio) y, mediante la librer\u00eda LiquidCrystal, anima en tiempo real expresiones faciales construidas con caracteres ASCII personalizados sobre la pantalla LCD gr\u00e1fica.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070179\">2.3. El Cerebro: Backend en Python 3.12<\/a><\/h2>\n\n\n\n<p>El backend est\u00e1 estructurado de forma modular en torno al fichero main.py, que act\u00faa como director de orquesta del flujo conversacional. Sus responsabilidades principales son:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Mantener una escucha permanente sobre el puerto serie a la espera de eventos generados por el Arduino.<\/li>\n\n\n\n<li>Activar el micr\u00f3fono del PC mediante la librer\u00eda speech_recognition y transcribir el audio capturado a texto.<\/li>\n\n\n\n<li>Enviar el texto transcrito al modelo Gemini 1.5 Flash a trav\u00e9s de google-generativeai y recibir la respuesta del LLM.<\/li>\n\n\n\n<li>Sintetizar localmente la respuesta hablada mediante pyttsx3.<\/li>\n\n\n\n<li>Coordinar las animaciones faciales devolviendo al Arduino los c\u00f3digos de comando v\u00eda pyserial, manteniendo la sincron\u00eda labial entre voz y boca animada.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070180\">2.4. Comunicaci\u00f3n entre subsistemas<\/a><\/h2>\n\n\n\n<p>El canal serie a 9.600 baudios resulta suficiente para transportar los peque\u00f1os paquetes de control que intercambian ambos extremos. El protocolo es deliberadamente simple: cada mensaje consiste en un \u00fanico car\u00e1cter ASCII que act\u00faa como token de estado o evento, lo que minimiza la latencia y el riesgo de tramas malformadas. Esta sencillez es deliberada y facilita la depuraci\u00f3n en cualquier monitor serie convencional.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><a id=\"_Toc229070181\">3. Hardware, Conexiones y Presupuesto<\/a><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070182\">3.1. Esquema de conexiones<\/a><\/h2>\n\n\n\n<p>La integraci\u00f3n hardware se ha realizado sobre una protoboard est\u00e1ndar, manteniendo el cableado lo m\u00e1s corto posible para reducir interferencias electromagn\u00e9ticas. Los principales m\u00f3dulos del sistema y sus conexiones son los siguientes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Pantalla GLCD 128\u00d764 (Type B): <\/strong>comunicaci\u00f3n paralela mediante los pines digitales D2 a D9. El pin V<em>o<\/em> (control de contraste) se conecta al cursor de un potenci\u00f3metro de 10 k\u03a9 que permite el ajuste fino del contraste.<\/li>\n\n\n\n<li><strong>M\u00f3dulo de micr\u00f3fono MAX4466: <\/strong>alimentaci\u00f3n a 3,3 V (en lugar de 5 V) para minimizar el ruido el\u00e9ctrico inducido por la fuente. Tierra com\u00fan con el Arduino y salida anal\u00f3gica conectada al pin A0.<\/li>\n\n\n\n<li><strong>Amplificador PAM8403 + altavoz: <\/strong>alimentaci\u00f3n desde la l\u00ednea de 5 V del Arduino. La se\u00f1al de audio procedente del PC se inyecta mediante un cable jack de 3,5 mm pelado (malla a GND, canal L\/R a la entrada del m\u00f3dulo) y la salida amplificada se conecta directamente al altavoz de 8 \u03a9.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"902\" height=\"518\" src=\"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-content\/uploads\/sites\/8\/2026\/05\/image.gif\" alt=\"\" class=\"wp-image-10504\" \/><\/figure>\n\n\n\n<p><strong>(En el tinkercad no estaba ni el micr\u00f3fono ni el amplificador por eso no sale en el dise\u00f1o en tinkercad)<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070183\">3.2. Desglose de costes<\/a><\/h2>\n\n\n\n<p>La siguiente tabla recoge el presupuesto detallado del prototipo. Los componentes marcados como \u201cKIT\u201d forman parte del kit docente disponible y no han generado coste adicional para el grupo:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td><strong>Componente<\/strong><\/td><td><strong>Especificaci\u00f3n<\/strong><\/td><td><strong>Coste aprox. (\u20ac)<\/strong><\/td><\/tr><\/thead><tbody><tr><td>Microcontrolador<\/td><td>Arduino Uno R3 (clon\/kit) + protoboard<\/td><td>0 \u20ac \u2014 KIT<\/td><\/tr><tr><td>Visualizaci\u00f3n<\/td><td>Pantalla GLCD 128\u00d764 (Pinout Type B)<\/td><td>0 \u20ac \u2014 KIT<\/td><\/tr><tr><td>Entrada de audio<\/td><td>M\u00f3dulo de micr\u00f3fono MAX4466 (pack de 3)<\/td><td>6,99 \u20ac<\/td><\/tr><tr><td>Salida de audio<\/td><td>Amplificador PAM8403 (pack de 3)<\/td><td>7,99 \u20ac<\/td><\/tr><tr><td>Componentes pasivos<\/td><td>Potenci\u00f3metro 10 k\u03a9, resistencias, cables jumper<\/td><td>0 \u20ac \u2014 KIT<\/td><\/tr><tr><td>Altavoz<\/td><td>Altavoz 8 \u03a9 (aportaci\u00f3n de un integrante del grupo)<\/td><td>0 \u20ac<\/td><\/tr><tr><td><strong>TOTAL<\/strong><\/td><td><em>Coste efectivo del prototipo<\/em><\/td><td><strong>\u2248 15 \u20ac<\/strong><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><em>El coste efectivo asumido por el grupo se sit\u00faa por tanto en aproximadamente 15 \u20ac, lo que confirma una de las hip\u00f3tesis de partida del proyecto: la viabilidad de un asistente rob\u00f3tico funcional con un presupuesto al alcance de cualquier estudiante.<\/em><\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><a id=\"_Toc229070184\">4. Implementaci\u00f3n e Integraci\u00f3n de IA<\/a><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070185\">4.1. Fases metodol\u00f3gicas<\/a><\/h2>\n\n\n\n<p>El desarrollo del proyecto se ha estructurado en cuatro fases secuenciales que han permitido validar de forma incremental cada subsistema antes de proceder a la integraci\u00f3n global:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Fase 1 \u2014 Montaje f\u00edsico: <\/strong>ensamblaje de los componentes sobre protoboard, soldadura de pines en m\u00f3dulos delicados y validaci\u00f3n de continuidad el\u00e9ctrica.<\/li>\n\n\n\n<li><strong>Fase 2 \u2014 Programaci\u00f3n frontend (Arduino): <\/strong>implementaci\u00f3n de la m\u00e1quina de estados, calibraci\u00f3n del umbral del micr\u00f3fono y dise\u00f1o de las expresiones faciales en la pantalla LCD.<\/li>\n\n\n\n<li><strong>Fase 3 \u2014 Desarrollo backend (Python): <\/strong>configuraci\u00f3n del entorno virtual, integraci\u00f3n con la API de Gemini, implementaci\u00f3n del reconocimiento y s\u00edntesis de voz, y pruebas unitarias de cada m\u00f3dulo.<\/li>\n\n\n\n<li><strong>Fase 4 \u2014 Sincronizaci\u00f3n (Serial Bridge): <\/strong>integraci\u00f3n de ambos extremos, definici\u00f3n del protocolo de comunicaci\u00f3n y ajuste fino de la latencia y la sincron\u00eda boca-voz.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070186\">4.2. Selecci\u00f3n del modelo de lenguaje<\/a><\/h2>\n\n\n\n<p>Tras evaluar varias alternativas (modelos locales tipo Llama, GPT-4o v\u00eda OpenAI y la familia Gemini de Google), se ha optado por Gemini 1.5 Flash a trav\u00e9s de su API oficial. La elecci\u00f3n obedece a tres criterios principales:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Latencia: <\/strong>la versi\u00f3n Flash est\u00e1 espec\u00edficamente optimizada para respuestas en tiempo real, lo cual resulta cr\u00edtico en una aplicaci\u00f3n conversacional.<\/li>\n\n\n\n<li><strong>Coste: <\/strong>la cuota gratuita ofrecida por Google Cloud cubre con holgura el volumen de inferencias de un prototipo acad\u00e9mico.<\/li>\n\n\n\n<li><strong>Calidad multiling\u00fce: <\/strong>el modelo presenta un rendimiento excelente en espa\u00f1ol, lengua nativa del proyecto.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070187\">4.3. Flujo de procesamiento en tiempo real<\/a><\/h2>\n\n\n\n<p>El ciclo conversacional completo se desarrolla en cinco etapas encadenadas, ejecutadas en menos de dos segundos en condiciones normales de red:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>El micr\u00f3fono del PC capta la voz del usuario y la librer\u00eda speech_recognition la transcribe a texto.<\/li>\n\n\n\n<li>El texto se concatena con un <em>system prompt<\/em> predefinido (\u201cEres BMO, un robot asistente amigable\u2026\u201d) que fija la personalidad y las restricciones del asistente.<\/li>\n\n\n\n<li>La consulta se env\u00eda a Gemini 1.5 Flash, que devuelve la respuesta textual en el contexto de la conversaci\u00f3n.<\/li>\n\n\n\n<li>El backend sintetiza el audio mediante pyttsx3 y comienza la reproducci\u00f3n local.<\/li>\n\n\n\n<li>De forma simult\u00e1nea, se env\u00edan al Arduino los comandos de animaci\u00f3n que mueven la \u201cboca\u201d en la pantalla LCD, manteniendo la ilusi\u00f3n de sincron\u00eda labial mientras dura la voz sintetizada.<\/li>\n<\/ul>\n\n\n\n<h1 class=\"wp-block-heading\"><a id=\"_Toc229070188\">5. Problemas T\u00e9cnicos y Soluciones<\/a><\/h1>\n\n\n\n<p>A lo largo del desarrollo se han enfrentado obst\u00e1culos significativos, tanto de hardware como de software. La siguiente tabla recoge los m\u00e1s relevantes junto con la soluci\u00f3n finalmente adoptada:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td><strong>Problema<\/strong><\/td><td><strong>Descripci\u00f3n<\/strong><\/td><td><strong>Soluci\u00f3n adoptada<\/strong><\/td><\/tr><\/thead><tbody><tr><td><strong>Limitaci\u00f3n de hardware del Arduino<\/strong><\/td><td>El microcontrolador no dispone de capacidad suficiente para procesar audio ni inferencias de modelos de lenguaje en tiempo real.<\/td><td>Implementaci\u00f3n de la arquitectura Serial Bridge: el procesamiento intensivo se delega al PC y el Arduino act\u00faa exclusivamente como interfaz sensorial y de actuaci\u00f3n.<\/td><\/tr><tr><td><strong>Fallo de compilaci\u00f3n de PyAudio en Windows<\/strong><\/td><td>Errores fatales al instalar PyAudio en Python 3.14 por incompatibilidad con la versi\u00f3n y por la ausencia de las herramientas Microsoft C++ Build Tools.<\/td><td>Migraci\u00f3n estable a Python 3.12 y reconfiguraci\u00f3n del int\u00e9rprete del entorno de desarrollo (VS Code \/ Cursor).<\/td><\/tr><tr><td><strong>Desincronizaci\u00f3n de los entornos virtuales<\/strong><\/td><td>El IDE mostraba avisos missing-import a pesar de tener las dependencias correctamente instaladas en el venv.<\/td><td>Recarga manual de la cach\u00e9 del IDE y revisi\u00f3n de las rutas del PATH del sistema operativo para alinear los int\u00e9rpretes activos.<\/td><\/tr><tr><td><strong>Inestabilidad del m\u00f3dulo de micr\u00f3fono<\/strong><\/td><td>Los pines del m\u00f3dulo MAX4466 ven\u00edan sin soldar de f\u00e1brica, lo que provocaba contactos intermitentes y la ca\u00edda del m\u00f3dulo durante el manejo.<\/td><td>Soldadura de los pines header al PCB del m\u00f3dulo, dejando una conexi\u00f3n mec\u00e1nica y el\u00e9ctrica robusta.<\/td><\/tr><tr><td><strong>Integraci\u00f3n f\u00edsica dentro de la carcasa<\/strong><\/td><td>El encapsulado generaba problemas de gesti\u00f3n de espacio, riesgo de desconexi\u00f3n de jumpers por dobleces y resonancias ac\u00fasticas indeseadas.<\/td><td>Planificaci\u00f3n previa de la distribuci\u00f3n interna, fijaci\u00f3n con adhesivo de doble cara y espaciadores, y cortes de precisi\u00f3n en el panel frontal para LCD y micr\u00f3fono.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\"><a id=\"_Toc229070189\">6. Reparto de Tareas y Casos de Uso<\/a><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070190\">6.1. Distribuci\u00f3n del trabajo<\/a><\/h2>\n\n\n\n<p>La asignaci\u00f3n de responsabilidades se ha realizado en funci\u00f3n de los perfiles e intereses de cada miembro del grupo, garantizando que cada subsistema contara con un responsable principal:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Hardware \u2014 \u00c1lvaro y Diego: <\/strong>dise\u00f1o del circuito, soldadura de los m\u00f3dulos, ensamblaje de la pantalla LCD y del amplificador PAM8403, y validaci\u00f3n el\u00e9ctrica del prototipo.<\/li>\n\n\n\n<li><strong>Backend en Python \u2014 \u00c1lvaro: <\/strong>configuraci\u00f3n de las API keys, resoluci\u00f3n de dependencias (notablemente PyAudio), implementaci\u00f3n de la l\u00f3gica de IA y orquestaci\u00f3n del flujo conversacional.<\/li>\n\n\n\n<li><strong>Frontend en C++ \u2014 \u00c1lvaro: <\/strong>dise\u00f1o de la matriz gr\u00e1fica de expresiones faciales, gesti\u00f3n del puerto COM e implementaci\u00f3n de la m\u00e1quina de estados del Arduino.<\/li>\n\n\n\n<li><strong>Infraestructura f\u00edsica \u2014 David e Iv\u00e1n: <\/strong>construcci\u00f3n de la carcasa, pintura, cortes de precisi\u00f3n en el panel frontal e integraci\u00f3n mec\u00e1nica de los componentes electr\u00f3nicos.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070191\">6.2. Casos de uso y aplicaciones pr\u00e1cticas<\/a><\/h2>\n\n\n\n<p>Aunque BMO se ha planteado como un prototipo acad\u00e9mico, la arquitectura y los principios de dise\u00f1o aplicados lo hacen extrapolable a tres familias de aplicaciones reales:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Asistencia de escritorio: <\/strong>consulta r\u00e1pida de informaci\u00f3n, gesti\u00f3n de agenda y recordatorios sin necesidad de interactuar con teclados ni pantallas, lo que mejora la concentraci\u00f3n del usuario en su tarea principal.<\/li>\n\n\n\n<li><strong>Educaci\u00f3n interactiva: <\/strong>compa\u00f1ero de aprendizaje amigable para la pr\u00e1ctica de lectura, comprensi\u00f3n oral o idiomas, especialmente \u00fatil en p\u00fablicos infantiles donde la presencia f\u00edsica aporta engagement.<\/li>\n\n\n\n<li><strong>Accesibilidad: <\/strong>interfaz operada al cien por cien por voz, particularmente valiosa para personas con movilidad reducida, discapacidad visual o usuarios de edad avanzada para los que las interfaces gr\u00e1ficas convencionales suponen una barrera.<\/li>\n<\/ul>\n\n\n\n<h1 class=\"wp-block-heading\"><a id=\"_Toc229070192\">7.C\u00f3digo Software<\/a><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070193\">7.1 Control de Pantalla Arduino y Micr\u00f3fono<\/a><\/h2>\n\n\n\n<p>#include &lt;LiquidCrystal.h&gt;<\/p>\n\n\n\n<p><em>LiquidCrystal<\/em> <em>lcd<\/em>(12, 11, 5, 4, 3, 2);<\/p>\n\n\n\n<p>const int micPin = A0;<\/p>\n\n\n\n<p>const int ruidoUmbral = 50;<\/p>\n\n\n\n<p>enum <em>RobotState<\/em> {<\/p>\n\n\n\n<p>&nbsp; IDLE,<\/p>\n\n\n\n<p>&nbsp; LISTENING,<\/p>\n\n\n\n<p>&nbsp; THINKING,<\/p>\n\n\n\n<p>&nbsp; SPEAKING<\/p>\n\n\n\n<p>};<\/p>\n\n\n\n<p>RobotState currentState = IDLE;<\/p>\n\n\n\n<p>unsigned long previousMillisSpeak = 0;<\/p>\n\n\n\n<p>const long speakInterval = 300;<\/p>\n\n\n\n<p>bool mouthOpen = false;<\/p>\n\n\n\n<p>unsigned long previousMillisThink = 0;<\/p>\n\n\n\n<p>const long thinkInterval = 400;<\/p>\n\n\n\n<p>int thinkFrame = 0;<\/p>\n\n\n\n<p>void <em>setup<\/em>() {<\/p>\n\n\n\n<p>&nbsp; Serial.<em>begin<\/em>(9600);<\/p>\n\n\n\n<p>&nbsp; lcd.<em>begin<\/em>(16, 2);<\/p>\n\n\n\n<p>&nbsp; <em>pinMode<\/em>(micPin, INPUT);<\/p>\n\n\n\n<p>&nbsp; lcd.<em>clear<\/em>();<\/p>\n\n\n\n<p>&nbsp; lcd.<em>setCursor<\/em>(0, 0);<\/p>\n\n\n\n<p>&nbsp; lcd.<em>print<\/em>(\u00abBMO Iniciando&#8230;\u00bb);<\/p>\n\n\n\n<p>&nbsp; <em>delay<\/em>(1500);<\/p>\n\n\n\n<p>&nbsp; Serial.<em>println<\/em>(\u00abBMO: Arduino Listo.\u00bb);<\/p>\n\n\n\n<p>&nbsp; <em>updateLCD<\/em>();<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>void <em>loop<\/em>() {<\/p>\n\n\n\n<p>&nbsp; if (Serial.<em>available<\/em>() &gt; 0) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; char command = Serial.<em>read<\/em>();<\/p>\n\n\n\n<p>&nbsp; &nbsp; if(command != &#8216;\\n&#8217; &amp;&amp; command != &#8216;\\r&#8217;) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; switch (command) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; case &#8216;I&#8217;: case &#8216;i&#8217;: currentState = IDLE; <em>updateLCD<\/em>(); break;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; case &#8216;L&#8217;: case &#8216;l&#8217;: currentState = LISTENING; <em>updateLCD<\/em>(); break;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; case &#8216;T&#8217;: case &#8216;t&#8217;: currentState = THINKING; thinkFrame = 0; <em>updateLCD<\/em>(); break;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; case &#8216;S&#8217;: case &#8216;s&#8217;: currentState = SPEAKING; <em>updateLCD<\/em>(); break;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; }<\/p>\n\n\n\n<p>&nbsp; if (currentState == IDLE) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; unsigned long startMillis = <em>millis<\/em>();<\/p>\n\n\n\n<p>&nbsp; &nbsp; int signalMax = 0;<\/p>\n\n\n\n<p>&nbsp; &nbsp; int signalMin = 1024;<\/p>\n\n\n\n<p>&nbsp; &nbsp; while (<em>millis<\/em>() &#8211; startMillis &lt; 50) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; int sample = <em>analogRead<\/em>(micPin);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; if (sample &gt; signalMax) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; signalMax = sample;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; if (sample &lt; signalMin) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; signalMin = sample;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; &nbsp; int peakToPeak = signalMax &#8211; signalMin;<\/p>\n\n\n\n<p>&nbsp; &nbsp; if (peakToPeak &gt; ruidoUmbral) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; Serial.<em>println<\/em>(\u00abRUIDO_DETECTADO\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; <em>delay<\/em>(1000);<\/p>\n\n\n\n<p>&nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; }<\/p>\n\n\n\n<p>&nbsp; unsigned long currentMillis = <em>millis<\/em>();<\/p>\n\n\n\n<p>&nbsp; if (currentState == THINKING) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; if (currentMillis &#8211; previousMillisThink &gt;= thinkInterval) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; thinkFrame = (thinkFrame + 1) % 4;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; previousMillisThink = currentMillis;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; <em>updateLCD<\/em>();<\/p>\n\n\n\n<p>&nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; }<\/p>\n\n\n\n<p>&nbsp; if (currentState == SPEAKING) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; if (currentMillis &#8211; previousMillisSpeak &gt;= speakInterval) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; mouthOpen = !mouthOpen;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; previousMillisSpeak = currentMillis;<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; <em>updateLCD<\/em>();<\/p>\n\n\n\n<p>&nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; }<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<p>void <em>updateLCD<\/em>() {<\/p>\n\n\n\n<p>&nbsp; lcd.<em>clear<\/em>();<\/p>\n\n\n\n<p>&nbsp; switch (currentState) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; case IDLE:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>setCursor<\/em>(4, 0);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00ab( ^_^ )\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>setCursor<\/em>(2, 1);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00abEsperando&#8230;\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; break;<\/p>\n\n\n\n<p>&nbsp; &nbsp; case LISTENING:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>setCursor<\/em>(4, 0);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00ab( O_O )\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>setCursor<\/em>(1, 1);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00abEscuchando&#8230;\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; break;<\/p>\n\n\n\n<p>&nbsp; &nbsp; case THINKING:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>setCursor<\/em>(4, 0);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00ab( -_- )\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>setCursor<\/em>(2, 1);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00abPensando\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; for(int i=0; i&lt;thinkFrame; i++) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00ab.\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; break;<\/p>\n\n\n\n<p>&nbsp; &nbsp; case SPEAKING:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>setCursor<\/em>(4, 0);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; if (mouthOpen) {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00ab( O_O )\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; } else {<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00ab( O-O )\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; }<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>setCursor<\/em>(3, 1);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; lcd.<em>print<\/em>(\u00abHablando&#8230;\u00bb);<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; break;<\/p>\n\n\n\n<p>&nbsp; }<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070194\">7.2 Control de audio e IA (Python)<\/a><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><a id=\"_Toc229070195\">7.2.1 ai_module.py (Conexi\u00f3n con la API de Gemini)<\/a><\/h3>\n\n\n\n<p><br>import google.generativeai as genai<\/p>\n\n\n\n<p>import os<\/p>\n\n\n\n<p>class <em>AIModule<\/em>:<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>__init__<\/em>(<em>self<\/em>, <em>api_key<\/em>=None):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.api_key = <em>api_key<\/em> or os.environ.<em>get<\/em>(\u00abGEMINI_API_KEY\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if not <em>self<\/em>.api_key or <em>self<\/em>.api_key == &#8216;TU_CLAVE_API_AQUI&#8217;:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[IA] ADVERTENCIA: No se ha configurado una GEMINI_API_KEY v\u00e1lida.\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.model = None<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.chat = None<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; genai.configure(<em>api_key<\/em>=<em>self<\/em>.api_key)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; instrucciones = \u00abEres BMO, un robot asistente interactivo simp\u00e1tico y \u00fatil. Responde siempre de manera breve (1 o 2 frases m\u00e1ximo) y conversacional. No uses formatos complejos como Markdown o listas, ya que tu respuesta ser\u00e1 le\u00edda en voz alta por un sintetizador de voz.\u00bb<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.model = genai.GenerativeModel(&#8216;gemini-flash-latest&#8217;, <em>system_instruction<\/em>=instrucciones)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.chat = <em>self<\/em>.model.start_chat(<em>history<\/em>=[])<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>get_response<\/em>(<em>self<\/em>, <em>text<\/em>):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if not <em>self<\/em>.chat:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return \u00abError: No tengo configurada mi clave de API. Por favor a\u00f1\u00e1dela en main.py.\u00bb<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; try:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[IA] Consultando a Gemini&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; response = <em>self<\/em>.chat.send_message(<em>text<\/em>)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; respuesta_limpia = response.text.replace(&#8216;\\n&#8217;, &#8216; &#8216;).strip()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[IA] Respuesta obtenida: &#8216;{respuesta_limpia}'\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return respuesta_limpia<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; except <em>Exception<\/em> as e:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[IA] Error comunicando con la API: {e}\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return \u00abLo siento, tuve un problema procesando tu mensaje.\u00bb<\/p>\n\n\n\n<p>}<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a id=\"_Toc229070196\">7.2.2 list_models.py (Script de utilidad no es parte del funcionamiento principal)<\/a><\/h3>\n\n\n\n<p>import google.generativeai as genai<\/p>\n\n\n\n<p>with <em>open<\/em>(\u00abmain.py\u00bb, \u00abr\u00bb, <em>encoding<\/em>=\u00bbutf-8&#8243;) as f:<\/p>\n\n\n\n<p>&nbsp; &nbsp; for line in f:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if line.<em>startswith<\/em>(\u00abGEMINI_API_KEY\u00bb):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; key = line.<em>split<\/em>(\u00ab=\u00bb)[1].<em>strip<\/em>().<em>strip<\/em>(\u00ab&#8216;\u00bb).<em>strip<\/em>(&#8216;\u00bb&#8216;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break<\/p>\n\n\n\n<p>genai.configure(<em>api_key<\/em>=key)<\/p>\n\n\n\n<p><em>print<\/em>(\u00abBuscando modelos compatibles con tu clave&#8230;\u00bb)<\/p>\n\n\n\n<p>for m in genai.list_models():<\/p>\n\n\n\n<p>&nbsp; &nbsp; if &#8216;generateContent&#8217; in m.supported_generation_methods:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(m.name)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a id=\"_Toc229070197\">7.2.3 serial_module.py (Comunicaci\u00f3n entre el ordenador y el Arduino)<\/a><\/h3>\n\n\n\n<p>import serial<\/p>\n\n\n\n<p>import time<\/p>\n\n\n\n<p>class <em>BmoSerial<\/em>:<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>__init__<\/em>(<em>self<\/em>, <em>port<\/em>=&#8217;COM3&#8242;, <em>baudrate<\/em>=9600):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.port = <em>port<\/em><\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.baudrate = <em>baudrate<\/em><\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.ser = None<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>connect<\/em>(<em>self<\/em>):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; try:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.ser = serial.<em>Serial<\/em>(<em>self<\/em>.port, <em>self<\/em>.baudrate, <em>timeout<\/em>=1)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.<em>sleep<\/em>(2)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[Serial] Conectado a BMO en {<em>self<\/em>.port}\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return True<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; except serial.<em>SerialException<\/em> as e:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[Serial] Error conectando a {<em>self<\/em>.port}. Aseg\u00farate de que el puerto es correcto y no est\u00e1 en uso por el IDE de Arduino. Detalle: {e}\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return False<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>send_state<\/em>(<em>self<\/em>, <em>state_char<\/em>):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if <em>self<\/em>.ser and <em>self<\/em>.ser.is_open:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.ser.write(<em>state_char<\/em>.encode(&#8216;utf-8&#8217;))<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[Serial] Comando enviado: {<em>state_char<\/em>}\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except serial.<em>SerialException<\/em> as e:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[Serial] Error enviando comando: {e}\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[Serial] Conexi\u00f3n perdida. Intentando reconectar&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.<em>reconnect<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; else:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[Serial] Advertencia: Intento de enviar estado pero no hay conexi\u00f3n abierta.\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.<em>reconnect<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>read_line<\/em>(<em>self<\/em>):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if <em>self<\/em>.ser and <em>self<\/em>.ser.is_open and <em>self<\/em>.ser.in_waiting &gt; 0:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; line = <em>self<\/em>.ser.readline().decode(&#8216;utf-8&#8217;).strip()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return line<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except <em>Exception<\/em> as e:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[Serial] Error leyendo datos: {e}\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return \u00ab\u00bb<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; return \u00ab\u00bb<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>reconnect<\/em>(<em>self<\/em>):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.<em>close<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; time.<em>sleep<\/em>(1)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; return <em>self<\/em>.<em>connect<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>close<\/em>(<em>self<\/em>):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; if <em>self<\/em>.ser and <em>self<\/em>.ser.is_open:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.ser.close()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[Serial] Conexi\u00f3n cerrada.\u00bb)<\/p>\n\n\n\n<p>if __name__ == \u00ab__main__\u00bb:<\/p>\n\n\n\n<p>&nbsp; &nbsp; <em>print<\/em>(\u00ab=== Iniciando test de comunicaci\u00f3n Serial con BMO ===\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; PUERTO_ARDUINO = &#8216;COM3&#8217;<\/p>\n\n\n\n<p>&nbsp; &nbsp; bmo_serial = <em>BmoSerial<\/em>(<em>port<\/em>=PUERTO_ARDUINO)<\/p>\n\n\n\n<p>&nbsp; &nbsp; if bmo_serial.<em>connect<\/em>():<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab\\nIniciando secuencia de prueba de expresiones&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; secuencia = [<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (&#8216;L&#8217;, &#8216;LISTENING&#8217;, 4),<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (&#8216;T&#8217;, &#8216;THINKING&#8217;, 5),<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (&#8216;S&#8217;, &#8216;SPEAKING&#8217;, 4),<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (&#8216;I&#8217;, &#8216;IDLE&#8217;, 3)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; ]<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; for caracter, nombre, espera in secuencia:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab\\n&#8211;&gt; Cambiando a estado: {nombre}\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>send_state<\/em>(caracter)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.<em>sleep<\/em>(espera)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>close<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab\\n=== Prueba finalizada con \u00e9xito ===\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; else:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab\\n=== No se pudo iniciar la prueba ===\u00bb)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a id=\"_Toc229070198\">7.2.4 stt_module.py (Escucha)<\/a><\/h3>\n\n\n\n<p>import speech_recognition as sr<\/p>\n\n\n\n<p>class <em>STTModule<\/em>:<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>__init__<\/em>(<em>self<\/em>):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.recognizer = sr.Recognizer()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.recognizer.pause_threshold = 2.0<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>listen<\/em>(<em>self<\/em>, <em>language<\/em>=\u00bbes-ES\u00bb):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; with sr.Microphone() as source:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab\\n[STT] Ajustando al ruido ambiental&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.recognizer.adjust_for_ambient_noise(source, <em>duration<\/em>=1)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[STT] Escuchando (habla ahora)&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; audio = <em>self<\/em>.recognizer.listen(source, <em>timeout<\/em>=10, <em>phrase_time_limit<\/em>=25)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[STT] Procesando audio&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; texto = <em>self<\/em>.recognizer.recognize_google(audio, <em>language<\/em>=<em>language<\/em>)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[STT] Texto reconocido: &#8216;{texto}'\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return texto<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except sr.WaitTimeoutError:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[STT] Tiempo de espera agotado. No se detect\u00f3 voz.\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return None<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except sr.UnknownValueError:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[STT] No se pudo entender el audio.\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return None<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; except sr.RequestError as e:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[STT] Error de conexi\u00f3n con el servicio de reconocimiento: {e}\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return None<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a id=\"_Toc229070199\">7.2.5 tts_module.py (Generador de voz)<\/a><\/h3>\n\n\n\n<p>import pyttsx3<\/p>\n\n\n\n<p>import threading<\/p>\n\n\n\n<p>class <em>TTSModule<\/em>:<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>__init__<\/em>(<em>self<\/em>):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; pass<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>speak<\/em>(<em>self<\/em>, <em>text<\/em>):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(<em>f<\/em>\u00ab[TTS] Sintetizando voz: &#8216;{<em>text<\/em>}'\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; engine = pyttsx3.init()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; for voice in engine.getProperty(&#8216;voices&#8217;):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if \u00abspanish\u00bb in voice.name.lower() or \u00abes\u00bb in voice.id.lower():<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; engine.setProperty(&#8216;voice&#8217;, voice.id)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; engine.setProperty(&#8216;rate&#8217;, 160)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; engine.setProperty(&#8216;volume&#8217;, 1.0)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; engine.say(<em>text<\/em>)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; engine.runAndWait()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab[TTS] Reproducci\u00f3n finalizada.\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; def <em>speak_async<\/em>(<em>self<\/em>, <em>text<\/em>, <em>callback<\/em>=None):<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; def <em>run_tts<\/em>():<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>self<\/em>.<em>speak<\/em>(<em>text<\/em>)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if <em>callback<\/em>:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>callback<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; thread = threading.<em>Thread<\/em>(<em>target<\/em>=<em>run_tts<\/em>)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; thread.<em>start<\/em>()<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a id=\"_Toc229070200\">7.2.6 main.py (Controlador e inicializador de todos los m\u00f3dulos)<\/a><\/h3>\n\n\n\n<p>import time<\/p>\n\n\n\n<p>import sys<\/p>\n\n\n\n<p>from serial_module import <em>BmoSerial<\/em><\/p>\n\n\n\n<p>from stt_module import <em>STTModule<\/em><\/p>\n\n\n\n<p>from ai_module import <em>AIModule<\/em><\/p>\n\n\n\n<p>from tts_module import <em>TTSModule<\/em><\/p>\n\n\n\n<p>PUERTO_ARDUINO = &#8216;COM3&#8217;<\/p>\n\n\n\n<p>GEMINI_API_KEY = \u2018Aqu\u00ed poner tu propia clave de API\u2019<\/p>\n\n\n\n<p>def <em>main<\/em>():<\/p>\n\n\n\n<p>&nbsp; &nbsp; <em>print<\/em>(\u00ab=\u00bb*50)<\/p>\n\n\n\n<p>&nbsp; &nbsp; <em>print<\/em>(\u00bb &nbsp; INICIANDO ARIB (AI Robot Interaction Bridge) &nbsp;\u00ab)<\/p>\n\n\n\n<p>&nbsp; &nbsp; <em>print<\/em>(\u00ab=\u00bb*50)<\/p>\n\n\n\n<p>&nbsp; &nbsp; <em>print<\/em>(\u00abCargando m\u00f3dulos&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; bmo_serial = <em>BmoSerial<\/em>(<em>port<\/em>=PUERTO_ARDUINO)<\/p>\n\n\n\n<p>&nbsp; &nbsp; stt = <em>STTModule<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; ai = <em>AIModule<\/em>(<em>api_key<\/em>=GEMINI_API_KEY)<\/p>\n\n\n\n<p>&nbsp; &nbsp; tts = <em>TTSModule<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; conexion_exitosa = bmo_serial.<em>connect<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; if not conexion_exitosa:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab\\nADVERTENCIA: Iniciando en modo &#8216;Solo Software&#8217; (Sin Arduino conectado).\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; <em>print<\/em>(\u00ab\\n\u00a1BMO est\u00e1 listo! (Presiona Ctrl+C en cualquier momento para salir)\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; bmo_serial.<em>send_state<\/em>(&#8216;I&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; try:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; while True:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab\\n[BMO] Esperando a que hagas ruido (palmada, hablar fuerte)&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>send_state<\/em>(&#8216;I&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; trigger_detectado = False<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while not trigger_detectado:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; linea = bmo_serial.<em>read_line<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if linea == \u00abRUIDO_DETECTADO\u00bb:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; trigger_detectado = True<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab\\n[BMO] \u00a1Ruido detectado! Despertando&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.<em>sleep<\/em>(0.1)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>send_state<\/em>(&#8216;L&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; user_text = stt.<em>listen<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if user_text:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>send_state<\/em>(&#8216;T&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ai_response = ai.<em>get_response<\/em>(user_text)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>send_state<\/em>(&#8216;S&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tts.<em>speak<\/em>(ai_response)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>send_state<\/em>(&#8216;I&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>send_state<\/em>(&#8216;I&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.<em>sleep<\/em>(0.5)<\/p>\n\n\n\n<p>&nbsp; &nbsp; except <em>KeyboardInterrupt<\/em>:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00ab\\nInterrupci\u00f3n por teclado detectada. Apagando sistema&#8230;\u00bb)<\/p>\n\n\n\n<p>&nbsp; &nbsp; finally:<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>send_state<\/em>(&#8216;I&#8217;)<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; bmo_serial.<em>close<\/em>()<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; <em>print<\/em>(\u00abCerebro desconectado. Adi\u00f3s.\u00bb)<\/p>\n\n\n\n<p>if __name__ == \u00ab__main__\u00bb:<\/p>\n\n\n\n<p>&nbsp; &nbsp; <em>main<\/em>()<\/p>\n\n\n\n<h1 class=\"wp-block-heading\"><a id=\"_Toc229070201\">8. Conclusiones y L\u00edneas de Trabajo Futuro<\/a><\/h1>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070202\">8.1. Conclusiones<\/a><\/h2>\n\n\n\n<p>El proyecto BMO ha demostrado de forma pr\u00e1ctica que es viable construir un asistente rob\u00f3tico con capacidades conversacionales avanzadas a partir de hardware de bajo coste y servicios de IA en la nube. La combinaci\u00f3n de la arquitectura Serial Bridge con un modelo de lenguaje de \u00faltima generaci\u00f3n como Gemini 1.5 Flash permite ofrecer una experiencia de usuario fluida, expresiva y emp\u00e1tica con un presupuesto muy contenido.<\/p>\n\n\n\n<p>M\u00e1s all\u00e1 del logro t\u00e9cnico, el desarrollo ha constituido un ejercicio integral de ingenier\u00eda que ha obligado al grupo a coordinar disciplinas tan diversas como la electr\u00f3nica anal\u00f3gica, la programaci\u00f3n de microcontroladores, el desarrollo backend en Python, la integraci\u00f3n con APIs de IA y la fabricaci\u00f3n f\u00edsica del producto. Esta transversalidad refuerza el valor formativo del proyecto.<\/p>\n\n\n\n<p>Los principales aprendizajes pueden sintetizarse en tres puntos: la importancia de separar adecuadamente las responsabilidades entre hardware y software, el valor de adoptar protocolos de comunicaci\u00f3n deliberadamente sencillos para reducir la superficie de error, y la conveniencia de validar cada subsistema de forma aislada antes de abordar la integraci\u00f3n global.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><a id=\"_Toc229070203\">8.2. L\u00edneas de trabajo futuro<\/a><\/h2>\n\n\n\n<p>De cara a pr\u00f3ximas iteraciones, el proyecto admite varias evoluciones naturales que mejorar\u00edan tanto su autonom\u00eda como su utilidad:<\/p>\n\n\n\n<p>Dise\u00f1ar una carcasa definitiva mediante impresi\u00f3n 3D que mejore la ac\u00fastica, la ergonom\u00eda y la robustez mec\u00e1nica del prototipo.<\/p>\n\n\n\n<p>Sustituir el Arduino Uno por una placa con WiFi integrado (ESP32) para eliminar la dependencia del PC y dotar al asistente de operaci\u00f3n standalone.<\/p>\n\n\n\n<p>Migrar la salida de voz a un servicio de TTS neuronal en la nube (por ejemplo, Google Cloud TTS o Azure Neural Voices) para conseguir una expresividad y naturalidad superiores a las que ofrece pyttsx3.<\/p>\n\n\n\n<p>Incorporar memoria conversacional persistente que permita a BMO recordar el contexto de interacciones pasadas y personalizar sus respuestas con el tiempo.<\/p>\n\n\n\n<p>A\u00f1adir una c\u00e1mara de bajo coste y un modelo de visi\u00f3n multimodal para que el asistente reaccione no solo al sonido sino tambi\u00e9n a la presencia y a las expresiones del usuario.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<div class=\"video-container\"><iframe loading=\"lazy\" title=\"8 de mayo de 2026\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/P7EilnqKBWg?feature=oembed&#038;wmode=opaque\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/div>\n<\/div><\/figure>\n\n\n\n<p>Este seria nuestro video explicando el proyecto a fondo.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Proyecto seytrma2526g10 \u00b7 URJC Autores Diego Garc\u00eda Garc\u00eda, David Garc\u00eda Rodr\u00edguez, \u00c1lvaro Monge P\u00e9rez e Iv\u00e1n Navarro \u00c1lvarez. Resumen Ejecutivo Este documento recoge el dise\u00f1o, desarrollo e implementaci\u00f3n de BMO, un asistente rob\u00f3tico de&#46;&#46;&#46;<\/p>\n","protected":false},"author":325,"featured_media":10505,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-10499","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-proyectos"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>ASISTENTE PERSONAL-IA BMO - Proyectos con Arduino.<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"ASISTENTE PERSONAL-IA BMO - Proyectos con Arduino.\" \/>\n<meta property=\"og:description\" content=\"Proyecto seytrma2526g10 \u00b7 URJC Autores Diego Garc\u00eda Garc\u00eda, David Garc\u00eda Rodr\u00edguez, \u00c1lvaro Monge P\u00e9rez e Iv\u00e1n Navarro \u00c1lvarez. Resumen Ejecutivo Este documento recoge el dise\u00f1o, desarrollo e implementaci\u00f3n de BMO, un asistente rob\u00f3tico de&#046;&#046;&#046;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/\" \/>\n<meta property=\"og:site_name\" content=\"Proyectos con Arduino.\" \/>\n<meta property=\"article:published_time\" content=\"2026-05-08T14:34:53+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-05-08T14:34:56+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-content\/uploads\/sites\/8\/2026\/05\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg\" \/>\n\t<meta property=\"og:image:width\" content=\"1512\" \/>\n\t<meta property=\"og:image:height\" content=\"2016\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"seytrma2526g10\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"seytrma2526g10\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data2\" content=\"26 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/\"},\"author\":{\"name\":\"seytrma2526g10\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#\\\/schema\\\/person\\\/3913b3c0f58389078542c92055d1bf44\"},\"headline\":\"ASISTENTE PERSONAL-IA BMO\",\"datePublished\":\"2026-05-08T14:34:53+00:00\",\"dateModified\":\"2026-05-08T14:34:56+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/\"},\"wordCount\":5137,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/wp-content\\\/uploads\\\/sites\\\/8\\\/2026\\\/05\\\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg\",\"articleSection\":[\"Proyectos\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/\",\"url\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/\",\"name\":\"ASISTENTE PERSONAL-IA BMO - Proyectos con Arduino.\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/wp-content\\\/uploads\\\/sites\\\/8\\\/2026\\\/05\\\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg\",\"datePublished\":\"2026-05-08T14:34:53+00:00\",\"dateModified\":\"2026-05-08T14:34:56+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/#primaryimage\",\"url\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/wp-content\\\/uploads\\\/sites\\\/8\\\/2026\\\/05\\\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg\",\"contentUrl\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/wp-content\\\/uploads\\\/sites\\\/8\\\/2026\\\/05\\\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg\",\"width\":1512,\"height\":2016},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/asistente-personal-ia-bmo\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Portada\",\"item\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"ASISTENTE PERSONAL-IA BMO\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#website\",\"url\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/\",\"name\":\"Proyectos con Arduino.\",\"description\":\"Blog de proyectos de Arduino de alumnos de la URJC\",\"publisher\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#organization\",\"name\":\"Universidad Rey Juan Carlos\",\"url\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/wp-content\\\/uploads\\\/sites\\\/8\\\/2022\\\/05\\\/logourjc-1.jpg\",\"contentUrl\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/wp-content\\\/uploads\\\/sites\\\/8\\\/2022\\\/05\\\/logourjc-1.jpg\",\"width\":745,\"height\":288,\"caption\":\"Universidad Rey Juan Carlos\"},\"image\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#\\\/schema\\\/person\\\/3913b3c0f58389078542c92055d1bf44\",\"name\":\"seytrma2526g10\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/8d8a9d2985a6b6b5dbf4a02706479f7356685551304d7028df6566e886772e24?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/8d8a9d2985a6b6b5dbf4a02706479f7356685551304d7028df6566e886772e24?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/8d8a9d2985a6b6b5dbf4a02706479f7356685551304d7028df6566e886772e24?s=96&d=mm&r=g\",\"caption\":\"seytrma2526g10\"},\"url\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/author\\\/seytrma2526g10\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"ASISTENTE PERSONAL-IA BMO - Proyectos con Arduino.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/","og_locale":"es_ES","og_type":"article","og_title":"ASISTENTE PERSONAL-IA BMO - Proyectos con Arduino.","og_description":"Proyecto seytrma2526g10 \u00b7 URJC Autores Diego Garc\u00eda Garc\u00eda, David Garc\u00eda Rodr\u00edguez, \u00c1lvaro Monge P\u00e9rez e Iv\u00e1n Navarro \u00c1lvarez. Resumen Ejecutivo Este documento recoge el dise\u00f1o, desarrollo e implementaci\u00f3n de BMO, un asistente rob\u00f3tico de&#46;&#46;&#46;","og_url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/","og_site_name":"Proyectos con Arduino.","article_published_time":"2026-05-08T14:34:53+00:00","article_modified_time":"2026-05-08T14:34:56+00:00","og_image":[{"width":1512,"height":2016,"url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-content\/uploads\/sites\/8\/2026\/05\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg","type":"image\/jpeg"}],"author":"seytrma2526g10","twitter_card":"summary_large_image","twitter_misc":{"Escrito por":"seytrma2526g10","Tiempo de lectura":"26 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/#article","isPartOf":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/"},"author":{"name":"seytrma2526g10","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#\/schema\/person\/3913b3c0f58389078542c92055d1bf44"},"headline":"ASISTENTE PERSONAL-IA BMO","datePublished":"2026-05-08T14:34:53+00:00","dateModified":"2026-05-08T14:34:56+00:00","mainEntityOfPage":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/"},"wordCount":5137,"commentCount":0,"publisher":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#organization"},"image":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/#primaryimage"},"thumbnailUrl":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-content\/uploads\/sites\/8\/2026\/05\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg","articleSection":["Proyectos"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/","url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/","name":"ASISTENTE PERSONAL-IA BMO - Proyectos con Arduino.","isPartOf":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/#primaryimage"},"image":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/#primaryimage"},"thumbnailUrl":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-content\/uploads\/sites\/8\/2026\/05\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg","datePublished":"2026-05-08T14:34:53+00:00","dateModified":"2026-05-08T14:34:56+00:00","breadcrumb":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/#primaryimage","url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-content\/uploads\/sites\/8\/2026\/05\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg","contentUrl":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-content\/uploads\/sites\/8\/2026\/05\/WhatsApp-Image-2026-05-08-at-12.39.05.jpeg","width":1512,"height":2016},{"@type":"BreadcrumbList","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/asistente-personal-ia-bmo\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Portada","item":"https:\/\/blogs.etsii.urjc.es\/dseytr\/"},{"@type":"ListItem","position":2,"name":"ASISTENTE PERSONAL-IA BMO"}]},{"@type":"WebSite","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#website","url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/","name":"Proyectos con Arduino.","description":"Blog de proyectos de Arduino de alumnos de la URJC","publisher":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blogs.etsii.urjc.es\/dseytr\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"},{"@type":"Organization","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#organization","name":"Universidad Rey Juan Carlos","url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#\/schema\/logo\/image\/","url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-content\/uploads\/sites\/8\/2022\/05\/logourjc-1.jpg","contentUrl":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-content\/uploads\/sites\/8\/2022\/05\/logourjc-1.jpg","width":745,"height":288,"caption":"Universidad Rey Juan Carlos"},"image":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#\/schema\/person\/3913b3c0f58389078542c92055d1bf44","name":"seytrma2526g10","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/secure.gravatar.com\/avatar\/8d8a9d2985a6b6b5dbf4a02706479f7356685551304d7028df6566e886772e24?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/8d8a9d2985a6b6b5dbf4a02706479f7356685551304d7028df6566e886772e24?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/8d8a9d2985a6b6b5dbf4a02706479f7356685551304d7028df6566e886772e24?s=96&d=mm&r=g","caption":"seytrma2526g10"},"url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/author\/seytrma2526g10\/"}]}},"_links":{"self":[{"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/posts\/10499","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/users\/325"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/comments?post=10499"}],"version-history":[{"count":2,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/posts\/10499\/revisions"}],"predecessor-version":[{"id":10544,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/posts\/10499\/revisions\/10544"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/media\/10505"}],"wp:attachment":[{"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/media?parent=10499"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/categories?post=10499"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/tags?post=10499"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}