{"id":307,"date":"2025-08-11T19:53:51","date_gmt":"2025-08-11T19:53:51","guid":{"rendered":"https:\/\/cascajolabs.es\/?p=307"},"modified":"2025-08-12T18:44:45","modified_gmt":"2025-08-12T18:44:45","slug":"esp32-usb-host-midi-controller","status":"publish","type":"post","link":"https:\/\/cascajolabs.es\/?p=307","title":{"rendered":"ESP32 USB HOST &#8211; MIDI (quickest\/simplest version)"},"content":{"rendered":"\n<p>Some time ago, I made a quick little project to connect my AKAI MPK Mini MIDI keyboard to an ESP32-S3.<br>Here\u2019s a short demo video:<\/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<iframe loading=\"lazy\" title=\"ESP32 S3 - AKAI MPK | USB host - LED, speaker, MIDI #music #piano #electronics #esp32\" width=\"750\" height=\"422\" src=\"https:\/\/www.youtube.com\/embed\/DEeGbKKkM1E?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>This post is about this very first, simple version.<\/p>\n\n\n\n<p>The first step, if you have the same board I do, is to make a small hardware change (solder USB-OTG pads) so that the USB-C port can power the keyboard.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1278\" height=\"629\" src=\"https:\/\/cascajolabs.es\/wp-content\/uploads\/2025\/08\/imagen-edited.png\" alt=\"\" class=\"wp-image-309\" style=\"width:501px;height:auto\" srcset=\"https:\/\/cascajolabs.es\/wp-content\/uploads\/2025\/08\/imagen-edited.png 1278w, https:\/\/cascajolabs.es\/wp-content\/uploads\/2025\/08\/imagen-edited-300x148.png 300w, https:\/\/cascajolabs.es\/wp-content\/uploads\/2025\/08\/imagen-edited-1024x504.png 1024w, https:\/\/cascajolabs.es\/wp-content\/uploads\/2025\/08\/imagen-edited-768x378.png 768w\" sizes=\"auto, (max-width: 1278px) 100vw, 1278px\" \/><\/figure>\n\n\n\n<p>The idea is:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The USB-C port labeled <strong><code>USB<\/code><\/strong> will be connected to the keyboard.<\/li>\n\n\n\n<li>The board itself will be powered through the port labeled <strong><code>COM<\/code><\/strong>.<\/li>\n<\/ul>\n\n\n\n<p>For the software, I used the <strong>EspUsbHost<\/strong> library by <em>tanakamasayuki<\/em> on <a href=\"https:\/\/github.com\/tanakamasayuki\/EspUsbHost\">GitHub<\/a>.<\/p>\n\n\n\n<p>Since this was just a quick experiment, I didn\u2019t need all the extra features from the library, so I tweaked it a bit to fit my needs.<br>Here\u2019s the <code><strong>.h<\/strong><\/code> file with my changes, compared to the original:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#ifndef __EspUsbHost_H__\n#define __EspUsbHost_H__\n\n#include &lt;Arduino.h&gt;\n#include &lt;usb\/usb_host.h&gt;\n#include &lt;class\/hid\/hid.h&gt;\n#include &lt;rom\/usb\/usb_common.h&gt;\n\nclass EspUsbHost {\npublic:\n  bool isReady = false;\n  uint8_t interval;\n  unsigned long lastCheck;\n\n  struct endpoint_data_t {\n<strong>    uint8_t bInterfaceNumber; \/\/ Added<\/strong>\n    uint8_t bInterfaceClass;\n    uint8_t bInterfaceSubClass;\n    uint8_t bInterfaceProtocol;\n    uint8_t bCountryCode;    \n  };\n  endpoint_data_t endpoint_data_list&#91;17];\n<strong>  uint8_t _bInterfaceNumber; \/\/ Added<\/strong>\n  uint8_t _bInterfaceClass;\n  uint8_t _bInterfaceSubClass;\n  uint8_t _bInterfaceProtocol;\n  uint8_t _bCountryCode;\n  esp_err_t claim_err;\n\n  usb_host_client_handle_t clientHandle;\n  usb_device_handle_t deviceHandle;\n  uint32_t eventFlags;\n  usb_transfer_t *usbTransfer&#91;16];\n  uint8_t usbTransferSize;\n  uint8_t usbInterface&#91;16];\n  uint8_t usbInterfaceSize;\n\n  hid_local_enum_t hidLocal;\n\n  void begin(void);\n  void task(void);\n\n  static void _clientEventCallback(const usb_host_client_event_msg_t *eventMsg, void *arg);\n  void _configCallback(const usb_config_desc_t *config_desc);\n <strong> void onConfig(const uint8_t bDescriptorType, const uint8_t *p); \/\/ Modified<\/strong>\n  static String getUsbDescString(const usb_str_desc_t *str_desc);\n  static void _onReceive(usb_transfer_t *transfer);\n\n  static void _printPcapText(const char* title, uint16_t function, uint8_t direction, uint8_t endpoint, uint8_t type, uint8_t size, uint8_t stage, const uint8_t *data);\n  esp_err_t submitControl(const uint8_t bmRequestType, const uint8_t bDescriptorIndex, const uint8_t bDescriptorType, const uint16_t wInterfaceNumber, const uint16_t wDescriptorLength);\n  static void _onReceiveControl(usb_transfer_t *transfer);\n\n  virtual void onReceive(const usb_transfer_t *transfer){};\n  virtual void onGone(const usb_host_client_event_msg_t *eventMsg){};\n\n  virtual uint8_t getKeycodeToAscii(uint8_t keycode, uint8_t shift);\n  virtual void onKeyboard(hid_keyboard_report_t report, hid_keyboard_report_t last_report);\n  virtual void onKeyboardKey(uint8_t ascii, uint8_t keycode, uint8_t modifier);\n\n  virtual void onMouse(hid_mouse_report_t report, uint8_t last_buttons);\n  virtual void onMouseButtons(hid_mouse_report_t report, uint8_t last_buttons);\n  virtual void onMouseMove(hid_mouse_report_t report);\n\n  void _onDataGamepad();\n <strong> static void _onReceiveMIDI(usb_transfer_t *transfer); \/\/ Added<\/strong>\n  void setHIDLocal(hid_local_enum_t code);\n};\n\n#define HID_KEYCODE_TO_ASCII_JA   \\\n    {0     , 0      }, \/* 0x00 *\/ \\\n    {0     , 0      }, \/* 0x01 *\/ \\\n    {0     , 0      }, \/* 0x02 *\/ \\\n    {0     , 0      }, \/* 0x03 *\/ \\\n    {'a'   , 'A'    }, \/* 0x04 *\/ \\\n    {'b'   , 'B'    }, \/* 0x05 *\/ \\\n    {'c'   , 'C'    }, \/* 0x06 *\/ \\\n    {'d'   , 'D'    }, \/* 0x07 *\/ \\\n    {'e'   , 'E'    }, \/* 0x08 *\/ \\\n    {'f'   , 'F'    }, \/* 0x09 *\/ \\\n    {'g'   , 'G'    }, \/* 0x0a *\/ \\\n    {'h'   , 'H'    }, \/* 0x0b *\/ \\\n    {'i'   , 'I'    }, \/* 0x0c *\/ \\\n    {'j'   , 'J'    }, \/* 0x0d *\/ \\\n    {'k'   , 'K'    }, \/* 0x0e *\/ \\\n    {'l'   , 'L'    }, \/* 0x0f *\/ \\\n    {'m'   , 'M'    }, \/* 0x10 *\/ \\\n    {'n'   , 'N'    }, \/* 0x11 *\/ \\\n    {'o'   , 'O'    }, \/* 0x12 *\/ \\\n    {'p'   , 'P'    }, \/* 0x13 *\/ \\\n    {'q'   , 'Q'    }, \/* 0x14 *\/ \\\n    {'r'   , 'R'    }, \/* 0x15 *\/ \\\n    {'s'   , 'S'    }, \/* 0x16 *\/ \\\n    {'t'   , 'T'    }, \/* 0x17 *\/ \\\n    {'u'   , 'U'    }, \/* 0x18 *\/ \\\n    {'v'   , 'V'    }, \/* 0x19 *\/ \\\n    {'w'   , 'W'    }, \/* 0x1a *\/ \\\n    {'x'   , 'X'    }, \/* 0x1b *\/ \\\n    {'y'   , 'Y'    }, \/* 0x1c *\/ \\\n    {'z'   , 'Z'    }, \/* 0x1d *\/ \\\n    {'1'   , '!'    }, \/* 0x1e *\/ \\\n    {'2'   , '\"'    }, \/* 0x1f *\/ \\\n    {'3'   , '#'    }, \/* 0x20 *\/ \\\n    {'4'   , '$'    }, \/* 0x21 *\/ \\\n    {'5'   , '%'    }, \/* 0x22 *\/ \\\n    {'6'   , '&amp;'    }, \/* 0x23 *\/ \\\n    {'7'   , '\\''   }, \/* 0x24 *\/ \\\n    {'8'   , '('    }, \/* 0x25 *\/ \\\n    {'9'   , ')'    }, \/* 0x26 *\/ \\\n    {'0'   , 0      }, \/* 0x27 *\/ \\\n    {'\\r'  , '\\r'   }, \/* 0x28 *\/ \\\n    {'\\x1b', '\\x1b' }, \/* 0x29 *\/ \\\n    {'\\b'  , '\\b'   }, \/* 0x2a *\/ \\\n    {'\\t'  , '\\t'   }, \/* 0x2b *\/ \\\n    {' '   , ' '    }, \/* 0x2c *\/ \\\n    {'-'   , '='    }, \/* 0x2d *\/ \\\n    {'^'   , '~'    }, \/* 0x2e *\/ \\\n    {'@'   , '`'    }, \/* 0x2f *\/ \\\n    {'&#91;'   , '{'    }, \/* 0x30 *\/ \\\n    {0     , 0      }, \/* 0x31 *\/ \\\n    {']'   , '}'    }, \/* 0x32 *\/ \\\n    {';'   , '+'    }, \/* 0x33 *\/ \\\n    {':'   , '*'    }, \/* 0x34 *\/ \\\n    {0     , 0      }, \/* 0x35 HANKAKU *\/ \\\n    {','   , '&lt;'    }, \/* 0x36 *\/ \\\n    {'.'   , '&gt;'    }, \/* 0x37 *\/ \\\n    {'\/'   , '?'    }, \/* 0x38 *\/ \\\n                                  \\\n    {0     , 0      }, \/* 0x39 *\/ \\\n    {0     , 0      }, \/* 0x3a *\/ \\\n    {0     , 0      }, \/* 0x3b *\/ \\\n    {0     , 0      }, \/* 0x3c *\/ \\\n    {0     , 0      }, \/* 0x3d *\/ \\\n    {0     , 0      }, \/* 0x3e *\/ \\\n    {0     , 0      }, \/* 0x3f *\/ \\\n    {0     , 0      }, \/* 0x40 *\/ \\\n    {0     , 0      }, \/* 0x41 *\/ \\\n    {0     , 0      }, \/* 0x42 *\/ \\\n    {0     , 0      }, \/* 0x43 *\/ \\\n    {0     , 0      }, \/* 0x44 *\/ \\\n    {0     , 0      }, \/* 0x45 *\/ \\\n    {0     , 0      }, \/* 0x46 *\/ \\\n    {0     , 0      }, \/* 0x47 *\/ \\\n    {0     , 0      }, \/* 0x48 *\/ \\\n    {0     , 0      }, \/* 0x49 *\/ \\\n    {0     , 0      }, \/* 0x4a *\/ \\\n    {0     , 0      }, \/* 0x4b *\/ \\\n    {0     , 0      }, \/* 0x4c *\/ \\\n    {0     , 0      }, \/* 0x4d *\/ \\\n    {0     , 0      }, \/* 0x4e *\/ \\\n    {0     , 0      }, \/* 0x4f *\/ \\\n    {0     , 0      }, \/* 0x50 *\/ \\\n    {0     , 0      }, \/* 0x51 *\/ \\\n    {0     , 0      }, \/* 0x52 *\/ \\\n    {0     , 0      }, \/* 0x53 *\/ \\\n                                  \\\n    {'\/'   , '\/'    }, \/* 0x54 *\/ \\\n    {'*'   , '*'    }, \/* 0x55 *\/ \\\n    {'-'   , '-'    }, \/* 0x56 *\/ \\\n    {'+'   , '+'    }, \/* 0x57 *\/ \\\n    {'\\r'  , '\\r'   }, \/* 0x58 *\/ \\\n    {'1'   , 0      }, \/* 0x59 *\/ \\\n    {'2'   , 0      }, \/* 0x5a *\/ \\\n    {'3'   , 0      }, \/* 0x5b *\/ \\\n    {'4'   , 0      }, \/* 0x5c *\/ \\\n    {'5'   , '5'    }, \/* 0x5d *\/ \\\n    {'6'   , 0      }, \/* 0x5e *\/ \\\n    {'7'   , 0      }, \/* 0x5f *\/ \\\n    {'8'   , 0      }, \/* 0x60 *\/ \\\n    {'9'   , 0      }, \/* 0x61 *\/ \\\n    {'0'   , 0      }, \/* 0x62 *\/ \\\n    {'.'   , 0      }, \/* 0x63 *\/ \\\n    {0     , 0      }, \/* 0x64 *\/ \\\n    {0     , 0      }, \/* 0x65 *\/ \\\n    {0     , 0      }, \/* 0x66 *\/ \\\n    {'='   , '='    }, \/* 0x67 *\/ \\\n\n#endif\n<\/code><\/pre>\n\n\n\n<p>I also added a new function called <strong><code>_onReceiveMIDI<\/code> <\/strong>(EspUsbHost.cpp) to process the messages from the keyboard:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Add this\nvoid EspUsbHost::_onReceiveMIDI(usb_transfer_t *transfer) {\n  static bool sustain_enabled = false;\n  static bool note_playing = false;\n  static uint8_t current_note = 0;\n\n  EspUsbHost *usbHost = (EspUsbHost *)transfer-&gt;context;\n\n  for (int i = 0; i &lt; transfer-&gt;actual_num_bytes; i += 4) {\n    uint8_t status_byte = transfer-&gt;data_buffer&#91;i + 1];\n    uint8_t param1 = transfer-&gt;data_buffer&#91;i + 2];  \/\/ Note number\n    uint8_t param2 = transfer-&gt;data_buffer&#91;i + 3];  \/\/ Velocity\n\n    uint8_t message_type = status_byte &amp; 0xF0;\n    uint8_t midi_channel = status_byte &amp; 0x0F;\n\n    \/\/ Sustain pedal \n    if (message_type == 0xB0 &amp;&amp; param1 == 64) {\n      sustain_enabled = (param2 &gt;= 64);\n      Serial.printf(\"Sustain %s\\n\", sustain_enabled ? \"ON\" : \"OFF\");\n\n      if (!sustain_enabled &amp;&amp; note_playing) {\n        noTone(buzzer_pin);\n        Serial.printf(\"OFF sustain note: %d\\n\", current_note);\n        note_playing = false;\n      }\n    }\n\n    \/\/ NOTE ON\n    else if (message_type == 0x90 &amp;&amp; param2 &gt; 0) {\n      int frequency = 440 * pow(2, (param1 - 69) \/ 12.0);\n      tone(buzzer_pin, frequency);\n      current_note = param1;\n      note_playing = true;\n      Serial.printf(\"NOTE ON: Note %d, Freq %d Hz\\n\", param1, frequency);\n    }\n\n    \/\/ NOTE OFF\n    else if (message_type == 0x80 || (message_type == 0x90 &amp;&amp; param2 == 0)) {\n      if (!sustain_enabled &amp;&amp; param1 == current_note) {\n        noTone(buzzer_pin);\n        note_playing = false;\n        Serial.printf(\"NOTE OFF: Note %d\\n\", param1);\n      } else if (sustain_enabled) {\n        Serial.printf(\"NOTE OFF (sustain active): %d\\n\", param1);\n      }\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p><br>As you can see, the <code>buzzer_pin<\/code> value needs to be set at the beginning of the file (EspUsbHost.cpp):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define  buzzer_pin 6<\/code><\/pre>\n\n\n\n<p>Another change was made to the <strong><code>onConfig<\/code> <\/strong>function, so the complete function is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Change this\nvoid EspUsbHost::onConfig(const uint8_t bDescriptorType, const uint8_t *p) {\n  switch (bDescriptorType) {\n    case USB_INTERFACE_DESC:\n      {\n        const usb_intf_desc_t *intf = (const usb_intf_desc_t *)p;\n        ESP_LOGI(\"EspUsbHost\", \"USB_INTERFACE_DESC(0x04)\\n\"\n                               \"# bInterfaceNumber   = %d\\n\"\n                               \"# bNumEndpoints      = %d\\n\"\n                               \"# bInterfaceClass    = 0x%x\\n\"\n                               \"# bInterfaceSubClass = 0x%x\\n\",\n                 intf-&gt;bInterfaceNumber,\n                 intf-&gt;bNumEndpoints,\n                 intf-&gt;bInterfaceClass,\n                 intf-&gt;bInterfaceSubClass);\n\n        \/* MIDI *\/\n        if (intf-&gt;bInterfaceClass == 0x01 &amp;&amp; intf-&gt;bInterfaceSubClass == 0x03) {\n          ESP_LOGI(\"EspUsbHost\", \" MIDI detected: interface %d\", intf-&gt;bInterfaceNumber);\n\n          esp_err_t err = usb_host_interface_claim(this-&gt;clientHandle, this-&gt;deviceHandle, intf-&gt;bInterfaceNumber, intf-&gt;bAlternateSetting);\n          if (err != ESP_OK) {\n            ESP_LOGI(\"EspUsbHost\", \"usb_host_interface_claim() err=%x\", err);\n          } else {\n            this-&gt;usbInterface&#91;this-&gt;usbInterfaceSize] = intf-&gt;bInterfaceNumber;\n            this-&gt;usbInterfaceSize++;\n            _bInterfaceNumber = intf-&gt;bInterfaceNumber;\n            _bInterfaceClass = intf-&gt;bInterfaceClass;\n            _bInterfaceSubClass = intf-&gt;bInterfaceSubClass;\n          }\n        }\n      }\n      break;\n\n    case USB_ENDPOINT_DESC:\n      {\n        const usb_ep_desc_t *ep_desc = (const usb_ep_desc_t *)p;\n        ESP_LOGI(\"EspUsbHost\", \"USB_ENDPOINT_DESC(0x05)\\n\"\n                               \"# bEndpointAddress = 0x%x\\n\"\n                               \"# bmAttributes     = 0x%x\\n\"\n                               \"# wMaxPacketSize   = %d\\n\",\n                 ep_desc-&gt;bEndpointAddress,\n                 ep_desc-&gt;bmAttributes,\n                 ep_desc-&gt;wMaxPacketSize);\n\n        \/* MIDI config *\/\n        if (_bInterfaceClass == 0x01 &amp;&amp; _bInterfaceSubClass == 0x03) {\n          ESP_LOGI(\"EspUsbHost\", \"Endpoint MIDI %d (Addr: 0x%02x, Size: %d)\",\n                   USB_EP_DESC_GET_EP_NUM(ep_desc),\n                   ep_desc-&gt;bEndpointAddress,\n                   ep_desc-&gt;wMaxPacketSize);\n\n          if (ep_desc-&gt;bEndpointAddress &amp; USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) {\n            esp_err_t err = usb_host_transfer_alloc(ep_desc-&gt;wMaxPacketSize + 1, 0, &amp;this-&gt;usbTransfer&#91;this-&gt;usbTransferSize]);\n            if (err != ESP_OK) {\n              this-&gt;usbTransfer&#91;this-&gt;usbTransferSize] = NULL;\n              ESP_LOGI(\"EspUsbHost\", \"usb_host_transfer_alloc() err=%x\", err);\n              return;\n            } else {\n              ESP_LOGI(\"EspUsbHost\", \"usb_host_transfer_alloc() ESP_OK data_buffer_size=%d\", ep_desc-&gt;wMaxPacketSize + 1);\n            }\n\n            this-&gt;usbTransfer&#91;this-&gt;usbTransferSize]-&gt;device_handle = this-&gt;deviceHandle;\n            this-&gt;usbTransfer&#91;this-&gt;usbTransferSize]-&gt;bEndpointAddress = ep_desc-&gt;bEndpointAddress;\n            this-&gt;usbTransfer&#91;this-&gt;usbTransferSize]-&gt;callback = _onReceiveMIDI;\n            this-&gt;usbTransfer&#91;this-&gt;usbTransferSize]-&gt;context = this;\n            this-&gt;usbTransfer&#91;this-&gt;usbTransferSize]-&gt;num_bytes = ep_desc-&gt;wMaxPacketSize;\n            interval = ep_desc-&gt;bInterval;\n            isReady = true;\n            this-&gt;usbTransferSize++;\n          }\n        }\n      }\n      break;\n\n    default:\n      {\n        ESP_LOGI(\"EspUsbHost\", \"USB unknown: 0x%02x\", bDescriptorType);\n      }\n  }\n}\n\n<\/code><\/pre>\n\n\n\n<p>And finally, here\u2019s my<strong> <code>main.cpp<\/code><\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;Arduino.h&gt;\n#include \"EspUsbHost.h\"\n\nEspUsbHost usbHost;\nvoid setup() {\n  Serial.begin(115200);\n  usbHost.begin();\n}\n\nvoid loop() {\n  usbHost.task();\n}\n<\/code><\/pre>\n\n\n\n<p>This is the most basic version possible \u2014 but there\u2019s a lot more that could be done.<br>In later experiments, I added LEDs, and even used the keyboard\u2019s joystick to change pitch and vibrato in real time.<\/p>\n\n\n\n<p>Of course, sending sound through just one digital pin is pretty limited. Ideally, you\u2019d use an external DAC (digital-to-analog converter) and expand the library to handle more MIDI messages for better control.<\/p>\n\n\n\n<p>The biggest limitation with this version is that notes below MIDI note 28 cause an error in the <code>tone<\/code> function.<\/p>\n\n\n\n<p>I\u2019ll keep updating this project and adding the features I mentioned above.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Some time ago, I made a quick little project to connect my AKAI MPK Mini MIDI keyboard to an ESP32-S3.Here\u2019s a short demo video: This post is about this very first, simple version. The first step, if you have the same board I do, is to make a small hardware [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":318,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[60,66,70,76],"class_list":["post-307","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-arduino","tag-esp32","tag-esp32s3","tag-midi"],"_links":{"self":[{"href":"https:\/\/cascajolabs.es\/index.php?rest_route=\/wp\/v2\/posts\/307","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cascajolabs.es\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cascajolabs.es\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cascajolabs.es\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cascajolabs.es\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=307"}],"version-history":[{"count":10,"href":"https:\/\/cascajolabs.es\/index.php?rest_route=\/wp\/v2\/posts\/307\/revisions"}],"predecessor-version":[{"id":329,"href":"https:\/\/cascajolabs.es\/index.php?rest_route=\/wp\/v2\/posts\/307\/revisions\/329"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cascajolabs.es\/index.php?rest_route=\/wp\/v2\/media\/318"}],"wp:attachment":[{"href":"https:\/\/cascajolabs.es\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=307"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cascajolabs.es\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=307"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cascajolabs.es\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=307"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}