2020年04月03日

xv6にネットワーク機能を実装した

はてなブックマークに登録

在宅勤務に移行してから1ヶ月半ほど経過しました。通勤という概念が消滅したおかげで午前中から活動できるようになった @pandax381 です。

要約

フルスクラッチで自作した TCP/IP プロトコルスタックを xv6 に組み込み、一通りの機能が動作するようになりました。

経緯

かれこれ7〜8年くらい経つんですけど、ライフワークとしてお勉強用のTCP/IPプロトコルスタックの開発をほそぼそと続けています。

一般的に、プロトコルスタックはOSの機能の一部としてカーネル内部に存在しているのですが、この自作プロトコルスタックは、作りやすさを重視した都合でユーザ空間のアプリケーション用のライブラリとして実装しています。

microps-01

最下層に位置するリンクレベルの入出力は PF_PACKET / BPF / TAP を利用するお手軽仕様で、純粋にパケット処理に専念できるようになっています。

microps-02

やや作りが粗い部分もありますが、Ethernetフレームの送受信からTCPセグメントのやり取りまで全てを自前で処理しています。「プロトコルスタックってどんな作りになっているんだろう」と興味を持った人が、手を出して雰囲気を掴むのに丁度いいくらいのボリュームになっているんじゃないかと思います。

(物理デバイスや論理インタフェースの抽象化とかそれなりに作り込んであるので、興味があったらコード見てください)

毎年、インターンシップを希望する学生を受け入れていて、彼らの頑張りによって DHCPクライアント機能やパケット転送機能、更にはDPDKサポートといった改良が加えられいますが、いずれも1〜2週間という短い期間で成果を出してくれました。

その流れで、昨夏には KLab Expert Camp という合宿イベントを開催しました。

4日間、ひたすらプロトコルスタックを開発するというマニアックな合宿イベントにもかかわらず、全国からたくさんの学生が参加してくれ、実装解説を通じて基本的な仕組みを学んだり、別言語への移植や機能追加に取り組んでくれました。

参加者からのフィードバックがすごく良くて今後も定期的に開催したいなと思う中で、最近の動向を見ていると「自作CPUで動かしている自作コンパイラでビルドした自作OSに自作プロトコルスタックを組み込みたい」というヤバイ人が出現する可能性も十分ありそうな気がしてきます。

少なくとも、ユーザ空間での動作を前提としてたライブラリの状態からOSのカーネルに組み込もうとした際にどんな罠が潜んでいるかを把握しておかないことには、十分にサポートしてあげられないことは目に見えています。そんなわけで「まずは自分でやってみるか」というのが、この取り組みのそもそもの動機です。

手頃なOSを選定

恥ずかしながら、僕はOS自作を嗜んでおらず「自分のOS」を所有していません。30日OS自作本も「いつか読もう」と積んである始末なので、OS自作からはじめていると時間が掛かりすぎてしまします。だからと言って Linux や BSD だと、ベースにするには規模が大きすぎます。

こういった用途には、コンパクトな xv6 が向いていそうな気がします。

CPU自作関連の話題でも頻繁に登場していますし、いまさら説明するまでもないと思いますが、xv6 は Version 6 UNIX (V6) を x86(マルチプロセッサ環境)向けに再実装したものです。MITがオペレーティングシステムの講義用に開発し、MIT以外にも数多くの教育機関が教材として採用しています。

コード量は10,000行程度で、OSの実装にさほど詳しくなくても全体を見渡すことができるボリュームです。充実したドキュメントと先人たちが公開してくれている情報が豊富にあるのも心強いです。

最近は RISC-V 向けの実装に移行したようで、MITの講義資料も最新のものは RISC-V 版がベースになっています。x86 版は 2018 年度以前のアーカイブを参照しましょう。

どこからはじめるか

xv6 にはネットワーク機能が一切含まれていないため、NICのドライバから書く必要があります。自作プロトコルスタックではリンクアクセスはOSの機能(PF_PACKET や BPF)を利用していてドライバにはノータッチだったので、もう一段下の世界へ降りることになります。

xv6 は QEMU 上で動作させながら開発を進めることになるので、NIC は QEMU で使えて情報も豊富そうな e1000 (Intel 8254x) をターゲットにするのが良さそうです。

まずは、ドライバのエントリポイントのダミーを用意して Hello, World! でも出力するようなコードを書き、PCI デバイスのドライバのリストに追加してあげるところからはじめたら良さそう ...と思ったのですが、 探してもそれっぽいコードが全く見当たりません。

はい、xv6 は PCI をサポートしていないのでした。

したがって xv6 で NIC を扱うためには

  • PCIバスをスキャン
  • 接続されているデバイスを検出
  • 対応するドライバを呼び出す

という一連の処理から作り込む必要があり、更にもう一段下の世界へ降りることになるわけです。だんだんと上昇負荷が厳しくなりそうですね。

PCI周りの処理

PCIバスのスキャン とか言うとなんだかすごく難しそうに聞こえますが、I/Oポートを叩いて情報を読む、という処理の繰り返しです。そして、この辺はOS自作界隈のみなさんが素晴らしい情報を公開してくれています。

コンフィグレーション領域ってなんぞ?という状態でしたが uchanさん の資料を読んで PCI を完全に理解しました(してません)。

以下の Linux Kernel を解説したドキュメントも参考になりました。

これらの解説を読みながら少しづつ進めていけば PCI バスをスキャンしてデバイスを検出するコードが書くのはさほど難しくないと思います。なお、xv6-net では PCI の最終的なコードは JOS という別の OS から拝借することにしました。

  • https://pdos.csail.mit.edu/6.828/2018/jos.git

JOS は、xv6 と同様に MIT がオペレーティングシステムの講義で使うために開発している OS です。JOS は一部が意図的に削られている未完成のOSであり、xv6 の解説を聞いたあとに、JOS の実装を進めて完成させるという構成のようです。また、xv6 がモノリシックなカーネルであるのに対して JOS はマイクロカーネルという違いもあるようです。

読み比べてみると JOS の方が諸々整理されていてキレイに書かれている気がするものの、全体の雰囲気はかなり似ています。そして、JOS には PCI 周りのコードと e1000 ドライバのスケルトン(ほぼなにも書かれていない空っぽのファイル)が用意されているため、これを使わせてもらうことにしました。

上記の pci.c は JOS のコードそのものです。xv6 に手を出す人は高確率で JOS のコードも読んでいるはずで、それであれば僕が書き散らかしたコードよりも JOS のコードを使わせてもらったほうが読む人が理解しやすいだろうと考えた結果です(コードをそのまま組み込むため、辻褄合わせに少し手間を掛けていたりします)。

pciinit()main() の中から呼んであげると、起動時に PCI バスをスキャンして接続されているデバイスが検出されます。

PCI: 0:0.0: 8086:1237: class: 6.0 (Bridge device) irq: 0
PCI: 0:1.0: 8086:7000: class: 6.1 (Bridge device) irq: 0
PCI: 0:1.1: 8086:7010: class: 1.1 (Storage controller) irq: 0
PCI: 0:1.3: 8086:7113: class: 6.80 (Bridge device) irq: 9
PCI: 0:2.0: 1234:1111: class: 3.0 (Display controller) irq: 0
PCI: 0:3.0: 8086:100e: class: 2.0 (Network controller) irq: 11

ベンダIDとデバイスIDをキーにドライバのエントリポイントを定義しておくと、デバイス検出時にそれを叩いてくれます。

struct pci_driver pci_attach_vendor[] = {
	{ 0x8086, 0x100e, &e1000_init },
	{ 0, 0, 0 },
};

NICのドライバ

PCI のデバイスを検出できるようになったら、次は NIC のドライバを書きます。

JOS のソースには kernel/e1000.c kernel/e1000.h が含まれていますが、どちらも空っぽで、Intel のドキュメントをちゃんと読めという圧が伝わってきます。

参考になりそうなコードがないか探してみると、xv6 の RISC-V 版のリポジトリには NIC の初期化処理まで書かれたドライバの雛形がありました。初期化処理を参考にしつつ、ヘッダファイルはそのまま利用させてもらいました。

また、大神さん が公開されている本がめちゃくちゃわかりやすくて助かりました。PCIの情報も詳しく書かれていて、こんな情報をタダで読ませてもらって良いのか...と感動しながら e1000 を完全に理解しました(してません)。EPUB版でたら買います。

OSDev.org で紹介されている以下のコードも参考にしました。

他にも、世の中には xv6 向けの e1000 ドライバを書いている人は沢山いるので、github 上でもいくつかプロジェクトを見つけることができます。ただし、どれもポーリングする前提で、割り込みがちゃんと動く状態のものは見つけられませんでした。

一連の作業の中で一番苦戦したのが、パケット受信時に割り込みを発生させることで、1週間くらいハマっていました。ちゃんと書いたつもりでもなかなか割り込みが発生しなく心が折れそうになっていたので、動いたときはめちゃくちゃ嬉しかったです。

(たぶん送受信で使うDMA用のバッファ設定がうまくできていなくて動いていなかったんじゃないかと思っています)

この辺の詳しいことは、また別の記事か薄い本でも書こうかなと思っています。

プロトコルスタック本体の移植

NIC のドライバが動いたので、いよいよ本題のプロトコルスタック本体の移植に取り掛かるのですが、あまり躓くことなくあっさり移植できてしまいまったので、あまり書くことがなかったりします。

pthread を使っていたので mutex を spinlock に置き換えたり、cond をタスクの sleep/wakeup に置き換えたりしたくらいです。あと、タイマーが使えていないので、TCPの再送とかタイマーに依存した処理はまだ動かせていません。

まず、ARP に応答できるようになって

ICMP に応答できるようになって

UDP で通信できるようになり

最終的に TCP も動くようになりました

ソケット

カーネル内でプロトコルスタックが動くようになってもソケットがなければユーザ空間で通信アプリケーションを書くことができません。

自作プロトコルスタックにはソケット風のAPIもあるのですが、これは単なるライブラリ関数なのでプロトコルスタックをカーネルに組み込んだ状態ではユーザ空間のアプリケーションから呼びだせません。

そんなわけで、ソケット風 ではなくガチのソケット(関連のシステムコール)を実装しました。

システムコールの追加にあたっては、xv6 の既存のシステムコールの中から似たようなプロトタイプのものを探して同じように実装しています。

