{"id":638,"date":"2015-08-26T16:00:12","date_gmt":"2015-08-26T23:00:12","guid":{"rendered":"http:\/\/visualgdb.com\/w\/?p=638"},"modified":"2017-05-26T10:30:57","modified_gmt":"2017-05-26T17:30:57","slug":"creating-a-freertos-based-wifi-http-server-with-esp8266-and-visual-studio","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/esp8266\/relay\/","title":{"rendered":"Creating a FreeRTOS-based WiFi HTTP server for ESP8266"},"content":{"rendered":"<p>This tutorial shows how to create a FreeRTOS-based HTTP server with the ESP8266 chip. Unlike the single-threaded IoT SDK that requires writing code in the form of event handlers, the newer RTOS SDK allows creating threads that will be automatically scheduled by the FreeRTOS scheduler and can utilize common patterns like &#8220;wait for more incoming data to arrive&#8221;. We will create a basic WiFi HTTP server and modify it to allow controlling the on-board LED and the relay by pressing buttons in the browser.<\/p>\n<p>Before you begin, follow the <a href=\"http:\/\/visualgdb.com\/tutorials\/esp8266\/openocd\/\">ESP8266 OpenOCD tutorial<\/a> to get started with JTAG debugging for your ESP8266 board.<\/p>\n<ol>\n<li>Start Visual Studio and launch the VisualGDB Embedded Project Wizard:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-wifiprj.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-639\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-wifiprj.png\" alt=\"01-wifiprj\" width=\"800\" height=\"470\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-wifiprj.png 800w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/01-wifiprj-300x176.png 300w\" sizes=\"(max-width: 800px) 100vw, 800px\" \/><\/a><\/li>\n<li>Select &#8220;New project&#8221; =&gt; &#8220;Embedded Binray&#8221; and uncheck the &#8220;bin&#8221; file checkbox:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-newprj.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-640\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-newprj.png\" alt=\"02-newprj\" width=\"702\" height=\"571\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-newprj.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/02-newprj-300x244.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>Select the ESP8266 toolchain and pick the normal ESP8266 device. As the RTOS code is relatively large, it won&#8217;t entirely fit in the RAM and the &#8220;NOFLASH&#8221; option won&#8217;t work:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-esp8266.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-641\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-esp8266.png\" alt=\"03-esp8266\" width=\"702\" height=\"571\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-esp8266.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/03-esp8266-300x244.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>On the sample selection page pick the HTTP Server (RTOS SDK) sample:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-server1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-649\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-server1.png\" alt=\"04-server\" width=\"702\" height=\"571\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-server1.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/04-server1-300x244.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>Finally choose the debugging settings that worked for the basic blinking LED program when you were trying the OpenOCD tutorial: <a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/openocd.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-1178\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/openocd.png\" alt=\"openocd\" width=\"702\" height=\"571\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/openocd.png 702w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/openocd-300x244.png 300w\" sizes=\"(max-width: 702px) 100vw, 702px\" \/><\/a><\/li>\n<li>Press Finish to create the project and build it with Ctrl-Shift-B:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-build6.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-644\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-build6.png\" alt=\"06-build\" width=\"841\" height=\"640\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-build6.png 841w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/06-build6-300x228.png 300w\" sizes=\"(max-width: 841px) 100vw, 841px\" \/><\/a><\/li>\n<li>Press F5 to start debugging the firmware. VisualGDB will program it into the SPI FLASH and start it automatically. Connect your computer or a mobile device to the ESP8266 WiFi network:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-network.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-645\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-network.png\" alt=\"07-network\" width=\"280\" height=\"395\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-network.png 280w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/07-network-213x300.png 213w\" sizes=\"(max-width: 280px) 100vw, 280px\" \/><\/a><\/li>\n<li>Use the network connection status (or the ipconfig utility) to verify the IP address of the ESP8266:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-netsettings.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-646\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-netsettings.png\" alt=\"08-netsettings\" width=\"788\" height=\"476\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-netsettings.png 788w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/08-netsettings-300x181.png 300w\" sizes=\"(max-width: 788px) 100vw, 788px\" \/><\/a><\/li>\n<li>Open the browser and enter the address there. You will see a simple &#8220;Hello, world&#8221; message followed by the URL you entered:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-helloworld.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-647\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-helloworld.png\" alt=\"09-helloworld\" width=\"640\" height=\"220\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-helloworld.png 640w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/09-helloworld-300x103.png 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/a><\/li>\n<li>Now we will look in the code that handles the HTTP requests. Set a breakpoint on the first call to strchr() in ServerTask() and hit the refresh button in the browser. Once the breakpoint is hit, you can see the HTTP request sent by the browser:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-buf.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-648\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-buf.png\" alt=\"10-buf\" width=\"841\" height=\"638\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-buf.png 841w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/10-buf-300x228.png 300w\" sizes=\"(max-width: 841px) 100vw, 841px\" \/><\/a><\/li>\n<li>The ServerTask() function uses the API provided by lwIP and does not look different from a function written for a Windows- or Linux-based environment:\n<ol style=\"list-style-type: lower-alpha;\">\n<li>First it calls socket() to create a socket<\/li>\n<li>Then it calls bind() and listen() to begin listening on the port 80<\/li>\n<li>It calls accept() to accept incoming connections<\/li>\n<li>For each incoming connection it calls read() until the HTTP header is received and then calls write() to send the reply<\/li>\n<li>Finally it calls close() to close the client socket<\/li>\n<\/ol>\n<p>Unlike the IoT SDK and the espconn API where the event handler must save its state to global variables and exit, the RTOS SDK is much easier to use and allows quickly porting network-related code from the desktop environment.<\/li>\n<li>We will now add a basic HTML form that will allow controlling the LED and the on-board relay:\n<pre class=\"\">&lt;form&gt;\r\n    LED period: &lt;input name=\"period\"\/&gt;msec&lt;input type=\"Submit\" value=\"Set\"\/&gt;\r\n&lt;\/form&gt;\r\n&lt;form&gt;\r\n    Relay state:&lt;br\/&gt;\r\n    New State:\r\n    &lt;input type=\"Submit\" name=\"relay\" Value=\"A\"\/&gt;\r\n    &lt;input type=\"Submit\" name=\"relay\" Value=\"B\"\/&gt;\r\n&lt;\/form&gt;<\/pre>\n<\/li>\n<li>Modify the global header\/footer variable as follows and add the &#8216;format&#8217; variable:\n<pre class=\"\">static const char szHeader[] = \"HTTP\/1.0 200 OK\\r\\nContent-type: text\/html\\r\\n\\r\\n&lt;html&gt;&lt;body&gt;&lt;h1&gt;LED &amp;amp; Relay Demo&lt;\/h1&gt;\";\r\nstatic const char szFormat[] = \"&lt;form&gt;LED period: &lt;input name=\\\"period\\\" value=\\\"%d\\\"\/&gt;msec&lt;input type=\\\"Submit\\\" value=\\\"Set\\\"\/&gt;&lt;\/form&gt;&lt;form&gt;Relay state: %s&lt;br\/&gt;New state:&lt;input type=\\\"Submit\\\" name=\\\"relay\\\" Value=\\\"A\\\"\/&gt;&lt;input type=\\\"Submit\\\" name=\\\"relay\\\" Value=\\\"B\\\"\/&gt;&lt;\/form&gt;\";\r\nstatic const char szFooter[] = \"&lt;\/body&gt;&lt;\/html&gt;\";\r\n\r\nstatic int s_LEDPeriod = 300;<\/pre>\n<\/li>\n<li>Create another thread that will be blinking the LED and start it from user_init():\n<pre class=\"\">extern \"C\"\r\n{\r\n#include &lt;gpio.h&gt;\r\n}\r\n\r\nstatic void RAMFUNC LEDBlinkTask(void *pvParameters)\r\n{\r\n\u00a0\u00a0\u00a0 PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1);\r\n\u00a0\u00a0 \u00a0for (int tick = 0;; tick++)\r\n\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0vTaskDelay(s_LEDPeriod \/ portTICK_RATE_MS);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0gpio_output_conf(0, BIT1, BIT1, 0);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0vTaskDelay(s_LEDPeriod \/ portTICK_RATE_MS);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0gpio_output_conf(BIT1, 0, BIT1, 0);\r\n\u00a0\u00a0 \u00a0}\r\n}\r\n\r\nvoid RAMFUNC user_init(void)\r\n{\r\n    \/\/...\r\n\u00a0\u00a0 \u00a0xTaskCreate(LEDBlinkTask, (signed char *)\"Blink\", 256, NULL, 2, NULL);\r\n}<\/pre>\n<\/li>\n<li>In this tutorial we are using the ESP8266 module from Olimex that has a relay connected to GPIO5. If you are using a different board, use the schematics to find the GPIO port connected to the relay or just skip the relay-related code:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-relay.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-650\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-relay.png\" alt=\"11-relay\" width=\"1105\" height=\"341\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-relay.png 1105w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-relay-300x93.png 300w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/11-relay-1024x316.png 1024w\" sizes=\"(max-width: 1105px) 100vw, 1105px\" \/><\/a><\/li>\n<li>Now we can modify the code responsible for parsing the request and sending the reply to parse the form fields and control the period and the relay:\n<pre class=\"\">\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0 if (pURL)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0char *pURLEnd = strchr(pURL + 1, ' ');\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (pURLEnd)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0pURL++;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0pURLEnd[0] = 0;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0const char *pRelay = strstr(pURL, \"relay=\");\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (pRelay)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (pRelay[6] == 'A')\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0gpio_output_conf(BIT5, 0, BIT5, 0);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0else\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0gpio_output_conf(0, BIT5, BIT5, 0);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0const char *pPeriod = strstr(pURL, \"period=\");\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if (pPeriod)\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0s_LEDPeriod = atoi(pPeriod + 7);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0write(client_sock, szHeader, sizeof(szHeader) - 1);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0sprintf(szBuf, szFormat, s_LEDPeriod, (GPIO_REG_READ(GPIO_OUT_ADDRESS) &amp; BIT5) ? \"A\" : \"B\");\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0write(client_sock, szBuf, strlen(szBuf));\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0write(client_sock, szFooter, sizeof(szFooter) - 1);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}<\/pre>\n<p>Also enable the GPIO5 in user_init():<\/p>\n<pre class=\"\">PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);<\/pre>\n<\/li>\n<li>Now you can build the new program and start it by pressing F5. Reconnect to the WiFi network and refresh the page in the browser:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-page.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-652\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-page.png\" alt=\"12-page\" width=\"648\" height=\"257\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-page.png 648w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/12-page-300x119.png 300w\" sizes=\"(max-width: 648px) 100vw, 648px\" \/><\/a><\/li>\n<li>Try changing the LED period and pressing &#8220;set&#8221; to update it, then press the &#8220;A&#8221; and &#8220;B&#8221; buttons and check that the relay is clicking. The relay has 2 states: in state A it connects the COM terminal with the NO terminal and instate B it connects COM with NC. You can use it to switch on and off various devices such as a 5V fan:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-fan.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-653\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-fan.jpg\" alt=\"13-fan\" width=\"700\" height=\"429\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-fan.jpg 700w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-fan-300x184.jpg 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><\/a><\/li>\n<li>You can set a breakpoint in the ServerTask() function, however you won&#8217;t be able to evaluate local variables as gdb won&#8217;t be able to find the function calling ServerTask(). To work around this, create the task using a wrapper function:\n<pre class=\"\">void RAMFUNC ServerTaskWrapper(void *pvParameters)\r\n{\r\n    ServerTask(pvParameters);\r\n}<\/pre>\n<\/li>\n<li>As long as gdb can find the function&#8217;s caller, it will show the variables correctly:<a href=\"http:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-var.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-654\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-var.png\" alt=\"13-var\" width=\"841\" height=\"638\" srcset=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-var.png 841w, https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2015\/08\/13-var-300x228.png 300w\" sizes=\"(max-width: 841px) 100vw, 841px\" \/><\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to create a FreeRTOS-based HTTP server with the ESP8266 chip. Unlike the single-threaded IoT SDK that<\/p>\n","protected":false},"author":1,"featured_media":659,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[142],"tags":[100,56,102],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/638"}],"collection":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/comments?post=638"}],"version-history":[{"count":5,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/638\/revisions"}],"predecessor-version":[{"id":1179,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/638\/revisions\/1179"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media\/659"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=638"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=638"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=638"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}