{"id":19141334,"url":"https://github.com/thealbertdev/roboticsub-imu","last_synced_at":"2026-06-29T20:31:51.298Z","repository":{"id":104041640,"uuid":"423472504","full_name":"TheAlbertDev/roboticsub-imu","owner":"TheAlbertDev","description":null,"archived":false,"fork":false,"pushed_at":"2021-11-01T13:24:57.000Z","size":3765,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-30T07:37:02.568Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TheAlbertDev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-01T13:20:52.000Z","updated_at":"2024-06-03T10:57:53.000Z","dependencies_parsed_at":"2023-03-13T15:00:08.871Z","dependency_job_id":null,"html_url":"https://github.com/TheAlbertDev/roboticsub-imu","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/TheAlbertDev/roboticsub-imu","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheAlbertDev%2Froboticsub-imu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheAlbertDev%2Froboticsub-imu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheAlbertDev%2Froboticsub-imu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheAlbertDev%2Froboticsub-imu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheAlbertDev","download_url":"https://codeload.github.com/TheAlbertDev/roboticsub-imu/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheAlbertDev%2Froboticsub-imu/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34942665,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-29T02:00:05.398Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T07:22:46.517Z","updated_at":"2026-06-29T20:31:51.270Z","avatar_url":"https://github.com/TheAlbertDev.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Control remoto de un servomotor con control de par\n\nEste es un proyecto basado en dos módulos ESP32. El primero, de ahora en adelante ***master***, monitorizará su orientación mediante una [\u003cabbr title=\"Inertial Measurement Unit\"\u003eIMU\u003c/abbr\u003e](https://es.wikipedia.org/wiki/Unidad_de_medici%C3%B3n_inercial) y la enviará inalámbricamente a un segundo módulo ESP32, de ahora en adelante ***slave***. El *slave* recibirá la orientación del *master* y, en función de esta, orientará un servomotor que emula uno de los motores del EndoWrist. A su vez, el *slave* devolverá al *master* el par de motor medido emulando el contacto con tejidos. Ese par de motor lo enviaremos por conexión serie a RoboDK junto a los ángulos Euler que ya estábamos enviando en la sesión anterior.\n\n## Objetivos\n\n- Monitorizar la orientación de un módulo ESP32 llamado *master*.\n- Enviar la orientación de manera inalámbrica a un segundo módulo ESP32 llamado *slave*.\n- Orientar un servomotor en *slave* según la orientación del *master*.\n- Delvolver al *master* una medición del par aplicado por el servomotor.\n- Enviar el par,  junto con la orientación, a RoboDK mediante conexión serie.\n\n## Procedimiento\n\n\u003e El el presente documento se muestra el código necesario para realizar la comunicación inalámbrica entre dos módulos ESP32. El código **debe de añadirse** al código ya existente de la sesión anterior (medición de ángulos Euler, envío por terminal serie, etc.). El código de la sesión anterior se ha obviado de los ejemplos para hacerlos más claros.\n\n### Comunicación inalámbrica\n\n#### Obtener las direcciones MAC\n\nPara realizar las transmisiones, tanto el *master* como el *slave* necesitan saber la dirección MAC del otro. Esta dirección MAC les identifica en una red de manera inequívoca. Para obtener la dirección MAC, utilizaremos el siguiente código:\n\n```c++\n// Incluimos la libreria para una conexion WiFi\n#include \"WiFi.h\"\n \nvoid setup(){\n  // Configuramos la comunicacion serie\n  Serial.begin(115200);\n  // Habilitamos el WiFi en modo estacion\n  WiFi.mode(WIFI_MODE_STA);\n  // Mostramos por terminal la direccion MAC del dispositivo\n  Serial.println(WiFi.macAddress());\n}\n \nvoid loop(){\n  // Aqui no hacemos nada de momento\n}\n```\n\nDe este modo, abrimos un terminal en Arduino IDE y podremos obtener la dirección MAC. Esto hay que ejecutarlo en ambos módulos. **¡Apuntaros el identificador!** Será imprescindible.\n\n### *Sketch* del *master*\n\nEn el *master*, ejecutamos el siguiente código:\n\n```c++\n// Incluimos las librerias para el protocolo ESP_NOW y \n// una conexion WiFi\n#include \u003cesp_now.h\u003e\n#include \u003cWiFi.h\u003e\n\n// Direccion MAC del slave (reemplazar con el que habeis obtenido)\nuint8_t slaveMacAddress[] = {0xAC, 0x67, 0xB2, 0x05, 0x64, 0xC0};\n\n// Esta es la estructura de los datos que enviaremos\n// De momento solo enviaremos un numero\ntypedef struct {\n    float exampleNumber;\n} TxMessage;\n// Creamos una varaible con la estructura recien creada\nTxMessage dataToSlave;\n\n// Esta es la estructura de los datos que reciviremos\ntypedef struct {\n    float torque;\n} RxMessage;\n// Creamos una varaible con la estructura recien creada\nRxMessage dataFromSlave;\n\n// Funcion que se ejecutara cada vez que se haya recibido un mensaje\nvoid OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {\n  // Copiamos los datos recibidos a nuestra variable dataFromSlave\n  memcpy(\u0026dataFromSlave, incomingData, sizeof(dataFromSlave));\n  Serial.print(\"Torque received:\\t\");\n  Serial.println(dataFromSlave.torque, DEC);\n}\n \nvoid setup() {\n  // Configuramos la comunicacion serie\n  Serial.begin(115200);\n\n  // Habilitamos el WiFi en modo estacion\n  WiFi.mode(WIFI_STA);\n\n  // Inicializamos ESP-NOW\n  if (esp_now_init() != ESP_OK) {\n    Serial.println(\"Error initializing ESP-NOW\");\n    return;\n  }\n  \n  // Registramos el slave\n  esp_now_peer_info_t peerInfo = {};\n  memcpy(peerInfo.peer_addr, slaveMacAddress, 6);\n  peerInfo.channel = 0;  \n  peerInfo.encrypt = false;\n  \n  // Anadimos el slave     \n  if (esp_now_add_peer(\u0026peerInfo) != ESP_OK){\n    Serial.println(\"Failed to add peer\");\n    return;\n  }\n  // Configuramos la funcion a utilizar cada vez que se reciva\n  // un mensaje\n  esp_now_register_recv_cb(OnDataRecv);\n}\n \nvoid loop() {\n\n  // Como ejemplo, enviaremos un numero que iremos incrementando\n  esp_err_t result = esp_now_send(slaveMacAddress, (uint8_t *) \u0026dataToSlave, sizeof(dataToSlave));\n   \n  if (result == ESP_OK) {\n    Serial.println(\"Sent with success\");\n  }\n  else {\n    Serial.println(\"Error sending the data\");\n  }\n  \n  // Dejaremos 1 segundo entre envios y incrementamos el numero\n  // a enviar\n  dataToSlave.exampleNumber = dataToSlave.exampleNumber + 1;\n  delay(1000);\n}\n```\n\n#### *Sketch* del *slave*\n\nAhora toca cargar el código al *slave*. **Recordad poner la dirección MAC del *master* en este caso.** Omito comentarios de código que ya estuvieran en el ejemplo anterior:\n\n```c++\n#include \u003cesp_now.h\u003e\n#include \u003cWiFi.h\u003e\n\n// Direccion MAC del master (reemplazar con el que habeis obtenido)\nuint8_t masterMacAddress[] = {0x7C, 0x9E, 0xBD, 0x61, 0xA3, 0x88};\n\n// Esta es la estructura de los datos que enviaremos\ntypedef struct {\n    float torque;\n} TxMessage;\n// Creamos una varaible con la estructura recien creada\nTxMessage dataToMaster;\n\n// Esta es la estructura de los datos que reciviremos\ntypedef struct {\n    float exampleNumber;\n} RxMessage;\n// Creamos una varaible con la estructura recien creada\nRxMessage dataFromMaster;\n\nvoid OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {\n  // Copiamos los datos recibidos a nuestra variable dataFromMaster\n  memcpy(\u0026dataFromMaster, incomingData, sizeof(dataFromMaster));\n  Serial.print(\"Example number received:\\t\");\n  Serial.println(dataFromMaster.exampleNumber, DEC);\n}\n \nvoid setup() {\n  Serial.begin(115200);\n  WiFi.mode(WIFI_STA);\n\n  if (esp_now_init() != ESP_OK) {\n    Serial.println(\"Error initializing ESP-NOW\");\n    return;\n  }\n  \n  // Registramos el master\n  esp_now_peer_info_t peerInfo = {};\n  memcpy(peerInfo.peer_addr, masterMacAddress, 6);\n  peerInfo.channel = 0;  \n  peerInfo.encrypt = false;\n  \n  // Anadimos el master     \n  if (esp_now_add_peer(\u0026peerInfo) != ESP_OK){\n    Serial.println(\"Failed to add peer\");\n    return;\n  }\n\n  esp_now_register_recv_cb(OnDataRecv);\n}\n \nvoid loop() {\n\n  // Como ejemplo, enviaremos un numero que iremos incrementando\n  // emulando el torque\n  esp_err_t result = esp_now_send(masterMacAddress, (uint8_t *) \u0026dataToMaster, sizeof(dataToMaster));\n   \n  if (result == ESP_OK) {\n    Serial.println(\"Sent with success\");\n  }\n  else {\n    Serial.println(\"Error sending the data\");\n  }\n  \n  // Incrementamos el numero a enviar\n  dataToMaster.torque = dataToMaster.torque + 1;\n  delay(1000);\n}\n```\n\n#### *Challenge* #1\n\nBien. Si hemos seguido las instrucciones al pie de la letra, ahora mismo tenemos dos ESP32 comunicándose de manera inalámbrica. Ahora el reto (y eso quiere decir, que tenéis que hacerlo vosotros) es editar el código del *master* para incorporar este código que teníais de la sesión anterior. Es decir, hacer que el *master* lea los ángulos de Euler de la IMU y los envíe al *slave*. ¿Qué deberéis hacer para ello?\n\n- Incorporar la parte del código de la IMU (importar librería, inicializar la IMU, obtener los datos y guardarlos en una variable, etc.).\n- Incorporar el código de comunicación.\n- Adaptar el código de comunicación (modificar las estructuras `RxMessage` y `TxMessage` (tanto del *master* como del *slave*) para incorporar RPW)\n- ¡Comprueba que todo funcione antes de seguir adelante!\n\n\u003e Si te clavas en el reto o te salen errores, peléate un poco pero pregunta de inmediato a los profesores. El reto, por su dificultad, está pensado para hacerse en 10~15 min. Si tardas más no te dará tiempo a hacer la práctica en clase. 😅\n\n### Control de un servomotor\n\n#### Conexionado del servomotor\n\nVamos a controlar el [servomotor KY66](http://www.ee.ic.ac.uk/pcheung/teaching/DE1_EE/stores/sg90_datasheet.pdf) con el *slave*. Para ello, conectamos nuestro ESP32 en una *protoboard* y conectamos los pines \u003ckbd\u003eVIN\u003c/kbd\u003e, \u003ckbd\u003eD33\u003c/kbd\u003e y \u003ckbd\u003eGND\u003c/kbd\u003e con los cables de color ![rojo](https://via.placeholder.com/15/ff0000/000000?text=+)`rojo`, ![naranja](https://via.placeholder.com/15/FFA500/000000?text=+)`naranja`,  y ![marrón](https://via.placeholder.com/15/744700/000000?text=+)`marrón`, respectivamente.\n\n#### Posicionamiento del motor\n\nPara controlar el motor utilizaremos la librería [Servo Library for ESP32 de John K. Bennett](https://github.com/madhephaestus/ESP32Servo). Esta librería genera una salida [\u003cabbr title=\"Pulse-Width Modulation\"\u003ePWM\u003c/abbr\u003e](https://es.wikipedia.org/wiki/Modulaci%C3%B3n_por_ancho_de_pulsos) para fijar la posición del motor. El motor se puede posicionar de -90º a 90º. O lo que es lo mismo, podemos girarlo 180º. \n\nPara instalar la librería, en Arduino IDE, nos vamos a `Sketch \u003e Include Library \u003e Manage Libraries`, escribimos `ESP32Servo` en el campo de búsqueda e instalamos la librería indicada. Ya la hemos instalado 😉\n\nVamos a ver el código para posicionar el servo:\n\n```c++\n// Incluimos la libreria del servo\n#include \u003cESP32Servo.h\u003e\n\n// Declaramos una variable para el servo\nServo servoMotor;\n\nvoid setup() {\n  // Configuramos la comunicacion serie\n  Serial.begin(115200);\n\n  // Nos agenciamos unos timers internos para poder operar\n  // (¿Que que es un timer? -\u003e https://bit.ly/2Y9BAwg)\n  ESP32PWM::allocateTimer(0);\n  ESP32PWM::allocateTimer(1);\n  ESP32PWM::allocateTimer(2);\n  ESP32PWM::allocateTimer(3);\n  servoMotor.setPeriodHertz(50);\n  \n  // Iniciamos el servo indicandole que utilizaremos el pin 33\n  servoMotor.attach(33);\n}\n\nvoid loop() {\n  \n  // Vamos a hacer un bucle en el que el motor vaya de -90 a 0 a +90\n  // en intervalos de 1 segundo (acordaros que esto es lo mismo que\n  // hacer 0, 90, 180)\n  Serial.println(\"Servo position: -90º\");\n  servoMotor.write(0);\n  delay(1000);\n\n  Serial.println(\"Servo position: 0º\");\n  servoMotor.write(90);\n  delay(1000);\n\n  Serial.println(\"Servo position: 90º\");\n  servoMotor.write(180);\n  delay(1000);\n}\n```\n\nEn este instante debemos de tener nuestro servo operando sin problemas 🥳\n\n#### *Challenge* #2\n\nAñade el código de posicionamiento del servo al código del primer apartado de tal modo que el servo se posicione según **el *pitch*** que enviemos desde el *master*.\n\n### Lectura del torque\n\nAhora nos falta leer el [torque o par](https://es.wikipedia.org/wiki/Par_motor) que está haciendo el motor. Lo mediremos midiendo (\"*¡Gracias, Capitan Obvious!*\") la potencia eléctrica que consume el motor. Acordemonos de cuál era la expresión de la potencia: P = I·V\n\nEn nuestro caso, la tensión será siempre la misma, así que lo que haremos será medir la corriente. Lo haremos con una resistencia conectada en serie con el motor. Midiendo la caída de tensión en la resistencia (cuyo valor conocemos), podemos obtener la corriente que pasa por el motor. Puesto que la caído de tensión en motor será **casi** constante (ya no será constante puesto que hemos añadido una resistencia en serie), la potencia es directamente proporciona a la corriente que medimos y el torque/par también.\n\nHasta aquí sería lo normal, pero si lo probáramos veríamos que no nos da mucha información. ¿Porqué? Porque el motor intenta ir a la posición designada a máxima velocidad (máxima potencia), por lo que no tendríamos modo de diferenciar cuando el motor está aplicando un torque máximo para ir a una posición o cuando lo está aplicando porque se ha encontrado un obstáculo.\n\nAún así, podemos diferencia ambos casos añadiendo una variable más a la medida: el tiempo. Si el tiempo durante el cual el motor aplica el par máximo es corto, supondremos que el motor simplemente se ha posicionado en una posición determinada. En cambio, si  el par es aplicado durante un tiempo relativamente largo, supondremos que el motor no puede llegar a su posición final (o le cuesta) porque se ha encontrado un obstáculo.\n\nPara añadir la variable tiempo, simplemente integraremos la medida de la corriente. Es decir, iremos sumando la medida de corriente constantemente y utilizaremos este valor como indicador del par. Es importante que de manera periódica vayamos reiniciándose esta integral, ¡si no, nos iríamos a infinito!\n\n#### Conexionado de la resistencia\n\nConectad la resistencia en serie con el motor. Hacedlo con el terminal que va a \u003ckbd\u003eGND\u003c/kbd\u003e. Obtened mediante el código de colores el valor de la resistencia.\n\n\u003e ¿Que no sabes qué significa colocar la resistencia en serie? ¿Que no sabes qué es lo del código de colores? No me hagas preguntárselo al Dr. Miribel ¡o os hago volver a Electrónica Aplicada! 😒\n\nAhora, conectad el nodo entre el \u003ckbd\u003eGND\u003c/kbd\u003e del motor y el terminal de la resistencia al pin \u003ckbd\u003eD34\u003c/kbd\u003e. Apoyaros/utilizad la *protoboard* para ello.\n\n#### *Sketch* para medir corriente\n\nPara medir la corriente utilizamos el siguiente *sketch* (utilizaremos como base el ejemplo del apartado anterior para tener el motor en movimiento):\n\n```c++\n#include \u003cESP32Servo.h\u003e\n\nServo servoMotor;\n\n// Constante con el valor de la resistencia que utilizamos\n// (Valor totalmente inventado para que no lo useis... poned\n// el valor de vuestra resistencia)\nconst float Rshunt = 1000.0;\n\n// Variable que almacenara el torque\nfloat torque = 0;\n\n// Funcion que utilizaremos para obtener la corriente a partir\n// del valor del ADC\nfloat getCurrent(void) {\n\n  uint16_t adcValue = analogRead(34);\n  \n  return ((float)adcValue/4095.0 * 3.3)/Rshunt;\n  \n}\n\nvoid setup() {\n  \n  Serial.begin(115200);\n  \n  ESP32PWM::allocateTimer(0);\n  ESP32PWM::allocateTimer(1);\n  ESP32PWM::allocateTimer(2);\n  ESP32PWM::allocateTimer(3);\n  servoMotor.setPeriodHertz(50);\n  \n  servoMotor.attach(33);\n\n  // Configuramos el pin D34 como de entrada\n  // Lo utilizaremos para leer un valor analogico\n  pinMode(34, INPUT);\n\n  // Posicionamos el motor a un de los extremos\n  Serial.println(\"Servo position: -70º\");\n  servoMotor.write(20);\n  // Le damos un segundo que llegue a su posición\n  delay(2000);\n\n  // Leemos la corriente (se supone que en reposo)\n  torque = getCurrent();\n  Serial.print(\"Current:\\t\");\n  Serial.println(torque, DEC);\n\n  // Ahora llevamos el motor al otro extremo\n  // y enseguida medimos corriente\n  Serial.println(\"Servo position: 70º\");\n  servoMotor.write(160);\n\n}\n\nvoid loop() {\n\n  // Leemos la corriente y lo sacamos por terminal\n  torque = getCurrent();\n  Serial.print(\"Current:\\t\");\n  Serial.println(torque, DEC);\n  \n}\n```\n\nSi lleváis la salida del terminal a una hoja de datos (dígase Excel), podréis observar la siguiente medición:\n\n![image-20211101134533729](./assets/imgs/torque-trans-no-obstacle.png)\n\nPodéis ver cómo hay un pico inicial en el que el motor quiere llegar rápido a su posición final y otro pequeño pico final en el que el motor corrige su error estacionario.\n\nAhora, si ejecutamos otra vez el programa obstaculizando el motor (frenando con la mano, por ejemplo), obtenemos la siguiente respuesta:\n\n![image-20211101135020818](./assets/imgs/torque-trans-obstacle.png)\n\nEn este caso, le he fastidiado bastante y no lo dejaba girar en absoluto, ocasionando que el motor intentara moverse, se parase, intentaba moverse otra vez, y así periódicamente. De ahí los pulsos que veis en la gráfica hasta llegar al final, donde se ve como ya no hay obstáculo.\n\nComo os comentaba, mirando simplemente el valor de la corriente no es suficiente para saber si hay un obstáculo o no. El valor pico ronda los 0.5 A (lo máximo que puede dar un puerto USB) y ese valor se alcanza tanto en el caso con y sin obstáculo.\n\nVamos a integrar la corriente para tener una medición que tenga en cuenta el tiempo.\n\n#### Integración de la corriente\n\nEl *sketch* seria el mismo y simplemente modificaríamos la función `getCurrent()` para integrar la corriente durante un determinado tiempo:\n\n```c++\n#include \u003cESP32Servo.h\u003e\n\nServo servoMotor;\n\nconst float Rshunt = 1000.0;\n\nfloat torque = 0;\n\nfloat getCurrent(uint32_t integrationTimeMs) {\n\n  uint32_t startTime = millis();\n  float integratedCurrent = 0;\n\n  // Vamos sumando la medicion de corriente durante el tiempo\n  // fijado integrationTimeMs (en milisegundos)\n  while(millis()\u003c startTime + integrationTimeMs) {\n    uint16_t adcValue = analogRead(34);\n    integratedCurrent = integratedCurrent + ((float)adcValue/4095.0 * 3.3)/Rshunt;\n  }\n  \n  return integratedCurrent;\n  \n}\n\nvoid setup() {\n  \n  Serial.begin(115200);\n  \n  ESP32PWM::allocateTimer(0);\n  ESP32PWM::allocateTimer(1);\n  ESP32PWM::allocateTimer(2);\n  ESP32PWM::allocateTimer(3);\n  servoMotor.setPeriodHertz(50);\n  \n  servoMotor.attach(33);\n\n  pinMode(34, INPUT);\n\n  Serial.println(\"Servo position: -45º\");\n  servoMotor.write(45);\n  delay(2000);\n\n  torque = getCurrent(20);\n  Serial.print(\"Initial current:\\t\");\n  Serial.println(torque, DEC);\n\n  Serial.println(\"Servo position: 45º\");\n  servoMotor.write(135);\n\n}\n\nvoid loop() {\n\n  torque = getCurrent(20);\n  Serial.print(\"Current:\\t\");\n  Serial.println(torque, DEC);\n  \n}\n```\n\nAhora, para el caso sin obstáculo tenemos:\n\n![image-20211101140230310](./assets/imgs/torque-trans-no-obstacle-integrated.png)\n\nVemos el mismo transitorio, pero destacan un par de aspectos debidos a la integración:\n\n1. La señal sufre un desfase.\n2. La señal dispone de menos muestras.\n3. Los *bias* o errores estacionarios que puedan haber se acumulan en la medición.\n4. Realmente, dejamos de leer corriente (ni de broma esta pasando 50 A por el motor).\n\n**El último punto es importante** y si no lo he modificado en el nombre de la función o en las gráficas es porque quiero que busquéis qué magnitud estamos leyendo realmente al integrar la corriente y que, en el proyecto final que presentéis, indiquéis correctamente esa magnitud en el nombre de la función (en lugar de la actual `getCurrent()`) e indiquéis correctamente las unidades de la magnitud leída (que no serán amperios).\n\nEn el caso del obstaculo obtenemos:\n\n![image-20211101140725189](./assets/imgs/torque-trans-obstacle-integrated.png)\n\nEn este caso, fijaros que tenemos los mismos pulsos porque el motor hace lo mismo que antes: si te pasas de fuerza el motor para de intentarlo y lo intenta unos milisegundos más tarde. Por esos siguen apareciendo los pulsos. Pero fijaros en su magnitud. Debido a que ahora estamos integrando, podemos observar que se obtiene un valor de par que dobla al caso sin obstáculo. Ahora, midiendo directamente el valor de la integral, sí que podemos diferenciar el caso en el que hay un obstáculo y en el que no.\n\n#### *Challenge* #3\n\n¡Último *challenge*! Incorpora la medición del torque al *slave*, envíaselo al *master* y haze que este último se lo envíe al RoboDK junto con los ángulos Euler.\n\n## Conclusiones\n\nEn esta sesión hemos visto cómo o realizar una comunicación inalámbrica entre dos módulos basado en ESP32. En esa comunicación inalámbrica, hemos enviado de un *master* a un *slave* los ángulos Euler de la orientación obtenida mediante una IMU. Con estos ángulos, con el *pitch* en particular, controlamos un servo con el *slave*. A su vez, el *slave* mide el par aplicado por el motor y se lo devuelve al *master*. Por último el *master* le envía toda la información al RoboDK para simular la escena.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthealbertdev%2Froboticsub-imu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthealbertdev%2Froboticsub-imu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthealbertdev%2Froboticsub-imu/lists"}