socket() で作られるディスクリプタは、もともと存在しているファイルディスクリプタと互換性を持つように作っているので、ソケットのディスクリプタを close() で閉じたり、recv() / senc() の代わりに read() / write() を使うことが出来ます。

単純なソケット通信のプログラムであれば、Linux 用に書いたコードがそのまま動く程度にはちゃんと作っています。

ifconfig コマンド

シェルからインタフェースの状態を確認したりIPアドレスを設定するために、当初は適当なコマンド(ifget / ifset / ifup / ifdown)と対応するシステムコールを作って、その場をしのいでいました。

ただ、これだとちょっとダサいので、最終的に ifconfig コマンドを作りました。ip コマンドじゃないのは NETLINK の実装はさすがに厳しいからという理由です。

ifconfig は ioctl() を通じてインタフェースの情報を取得/設定しているので、ioctl のシステムコールを追加し、SIOCGXXXSIOCSXXX をひたすら作り込んでいます。

おわりに

あれもこれもと作り込んでいたら「自作プロトコルスタックを xv6 に移植した」というよりは「xv6 にネットワーク機能を実装した」という気持ちになったので、このようなタイトルになりました。

あと、勢い余って reddit デビューもしました。

reddit でコメントくれた方の自作OSがしっかり作られていて感動したので紹介しておきます。

まだ先の予定を立てるのは難しいですが、イベントが開催できるような状況になったら、また KLab Expert Camp でプロトコルスタック自作の合宿をやりたいと思っています。その際は自作OSへの組み込みもサポートできるように準備しておきますので楽しみにしていてください!


@pandax381

pandax381 at 18:58
この記事のURLComments(0)network 
2020年03月04日

デプスカメラを「バーチャル背景」用 Web カメラとして使う

はてなブックマークに登録

先日ちょっとしたきっかけから Intel 製 RealSense D415 を買いました。デプスカメラに触れるのは初めてでしたが、Microsoft Azure Kinect の国内販売がまもなく開始される旨もアナウンスされておりこの分野の今後の動向が楽しみです。

試作の内容

さっそくこの D415 をあれこれ試しています。公式の RealSense SDK 2.0 の勉強をかね習作のひとつとして次の内容のプログラムを作ってみました。

  • RGB カラーフレームの画角へ Depth フレームを位置合わせして両方の情報を使用
  • カメラのスコープから「1m 以内」にあるものの RGB 画像を抽出して表示する
  • スペースキー押下で背景画像を切り替え上の画像との合成を行う

静止状態でも常に微妙な揺れを伴う深度情報を RGB 画像切り抜き領域の判定に用いているため静的なクロマキー合成とは異なり境界にノイズが発生しがちですが、デプスカメラ利用の一例として紹介します。

動作の様子

デモ動画:1分4秒 無音

ソースコード

プログラムでは画像処理と表示に OpenCV を併用しています。

  • SDK 内の以下のサンプルプログラムを参考にしておりコードの一部を引用しています
  • 手元の開発環境は Windows 10 + Visual Studio 2019 Community です
  • プロジェクトファイルは上記サンプルのものをコピー〜編集して利用するのが手早いでしょう
//
// Intel RealSense D400 シリーズ
//
// カメラから所定の距離内の RGB 画像を抽出して表示.
// スペースキー押下で背景画像を差し替え上の画像と合成する.
//
// 2020-02
//

#include <librealsense2/rs.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <cmath>

#define Width  640
#define Height 480
#define Fps     15

// RGB 画像抽出対象圏内の距離
#define Depth_Clipping_Distance 1.0f  // 1メートル

// 背景画像ファイル数
#define NumBgImages 8

// Depth スケール情報を取得
float get_depth_scale(rs2::device dev)
{
  // センサ情報を走査
  for (rs2::sensor& sensor : dev.query_sensors()) {
    // 深度センサなら Depth スケール情報を返す
    if (rs2::depth_sensor dpt = sensor.as<rs2::depth_sensor>()) {
      return dpt.get_depth_scale();
    }
  }
  throw std::runtime_error("Device does not have a depth sensor");
}

