Email me: jollen # jollen.org

more: Jollen 的 Embedded Linux 教育訓練

« September 2015 | (回到Blog入口) | January 2016 »

December 2015 歸檔

December 28, 2015

[IoT] 在 NodeMCU 上使用 er-coap-13

源由

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

本文章採用 Markdown 語法撰寫(why?),若無法閱讀內文,請點擊這裡。

node-wot 專案:ESP8266 CoAP SDK

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

  • er-coap-13 有比較嚴謹的 API 設計,可做為 NodeMCU 的 CoAP SDK,未來可以更方便開發 CoAP 應用程式
  • 「有朝一日」,或許可加入基於 er-coap-13 的 LWM2M 實作 (wakaama)
  • 未來計畫引入 protothreads 機制,取代 FreeRTOS multitasking
  • Heap management

現階段,僅將 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_init_message() 函數即可:

// 引入 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 發送一個 LWM2M 的註冊請求
  • LWM2M broker 協助 NodeMCU 準備完整的 LWM2M object,並向 LWM2M server 註冊
  • LWM2M broker 作為一個代理(proxy)的角色

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

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

關於 LWM2M

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

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

  • LWM2M 只佈署在 connected device 上(具對外網路能力的裝置),現階段可使用 Notebook、LinktIt Smart 7688 或 Intel Edison 做為 connected device

  • ESP8266 做為 physical data object(IoT data device),並且將 CoAP blocking request 修改為 protothread 機制

  • Physical data object 不具備 control 能力

留言與討論

[IoT] 如何用 Wio Link 快速自製 GoPro Remote

上週(12/23 與 12/24)分別在「WoT.City x 台灣大學電機系:Wio Link 工作坊」課程,以及「LinkIt Smart 7688 與 Wio Link(ESP8266)技術沙龍」活動上,閃電展示了如何使用 Wio Link 自製 GoPro 的 Remote 控制器,整個過程只需要大約 30 分鐘,以下分享製作方法。

本文章採用 Markdown 語法撰寫(why?),若無法完整閱讀全文,請點擊這裡。

實作原理

本專案的技術原理非常簡單:

  • GoPro 內建 Cherokee Web Server
  • 使用 HTTP GET 方法呼叫 GoPro 的拍照 API

本文使用 Wio Link 或 NodeMCU 開發板,並以 Lua 來撰寫 HTTP request 程式碼。改用 Node.js 來實作 HTTP request 的話,可以改用近期火紅的 MediaTek LinkIt Smart 7688 (Duo) 開發板來製作 GoPro 控制器。

Step 1: 材料準備

  • GoPro 一台,筆者使用的是 GoPro Hero 4 Silver
  • Wio Link 或 NodeMCU 開發板(皆使用 ESP8266 模組)

Step 2: 更新 GoPro Firmware

下載 GoPro Studio 安裝後,將 GoPro 連接到電腦。GoPro Studio 會自動更新 GoPro firmware。

Step 3: 更新 Wio Link Firmware

使用 NodeMCU 的開發者可忽略這個步驟。

Wio Link 預載 Seeed Stduio 專門打造的 firmware,可搭配 Wio Link App[1] 快速打造 IoT application。在這個小專題裡,我們會使用 Lua 來撰寫簡單的 GoPro 控制器,因此要更換為 NodeMCU firmware。

更新 NodeMCU firmware 的詳細步驟,請參考ESP8266 & NodeMCU 開發入門 (Part 5) - 編譯並更新 NodeMCU Firmware。

Step 4: 連接 GPIO Button

如圖 1,將 Grove Kit 的 Button 接到 Wio Link 的 Digital 插糟。以圖 1 為例,Button 將經由 GPIO14 來控制,後續撰寫程式時,須將 GPIO14 設定為中斷觸發。

wio-link-gopro-1 圖 1:連接 GPIO Button

Step 5: 認識 Wio Link GPIOs

Wio Link 設計了 3 個 Digital 插糟,每個插糟上都有 2 根 GPIO 腳位。以上圖為例,觀察黃色接線,可以看出,Grove Kit 的 Button 是經由 GPIO14 來控制。Wio Link 的 GPIO 腳位,相容於 NodeMCU,因此應用上可視為 NodeMCU 的替代品。

| Wio Link 腳位名稱 | NodeMCU 腳位名稱 | Note | |-------|---------|------------|-------| | GPIO-13 | GPIO-13 | D7 | | GPIO-12 | GPIO-12 | D6 | | GPIO-14 | GPIO-14 | D5 |

以圖 1 為例,撰寫 Lua 程式將 GPIO-14(D5)設定為中斷觸發模式:

pin = 5
gpio.mode(pin, gpio.INT)

再定義中斷觸發的 callback function:

--set the interrupt callback function
gpio.trig(pin, "both", button)

對 Wio Link 與 NodeMCU 的 Lua 開發環境不熟悉的話,可以參考以下文章:

WoT.City 提供的ESP8266 & NodeMCU 開發入門系列文章,可做為初學者的自學教材。

Step 6: 撰寫 Lua 程式

將以下程式碼上傳至 Wio Link:

--
-- GoPro Remote Simple
-- See: https://github.com/jollen/blog/issues/6
--

-- Configs
host = "10.5.5.9"
port = 80
ssid = "<SSID>"
pass = "<PASSWORD>"
pin = 5
api = "/gp/gpControl/command/shutter?p=1"

-- Configure the ESP as a station (client)
wifi.setmode(wifi.STATION)
wifi.sta.config(ssid, pass)
wifi.sta.autoconnect(1)

-- Create a TCP socket
sk = net.createConnection(net.TCP, 0)
sk:on("receive", function(sck, c) 
  print(c)
end)

-- Set GPIO
gpio.mode(pin, gpio.INT)

-- Poll GPIO
now = 0
duration = 0
    
function button(level)
  duration = tmr.now()-now
  print(duration)
  now = tmr.now()

  if level == 1 then 
    print("down")
    sk:send(headers)
  else 
    print("up")
  end
end

--set the interrupt callback function
gpio.trig(pin, "both", button)
    
-- Print IP address
local ip = wifi.sta.getip()

-- Make HTTP request headers
if (ip == nil) then
  ip = "localhost"
end
headers = "GET " .. api .. " HTTP/1.1\r\nHost: " .. ip .. "\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n"
print(headers)

-- Connect to GoPro host
sk:connect(port, host)

可將程式碼儲存為 init.lua 後上傳至 Wio Link。Wio Link 開機時,會自動執行 init.lua。

Step 7: 開啟 GoPro Wifi 並進行測試

  1. 開啟 GoPro Wifi
  2. 將 GoPro 設定為拍照模式
  3. 上傳上述程式碼至 Wio Link
  4. 按下 Button 拍照

實境測試影片如下。

網路資源

[1]: Wio Link App, https://itunes.apple.com/tw/app/wio-link/id1054893491?mt=8

留言與討論

關於 December 2015

此頁面包含了在December 2015發表於Jollen's Blog的所有日記,它們從老到新列出。

前一個存檔 September 2015。

後一個存檔 January 2016。

更多信息可在 主索引 頁和 歸檔 頁看到。

Top | 授權條款 | Jollen's Forum: Blog 評論、討論與搜尋
Copyright(c) 2006 www.jollen.org