0.前言
CoAP是受限制的應用協議(Constrained Application Protocol)的代名詞。在當前由PC機組成的世界,信息交換是通過TCP和應用層協議HTTP實現的。但是對于小型設備而言,實現TCP和HTTP協議顯然是一個過分的要求。為了讓小設備可以接入互聯網,CoAP協議被設計出來。CoAP是一種應用層協議,它運行于UDP協議之上而不是像HTTP那樣運行于TCP之上。CoAP協議非常小巧,最小的數據包僅為4字節。
本文將使用STM32平臺實現一個CoAP Server Demo。本文將詳細說明如何使用STM32這樣的低成本MCU實現CoAP Server的步驟,本文試圖說明CoAP協議雖然很“年輕”,但是有用、好用且易用。
【代碼倉庫】
如果想獲得本文的示例代碼請點擊——【bitbucket】,示例代碼中的doc目錄有本文所使用開發板的原理圖和相關說明。
【相關博文】
【物聯網學習筆記——索引博文】
【CoAP學習筆記——nodeJS node-coap安裝和使用(windows平臺)】
1.使用LwIP處理CoAP數據包
新建一個套接字,綁定UDP 5683端口,偵聽該端口數據使用microcoap響應函數解析,最后獲得返回結果即可。示例中使用了RT Thread中移植好的LwIP協議棧,網卡驅動為ENC28J60。
void coap_server(void* para)
{
int fd;
struct sockaddr_in servaddr, cliaddr;
coap_rw_buffer_t scratch_buf = {scratch_raw, sizeof(scratch_raw)};
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
printf("Socket Error\r\n");
return;
}
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);
rt_memset(&(servaddr.sin_zero), 0, sizeof(servaddr.sin_zero));
if ((bind(fd, (struct sockaddr *)&servaddr, sizeof(servaddr))) == -1)
{
printf("Bind error\r\n");
return;
}
endpoint_setup();
rt_kprintf("Coap Server Start!\r\n");
while(1)
{
int n, rc;
socklen_t len = sizeof(cliaddr);
coap_packet_t pkt;
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &len);
#ifdef MICROCOAP_DEBUG
printf("\r\n--------------------\r\n");
printf("Received Buffer: \r\n");
coap_dump(buf, n, true);
printf("\r\n");
#endif
if (0 != (rc = coap_parse(&pkt, buf, n)))
{
printf("Bad packet rc=%d\r\n", rc);
}
else
{
size_t rsplen = sizeof(buf);
coap_packet_t rsppkt;
#ifdef MICROCOAP_DEBUG
printf("Dump Packet: \r\n");
coap_dumpPacket(&pkt);
#endif
coap_handle_req(&scratch_buf, &pkt, &rsppkt);
if (0 != (rc = coap_build(buf, &rsplen, &rsppkt)))
{
printf("coap_build failed rc=%d\n", rc);
}
else
{
#ifdef MICROCOAP_DEBUG
printf("--------------------\r\n");
printf("Sending Buffer: \r\n");
coap_dump(buf, rsplen, true);
printf("\r\n");
#endif
#ifdef MICROCOAP_DEBUG
coap_dumpPacket(&rsppkt);
#endif
sendto(fd, buf, rsplen, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
}
}
}
}
代碼中使用了多個LwIP Socket部分的函數,例如socket, bind, recvfrom, sendto等。
其中coap_parse函數把從UDP獲得的payload轉化為符合CoAP規范的結構體,coap_handle_req函數根據CoAP請求中的URI,調用響應的處理函數。最后由coap_build函數把處理的結果系列化為UDP負載。
2.終端描述
所有的終端信息均保存在endpoints全局數組中,該全局數組位于endpoints.c文件中。
const coap_endpoint_t endpoints[] =
{
{COAP_METHOD_GET, handle_get_well_known_core, &path_well_known_core, "ct=40"},
{COAP_METHOD_GET, handle_get_light, &path_light, "ct=0"},
{COAP_METHOD_PUT, handle_put_light, &path_light, NULL},
{COAP_METHOD_GET, handle_get_test_json, &path_test_json, "ct=50"},
{(coap_method_t)0, NULL, NULL, NULL}
};
【1】每個endpoint需要CoAP訪問方法,相應的處理函數,URI路徑描述,資源描述方法等。
【2】CoAP協議中定義了多種訪問方法,GET、PUT、POST和DELETE等方法。
【3】handle_get_light等函數主要用于處理CoAP請求,根據不同的請求調用不同的處理方法。
【4】ct=xx指定資源描述方法,例如ct=0表示字符串形式描述,ct=50表示JSON形式描述。
URI采用以下方式描述:
static int handle_get_light(coap_rw_buffer_t *scratch,
const coap_packet_t *inpkt,
coap_packet_t *outpkt,
uint8_t id_hi, uint8_t id_lo)
{
return coap_make_response(scratch,
outpkt,
(const uint8_t *)&light, 1,
id_hi, id_lo,
&inpkt->tok,
COAP_RSPCODE_CONTENT,
COAP_CONTENTTYPE_TEXT_PLAIN);
}
除了指定返回內容之外,可通過COAP_RSPCODE_CONTENT指定返回是否成功,也可以通過COAP_CONTENTTYPE_TEXT_PLAIN指定返回內容的格式。更多的定義請查看microcoap的源代碼。
3.簡單測試
可使用CoAP命令行工具測試CoAP Server工作是否正常,或者使用火狐瀏覽器的coap插件。
使用CoAP命令行測試工具——coap-cli,詳細的安裝步驟請參考【CoAP學習筆記——nodeJS node-coap安裝和使用(windows平臺)】第2部分
3.1 light Demo
輸入指令,嘗試修改light狀態
coap put -p 1 coap://10.13.11.116/light
返回
(2.05) 1
說明
-p參數可用于指定coap的負載,此處1表示打開light,0表示關閉light。
圖3.1 light PUT方法輸出
輸入指令,嘗試獲得light狀態
coap get coap://10.13.11.116/light
返回
(2.05) 1
控制臺輸出
圖3.2 light GET方法輸出
3.2 JSON格式Demo
指令
coap get coap://10.13.11.116/test_json
返回
(2.05)
{
"value": 12
}
控制臺輸出
圖3.3 JSON格式測試輸出
4.CoAP格式分析
通過示例代碼并借助wireshark可分析CoAP數據包的各個部分,可加上CoAP協議的理解。wireshark中已經支持CoAP協議,在過濾窗口中輸入coap便可抓取所有coap數據包。
CoAP協議的分析請參考——【CoAP學習筆記——CoAP格式詳解】
圖4.1 wireshark分析CoAP
5. 總結
microcoap正如它的名稱一樣,簡單好用,函數不多但是可以實現最基本的功能。(by xukai871105)
STM32單片機中文官網
意法半導體/ST/STM
評論