{"id":2585,"date":"2019-12-13T22:10:47","date_gmt":"2019-12-13T22:10:47","guid":{"rendered":"https:\/\/blogs.etsii.urjc.es\/dseytr\/?p=2585"},"modified":"2020-11-22T10:52:14","modified_gmt":"2020-11-22T10:52:14","slug":"bateria-virtual","status":"publish","type":"post","link":"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/","title":{"rendered":"Bater\u00eda virtual"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>Idea<\/strong><\/h2>\n\n\n\n<p>Este\nproyecto est\u00e1 estrechamente ligado con el \u00e1mbito musical. Desde el primer\nmomento, ha estado orientado a la creaci\u00f3n de un dispositivo MIDI capaz de\nsimular el comportamiento de una bater\u00eda, pero evitando el coste espacial que\neste supone.<\/p>\n\n\n\n<p>En primera\ninstancia, se definieron unos objetivos ideales a los que aspirar. En el mejor\nde los casos, el dispositivo deber\u00eda ser capaz de:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Detectar\nel supuesto golpeo a un tambor.<\/li><li>Producir\nuna se\u00f1al MIDI con sensibilidad a la velocidad (<em>velocity<\/em>).<\/li><li>Reconocer\nel tambor golpeado a partir de la posici\u00f3n.<\/li><li>Calibrar\nla ubicaci\u00f3n de los tambores virtuales.<\/li><li>Interactuar\ncon <em>DAWs (Digital Audio Workstation).<\/em><\/li><li>Conectarse\na dispositivos v\u00eda bluetooth.<\/li><li>Funcionar\nen plataformas m\u00f3viles.<\/li><li>Dise\u00f1o\nminimalista, c\u00f3modo y ergon\u00f3mico.<\/li><\/ul>\n\n\n\n<p>Despu\u00e9s de\ndefinir la situaci\u00f3n ideal toca poner los pies sobre la tierra. A partir de esta\nidea ambiciosa, se decidi\u00f3 diseccionar en etapas donde el proyecto\nevolucionar\u00eda paso a paso. Esta pr\u00e1ctica se corresponde con la primera etapa.\nEn esta, el dispositivo deber\u00eda ser capaz de:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Detectar\nel supuesto golpeo a un tambor.<\/li><li>Reconocer\nel tambor golpeado a partir de la posici\u00f3n.<\/li><li>Calibrar\nla ubicaci\u00f3n de los tambores virtuales.<\/li><li>Reproducir\nel sonido v\u00eda Arduino.<\/li><li>Ofrecer\n3 modos:<ul><li>Play<\/li><\/ul><ul><li>Calibrate<\/li><\/ul><ul><li>Tempo<\/li><\/ul><\/li><\/ul>\n\n\n\n<p><strong>Componentes<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>\n  <strong>Material<\/strong>\n  <\/td><td>\n  <strong>Coste Proyecto<\/strong>\n  <\/td><td>\n  <strong>Coste Total<\/strong>\n  <\/td><\/tr><tr><td>\n  Placa Arduino Uno\n  <\/td><td>\n  <em>Proporcionado por el profesor<\/em><em><\/em>\n  <\/td><td>\n  <em>Proporcionado por el profesor<\/em>\n  <\/td><\/tr><tr><td>\n  Bot\u00f3n x3 (x6)\n  <\/td><td>\n  0,10\u20ac\/ud\n  <\/td><td>\n  0,60\u20ac\n  * 6 uds\n  <\/td><\/tr><tr><td>\n  Cables m-h\n  <\/td><td>\n  1,6\u20ac\n  <\/td><td>\n  1,6\u20ac\n  <\/td><\/tr><tr><td>\n  Altavoz\n  <\/td><td>\n  2,46\u20ac\n  <\/td><td>\n  2,46\u20ac\n  <\/td><\/tr><tr><td>\n  M\u00f3dulo aceler\u00f3metro MPU6050 GY-521 x1 (x6)\n  <\/td><td>\n  4,01\u20ac\/ud\n  <\/td><td>\n  4,01\u20ac\/ud&nbsp; * 6\n  uds\n  <\/td><\/tr><tr><td>\n  Total\n  <\/td><td>\n  8,67\u20ac\n  <\/td><td>\n  31,72\u20ac\n  <\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Para la\nrealizaci\u00f3n del proyecto, se utilizar\u00e1 como componente principal el\naceler\u00f3metro MPU6050 GY-521; un m\u00f3dulo aceler\u00f3metro y giroscopio de 3 ejes con\n6 DOF en total. Este sensor ser\u00e1 el encargado de monitorizar los movimientos\ndel usuario y darle una respuesta que emule una bater\u00eda.<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>Mpu 6050 GY-521<\/strong><\/p>\n\n\n\n<p>El m\u00f3dulo aceler\u00f3metro mide\naceleraci\u00f3n lineal, aceleraci\u00f3n angular y temperatura; los dos primeros devolviendo\nvalores de entre -32767 y 32767. Este valor no est\u00e1 convertido al Sistema\nM\u00e9trico Internacional, por lo que deberemos realizar su conversi\u00f3n para entender\nmejor los datos.<\/p>\n\n\n\n<p>Por otro\nlado, los valores medidos se almacenan en los registros internos del aceler\u00f3metro.\nMientras que estos son de un tama\u00f1o de 8 bits, los valores medidos son de 16\nbits; con lo que cada palabra ser\u00e1 conformada por dos lecturas de registro.<\/p>\n\n\n\n<p>Sin embargo,\neste m\u00f3dulo tambi\u00e9n cuenta con una imperfecci\u00f3n. La medida del giroscopio y el\naceler\u00f3metro se traspasa entre ejes con cierta sensibilidad, lo cual introduce\nimperfecciones en nuestros c\u00e1lculos. <\/p>\n\n\n\n<p><strong>Dise\u00f1o inicial<\/strong><\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>Detecci\u00f3n de golpeo<\/strong><\/p>\n\n\n\n<p>El dise\u00f1o\ninicial se basaba en reconstruir la posici\u00f3n de la baqueta a partir del \u00e1ngulo\nde giro y la aceleraci\u00f3n por integraci\u00f3n num\u00e9rica:<\/p>\n\n\n\n<p>En el\nmomento en el que la variaci\u00f3n de la posici\u00f3n sufriese un cambio repentino y\ndespu\u00e9s un par\u00f3n, se identificar\u00eda como golpeo.<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>Calibraci\u00f3n de los tambores<\/strong><\/p>\n\n\n\n<p>Por otro\nlado, los tambores ser\u00edan introducidos por el usuario cada vez que se\nreiniciase la aplicaci\u00f3n. El sistema tiene soporte para tantos tambores como\npermita la memoria de la placa Arduino Uno, permiti\u00e9ndole al usuario definir tantos\ntambores como desee, al igual que repartirlos por el espacio a su gusto.<\/p>\n\n\n\n<p>Para definir un nuevo tambor, el\nusuario tendr\u00eda que seguir los siguientes pasos:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Acceder al modo de calibraci\u00f3n.<\/li><li>Opci\u00f3n: Introducir un nuevo tambor.<\/li><li>Delimitar las esquinas del tambor:<ol><li>Poner la baqueta sobre la esquina\nizquierda y pulsar el bot\u00f3n 2.<\/li><\/ol><ol><li>Poner la baqueta sobre la esquina derecha\ny pulsar el bot\u00f3n 2.<\/li><\/ol><ol><li>Poner\nla baqueta sobre la esquina superior y pulsar el bot\u00f3n 2.<\/li><\/ol><ol><li>Poner la baqueta sobre la esquina inferior\ny pulsar el bot\u00f3n 2.<\/li><\/ol><\/li><li>Volver a elegir la misma opci\u00f3n para\ncontinuar introduciendo tambores.<\/li><\/ol>\n\n\n\n<p>Al elegir\neste orden de calibraci\u00f3n en las esquinas de los tambores, somos capaces de\nobtener el vector que describe la normal del mismo. \u00bfDe qu\u00e9 nos sirve esto?\nBien.<\/p>\n\n\n\n<p><strong>Detecci\u00f3n de tambor golpeado<\/strong><\/p>\n\n\n\n<p>Una vez se\nha detectado un golpe, se compara la posici\u00f3n de la baqueta en el momento del\nimpacto con la zona delimitada de cada uno de los tambores. Sin embargo, surge un\nproblema: \u00bfQu\u00e9 pasa si quiero tocar un tambor sobre una pared? <\/p>\n\n\n\n<p>En este\nmomento entra en juego la normal de cada tambor. En funci\u00f3n de este vector, los\ntambores se almacenan en estructuras diferentes. Despu\u00e9s, al detectar el golpeo,\nse extrae el sentido del golpeo de la diferencia de la posici\u00f3n y se busca \u00fanicamente\nen aquella estructura cuyo vector sea similar.<\/p>\n\n\n\n<p>Se definen 3\nestructuras diferentes: Down, Mid y Up. Down se corresponder\u00eda con los bombos\nque tocamos normalmente en una bater\u00eda, Mid se corresponde con el acto de golpear\nuna pared y Up; con tocar mientras estamos tumbados.<\/p>\n\n\n\n<p><strong>Problemas \u2013 Deriva en el c\u00e1lculo de la aceleraci\u00f3n<\/strong><\/p>\n\n\n\n<p>Como ya se ha mencionado antes, el\naceler\u00f3metro presenta un error natural a la hora de medir aceleraci\u00f3n y giro.\nEstas imperfecciones hacen que reconstruir la posici\u00f3n de forma fiel y\nconsistente sea una tarea imposible. <\/p>\n\n\n\n<p>Al obtener los valores de la aceleraci\u00f3n en el eje Z, comprobamos que este dato est\u00e1 cerca del valor real de la gravedad (9.8), sin embargo tiene imprecisiones de m\u00e1s de 2 d\u00e9cimas en algunos instantes.<\/p>\n\n\n\n<p>Esto introduce un error\ndemasiado elevado como para reconstruir la posici\u00f3n a partir de esta\ncomponente, ya que la deriva ir\u00eda aumentando progresivamente hasta distorsionar\npor completo la posici\u00f3n de la baqueta y mostrar valores no representativos.<\/p>\n\n\n\n<p><strong>Soluci\u00f3n 1: C\u00e1lculo del giro a partir\nde la tangente<\/strong><\/p>\n\n\n\n<p>Este m\u00e9todo aprovecha\nla descomposici\u00f3n del valor de la gravedad para calcular los giros en X e Y en\nfunci\u00f3n de la magnitud de la componente de dicha aceleraci\u00f3n.<\/p>\n\n\n\n<p>Como ya he mencionado en el apartado\nanterior, la medici\u00f3n de la aceleraci\u00f3n cuenta con un error m\u00ednimo, pero\napreciable; lo cual resta valor a este m\u00e9todo.<\/p>\n\n\n\n<p>El argumento\nde peso para descartar este m\u00e9todo es que calcula el giro a partir de la\ngravedad. Para ello, no debe haber presente ninguna otra aceleraci\u00f3n en el\nsistema. El nuestro se mueve constantemente.<\/p>\n\n\n\n<p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Soluci\u00f3n 2: Combinaci\u00f3n del\nm\u00e9todo de la tangente con la integraci\u00f3n del giro<\/strong><\/p>\n\n\n\n<p>Nuevamente\nse utiliza el giro obtenido por el m\u00e9todo de la tangente, pero en este caso se\nusa como apoyo. Este m\u00e9todo consiste en integrar los valores de la velocidad\nangular obtenidos por el giroscopio y combinarlos linealmente con los datos\nobtenidos por el m\u00e9todo de la tangente.<\/p>\n\n\n\n<p>Matem\u00e1ticamente, este m\u00e9todo se describe como: <\/p>\n\n\n\n<p>\u00e1ngulo x = 0.98 * (angulo x + gx * dt) + 0.02 * accel_tan_x<\/p>\n\n\n\n<p> \u00e1ngulo y = 0.98 * (angulo y + gy * dt) + 0.02 * accel_tan_y <\/p>\n\n\n\n<p>De esta manera, conseguimos suavizar la onda y\nconseguimos una se\u00f1al m\u00e1s fiel al movimiento real.<\/p>\n\n\n\n<p>El inconveniente\nde este m\u00e9todo es que calculamos los giros X e Y a partir de la componente\ngravitacional y su descomposici\u00f3n, lo cual hace que sea imposible calcular la\nrotaci\u00f3n en Z ya que en un inicio es paralela al suelo.<\/p>\n\n\n\n<p>Este percance introduce una deriva que trastorna el\ndato hasta sacarlo fuera de rango.<\/p>\n\n\n\n<p>Para solucionarlo, introducimos un\nvalor corrector que hace que el giro en Z tienda siempre hacia el centro. <\/p>\n\n\n\n<p><strong>Dise\u00f1o\nfinal<\/strong><\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>Selecci\u00f3n de tambor<\/strong><\/p>\n\n\n\n<p>Tras\nobservar que la reconstrucci\u00f3n de la posici\u00f3n no era una medida ni fiable ni\nfactible, se remodel\u00f3 el sistema por completo.<\/p>\n\n\n\n<p>En lugar de\nposicionar \u00e1reas en el espacio para emular la zona de los tambores, se define\nun rango angular para cada sonido. De esta manera, podemos realizar la selecci\u00f3n\ndel tambor mediante la medida del eje Z anteriormente citada.<\/p>\n\n\n\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>Calibraci\u00f3n del entorno de\nbater\u00eda<\/strong><\/p>\n\n\n\n<p>Al cambiar\nla forma en la que se representan las \u00e1reas de golpeo, la forma de definirlas tambi\u00e9n\ndebe hacer lo mismo. Se remodela el sistema desde el principio.<\/p>\n\n\n\n<p>En este\ncaso, el sistema cuenta con un l\u00edmite angula lateral en -90 grados, a partir\ndel cual no se pueden definir \u00e1reas. Es a partir de este \u00e1ngulo a partir del cual\nse definen la sucesi\u00f3n de fronteras que delimitan las \u00e1reas y, por tanto, los\ntambores.<\/p>\n\n\n\n<p>                  <strong>Detecci\u00f3n de golpeo<\/strong><\/p>\n\n\n\n<p>Paralelamente,\nel golpeo se medir\u00e1 con la aceleraci\u00f3n en el eje Z. Esta decisi\u00f3n de dise\u00f1o viene\ncondicionada por el movimiento que los bater\u00edas acostumbran a realizar.<\/p>\n\n\n\n<p>Los bater\u00edas\nno levantan la baqueta hasta que su punta se dirige al cielo para dar un golpe\na un tambor, sino que realizan un golpe de mu\u00f1eca. Esto se traduce en que el\nvalor que mejor refleja los golpes es la aceleraci\u00f3n y no la rotaci\u00f3n, pero\nentonces tenemos un problema. \u00bfToda aceleraci\u00f3n en z es un golpe?<\/p>\n\n\n\n<p>Si\nobservamos cuidadosamente, cuando tocamos la bater\u00eda, cogemos carrerilla antes\nde atizar un tambor. A partir de este comportamiento, deduje que el algoritmo\ndeber\u00eda constar de dos estados: cargado cuando preparamos el golpe y golpeado\ncuando descargamos el brazo.<\/p>\n\n\n\n<p>En amarillo puede\nobservarse la variaci\u00f3n de la aceleraci\u00f3n en el eje Z.<\/p>\n\n\n\n<p><strong>Problemas finales<\/strong><\/p>\n\n\n\n<p>A causa de\nun error desconocido, el sistema de calibraci\u00f3n deja de funcionar al intentar\ndefinir una nueva \u00e1rea. Existe una alta probabilidad de que se trate de un\nerror de tratamiento de punteros, ya que, al cambiar de dise\u00f1o, hubo que\nremodelar todo el sistema.<\/p>\n\n\n\n<p>Debido a\neste error, no se puede calibrar correctamente los tambores y, por tanto, no se\npueden crear m\u00faltiples \u00e1reas de golpeo de forma manual. <\/p>\n\n\n\n<p><strong>Objetivos cumplidos<\/strong><\/p>\n\n\n\n<p>En\ncomparaci\u00f3n con los objetivos iniciales, considero el trabajo como satisfactorio,\nsobre todo despu\u00e9s de haber conseguido llegar a una soluci\u00f3n tras tantas\ntrabas.<\/p>\n\n\n\n<p>El prototipo\nfinal es capaz de detectar golpeo con sensibilidad y reproducir sonidos perfectamente.\nConsidero que ambos apartados est\u00e1n desarrollados con solidez y que est\u00e1n a la\naltura de los objetivos iniciales que me marqu\u00e9.<\/p>\n\n\n\n<p>Sin embargo,\nel peque\u00f1o fallo en la calibraci\u00f3n del entorno hace que este apartado no sea\nfuncional, lo cual hace invisible un apartado que considero suficientemente robusto.<\/p>\n\n\n\n<p><strong>Mejoras a implementar<\/strong><\/p>\n\n\n\n<p>Como primera\nmejora, se tratar\u00e1 de solucionar el error en el m\u00e9todo de calibraci\u00f3n para desbloquear\ndicha funcionalidad. Adem\u00e1s, se podr\u00eda incluir una peque\u00f1a mejora que consista\nen fijar la rotaci\u00f3n en Z a la media del \u00e1ngulo de la zona golpeada. Esto har\u00eda\nque la correcci\u00f3n que hace que el giro en Z tienda a 0 sea menos importante.<\/p>\n\n\n\n<p>Por otro\nlado, la implementaci\u00f3n en el DAW har\u00eda que el proyecto se tornase mucho m\u00e1s\natractivo, ya que podr\u00edamos reproducir sonidos de bater\u00eda controlados por el\ndispositivo con paso de sensibilidad. Adem\u00e1s, a\u00f1adir una segunda baqueta dar\u00eda\nmucho m\u00e1s realismo y libertad al usuario.<\/p>\n\n\n\n<p>La\nimplementaci\u00f3n de un dise\u00f1o ergon\u00f3mico y comunicaci\u00f3n por bluetooth queda\npendiente como mejor a largo plazo, ya que se tratan de mejoras de comodidad.<\/p>\n\n\n\n<p>Por \u00faltimo,\nrealizar una adaptaci\u00f3n de la aplicaci\u00f3n para m\u00f3viles dar\u00eda el toque final al\nproyecto.<\/p>\n\n\n\n<p><strong>Presentaci\u00f3n<\/strong><\/p>\n\n\n\n<div class=\"wp-block-file\"><a href=\"https:\/\/blogs.etsii.urjc.es\/wp-content\/uploads\/sites\/8\/2019\/12\/Bater\u00eda-virtual-con-Arduino-1.pptx\">Presentacion Bater\u00eda-virtual-con-Arduino-1<\/a><a href=\"https:\/\/blogs.etsii.urjc.es\/wp-content\/uploads\/sites\/8\/2019\/12\/Bater\u00eda-virtual-con-Arduino-1.pptx\" class=\"wp-block-file__button\" download>Descarga<\/a><\/div>\n\n\n\n<p><strong>C\u00f3digo<\/strong> <\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include \"DrumSet.h\"\n\/\/#include \"Drum.h\"\n\/\/#include \"Vector3.h\"\n#include \"DrumStick.h\"\n#include \"Button.h\"\n#include \"AudioManager.h\"\n#include \"Sound.h\"\n#include \"Tempo.h\"\n\n\/\/Global objects\n  \/\/Managers\n  AudioManager audioManager;  \/\/The speaker must be connected to the 9th pin.\n  Tempo tempoManager;\n\n  \/\/Hardware\n  DrumStick drumStick;\n  Button buttons[3] = { \/\/Button. Underneath: each button's pin.\n    4,\n    5,\n    6\n  };  \n\n  \/\/Middleware\n  DrumSet drumSet;\n\n\/\/Global variables\n  int mode = 1;\n  Hit hit;\n\nvoid setup() {\n  \/\/Init objects\n  drumStick.init(&amp;buttons[0], &amp;buttons[1], &amp;buttons[2]);\n  Serial.begin(9600);\n  tempoManager.init(audioManager);\n\n  \/\/Mostrar men\u00fa\n  Serial.println(\"Men\u00fa:\\n\\t- Mode 1: Play mode.\\n\\t- Mode 2: Set and calibrate DrumSet.\\n\\t- Mode 3: Calibrate tempo.\\nYou can switch between modes with the 1st button.\\nTo continue, press the 1st button.\");\n\n  while(!buttons[0].readValue()){\n  \n  }\n\n  Serial.print(\"Mode: \");\n  Serial.println(mode);\n}\n\nvoid loop() {\n  \/\/Actualizar angulo de la baqueta\n  drumStick.updateAngle(); \n    \n  \/\/Actualizar valores de los botones\n  if(buttons[0].readValue()){\n    mode=(mode+1)%3;\n    Serial.print(\"Mode: \");\n    Serial.println(mode);\n\n    if(mode==0){\n      Serial.println(\"Press the 2nd button to enable tempoFunctions or press the 1st one to change between modes.\");\n    }\n  }\n\n  \/\/Dependiendo del modo... (Bot\u00f3n 1)\n  switch(mode){\n    \n    \/\/0 - Play\n    case 0:\n      \/\/Reproducir tempo\n      if(buttons[1].readValue()){\n        tempoManager.switchActivate();\n      }\n      \n      if(tempoManager.isActivated()){\n        tempoManager.playTempo();\n      }\n\n      \/\/Detectar golpe\n      \/\/Llamada a funci\u00f3n --> Input: Drumstick pos. Output: Hit.\n      if(drumStick.checkHit(&amp;hit)){\n        \/\/Debug\n        audioManager.playNiapa(&amp;hit);\n        \n        \/\/Identificar area golpeado\n        drumStick.setHitArea(&amp;hit);\n        if(drumSet.findDrumArea(&amp;hit)){\n          \n          \/\/Reproducir sonido\n          Serial.print(\"DrumArea \");\n          Serial.print(hit.getArea());\n          Serial.println(\" hit.\");\n          audioManager.playSound(drumSet.getDrumArea(hit.getArea())->getSound());\n          \n        }else{\n          \/\/(else ---> mensaje por consola \"MISSED\")\n          Serial.println(\"Find drum area: MISSED.\");\n        }\n      }\n      break;\n\n    \/\/1 - Calibrate\n    case 1:\n      \/\/Crea un set desde 0\n      drumSet.restart();\n      \n      Serial.println(\"Press the 2nd button to add a new DrumArea or press the 1st one to change between modes.\\n Mode: \");\n      Serial.println(mode);\n      while(mode == 1){\n        \/\/Capturar movimiento de la baqueta\n        drumStick.updateAngle();\n        \n        \/\/Capturar cambio de modo\n        if(buttons[0].readValue()){\n          mode=(mode+1)%3;\n        }\n\n        \/\/A\u00f1adir bombo (Bot\u00f3n 2)\n        if(buttons[1].readValue()){\n          \/\/Calibrar posici\u00f3n\n          Serial.println(\"Crear DrumArea.\");\n          DrumArea* d = drumSet.createDrumArea(drumStick);\n                    \n          \/\/Asignar sonido (Bot\u00f3n 3) (mientras que no se pulse ning\u00fan otro bot\u00f3n)\n          Serial.println(\"Options:\");\n          Serial.println(\"  - Button 1: Change mode.\");\n          Serial.println(\"  - Button 2: Insert new Drum.\");\n          Serial.println(\"  - Button 3: Change Drum's sound.\");\n          \n          while(mode==1 &amp;&amp; buttons[1].readValue()==false){\n            if(buttons[0].readValue()){\n              mode=(mode+1)%3;\n            }\n            \n            if(buttons[2].readValue()){\n              Serial.println(\"Sound changed.\");\n              d->changeSound();\n              audioManager.playSound(d->getSound());\n            }\n          }\n        }\n      }\n      break;\n\n    \/\/2 - Set tempo\n    case 2:\n      \/\/Primera marca de timepo\n      int counter = 0;\n      \n      Serial.println(\"Press the 2nd button to start the tempo calibration or press the 1st one to change between modes.\");\n      while(buttons[1].readValue() &amp;&amp; mode==2){\n        if(buttons[0].readValue()){\n          mode=(mode+1)%3;\n        }\n      }\n\n      tempoManager.setTimeStamp(counter);\n      \n      while(mode == 2){\n        counter++;\n        \n        \/\/Capturar cambio de modo\n        if(buttons[0].readValue()){\n          mode=(mode+1)%3;\n        }\n\n        \/\/Nueva marca de tiempo (Bot\u00f3n 2)\n        if(buttons[1].readValue()){\n          tempoManager.setTimeStamp(counter);\n          counter=0;\n          Serial.print(\"Tempo: \");\n          Serial.println(tempoManager.getCalibrationCounter());\n        }\n      }\n\n      tempoManager.updateTempo();\n      break;\n   \n    default:\n      Serial.println(\"Mode: Default.\");\n      break;\n  }\n}<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#ifndef __Acelerometer__\n#define __Acelerometer__\n\n#include &lt;Wire.h>\n#include \"Hit.h\"\n\nclass Acelerometer{\n  private:\n    \/\/Direcci\u00f3n de acceso a memoria del MPU\n    #define MPU 0X68\n    \n    \/\/Ratios de conversi\u00f3n\n    #define A_R 16384.0   \/\/aceleraci\u00f3n a g\/s^2\n    #define G_R 131.0     \/\/rotaci\u00f3n a grados\/s\n    \n    \/\/Conversi\u00f3n de radianes a grados\n    #define RAD_A_DEG = 57.295779\n    \n    \/\/MPU-6050 tiene registros de 8 bits, pero los datos son de 16 bits (MEMORIA)\n      \/\/Valores RAW\n      \/\/int16_t reg_acc [3];  \/\/AcX, AcY, AcZ\n    \n      \/\/Detecci\u00f3n de golpeo\n      bool charged=false;\n      \/\/float acelZ;            \/\/Se recoje primero el RAW, despues se convierte a SI. Local.\n      const float threshHoldCharge = -2.9;\n      const float threshHoldSound = 0.9;\n\n      long previous_time_hit;\n      float dt_hit;\n    \n      long previous_time_loop_checkHit;\n      float dt_loop_checkHit;\n    \n      float posZ;\n      float velZ;\n      \/\/float zHitLength;\n      \/\/float velocity;\n      \/\/float mappedVelocity; \/\/Local\n\n      \/\/Deteccion area\n      \/\/float giroZ;  \/\/Local\n      \/\/float giroZ_SI; \/\/Se calcula sobre giroZ\n      float angleZ;\n\n      long previous_time_loop_area;\n      float dt_loop_area;\n    \n      \/\/Debug\n      \/\/String valores;\n\n      \/\/Functions\n      float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) {\n        return (x - in_min) * (out_max - out_min) \/ (in_max - in_min) + out_min;\n      }\n\n  public:\n    Acelerometer(){\n      \n    }\n  \n    void init(){\n      Wire.begin();\n      Wire.beginTransmission(MPU);\n      Wire.write(0x6B);             \/\/Power management register ()\n      Wire.write(0);                \/\/Poner valores a 0 reinicia el mpu\n      Wire.endTransmission(true);\n    }\n    \n    bool checkHit(Hit* hit){\n      bool isHit=false;\n      float acelZ;\n      float mappedVelocity;\n        \n        \/\/Aceleraci\u00f3n (solo z)\n      Wire.beginTransmission(MPU);\n      Wire.write(0x3F);               \/\/Direcci\u00f3n a partir de la cual empezamos a leer.\n      Wire.endTransmission(false);\n      Wire.requestFrom(MPU,2,true);   \/\/Le pide 6 registros al MPU a partir de la direcci\u00f3n especificada arriba.\n      \n      acelZ=Wire.read()&lt;&lt;8|Wire.read();    \/\/Pide 2 registros y los junta cada vez. El primero es high y el segundo low.\n      \n      \/\/Detecci\u00f3n de golpeo (en funci\u00f3n de aceleraci\u00f3n en z)\n      acelZ = acelZ\/A_R -1;\n\n      \/\/Serial.println(\"IS CHARGED?\");\n      \n      if(acelZ&lt;=threshHoldCharge &amp;&amp; !charged){\n        Serial.println(\"Charged.\");\n        charged = true;\n        previous_time_hit = micros();\n        velZ=0;\n        posZ=0;\n      }\n    \n      if(acelZ >= threshHoldSound &amp;&amp; charged){\n        Serial.println(\"Hit.\");\n        charged=false;\n        dt_hit=(micros()-previous_time_hit)*1e-6;   \/\/El \u00faltimo valor depende de tu delay al final. (x ms * 100.0) (En este caso son 10ms).\n        \/\/zHitLength = -posZ;\n        \/\/velocity = -posZ\/dt_hit;\n        mappedVelocity = mapFloat(-posZ\/dt_hit, 0.6, 1.2, 20.0, 127.0);\n        mappedVelocity = constrain(mappedVelocity, 0.0, 127.0);\n    \n        \/\/Serial.print(\"Z: \"); Serial.println(zHitLength);  \/\/m\n        \/\/Serial.print(\"dt: \"); Serial.println(dt_hit);   \/\/ s\n        \/\/Serial.print(\"Velocity: \"); Serial.println(velocity);  \/\/ m\/s\n        Serial.print(\"Midi velocity: \"); Serial.println(mappedVelocity);\n\n        hit->setVelocity(mappedVelocity);\n        isHit=true;\n      }\n\n      \/\/Calculate vel and pos\n      dt_loop_checkHit= micros()-previous_time_loop_checkHit;\n      previous_time_loop_checkHit = micros();\n      \n      velZ= velZ + acelZ * 9.8 * dt_loop_checkHit * 1e-6; \/\/m\/s\n      posZ= posZ + velZ * dt_loop_checkHit * 1e-6;  \/\/m\n\n      return isHit;\n    }\n\n    void setHitArea(Hit* hit){\n      \/\/Clasificar area en hit\n      \/\/Serial.print(\"Venga porfa: \"); Serial.println(angleZ);\n      hit->setAngle(angleZ);\n    }\n\n    void updateAngle(){\n      float giroZ;\n      \n        \/\/Read data - Giro (solo z)\n      Wire.beginTransmission(MPU);\n      Wire.write(0x47);               \/\/Direcci\u00f3n a partir de la cual empezamos a leer.\n      Wire.endTransmission(false);\n      Wire.requestFrom(MPU,2,true);   \/\/Le pide 6 registros al MPU a partir de la direcci\u00f3n especificada arriba.\n\n      giroZ = Wire.read()&lt;&lt;8|Wire.read();\n      giroZ = giroZ\/G_R;\n      \/\/Serial.println(giroZ_SI);\n\n        \/\/Obtain elapsed time\n      dt_loop_area= (millis()-previous_time_loop_area)*1e-3;\n      previous_time_loop_area = millis();\n\n        \/\/Integrate angleZ\n      angleZ= angleZ + giroZ*dt_loop_area - angleZ*0.005; \n      \/\/Serial.println(angleZ);\n        \/\/Sacar por pantalla\n      \/\/valores = \"90, \" + String(angleZ) + \", -90\";\n      \/\/Serial.println(valores);\n    }\n\n    float getAngle(){\n      return angleZ;\n    }\n  };\n\n#endif<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#ifndef __AudioManager__\n#define __AudioManager__\n\n#include \"Sound.h\"\n\n\/\/ Frecuencia en hercios de las notas musicales\n#define DO3   261.626 \/\/DO3\n\/\/#define REb3  277.183\n#define RE3   293.665\n\/\/#define MIb3  311.127\n#define MI3   293.628\n#define FA3   349.228\n\/\/#define SOLb3  369.994\n#define SOL3  391.995\n\/\/#define LAb3 415.305\n#define LA3   440.000\n\/\/#define SIb3  466.164\n#define SI3   493.883\n#define DO4   523.251\n\nclass AudioManager{\n  private:\n    int pin;\n    \/\/double notas[13]={DO3, REb3, RE3, MIb3, MI3, FA3, SOLb3, SOL3, LAb3, LA3, SIb3, SI3, DO4};\n    \/\/double notas[8]={DO3, RE3, MI3, FA3, SOL3, LA3, SI3, DO4};\n    double notas[3]={DO3, SOL3, SI3};\n    \/\/double tempoSound = DO3;\n    \/*TMRpcm Audio;*\/\n    \n  \n  public:    \n    AudioManager(){\n      pin = 9;\n      \/*Audio.speakerPin(9);\n      Audio.quality(1);\n      Audio.setVolume(5);*\/\n    }\n\n    void playSound(Sound s){\n      \/\/Serial.print(\"Audio: \"); Serial.println(s.getAudioIndex());\n      tone(pin, notas[s.getAudioIndex()], 100);\n    }\n\n    void playTempo(){\n      tone(pin, DO3, 100);\n    }\n\n    void playNiapa(Hit* hit){\n      tone(pin, notas[5], 20);\n    }\n};\n\n#endif<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#ifndef __Button__\n#define __Button__\n\nclass Button{\n  private: \n    int pin;\n    bool value = false;\n\n  public:\n    Button(int inputPin){\n      pin = inputPin;\n      pinMode(pin, INPUT);\n    }\n    \n    bool readValue(){\n      if(digitalRead(pin)==HIGH){\n          Serial.println(\"Please, release the button.\");\n          while(digitalRead(pin)==HIGH){    \/\/Evita falsas lecturas (pulsar el bot\u00f3n solo se realiza en un ciclo de arduino)  \/\/MEMORIA\n          }\n          value = true;\n        }else{\n          value = false;\n        }\n      return value;\n    }\n    \n    \/*bool readValue(){\n      return false;\n    }*\/\n};\n\n\n#endif<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#ifndef __DrumArea__\n#define __DrumArea__\n\n#include \"DrumStick.h\"\n#include \"Sound.h\"\n#include \"Hit.h\"\n\n\nclass DrumArea{\n  private:\n    float iniAngle;   \/\/Max 90  (izquierda)\n    float endAngle;   \/\/Max -90 (derecha)\n    Sound sound;\n\n  public:\n    DrumArea(){\n  }\n\n  void init(DrumStick drumStick, float previousAngle){\n    \n    setIniAngle(previousAngle);\n    Serial.println(\"New Drum Created.\"); Serial.println(previousAngle);Serial.println(iniAngle);\n    \n    calibrateZone(drumStick);\n  }\n\n  void initZero(){\n    Serial.println(\"First Drum Created.\");\n    iniAngle=90;\n    endAngle=90;\n  }\n\n  private:\n    \n    void calibrateZone(DrumStick drumStick){\n      do{\n        Serial.print(\"Posiciona la baqueta en la orientaci\u00f3n deseada y pulsa el bot\u00f3n 2.\");\n\n        \/\/Mientras que el bot\u00f3n est\u00e9 en false, nada. Cuando se accione:\n        while(!drumStick.getButton(1)->readValue()){\n          drumStick.updateAngle();       \/\/La posici\u00f3n de la baqueta se debe actualizar aunque la ejecuci\u00f3n est\u00e9 en este bucle. HACER PRUEBAS\n          Serial.println(\"Vamos.\");\n        }\n        \n       \/\/Guardar posici\u00f3n de la baqueta en pos. \n        endAngle = drumStick.getAngle();\n        \n        if(endAngle>iniAngle){\n          Serial.println(\"Error. You have set a negative area.\");\n          showAngles();\n        }  \n      }while(endAngle>iniAngle);\n      Serial.println(\"Drum calibrated: \");\n      showAngles();\n      \n    }\n\n  public:\n    \/\/Setters\n    void setIniAngle(float newIniAngle){\n      iniAngle= newIniAngle;\n    }\n    \n    void changeSound(){\n      sound.changeSound();\n    }\n\n    \/\/Getters\n    float getEndAngle(){\n      return endAngle;\n    }\n\n    float getMidPoint(){\n      return (endAngle+iniAngle)\/2;\n    }\n    \n    Sound getSound(){\n      return sound;\n    }\n    \n    \/\/Methods\n    bool checkArea(Hit* hit){\n      showAngles();\n      return (hit->getAngle() &lt; iniAngle\n           &amp;&amp; hit->getAngle() > endAngle);\n    }\n\n    void showAngles(){\n       Serial.print(\"Ini angle: \"); Serial.println(iniAngle);\n       Serial.print(\"End angle: \"); Serial.println(endAngle);\n    }\n};\n\n#endif<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#ifndef __DrumSet__\n#define __DrumSet__\n\n#include \"DrumArea.h\"\n\nclass DrumSet{\n  private:\n    DrumArea drumArea[4];\n    int nDrumAreas=1;\n\n  public:\n    static const int arrayLength=4;\n  \n    DrumSet(){\n    }\n\n    void restart(){\n      nDrumAreas=1;\n      drumArea[0]= *new DrumArea();\n      drumArea[0].initZero();\n      drumArea[0].showAngles();\n    }\n\n    \/\/Getters\n    DrumArea* getDrumArea(int index){\n      return &amp;drumArea[index];\n    }\n    \n    \/\/Methods\n    DrumArea* createDrumArea(DrumStick drumStick){\n      if(nDrumAreas == arrayLength-1){\n        Serial.println(\"Error. Max DrumAreas already created.\");\n        exit;\n      }\n      nDrumAreas=nDrumAreas+1;\n      drumArea[nDrumAreas]= *new DrumArea();\n      drumArea[nDrumAreas].init(drumStick, drumArea[nDrumAreas-1].getEndAngle());\n\n      return &amp;drumArea[nDrumAreas];\n    }\n\n    bool findDrumArea(Hit* hit){\n      bool found = false;\n      int i=0;\n      Serial.println(nDrumAreas);\n\n      while(!found &amp;&amp; i!=nDrumAreas){\n        if(drumArea[i].checkArea(hit)){\n          found = true;\n        }else{\n          i++;\n        }\n      }\n\n      if(!found){\n        Serial.print(\"Hit Angle: \"); Serial.println(hit->getAngle());\n      }\n\n      hit->setArea(i);\n      return found;\n    }\n};\n\n#endif<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#ifndef __DrumStick__\n#define __DrumStick__\n\n#include \"Acelerometer.h\"\n#include \"Hit.h\"\n#include \"Button.h\"\n\nclass DrumStick{\n  private:\n    Acelerometer accel;\n    Button* buttons [3];\n\n  public: \n    Drumstick(){\n    }\n\n    void init(Button* one, Button* two, Button* three){\n      accel.init();\n      buttons[0]=one;\n      buttons[1]=two;\n      buttons[2]=three;\n    }\n\n    bool checkHit(Hit* hit){\n      return accel.checkHit(hit);\n    }\n\n    float getAngle(){\n      return accel.getAngle();\n    }\n\n    void updateAngle(){\n      accel.updateAngle();\n    }\n\n    Button* getButton(int index){\n      return buttons[index];\n    }\n\n    bool setHitArea(Hit* hit){\n      accel.setHitArea(hit);\n    }\n};\n\n#endif<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#ifndef __Hit__\n#define __Hit__\n\n\/\/#define MAX_TIME 500\n      \nclass Hit{\n  private:\n    float velocity;\n    float angle;\n    int area = 0;\n  \n  public:\n    Hit(){}\n\n  \/\/Setters\n    void setVelocity(float newVelocity){\n      velocity = newVelocity;\n    }\n\n    void setAngle(float angleZ){\n      angle = angleZ;\n    }\n\n    void setArea(int index){\n      area = index;\n    }\n    \n  \/\/Getters\n    float getAngle(){\n      return angle;\n    }\n    \n    float getVelocity(){\n      return velocity;\n    }\n\n    int getArea(){\n      return area; \n    }\n};\n\n#endif<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#ifndef __Sound__\n#define __Sound__\n\nclass Sound{\n  private:\n    int audioIndex = 0;\n\n  public:\n    Sound(){\n      int audioIndex=0;\n    }\n\n    Sound(int index){\n      audioIndex=index;\n    }\n\n    void changeSound(){\n      audioIndex=(audioIndex+1)%3;\n    }\n    \n    int getAudioIndex(){\n      return audioIndex;\n    }\n};\n\n#endif<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#ifndef __Tempo__\n#define __Tempo__\n\n#include \"AudioManager.h\"\n#include \"Sound.h\"\n\nclass Tempo{\n  int tempoValue = 100;   \/\/Cuanto equivale a 100 BPM (\u00bf?)\n  bool tempoActivated = false;\n\n  int calibrationCounter = 0;\n  int nTimeStamps = 0;\n  \n  int playCounter = 0;\n\n  AudioManager audioManager;\n  \n  public:\n    Tempo(){\n      tempoActivated = false;\n      tempoValue = 100;\n    }\n\n    void init(AudioManager newAudioManager){\n      this->audioManager=newAudioManager;\n    }\n\n\/\/Getters\n    bool isActivated(){\n      return tempoActivated;\n    }\n\n    int getCalibrationCounter(){\n      return calibrationCounter;\n    }\n    \n\/\/Setters\n    void setTimeStamp(int counter){\n      nTimeStamps++;\n      calibrationCounter = (calibrationCounter*(nTimeStamps-1) + counter)\/nTimeStamps;\n    }\n\n    void resetTimeStamp(){\n      calibrationCounter=0;\n      nTimeStamps = 0;\n    }\n\n    void updateTempo(){\n      tempoValue = calibrationCounter;\n    }\n    \n\/\/Functions\n    void activateTempo(){\n      tempoActivated = true;\n      playCounter = 0;\n    }\n\n    void deactivateTempo(){\n      tempoActivated = false;\n    } \n\n    void switchActivate(){\n      if(isActivated()){\n        deactivateTempo();\n      }else{\n        activateTempo();\n      }\n    }\n\n    void playTempo(){\n      playCounter++;\n      if(playCounter >= calibrationCounter){\n        playCounter=0;\n        audioManager.playTempo();\n      }\n    }\n};\n\n#endif<\/pre>\n\n\n\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<div class=\"wp-block-file\">AudioManager.hDescarga<\/div>\n\n\n\n<div class=\"wp-block-file\">Acelerometer.hDescarga<\/div>\n\n\n\n<div class=\"wp-block-file\">Button.hDescarga<\/div>\n\n\n\n<div class=\"wp-block-file\">DrumArea.hDescarga<\/div>\n\n\n\n<div class=\"wp-block-file\">DrumSet.hDescarga<\/div>\n\n\n\n<div class=\"wp-block-file\">DrumStick.hDescarga<\/div>\n\n\n\n<div class=\"wp-block-file\">Hit.hDescarga<\/div>\n\n\n\n<div class=\"wp-block-file\">Sound.hDescarga<\/div>\n\n\n\n<div class=\"wp-block-file\">Tempo.hDescarga<\/div>\n<\/div><\/div>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Idea Este proyecto est\u00e1 estrechamente ligado con el \u00e1mbito musical. Desde el primer momento, ha estado orientado a la creaci\u00f3n de un dispositivo MIDI capaz de simular el comportamiento de una bater\u00eda, pero evitando&#46;&#46;&#46;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2585","post","type-post","status-publish","format-standard","hentry","category-proyectos"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Bater\u00eda virtual - 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\/bateria-virtual\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Bater\u00eda virtual - Proyectos con Arduino.\" \/>\n<meta property=\"og:description\" content=\"Idea Este proyecto est\u00e1 estrechamente ligado con el \u00e1mbito musical. Desde el primer momento, ha estado orientado a la creaci\u00f3n de un dispositivo MIDI capaz de simular el comportamiento de una bater\u00eda, pero evitando&#046;&#046;&#046;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/\" \/>\n<meta property=\"og:site_name\" content=\"Proyectos con Arduino.\" \/>\n<meta property=\"article:published_time\" content=\"2019-12-13T22:10:47+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-11-22T10:52:14+00:00\" \/>\n<meta name=\"author\" content=\"Administrador de la red\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Escrito por\" \/>\n\t<meta name=\"twitter:data1\" content=\"Administrador de la red\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data2\" content=\"19 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\\\/bateria-virtual\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/bateria-virtual\\\/\"},\"author\":{\"name\":\"Administrador de la red\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#\\\/schema\\\/person\\\/bdc38fea664f13638d2a51f4d2fc7211\"},\"headline\":\"Bater\u00eda virtual\",\"datePublished\":\"2019-12-13T22:10:47+00:00\",\"dateModified\":\"2020-11-22T10:52:14+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/bateria-virtual\\\/\"},\"wordCount\":2020,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#organization\"},\"articleSection\":[\"Proyectos\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/bateria-virtual\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/bateria-virtual\\\/\",\"url\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/bateria-virtual\\\/\",\"name\":\"Bater\u00eda virtual - Proyectos con Arduino.\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/#website\"},\"datePublished\":\"2019-12-13T22:10:47+00:00\",\"dateModified\":\"2020-11-22T10:52:14+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/bateria-virtual\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/bateria-virtual\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/bateria-virtual\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Portada\",\"item\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Bater\u00eda virtual\"}]},{\"@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\\\/bdc38fea664f13638d2a51f4d2fc7211\",\"name\":\"Administrador de la red\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/e8e366128f81a82735b0b00fe6d280414b4bad087e380fa9fee9694454b8a6fa?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/e8e366128f81a82735b0b00fe6d280414b4bad087e380fa9fee9694454b8a6fa?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/e8e366128f81a82735b0b00fe6d280414b4bad087e380fa9fee9694454b8a6fa?s=96&d=mm&r=g\",\"caption\":\"Administrador de la red\"},\"sameAs\":[\"https:\\\/\\\/blogs.etsii.urjc.es\"],\"url\":\"https:\\\/\\\/blogs.etsii.urjc.es\\\/dseytr\\\/author\\\/etsiiadmin\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Bater\u00eda virtual - 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\/bateria-virtual\/","og_locale":"es_ES","og_type":"article","og_title":"Bater\u00eda virtual - Proyectos con Arduino.","og_description":"Idea Este proyecto est\u00e1 estrechamente ligado con el \u00e1mbito musical. Desde el primer momento, ha estado orientado a la creaci\u00f3n de un dispositivo MIDI capaz de simular el comportamiento de una bater\u00eda, pero evitando&#46;&#46;&#46;","og_url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/","og_site_name":"Proyectos con Arduino.","article_published_time":"2019-12-13T22:10:47+00:00","article_modified_time":"2020-11-22T10:52:14+00:00","author":"Administrador de la red","twitter_card":"summary_large_image","twitter_misc":{"Escrito por":"Administrador de la red","Tiempo de lectura":"19 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/#article","isPartOf":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/"},"author":{"name":"Administrador de la red","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#\/schema\/person\/bdc38fea664f13638d2a51f4d2fc7211"},"headline":"Bater\u00eda virtual","datePublished":"2019-12-13T22:10:47+00:00","dateModified":"2020-11-22T10:52:14+00:00","mainEntityOfPage":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/"},"wordCount":2020,"commentCount":0,"publisher":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#organization"},"articleSection":["Proyectos"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/","url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/","name":"Bater\u00eda virtual - Proyectos con Arduino.","isPartOf":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/#website"},"datePublished":"2019-12-13T22:10:47+00:00","dateModified":"2020-11-22T10:52:14+00:00","breadcrumb":{"@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blogs.etsii.urjc.es\/dseytr\/bateria-virtual\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Portada","item":"https:\/\/blogs.etsii.urjc.es\/dseytr\/"},{"@type":"ListItem","position":2,"name":"Bater\u00eda virtual"}]},{"@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\/bdc38fea664f13638d2a51f4d2fc7211","name":"Administrador de la red","image":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/secure.gravatar.com\/avatar\/e8e366128f81a82735b0b00fe6d280414b4bad087e380fa9fee9694454b8a6fa?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/e8e366128f81a82735b0b00fe6d280414b4bad087e380fa9fee9694454b8a6fa?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e8e366128f81a82735b0b00fe6d280414b4bad087e380fa9fee9694454b8a6fa?s=96&d=mm&r=g","caption":"Administrador de la red"},"sameAs":["https:\/\/blogs.etsii.urjc.es"],"url":"https:\/\/blogs.etsii.urjc.es\/dseytr\/author\/etsiiadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/posts\/2585","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\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/comments?post=2585"}],"version-history":[{"count":11,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/posts\/2585\/revisions"}],"predecessor-version":[{"id":2878,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/posts\/2585\/revisions\/2878"}],"wp:attachment":[{"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/media?parent=2585"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/categories?post=2585"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.etsii.urjc.es\/dseytr\/wp-json\/wp\/v2\/tags?post=2585"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}