// RGB フレーム中の Depth_Clipping_Distance 圏外を塗りつぶし
// 圏内 = 255, 圏外 = 0 のマスクイメージを得る
cv::Mat remove_background(rs2::video_frame& video_frame,
            const rs2::depth_frame& depth_frame,
            float depth_scale)
{
  const uint16_t* p_depth_frame = (const uint16_t*)(depth_frame.get_data());
  uint8_t* p_video_frame = (uint8_t*)((void*)(video_frame.get_data()));

  // マスク用の Matrix をモノクロで用意
  cv::Mat m = cv::Mat(cv::Size(Width, Height), CV_8UC1);

  // 当該 video_frame の幅, 高さ, 1ピクセルのバイト長
  int width = video_frame.get_width();
  int height = video_frame.get_height();
  int other_bpp = video_frame.get_bytes_per_pixel(); // RGB につき 3

  // Depth フレームを走査して RGB フレームの画像情報を加工
  //   OpenMP で二重 for ループを並列に処理
  //   VC++ ではコンパイルオプション /openmp が必要
  //   https://docs.microsoft.com/ja-jp/cpp/parallel/openmp/openmp-in-visual-cpp?view=vs-2019
  //   https://docs.microsoft.com/ja-jp/cpp/parallel/openmp/d-using-the-schedule-clause?view=vs-2019
  #pragma omp parallel for schedule(dynamic)
  for (int y = 0; y < height; y++) {
    auto depth_pixel_index = y * width;
    for (int x = 0; x < width; x++, ++depth_pixel_index) {
      // 現座標箇所のセンサからのメートル距離を得る
      auto pixels_distance = depth_scale * p_depth_frame[depth_pixel_index];
      // Depth の死角領域 (<=0) および 対象圏外に該当の場合
      if (pixels_distance <= 0.f || pixels_distance >
Depth_Clipping_Distance) {
        // RGB フレーム内の対象オフセット
        auto offset = depth_pixel_index * other_bpp;
        // 0x999999 で塗りつぶす
        std::memset(&p_video_frame[offset], 0x99, other_bpp);
        // 背景部分としてマーク
        m.at<uchar>(y, x) = 0;
      } else {
        // 前景部分としてマーク
        m.at<uchar>(y, x) = 255;
      }
    }
  }
  // 作成したマスクイメージを CV_8UC1 から CV_8UC3 に変換して返す
  cv::Mat mask;
  cv::cvtColor(m, mask, CV_GRAY2BGR);
  return mask;
}

// 所定の画像データをロード
cv::Mat loadImage(int n)
{
  char fn[16];
  sprintf(fn, "%02d.jpg", n);
  return cv::imread(fn);
}

int main(int argc, char * argv[]) try
{
  rs2::log_to_console(RS2_LOG_SEVERITY_ERROR);

  int photoNumber = 0;
  // ストリーミング用のパイプライン
  rs2::pipeline pipeline;
  rs2::config cfg;
  cfg.enable_stream(RS2_STREAM_COLOR); // RGB ストリーム
  cfg.enable_stream(RS2_STREAM_DEPTH); // 深度ストリーム
  rs2::pipeline_profile profile = pipeline.start(cfg);
  // このカメラの Depth スケール情報を取得
  // "Depth スケール * Depth フレーム内の各ピクセルの値" がセンサからのメートル距離
  float depth_scale = get_depth_scale(profile.get_device());

  // info
  rs2::device rs_dev = profile.get_device();
  std::cout << "Device Name" << ": " <<
rs_dev.get_info(RS2_CAMERA_INFO_NAME) << std::endl;
  std::cout << "Firmware Version" << ": " <<
rs_dev.get_info(RS2_CAMERA_INFO_FIRMWARE_VERSION) << std::endl;
  std::cout << "Recomended Firmware Version" << ": "
<< rs_dev.get_info(RS2_CAMERA_INFO_RECOMMENDED_FIRMWARE_VERSION)
<< std::endl;
  std::cout << "Serial Number" << ": " <<
rs_dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER) << std::endl;
  std::cout << "Product Id" << ": " <<
rs_dev.get_info(RS2_CAMERA_INFO_PRODUCT_ID) << std::endl;
  std::cout << "USB Type" << ": " <<
rs_dev.get_info(RS2_CAMERA_INFO_USB_TYPE_DESCRIPTOR) <<
std::endl;
  std::cout << "Depth Scale" << ": " << depth_scale
<< std::endl;

  // RGB ストリーム分の align オブジェクトを用意
  rs2::align align(RS2_STREAM_COLOR);

  // 最初の背景画像をロード
  cv::Mat photo = loadImage(photoNumber);

  while (1) {
    // カメラからのフレームセット受信を待つ
    rs2::frameset frameset = pipeline.wait_for_frames();
    // アライメントを RGB ストリーム分のビューポートに揃える
    frameset = align.process(frameset);
    // RGB フレームを取得 (video_frame クラスに注意)
    rs2::video_frame video_frame = frameset.get_color_frame();
    // Depth フレームを取得
    rs2::depth_frame depth_frame = frameset.get_depth_frame();

    // RGB フレーム中の Depth_Clipping_Distance 圏外を塗りつぶし
    // 圏内 = 255, 圏外 = 0 のマスクイメージを得る
    cv::Mat mask = remove_background(video_frame, depth_frame, depth_scale);

    cv::Mat rgbCvMatsrc, rgbCvMatDst;
    // RGB フレームからピクセルデータを取得し OpenCV のマトリックスに変換
    rgbCvMatsrc = cv::Mat(cv::Size(Width, Height), CV_8UC3,
(void*)video_frame.get_data(), cv::Mat::AUTO_STEP);
    // チャネル並びを RGB から BGR に
    cv::cvtColor(rgbCvMatsrc, rgbCvMatDst, cv::COLOR_RGB2BGR, 0);

    // 抽出した RGB 画像
    //cv::imshow("Src", rgbCvMatDst);

    // 背景画像
    //cv::imshow("pic", photo);

    // マスクと反転マスク
    cv::Mat mask_inv, fg, bg, mix;
    cv::bitwise_not(mask, mask_inv);
    //cv::imshow("mask", mask);
    //cv::imshow("mask_inv", mask_inv);

    // 前景となる抽出画像にマスクをかけて背景色を 0 に
    cv::bitwise_and(rgbCvMatDst, mask, fg);
    //cv::imshow("fg", fg);

    // 背景画像に反転マスクをかけて前景部分を 0 に
    cv::bitwise_and(photo, mask_inv, bg);
    //cv::imshow("bg", bg);

    // 加工した前景と背景をマージして表示
    //cv::bitwise_or(fg, bg, mix);
    cv::add(fg, bg, mix);
    cv::imshow("Enter", mix);

    // キー押下チェック
    int key = cv::waitKey(1);
    if (key == 32) { // SPACE
      // 背景画像を変更
      if (++photoNumber > NumBgImages) {
        photoNumber = 0;
      }
      photo = loadImage(photoNumber);
    } else if (key == 'q' || key == 27) { // 'q' or ESC
      cv::destroyAllWindows();
      break;
    }
  }
  return EXIT_SUCCESS;
}
catch (const rs2::error & e)
{
  std::cerr << "RealSense error calling " <<
e.get_failed_function() << "(" << e.get_failed_args()
<< "):\n  " << e.what() << std::endl;
  return EXIT_FAILURE;
}
catch (const std::exception& e)
{
  std::cerr << e.what() << std::endl;
  return EXIT_FAILURE;
}

メモ:マスク合成の手順

A: 圏内の RGB 画像
B: 0, 255 のマスク
C: 元画像 & マスク C: + c:
a: 背景画像
b: 255, 0 のマスク
c: 背景 & マスク

仮想 Web カメラと組み合わせてみる

上のプログラムはあくまでも習作ですが、ふと、以下のような仮想 Web カメラソフトウェアと組み合わせれば、たとえばテレワーク環境からのビデオミーティング参加時に背後のプライベート空間の露出を避ける目的などに利用できるのではないかと考えました。

ちなみに所属部署ではリモート会議に Google Hangouts Meet を利用しています。上のデモを部内で紹介した折に Zoom にはこれと同様のことをスマートに実現できる「バーチャル背景」機能が用意されていることを知り、この手の需要が少なくないことをあらためて実感しました。

上記の SplitCam を使えば PC 上の所定のウィンドウの任意の領域をローカルのカメラ映像として流すことができます。最新版では領域選択方法が不明でしたが、「SplitCam links to download OLD VERSIONS!」ページから「SPLITCAM 8.1.4.1」をダウンロードして試すと期待する結果が得られました。他のプログラムからは普通の Web カメラとして認識されるため汎用的に扱えそうです。

以下にざっくりと組み合わせの手順を示します。(クリックで大きく表示)

1. SplitCam を起動して領域を設定

2. ミーティング用クライアントからカメラとして SplitCam を選択

3. こんな感じで利用できる


(tanabe)
klab_gijutsu2 at 17:46
この記事のURLComments(0)その他 
2019年11月01日

ひもスイッチでスマート照明を操作する

はてなブックマークに登録

Philips Hue を代表格とするスマートバルブ(電球)は随所でその利便性がうたわれてきました。手元ではこれまでこの方面の製品には触れずにいましたが、先日 Meross 社の「MSL120」を買ってみました。常用している同社のスマートプラグ MSS110 と同一の公式アプリで利用できることも都合がよく楽しみながら使っています

社内でその話をしたところ冗談まじりに「照明つながりで現代のスマートバルブを昭和期のひも式のスイッチで操作してみたりとか」という話題があり実際に試してみました。せっかくなので "出落ちネタ" として動作の様子を紹介します。制御に ESP32 ボードを使い IFTTT アプレットを呼び出して点灯・消灯を行っています。

 
動画:1分2秒

延長用のひもを取りつけ体の位置にあわせて伸縮させたり、風を送って振り子の要領でひもを手元に引き寄せるなど、ひもスイッチは家庭生活に馴染みのよい人間的な柔軟性と便宜をそなえたユーザインターフェイスでした。 スイッチ自体を駆動させるための電力や配電を必要とせず素朴なしくみでありながらある程度の遠隔操作を実現できるメリットは大きく、今後もこの形式のスイッチが姿を消すことはないでしょう。誰にでも扱うことのできる間口の広さは家電製品のスマート化の進む今の時代にも有用かもしれません。

(使用した資材)


(tanabe)
klab_gijutsu2 at 18:13
この記事のURLComments(0)IoT 
2019年10月08日

電子工作での AC 給電制御と米国製「IoT Relay」のこと

はてなブックマークに登録

マイコンを使っていると AC 100V で稼働する機器への給電を制御したくなることがあります。このブログにも以前、秋月電子通商様によるソリッドステートリレー(SSR)キットを使ってそれを試みた内容を含む記事を掲載しました。

SSR にはわずかな電流で制御できる手軽さがあり、機械式のリレーには絶縁性等での優位性があります。また、単品の機械式リレーをマイコンで取り回す際の手かずは必要な要素がまとめて実装されたリレーモジュールを利用することで省略できます。 このあたりの話題はネットを検索すると様々な実例とともに紹介されています。ただ、私自身はこういったものを「自作」するたびに一抹の不安を感じていました。上の 4年前の記事には次のように書いています。

個人的にはこのように高電圧を扱うものは本当はあまり自作したくないです。その方面に素養がなくても部品を揃えれば作るのは簡単ですが、「手作りの楽しさ」などよりも安全性がもっとも重要ですから、手頃な価格で堅牢な完成品を入手できるならそれを使いたいというのが正直なところです。残念ながら今のところそういう商品は見当たらないようですが(需要はあると思うのですが・・)、 :

こういった事情により単純なトリガー信号を送るだけで所定の機器への給電を取り回してくれるブラックボックス的な完成品をひとつの理想形としてイメージしていたのですが、すでにそういう製品が登場していることを最近知りました。Kickstarter 発の「IoT Relay」という米国の製品です。これはまさに上記のような需要に対応する内容で構成された "リレーの堅牢なラッパー" です。現物の写真を以下に示します。

残念ながら今のところ IoT Relay は日本の PSE 認定を受けておらず国内では販売されていません。個人的な好奇心から先日この製品を調達し手元で試してみました。今回はそのレポートです。

ちなみに、所定の機器への給電にはこうしたローカルなリレー類を取り回すばかりではなく、近年手頃になったスマートプラグを利用する選択もあります。ただし、スマートプラグにはインターネット接続環境が必須であることに加え、目の前にある機器を扱う際にも「遠回り」の結果として生じる微妙な反応の遅れに違和感を覚えるケースもあります。一方、スマートプラグには遠隔地の機器への給電制御にも対応できる大きなメリットがあります。そのため、両者は適材適所で使い分けるべきでしょう。

IoT Relay について

1. 概要

Kickstarter 上のプロジェクトページ

SparkFun での販売ページ

  • IoT Power Relay - www.sparkfun.com US $26.95
    Do you want to control a standard wall outlet device with your microcontroller, but don’t want to mess with the high-voltage wiring? The IoT Power Relay is a controllable power relay equipped with four outputs that help you create an Internet of Things project with safe, reliable power control. With the IoT Power Relay you can easily control the power going to a device with an Arduino, Raspberry Pi or other single-board computer or microcontroller. It provides an alternative to the Power Switch Tail.

    The IoT Power Relay is designed to allow you to safely control an outlet device that operates at 3--48VDC or 12--120VAC. Each IoT Power Relay features a single input (from the included C13 power cable) to four outputs: one normally on, one always on, and two normally off.   :
    みらい翻訳 結果
    マイクロコントローラで標準的なコンセントデバイスを制御したいが、高電圧配線には手を出したくないと思うだろうか?IoT Power Relayは4つの出力を備えた制御可能なパワーリレーで、安全で信頼性の高い電源制御を備えたIoTプロジェクトの構築に役立ちます。IoT Power Relayを使えば、ArduinoやRaspberry Piなどのシングルボードコンピュータやマイクロコントローラを使って、デバイスへの電力供給を簡単に制御できる。電源スイッチテールの代わりに使用できます。

    IoT Power Relayは、DC3~48Vまたは12~120VACで動作するコンセントデバイスを安全に制御できるように設計されています。各IoT Power Relayには、単一の入力(付属のC13電源ケーブルから)から四つの出力があります。一つは通常オン、一つは常時オン、二つは通常オフです。  :

Amazon.com での販売ページ ("does not ship to Japan.")

2. 発注 〜 到着

手元で IoT Relay の存在を知った 2019年7月の時点では SparkFun をはじめ主だったディストリビュータのサイトにおいても Amazon.com においてもことごとく在庫切れの状態だった。製造終了の可能性も想像しつつ SparkFun へ入荷通知をリクエストしておいたところ 2019-09-05 に下のメールが届いた。
本体の販売価格は既知のとおり 米 $26.95 であり、とりあえず購入手続きを進めてみると日本への送料は最安の「Economy(1-4 Weeks)」で $12.02 とのこと。微妙に悩ましい金額だがしばらく考えた末に PayPal 経由で $38.97 を支払った。

2019-09-06 にニュージャージー州ニューアークから発送され 2019-09-19 に自宅の郵便受けに届いた。スペックは外箱に記載されており説明書の類は付属しない。現物を見れば使い方がわかるように工夫されている。

(クリックで可視大縦表示)

(本体に印字された謎マーク:後述)

3. 動作の様子

以下の動画には IoT Relay のよっつのプラグ差込口それぞれに LED ライトを装着し ESP32 ボードから High, Low のトリガー信号を送って反応をみた様子を収めている。

動画: 35秒

4. 試用を通じてのメモ

  • IoT Relay は自分が期待していたものにきわめて近い製品
  • よっつの差込口からの給電を個別のトリガーで制御可能とするオプションがあればより有用かも
  • 差込口がひとつだけのシンプルなバージョンも望まれる
  • 製品の安全性を示す指標としては本体にそれと思しき "SAFETY CPI TESTED", "computer performance inc QC pass" と書かれたマークがあるが、手元で情報を探した範囲では今のところこれらの実体は判然としない

    関連して米アマゾンにはこういう Q&A も。上の事情は米国人にとっても「?」な様子。
    • Amazon.com: Customer Questions & Answers
      Is this unit UL listed?

      Showing 1-2 of 2 answers

      • I can't say for sure, but there is no UL markings on the unit. There is 2 markings: "Safety CPI Tested" and "computer performance inc QC pass"
      • Not yet.
    同じくレビュー欄より。
    • Hackable hardware - www.amazon.com
      This is a well designed and constructed product, but be aware that it is not yet UL listed. My only suggestion for improvement would be to add holes or tabs for secure mounting.   :
    UL について
    • UL (安全機関) - Wikipedia
      UL LLC(英語: Underwriters Laboratories以下UL)は、アメリカ合衆国イリノイ州ノースブルックに本拠を構え、試験、検査および認証を行う企業。認証企業として、世界10位前後の規模を持つ。  :

5. 個人的な所感

今回ピックアップした IoT Relay は世界中の Maker の潜在的な需要のひとつを的確に反映した良い製品だと思います。ただし、利用者への安全性の提供を最大のアピールポイントとする一方で、現時点においてこの製品自体の米本国内での一般的な安全基準への適合状況が判然としない状況にはいささか自己矛盾の感もあり、その点がマイナスイメージにつながりかねない懸念もあります。今後その方面の整備が適切に行われればこの製品は IoT, スマートホームの波に乗りよりメジャーな存在になり得るかもしれません。特定の製品が目立つ形で成功を収めれば同等(あるいはそれ以上)の機能を持つ製品がより低価格で登場しジャンル化して普及の広がるケースは多々あり、そうした流れがいずれ PSE 認定製品の出現につながる可能性も想定されます。そういった期待を含め、この製品の今後の動向には随時目を向けていきたいと考えています。


(tanabe)
klab_gijutsu2 at 07:09
この記事のURLComments(0)IoT 
2019年09月03日

第一回 KLab Expert Camp「TCP/IPプロトコルスタック自作開発」を開催しました

はてなブックマークに登録

KLab Expert Camp というイベントを8/26〜29の4日間で開催しました。

KLab Expert Camp とは

KLab Expert Camp は、技術的に深いテーマに取り組んでいる学生の発掘・育成を目的とした KLab の新しい取り組みです。
記念すべき第1回のテーマは「TCP/IPプロトコルスタック自作開発」で、以下のような触れ込みで開催しました。

「OSを作ってみたい」「コンパイラを作ってみたい」と思う人が多いように「プロトコルスタックを作ってみたい」と思う人も多いのではないでしょうか。今回の KLab Expert Camp のテーマは、そんな皆さんにピッタリの「TCP/IPプロトコルスタック自作開発」です。担当講師が開発している教育用のプロトコルスタック(https://github.com/pandax381/microps)を教材に、Ethernetフレームを組み立てて送受信するところから ARP、IP、ICMP、UDP、TCP などのプロトコルを全て自分の手で実装することで、これまでブラックボックスだったプロトコルスタックの処理を紐解いていきます。

参加にあたっては、開発環境やプログラミング言語に関する初歩的な知識は身につけていることを期待していますが、期間中は KLab の講師陣が手厚くサポートしますのでご安心下さい。また、意欲的な参加者においては、期間中に追加機能の実装や組み込み機器への移植などへのチャレンジングな取り組みも支援します。

また、参加者には各自の取り組みたい内容に応じて2つのコースの何れかを選択してもらう形をとりました。

【基本コース】 教材となるリファレンス実装の解説を通じてプロトコルスタックそのものへの理解を深める

【応用コース】 リファレンス実装の別言語への移植や機能追加など各自がテーマを決めて取り組む

開催の経緯

定期的にこんなツイートをしながら、年に1〜2人のペースで前途有望な学生を低レイヤ沼に引きずり込んでるんですけど、たまたま5人くらいの学生が同じタイミングでリアクションしてくれまして。それだけの反響があるのは嬉しいものの、個別に対応してあげるのはおじさんの体力的にちょっと厳しそうだなという感じもあって「もういっそのこと全員集めて一緒にやればいいのでは???」という考えに至ったわけです。

そして、インターン関連をとりまとめている人事の担当者に「こんな感じで何人か集めてやりたいんだけど」と雑に話をしたところ「イイっすね!なんならもう少し集めてイベントにしちゃいます???」みたいな凄いノリの良い返事をもらって、1週間くらい経ったら「例のイベントの企画、Go出ました!」と、あっさり開催が決まってしまった感じです。

取り急ぎ告知に向けてイベントの名称を決めようということになったのですが、これが結構難しくて... パッと思いつくものはだいたい既に他で使われてるんですよね。Expert Camp は僕の出した案なんですけど「Expert...ちょっとイキりすぎでは...?」みたいな意見もあったりして。最終的には「カッコ良さそうだからいいじゃん」「僕(自称)エキスパートだから無問題」という感じで落ち着きました。

そんなこんなで漕ぎ着けたイベント告知が以下のツイートです。

万人に向けたイベントでもないし、SNSを通じてニッチな層に届けばいいやくらいの軽い気持ちだったので、イベントの告知は僕の個人アカウントによる上記のツイートのみです。 この時点ではマジでどのくらい集まるのか未知数で「追加で数名集まったらわいわい楽しくできそうだけど、果たして応募してくれる人はいるのだろうか...」と不安な気持ちでいっぱいでした。

そんな不安をよそに、つよつよの学生達や「拙者、この分野は素人でござるが」みたいなこと言い出しそうな強い大人の方々にもRTされ、それなりに拡散されることになりました。面白そう!と反応してくれたみなさん、本当にありがとうございました。

そして、いざ蓋を開けてみると総勢20名以上の学生がエントリーしてくれ、最終的に13名の参加者が決まりました。 募集しておいてなんですが「世にはプロトコルスタックを自作したい学生がそんなにいるのか...」と正直びっくりしましたね。

(多数エントリーいただいて嬉しかった一方で、運営のリソース的に希望者全員を受け入れることができず、参加者を絞らなければならなかった点はすごく心残りです。残念ながら今回は参加できなかった方も次回の開催につなげることができたらその際はまたエントリーして欲しいなと思います)

開催までの準備

告知から開催まではちょうど4ヶ月くらいあったので、そのあいだに人事の担当者と細かなことを決めつつ準備を進めていきました。といっても、僕が雑に「あれやりたい」「これやりたい」と言ったものを、イベント慣れしている人事の担当者がよしなにアレンジして的確に落とし込んでくれるという連携プレーで進めていきました。だいぶ無茶振りもしたと思うのに、いい感じでまとめてくれて本当にありがとうございました。

以下は個人的に気に入っていて満足しているポイントです。

  • 参加者と運営メンバー全員分のIDカード(プラスチック製)
  • お昼のお弁当
  • 最終日に授与した修了証
IMG_1449

どれも過去に自分が参加したり運営に携わったイベントで「これ良かったなぁ」「嬉しかったなぁ」と印象に残っていたものを盛り込んでもらいました。 参加者からのアンケートでも「お弁当おいしかった!」とか「記念に残るものがもらえて嬉しかった!」とか、それなりに喜んでもらえていたようで、すごく嬉しいです。

あとは、教材として使う予定の「リファレンス実装」をもう少し理解しやすい作りに改良したり、解説用の資料を作ったりと、なかなか忙しくギリギリまで準備を進めていました。

イベント期間中の様子

運営スタッフや参加者が以下のハッシュタグを付けてツイートしてくれているので、これらのタイムラインを追ってもらうと、どんな雰囲気で開催されていたのか分かる思います。(ごはんのツイートが多いので飯テロ注意)

#KLabExpertCamp

#プロトコルスタック自作

開催してみて

まず、これだけの人数の参加者に集まってもらえたことが驚きと共に一番うれしかったです。そして、皆さん起床ミッションに失敗することもなく初日から最終日まで黙々と開発に取り組んでくれていたのが本当に凄いと思いました。

あと、このイベントを開催した目的のうちの1つでもあるんですけど、参加者同士の交流が活発に行われていたようで良かったです!尊い。いや、これまで個別にインターン生として受け入れていた学生達とのやり取りの中で「ニッチな分野になるとなかなか周りで同じレベル感で話せる友達や仲間がいなくて寂しい(とくに地方の場合)」という話が上がることが多かったんですよ。なので、今回のイベントでは全国各地から同じニッチな分野に興味を持っている学生が集まる絶好の機会だし、そういった交流の場としても機能して欲しいなと思っていたのでした。

このイベントに参加して「TCP/IPを完全に理解した」となった方、その先に進んで「TCP/IPまるでわからん」となった方「TCP/IPチョットデキル」の域に達した方、得られたものはそれぞれ異なると思いますが、参加者全員に「楽しかった」と思ってもらえていたら開催した甲斐があったかなと思うし、第2回はもっとクオリティ高くヤルぞ!という気持ちになります。

謝辞

もともとこのイベントは僕が一人で講師を務めるつもりで企画がスタートしたのですが、予想を上回る参加人数となったことから、社内のエンジニアに協力を仰いで講師やサポート役を引き受けてもらいました。

また、社内からだけではなく外部の方にも講師役を引き受けて頂きました。

ねりさんはプロトコルスタック自作クラスタとして以前から認識していて、#tcfm のミートアップでのLTやサイボウズ・ラボユースでの活躍を見て「ヤバイ人がいる」とウォッチしていたのですが、こんな雑な絡み(なんとこれが初コンタクトなんですよ)にも関わらず、快く引き受けていただき本当にありがとうございました。

僕はR&D部門に所属しているものの、学問として研究をしたことがないため、研究者の立場として参加者に対してアドバイスや相談に対応してくれていたのはものすごく心強かったし、めちゃくちゃカッコいいなと感じました。

他にも、めんどくさい細々としたタスクを含めキッチリこなしてくれた人事の担当者や、仕事の合間にIDカードのデザインを引き受けてくれたデザイナーさんなど運営に関わってくれたみなさん、ほんとうにありがとうございました!

参加者のブログ記事

最後に、観測できている範囲で参加してくれたみなさんが書いてくれたブログ記事へのリンクを貼っておきます。

TCP/IP プロトコルスタックを自作した - kawasin73のブログ

KLab Expert Campに参加したよ。 - よくきたわね、いらっしゃい

KLab Expert Camp に参加しました - veqcc’s diary

KLab Expert Camp にチューターとして参加した - Around The Computer

KLab Expert Campに参加してきました - teru_0x01.log

KLab Expert Campに参加した - 抹茶うまい

第1回 KLab Expert Campに行ってきました(TCP/IPプロトコルスタック自作インターン) - 迫真の氷結晶

pandax381 at 18:23
この記事のURLComments(0)network | 開発
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント
「最新トラックバック」は提供を終了しました。
Syndicate this site