{"id":1735,"date":"2019-03-01T22:17:17","date_gmt":"2019-03-01T21:17:17","guid":{"rendered":"http:\/\/wildlab.org\/?p=1735"},"modified":"2021-06-22T09:57:22","modified_gmt":"2021-06-22T08:57:22","slug":"stm32-i2c-scanner","status":"publish","type":"post","link":"https:\/\/wildlab.org\/index.php\/2019\/03\/01\/stm32-i2c-scanner\/","title":{"rendered":"STM32 I2C Scanner"},"content":{"rendered":"<h2>I2C scanner for <a href=\"https:\/\/www.banggood.com\/STM32F103C8T6-ARM-STM32-Mini-System-Development-Board-STM32F103-Core-Board-p-1207605.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">STM32f10x<\/a> series<\/h2>\n<p>I2C scanner is fairly simple, yet fast and effective way to find whatever device you put onto I2C bus. Some devices (boards) comes with clear designation on the PCB, some with misleading designation &#8211; address is shifted to the left, so for example <a href=\"https:\/\/www.banggood.com\/0_96-Inch-4Pin-White-IIC-I2C-OLED-Display-Module-12864-LED-For-Arduino-p-958196.html?p=EH10221611330201505Q\" target=\"_blank\" rel=\"noopener noreferrer\">OLED<\/a> display typical address is 0x3C\/0x3D (depends of address selector jumper), but shifted to the left by 1 bit is 0x78 or 0x7A respectively. They made it as if zero bit is inserted at LSB place so that you &#8216;just&#8217; send it as-is. But that is bad practice. Rather make your own shifting routine and insert 1 or 0, whether you want to read or write at this address.<\/p>\n<p>Example:<\/p>\n<p><a href=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/IIc_addr.jpg\"><img loading=\"lazy\" class=\"aligncenter size-large wp-image-1740\" src=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/IIc_addr-1024x664.jpg\" alt=\"scanner\" width=\"474\" height=\"307\" srcset=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/IIc_addr-1024x664.jpg 1024w, https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/IIc_addr-300x195.jpg 300w, https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/IIc_addr.jpg 1482w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a>The code is even simpler:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\/* Part of the code, will not work alone !!! *\/\r\n\r\nvoid twiScan(void)\r\n{\t\tint a=0; \r\n\t \tfor (uint8_t i=0;i&lt;128;i++)\r\n   {\r\n\t\t\tI2C1-&gt;CR1 |= I2C_CR1_START;\r\n\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_SB));\r\n\t\t\tI2C1-&gt;DR=(i&lt;&lt;1|0); \r\n\t\t\twhile(!(I2C1-&gt;SR1)|!(I2C1-&gt;SR2)){}; \r\n\t\t\tI2C1-&gt;CR1 |= I2C_CR1_STOP; \r\n\t\t\tdelay(100);\/\/minium wait time is 40 uS, but for sure, leave it 100 uS\r\n\t\t\ta=(I2C1-&gt;SR1&amp;I2C_SR1_ADDR);\r\n\t\t\tif (a==2)\r\n\t\t {\r\n\t\t\t\tprintMsg(\"Found I2C device at adress 0x%X (hexadecimal), or %d (decimal)\\n\",i,i);\r\n\t\t }\r\n\t }\r\n}<\/pre>\n<p>Whole thing scan all 127 addresses in 45 mS.<\/p>\n<p>Here is how looks whole packet with six addresses found. After each address found, there is 5.5 mS delay until timeout.:<\/p>\n<p><a href=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/I2C_scanner_screenshot-e1624352201421.jpg\"><img loading=\"lazy\" class=\"aligncenter size-large wp-image-1743\" src=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/I2C_scanner_screenshot-e1624352201421-1024x576.jpg\" alt=\"scanner\" width=\"474\" height=\"267\" srcset=\"https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/I2C_scanner_screenshot-e1624352201421-1024x576.jpg 1024w, https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/I2C_scanner_screenshot-e1624352201421-300x169.jpg 300w, https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/I2C_scanner_screenshot-e1624352201421-1536x864.jpg 1536w, https:\/\/wildlab.org\/wp-content\/uploads\/2019\/03\/I2C_scanner_screenshot-e1624352201421.jpg 1920w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><br \/>\n<iframe loading=\"lazy\" title=\"STM32 programming I2C scanner in Keil\" width=\"474\" height=\"267\" src=\"https:\/\/www.youtube.com\/embed\/_GJVw3uEllY?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><\/p>\n<h2>Almost standard wire.c library<\/h2>\n<p>Since I am beginner, I just begin to make few more or less standard libraries for STM32f10x series. Right now there are three functions in wire.c; twiScan(), twiSend(), and twiReceive(). So far I did not found need for anything else, but that may change, so stay tuned. Since I have none of other MCU that has different set of registers with also different syntax for those. Here is complete thing with example of main.c code that does nothing except scan.:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#include \"stm32f10x.h\"\r\n#include \"printMsg.h\"\r\n#include \"wire.h\"\r\n#include \"delayUs.h\"\r\n\r\nint main()\r\n{\r\n  usart_1_enable(); \/\/enabling printMsg();\r\n  twiEnable();\/\/enabling two wire interface, IIC or I2C\r\n  twiScan();\/\/before anything else, lets check which devices are on the bus, \r\n}\r\n<\/pre>\n<p>Implemented in wire.c with wireSend() and wireReceive() functions:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">\/* Two Wire Interface, I2C (or IIC), here will be called 'twi', and we have\r\n   only twiEnable(), twiSend() and twiReceive(). The twiSend() function is \r\n\t fairly simple, we just send address of the device shifted to the left by\r\n\t 1 bit, or-red | zero (0) at free space that tell I2C bus it is for write operation.\r\n\t The receive twiReceive() function works by sending address also shifted left\r\n\t one bit with logic or | zero (0) at empty bit (LSB), but then we must send command \r\n\t to the device depending what device has. After command, we stop (although\r\n\t we can remove STOP condition and continue to \"repeated start\", then we\r\n\t must change bit after address of the device, now it is one (1) that tells\r\n\t I2C bus we want to read. If we try only read from some address, device\r\n\t don't know what to send. So we must first issue command, then read. For\r\n\t specific command set read datasheet of particular device - it is different\r\n\t for all different devices. More on my website: http:\/\/wp.me\/p7jxwp-nD *\/\r\n\r\n#include \"stm32f10x.h\"\r\n#include \"delayUs.h\"\r\n#include \"wire.h\"\r\n#include \"printMsg.h\"\r\n\r\nvoid twiEnable(void) \r\n{\r\n  \/\/just set all registries, but NOT START condition - execute once in main.c\r\n\tRCC-&gt;APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN; \/\/B port enabled, alternate function \r\n\tRCC-&gt;APB1ENR |= RCC_APB1ENR_I2C1EN; \/\/I2C 1 enabled \r\n\tGPIOB-&gt;CRL = 0xFF000000;\/\/ setting just pins B7 (SDA) and B6 (SCL), while leaving the rest intact 50 MHz!\r\n\tI2C1-&gt;CR2 |= 50; \/\/ GPIO clock freq=50 MHz MUST !!! be equal APB frequency (GPIO, 2, 10 or 50 MHz)\r\n\tI2C1-&gt;CCR |= I2C_CCR_FS; \/\/fast mode\r\n\tI2C1-&gt;CCR |= 30; \/\/not sure for 400 000 - (10= 1.2 MHz, 15=800 kHz, 30=400 kHz)\r\n\tI2C1-&gt;TRISE |= 51; \/\/ maximum rise time is 1000 nS\r\n\tI2C1-&gt;CR1 |= I2C_CR1_PE; \r\n}\r\n\r\nvoid twiScan(void)\r\n{\t\tint a=0; \r\n\t \tfor (uint8_t i=0;i&lt;128;i++)\r\n   {\r\n\t\t\tI2C1-&gt;CR1 |= I2C_CR1_START;\r\n\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_SB));\r\n\t\t\tI2C1-&gt;DR=(i&lt;&lt;1|0); \r\n\t\t\twhile(!(I2C1-&gt;SR1)|!(I2C1-&gt;SR2)){}; \r\n\t\t\tI2C1-&gt;CR1 |= I2C_CR1_STOP; \r\n\t\t\tdelay(100);\/\/minium wait time is 40 uS, but for sure, leave it 100 uS\r\n\t\t\ta=(I2C1-&gt;SR1&amp;I2C_SR1_ADDR);\r\n\t\t\tif (a==2)\r\n\t\t {\r\n\t\t\t\tprintMsg(\"Found I2C device at adress 0x%X (hexadecimal), or %d (decimal)\\n\",i,i);\r\n\t\t }\r\n\t }\r\n}\r\n\t\r\n\/* Command or commands, or sending bytes, just the same name of the variable 'command' *\/\r\nvoid twiSend(uint8_t address, uint8_t command, uint8_t length)\r\n{\r\n\tI2C1-&gt;CR1 |= I2C_CR1_START; \/\/START condition \r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_SB));\r\n\tI2C1-&gt;DR=(address&lt;&lt;1|0); \/\/sending address of the device, 0 = sending\r\n  while(!(I2C1-&gt;SR1 &amp; I2C_SR1_ADDR)|!(I2C1-&gt;SR2));\t\t\r\n\tI2C1-&gt;DR=command; \/\/filling data register with byte, if single - command, multiple - command(s) and data\r\n\tfor (uint8_t i=0;i&lt;length;i++)\r\n\t{ \r\n\t\tI2C1-&gt;DR=buffer[i]; \/\/filling buffer with command or data\r\n\t\tdelay(60);\r\n\t}\r\n\tI2C1-&gt;CR1 |= I2C_CR1_STOP;\r\n}\r\n\r\nvoid twiReceive(uint8_t address, uint8_t command, uint8_t length) \r\n{\r\n\tI2C1-&gt;CR1 |= I2C_CR1_ACK;\r\n  I2C1-&gt;CR1 |= I2C_CR1_START; \/\/start pulse \r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_SB));\r\n\tI2C1-&gt;DR=(address&lt;&lt;1|0); \/\/sending address of the device, 0 = sending\r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_ADDR)|!(I2C1-&gt;SR2 &amp; I2C_SR2_BUSY));\r\n\tI2C1-&gt;DR=command; \/\/sending command to the device in order to request data\r\n\tI2C1-&gt;CR1 |= I2C_CR1_START; \/\/REPEATED START condition to change from sending address + command to receive data\r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_SB));\r\n\tI2C1-&gt;DR=(address&lt;&lt;1|1); \/\/sending address of the device, 1 = reading \r\n\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_ADDR)|!(I2C1-&gt;SR2));\r\n\t\r\nif (length==1)  \/\/receiving single byte, N=1\r\n\t{\r\n\t\twhile(!(I2C1-&gt;SR1)|!(I2C1-&gt;SR2));\r\n\t\tI2C1-&gt;CR1 &amp;= ~I2C_CR1_ACK; \/\/this will send later NAK (not acknowledged) to signal it is last byte\r\n\t\tI2C1-&gt;CR1 |= I2C_CR1_STOP; \/\/issuing STOP condition before (!) reading byte\r\n\t\tbuffer[0]=I2C1-&gt;DR; \/\/single byte is read AFTER NAK (!) and STOP condition\r\n\t} \r\n\tif (length==2) \/\/receiving two bytes, N=2\r\n\t{\r\n\t\twhile(!(I2C1-&gt;SR1)|!(I2C1-&gt;SR2));\r\n\t\tI2C1-&gt;CR1 &amp;= ~I2C_CR1_ACK; \/\/this will send later NAK (not acknowledged) before last byte\r\n    I2C1-&gt;CR1 |= I2C_CR1_STOP;\r\n\t\tbuffer[0]=I2C1-&gt;DR; \/\/reading N-1 byte, next to last byte is in DR, last one still in shift register\r\n\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2));\r\n\t\tbuffer[1]=I2C1-&gt;DR; \/\/read last N byte now available \r\n\t} \r\n  if (length&gt;2) \/\/receiving more than two bytes, N&gt;2\r\n\t{\r\n\t\t\r\n\t  for (uint8_t i=0;i&lt;length;i++)\r\n\t  { \r\n\t\t\t                     \r\n\t\t  if (i&lt;(length-3))      \/\/ if it is not N-2, then read all bytes\r\n\t\t\t{\r\n\t\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2));\r\n\t\t\t\tbuffer[i]=I2C1-&gt;DR;  \r\n\t\t\t}\r\n\t\t  else if (i==length-3) \/\/ if it is N-2 then read \r\n\t\t\t{\r\n\t\t\t\twhile(!(I2C1-&gt;SR1)|!(I2C1-&gt;SR2));\r\n\t\t\t\tbuffer[i]=I2C1-&gt;DR; \r\n\t\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2));\r\n\t\t\t\tI2C1-&gt;CR1 &amp;= ~I2C_CR1_ACK; \/\/this will send later NAK (not acknowledged) before last byte\r\n\t\t\t\tI2C1-&gt;CR1 |= I2C_CR1_STOP;\r\n\t\t\t}\r\n\t    else if (i==length-2) \/\/ if it is N-1 then read\r\n\t\t\t{\r\n\t\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2));\r\n\t\t\t\tbuffer[i]=I2C1-&gt;DR; \r\n\t\t\t}\r\n\t\t\telse if (i==length-1) \/\/ else it is N byte \r\n\t\t\t{\r\n\t\t\t\twhile(!(I2C1-&gt;SR1 &amp; I2C_SR1_RXNE)|!(I2C1-&gt;SR2)){};\r\n\t\t    buffer[i]=I2C1-&gt;DR;  \r\n\t\t\t}\r\n    } \r\n }\r\n}\r\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#ifndef wire_h\r\n#define wire_h\r\n\r\n#include &lt;stdint.h&gt;\r\n\r\nextern uint8_t  address, command, length; \r\nextern uint8_t  buffer[];\r\nextern uint8_t  status1;\r\nextern uint8_t  status2;\r\n\r\nvoid twiEnable(void);\r\nvoid twiScan(void);\r\nvoid twiSend(uint8_t address, uint8_t command, uint8_t length);\r\nvoid twiReceive(uint8_t address, uint8_t command, uint8_t length);\r\n\r\n\r\n#endif\r\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#include \"stm32f10x.h\"\r\n#include \"stdint.h\"\r\n#include &lt;stdio.h&gt;\r\n#include \"stdarg.h\"\r\n#include \"string.h\"\r\n#include \"printMsg.h\"\r\n\r\nchar buff[256];\r\n\r\nvoid usart_1_enable(void)\r\n{\r\n  \/\/enabling pin A9 for alternating funct. for uart\/usart\r\n  RCC-&gt;APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_USART1EN; \/\/clock to GPIO A enabled, port A(2), alt.funct.en(0), usart1 clock enabled(14)\r\n\tGPIOA-&gt;CRH |= GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9_0 | GPIO_CRH_MODE9_1; \/\/port A9\r\n\tGPIOA-&gt;CRH &amp;= ~GPIO_CRH_CNF9_0; \/\/port A9\r\n\tGPIOA-&gt;CRH &amp;= ~(GPIO_CRH_MODE10_0|GPIO_CRH_MODE10_1); \/\/port A10 is RX\r\n\tGPIOA-&gt;CRH |= GPIO_CRH_CNF10_0; \/\/port A10 is RX\r\n\t\r\n\t\/\/GPIOA-&gt;CRH = 0x444444B4; \/\/ A9 is alternate output, 50 MHz, push-pull - not this time short version (!)\r\n\t\/\/clkPer\/(baudRx_16bit)=72MHZ\/9600 = 7500 = 0x1D4C\r\n\t\/* Remove comment line for speed that you want to use*\/\r\n\t\/\/USART1-&gt;BRR = (0xEA60); \/\/   1200 Baud\r\n\t\/\/USART1-&gt;BRR = (0x7530); \/\/   2400 Baud\r\n\t\/\/USART1-&gt;BRR = (0x3A98); \/\/   4800 Baud\r\n\t\/\/USART1-&gt;BRR = (0x1D4C); \/\/   9600 Baud\r\n\t\/\/USART1-&gt;BRR = (0x1388); \/\/  14400 Baud\r\n\t\/\/USART1-&gt;BRR = (0xEA6) ; \/\/  19200 Baud\r\n\t\/\/USART1-&gt;BRR = (0x9c4) ; \/\/  28800 Baud\r\n\t\/\/USART1-&gt;BRR = (0x753) ; \/\/  38400 Baud\r\n\t\/\/USART1-&gt;BRR = (0x505) ; \/\/  56000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x4E2) ; \/\/  57600 Baud\r\n\tUSART1-&gt;BRR = (0x271) ; \/\/ 115200 Baud\r\n\t\/\/USART1-&gt;BRR = (0x232) ; \/\/ 128000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x119) ; \/\/ 256000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x8C)  ; \/\/ 512000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x46)  ; \/\/ 1024000 Baud\r\n\t\/\/USART1-&gt;BRR = (0x23)  ; \/\/ 2048000 Baud\r\n  \/\/USART1-&gt;BRR = (0x18)  ; \/\/ 3000000 Baud (3 MHz, max speed that HTerm can get, non-standard speed)\r\n\t\r\n\t\r\n\tUSART1-&gt;CR1 |= USART_CR1_TE; \/\/transmitter enable\r\n\tUSART1-&gt;CR1 |= USART_CR1_RE; \/\/receiver enable\r\n\tUSART1-&gt;CR1 |= USART_CR1_UE; \/\/usart enable\r\n}\r\n\r\nvoid printMsg(char *msg, ...)\r\n{\r\n\t\/\/char buff[120]; \/\/was 80\r\n\tva_list args;\r\n\tva_start(args,msg); \r\n\tvsprintf(buff,msg,args);\r\n\r\n\tfor(int i=0;i&lt;strlen(buff);i++)\r\n\t{\r\n\t  USART1-&gt;DR = buff[i];\r\n\t  while(!(USART1-&gt;SR &amp; USART_SR_TXE)); \/\/wait for TXE, 1 = data transferred\r\n  }\r\n}\r\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#ifndef printMsg_h\r\n#define printMsg_h\r\n\r\nextern int len;\r\nextern char buff[];\r\n\/\/char buff[];\r\n\r\nvoid usart_1_enable(void);\r\nvoid printMsg(char *msg, ...);\r\n\r\n#endif\r\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">void delay(unsigned long cycles)\r\n{\r\n  while(cycles &gt;0)\r\n\t{\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");asm(\"nop\");\r\n\t\tasm(\"nop\");asm(\"nop\");asm(\"nop\");  \/\/to get 1 uS if delay(1)\r\n  cycles--; \r\n\t}\r\n}\r\n<\/pre>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\">#ifndef delayUs_h\r\n#define delayUs_h\r\n\r\nextern void delay(unsigned long cycles);\r\n\r\n#endif\r\n<\/pre>\n<p>That is all. Happy scanning! \ud83d\ude00<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I2C scanner for STM32f10x series I2C scanner is fairly simple, yet fast and effective way to find whatever device you put onto I2C bus. Some devices (boards) comes with clear designation on the PCB, some with misleading designation &#8211; address is shifted to the left, so for example OLED display typical address is 0x3C\/0x3D (depends &hellip; <a href=\"https:\/\/wildlab.org\/index.php\/2019\/03\/01\/stm32-i2c-scanner\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">STM32 I2C Scanner<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1738,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1735"}],"collection":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/comments?post=1735"}],"version-history":[{"count":17,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1735\/revisions"}],"predecessor-version":[{"id":3828,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/posts\/1735\/revisions\/3828"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/media\/1738"}],"wp:attachment":[{"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/media?parent=1735"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/categories?post=1735"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wildlab.org\/index.php\/wp-json\/wp\/v2\/tags?post=1735"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}