HTTP風なCoAP

そういやCoAPについて何も書いてなかったな…と思って書いておくことに。
CoAP(RFC7252)で定義されているUDPベースのプロトコルで、MQTTと同じくIoT/M2M向けの軽量版みたいな感じ。
ググればRFC/Wikipediaとかに詳しい説明があるから、そっちを見て貰った方が早いと思われ。MQTTとの比較はこんな感じ。

■ Simple Coap Library

このシンプルな実装を何年か前に作って公開している。ArduinoやPlatformIOで”CoAP”で検索すると出てくる、Simple Coap Libraryというもので。シンプルという名の通り、ライブラリのサイズ・実装も非常に軽量なものにしている。
一応動作するのは、Ardiuno物、ESP8266、ESP32、Particleといった環境での動作確認をしている。少し改造すれば、まぁ何でも動くと思われ。

Arduino libraryでCoAPで検索すると出てくる

このCoAPだけど、見事なほどまで人気が無いと個人的には思っている。MQTTとかはよく見るし、Google/AWS/AzureのIoT向けサービスもMQTTに対応している。ただ、CoAPに対応している物は数少ない。MQTT(TCP)、CoAP(UDP)とあるにも関わらず。CoAP over TCP/TLS/WebSocket(RFC 8323)という、TCP上に実装した物や、DTLS(TLSのUDP版みたいなもの)上に実装したりセキュリティには配慮していたりするのだけど。
個人的にCoAPの良いと思う点は、

・UDPで軽い
・HTTP Likeでとっつきやすい
・ノード間通信を意識している

このあたりかな。まぁ、こういう選択肢もあるよ、くらいな感じで使う感じだとは思っている。

■ サンプルコード

ESP32向けのサンプルコードはこんな感じ。githubにあるサンプルそのまんま、UDP サーバー・クライアントとして動作する。もちろん、クライアントだけで動作させてもOK。

#include <WiFi.h>
#include <WiFiUdp.h>
#include <coap-simple.h>

// WiFi接続用
const char* ssid     = "your-ssid";
const char* password = "your-password";

// デフォルトコールバック用関数
void callback_response(CoapPacket &packet, IPAddress ip, int port);

// endpoint(/light)に対するコールバック用関数
void callback_light(CoapPacket &packet, IPAddress ip, int port);

// UDPとCoAPクラスの定義
WiFiUDP udp;
Coap coap(udp);

// Lチカ用の状態
bool LEDSTATE;

// endpoint(/light)に対するコールバック用関数
// LEDのON/OFFに対応する
void callback_light(CoapPacket &packet, IPAddress ip, int port) {
  char p[packet.payloadlen + 1];
  memcpy(p, packet.payload, packet.payloadlen);
  p[packet.payloadlen] = NULL;
  
  String message(p);
  if (message.equals("0"))
    LEDSTATE = false;
  else if(message.equals("1"))
    LEDSTATE = true;

  // 状態によってLEDをON/OFFして、応答パケットを返送   
  if (LEDSTATE) {
    digitalWrite(9, HIGH) ; 
    coap.sendResponse(ip, port, packet.messageid, "1");
  } else { 
    digitalWrite(9, LOW) ; 
    coap.sendResponse(ip, port, packet.messageid, "0");
  }
}

// デフォルトのコールバック。CoAPのプロトコルをハンドリングする。
void callback_response(CoapPacket &packet, IPAddress ip, int port) {
  Serial.println("[Coap Response got]");
  
  char p[packet.payloadlen + 1];
  memcpy(p, packet.payload, packet.payloadlen);
  p[packet.payloadlen] = NULL;
  
  Serial.println(p);
}

void setup() {
  Serial.begin(9600);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }

  // Lチカ用の状態
  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  LEDSTATE = true;
  
  // エンドポイント(/light)に対応するコールバック登録
  coap.server(callback_light, "light");

  // デフォルトのコールバックを登録
  coap.response(callback_response);

  // CoAPの起動
  coap.start();
}

void loop() {
  delay(1000);
  coap.loop();
}

URIのコールバック用の関数を登録して動かすだけ。登録以外のURLにパケットが来た場合はデフォルトコールバックが動作する(コールバックを登録していないとパケットを無視する)。例えば…

coap.server(callback_light, "light");
coap.server(callback_light_state, "light/state");
coap.server(callback_light_hogehoge, "light/hogehoge");

こんな感じで複数登録して、そのURIに来たリクエストをハンドリングすることが出来る。クライアントだけで動作させたい場合は、次の関数を用意している。

coap.get(IPAddress(XXX, XXX, XXX, XXX), 5683, "time");
coap.put(IPAddress(XXX, XXX, XXX, XXX), 5683, "light", "1");

PUT/GETに対応した関数で、[IPアドレス、ポート番号、endpoint(time/)、value(PUTする時)] を投げることが出来る。CoAPサーバー(UDPサーバ)としての動作と、クライアント単体としての動作のどちらでも動かせる感じ。
Simple CoAP Libraryという名前の通り、シンプルな動作でProxyとしての動作、DELETEや他のHTTPメソッドについては実装していない。もしその手の動作をさせたい場合は、デフォルトコールバック関数で自分でやれば良いんじゃね?基本的なPUT/GETで十分でしょ、と思っていたりする。

MQTTをこの手のデバイスで使う時は、サーバーが必要になるけど、CoAPはクライアント&サーバーとしての役割があるから、単体で動作させることが出来るというのが大きな違いかな。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください