jollen / blog

Jollen's Blog
http://www.jollen.org/blog
66 stars 4 forks source link

在 NodeMCU 上使用 er-coap-13 #1

Closed jollen closed 8 years ago

jollen commented 8 years ago

為了在 NodeMCU 上進行 WoT 實驗,日前開啟了一個小型的專案 node-wot,node-wot 將會持續小幅修改 nodemcu-firmware,以做為 WoT 的實驗用 firmware。

目前的主要修改內容,是將 NodeMCU firmware 裡的 libcoap 更換為 Contiki OS 的 er-coap-13 實作,並且只保留了 CoAP client。主要的原因如下:

現階段,僅將 ESP8266 做為 data push object,並不做為 CoAP server,因此只保留 CoAP client 的實作。主要考量是節省 RAM + ROM 的空間(ESP8266 的 iram1_0_seg + irom0_0_seg)。

使用 CoAP API

er-coap-13 的完整 API 設計,是 node-wot 專案的重要考量。例如,要建立一個新的 CoAP 封包,只要呼叫 _coap_initmessage() 函數即可:

// 引入 er-coap-13 APIs
#include "er-coap-13.h"
#include "er-coap-13-transactions.h"

// 宣告 CoAP packet
coap_packet_t request[1];

// 將 CoAP packet 初始化為 COAP_TYPE_CON 類型,並使用 HTTP POST
coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0);

要初始化 CoAP headers,例如:填寫 CoAP header 的 Uri Path 與 Uri Host,只需要這樣寫:

coap_set_header_uri_path(request, 'object/123456/send');
coap_set_header_uri_host(request, 'wot.city');

要加入 payload(本文),也只要這樣寫:

const char *payload = "{}";
coap_set_payload(request, (uint8_t *)payload, strlen(payload));

最後也只要呼叫不到 5 個 APIs,就可以將 CoAP 封包送出。

er-coap-13 有更好的 abstraction level,可以隱藏所有 CoAP 標準的技術細節。

Heap Management

node-wot 上的 er-coap-13 做過微幅修改,以及完整的測試,可保證 CoAP requests 不會消耗 Heap 空間。Heap 空間若持續減少,會發生記憶體不足的現象,Lua 程式會 crash。

Lua 範例程式

node-wot 的修改並不影響 Lua programming model,因此 Lua 程式可同時在 nodemcu-firmware 與 node-wot 上執行。

以下是 2015 年 12 月 8 日在 ESP8266 IoT Workshop 上使用的 Lua 範例(搭配 node-wot firmware 使用)。

Hello, Lua

以 HTTPD 做為學習 Lua 的 "Hello, World"。

-- Select IO - GPIO4
outpin=4
gpio.mode(outpin,gpio.OUTPUT)
gpio.write(outpin,gpio.HIGH)    

function power(stat)
  if stat=="ON"  then gpio.write(outpin,gpio.HIGH) 
    return 
   end
  if stat=="OFF" then gpio.write(outpin,gpio.LOW) 
    return 
   end
end

-- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Configure the ESP as a station (client)
wifi.setmode(wifi.STATION)  
wifi.sta.config("JY", "1234567654321")  
wifi.sta.autoconnect(1)

-- Create a server
-- and set 30s time out for a inactive client
sv = net.createServer(net.TCP, 30)

-- Server listen on 80
-- Print HTTP headers to console
sv:listen(80,function(c)  
    c:on("receive", function(conn, payload)
        print(payload)

        if (string.find(payload, "GET /power/on") ~= nil) then
            power("ON")
        elseif (string.find(payload, "GET /power/off") ~= nil) then
            power("OFF")
        end

        conn:send("HTTP/1.1 200 OK\n\n")
        conn:close()
    end)
end)

Hello, CoAP

以 CoAP request 做為學習 IoT 的 "Hello, World"。

-- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Configure the ESP as a station (client)
wifi.setmode(wifi.STATION)  
wifi.sta.config("JY", "1234567654321")  
wifi.sta.autoconnect(1)

-- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Create a CoAP client
cc = coap.Client()

-- Make a POST request
uri="coap://127.0.0.1:8000/object/12345678/send"

tmr.alarm(0, 1000, 1, function() 
    cc:post(uri, "{\"temp\":20}\r\n")
end)

Hello, LWM2M

最後是一個 CoAP-LWM2M Broker 的範例。需搭配 WoT.City 專案使用。

- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Configure the ESP as a station (client)
wifi.setmode(wifi.STATION)  
wifi.sta.config("JY", "1234567654321")  
wifi.sta.autoconnect(1)

-- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Create a CoAP client
cc = coap.Client()

-- Make a POST request
uri="coap://172.20.10.4:8000/object/12345/send"
cc:post(uri, "{\"temp\":30}\r\n")

-- Register LWM2M object
registry="coap://172.20.10.4:8000/75000/1/create"
cc:post(registry, "{}\r\n")

這個範例需要搭配一個 LWM2M broker server \ 來使用:

這個範例,假設 NodeMCU 僅做為 CoAP client 使用,因此 node-wot 目前並沒有移植 er-coap-13 的 CoAP server 實作。

此外,考量硬體限制(ROM 與 RAM 大小),也沒有移植 LWM2M 程式庫至 node-wot。

關於 LWM2M

OMA LWM2M 建構在 CoAP 協定層之上,在這次的工作坊裡,展示了如圖 1 的佈署(deploy)策略。

以下是這個佈署策略的簡要說明: