chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

Raspberry Pi Picoを買った

諸般の事情で、RaspberryPi Picoを買った。ざっと見た限り、MicroPythonがサクッと動かせることが分かった。WiFiとか、BLEはないけど、まぁ安いし消費電力低いしこれはこれでありかと。

Programmable IOが8本という記載があって、サンプルソースもあるのだけど、なんでわざわざアセンブラでIO操作をするのか目的が分からなかった。

Programmable I/O with Raspberry Pi Pico - Latest open tech from seeed studio

そんな中で、上記の記事の解説によると、Programmable IOは専用ハードによるステートマシンとして動作して、CPUリソースを食わないんだと。しかも、、CPU使わないから高速で、めちゃくちゃタイミングが正確だろう多分。これは素晴らしい。Picoに搭載されたRP2040というマイコンを味わい尽くすには、ステートマシンで動くPIOを使い倒すのがポイントでは!?と思えるのでした。

ボード上のLEDのLチカ (あれだけ言ったのに、ステートマシン使ってませんが。。。)

from machine import Pin
import utime
pin = Pin(25,Pin.OUT)
pin.low()
while True:
   pin.toggle()
   utime.sleep(1)

補足:
MicroPythonにはNeoPixelのドライバがあるのだけど、多分タイミングの問題と思うけど、結構誤動作をしてしまう問題があった(あるいは、、NeoPixelの制御信号を3.3V駆動したから??)。能力は低いけど、余計な割り込みの入らない、ArduinoでNeoPixelを動かすと、全く問題なく動作した(5Vで駆動したから??)。

■ご参考URL
A closer look at Raspberry Pi RP2040 Programmable IOs (PIO)
https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf
3.3. PIO Assembler (pioasm) (P333)
Hands-On with the RP2040 and Pico, the First In-House Silicon and Microcontroller From Raspberry Pi - Hackster.io

YoutubeでPIOの解説動画(英語だ。。)
https://youtu.be/LXAwW2IYT7o?t=3238
アセンブラでステートマシンのコードを書く・・・変態の血が騒ぐ・・


MicroPythonとCIrcuitPythonの違い
CircuitPython — Adafruit CircuitPython 6.2.0-beta.1 documentation

メモ: url parse error を回避するためurllib3をupgrade

urlがパーズエラーと怒られるので回避

requests.exceptions.InvalidURL: Failed to parse: https://192.168.10.216/api/v1/controllers/aiseg
>>>
$ sudo python -m pip install --upgrade urllib3
Collecting urllib3
  Downloading urllib3-1.26.3-py2.py3-none-any.whl (137 kB)
     |????????????????????????????????| 137 kB 7.0 MB/s
Installing collected packages: urllib3
  Attempting uninstall: urllib3
    Found existing installation: urllib3 1.25.8
    Not uninstalling urllib3 at /usr/lib/python3/dist-packages, outside environment /usr
    Can't uninstall 'urllib3'. No files were found to uninstall.
Successfully installed urllib3-1.26.3
uburp4:~/lang/py/webapi/crt$

次にバージョン不整合と怒られる

/usr/lib/python3/dist-packages/requests/__init__.py:89: RequestsDependencyWarning: urllib3 (1.26.3) or chardet (3.0.4) doesn't match a supported version!
  warnings.warn("urllib3 ({}) or chardet ({}) doesn't match a supported "

これは、requestを更新する

sudo python3 -m pip install --upgrade requests

uninstallするファイルがないというメッセージが気になるけど。。

$ sudo python3 -m pip install --upgrade requests
Collecting requests
  Downloading requests-2.25.1-py2.py3-none-any.whl (61 kB)
     |????????????????????????????????| 61 kB 1.9 MB/s
Requirement already satisfied, skipping upgrade: certifi>=2017.4.17 in /usr/lib/python3/dist-packages (from requests) (2019.11.28)
Requirement already satisfied, skipping upgrade: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.8/dist-packages (from requests) (1.26.3)
Requirement already satisfied, skipping upgrade: chardet<5,>=3.0.2 in /usr/lib/python3/dist-packages (from requests) (3.0.4)
Requirement already satisfied, skipping upgrade: idna<3,>=2.5 in /usr/lib/python3/dist-packages (from requests) (2.8)
Installing collected packages: requests
  Attempting uninstall: requests
    Found existing installation: requests 2.22.0
    Not uninstalling requests at /usr/lib/python3/dist-packages, outside environment /usr
    Can't uninstall 'requests'. No files were found to uninstall.
Successfully installed requests-2.25.1

SBCLをUbuntuに入れてみる

高速実行がウリらしいSteel Bank Common Lisp(SBCL)をWindows上で動作するUbuntu(Windows Subsystem for Linux (WSL) )に入れてみる

$ uname -a
Linux DESKTOP-TRNV8F8 4.19.104-microsoft-standard #1 SMP Wed Feb 19 06:37:35 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

$ sudo apt-get install sbcl
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  gyp javascript-common libc-ares2 libfile-basedir-perl libfile-desktopentry-perl libfile-mimeinfo-perl
  libio-stringy-perl libipc-system-simple-perl libjs-inherits libjs-is-typedarray libjs-psl libjs-typedarray-to-buffer
  libnet-dbus-perl libnode-dev libnode64 libtie-ixhash-perl libuv1-dev libx11-protocol-perl libxml-twig-perl
  libxml-xpathengine-perl nodejs-doc python-pkg-resources xdg-utils
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
  binfmt-support
Suggested packages:
  sbcl-doc sbcl-source slime
The following NEW packages will be installed:
  binfmt-support sbcl
0 upgraded, 2 newly installed, 0 to remove and 4 not upgraded.
Need to get 8497 kB of archives.
After this operation, 43.5 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://archive.ubuntu.com/ubuntu focal/universe amd64 binfmt-support amd64 2.2.0-2 [58.2 kB]
Get:2 http://archive.ubuntu.com/ubuntu focal/universe amd64 sbcl amd64 2:2.0.1-3 [8439 kB]
Fetched 8497 kB in 6s (1331 kB/s)
Selecting previously unselected package binfmt-support.
(Reading database ... 76341 files and directories currently installed.)
Preparing to unpack .../binfmt-support_2.2.0-2_amd64.deb ...
Unpacking binfmt-support (2.2.0-2) ...
Selecting previously unselected package sbcl.
Preparing to unpack .../sbcl_2%3a2.0.1-3_amd64.deb ...
Unpacking sbcl (2:2.0.1-3) ...
Setting up binfmt-support (2.2.0-2) ...
Created symlink /etc/systemd/system/multi-user.target.wants/binfmt-support.service → /lib/systemd/system/binfmt-support.service.
invoke-rc.d: could not determine current runlevel
Setting up sbcl (2:2.0.1-3) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for systemd (245.4-4ubuntu3.2) ...

SBCLを起動してみる

$ sbcl
This is SBCL 2.0.1.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* (print "hello")

"hello"
"hello"
* (exit)

LISPソースを書いて実行してみる

作成したLISPソース

$ cat hello.lisp
(defun test()
  (print "hello world"))

SBCLを起動してプログラムをロード、関数を実行

$ sbcl
This is SBCL 2.0.1.debian, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* (load "hello.lisp")
T
* (test)

"hello world"
"hello world"
* (exit)

スクリプトオプションで自動実行してみる
フォーマット関数による整形版ソース

$ cat hello.lisp
(defun test()
  (format t "~A~%" "hello world"))

(test)

scriptオプションを付けて上記ソースを実行。文字列最後に改行が付いた。

$ sbcl --script hello.lisp
hello world

LISPを学ぶ・・

今でもLISP(Scheme)は適材適所で活用されていると思うけど、学習コストが高いのが課題(手頃な解説本があまりない)。
これからLISPを勉強しようと思う人が非常に少ないから、本を出してもうまみがないんだろう。

そんな状況だけど、、ネットで探すと解説記事がいくつかある。
丁寧に説明された入門編記事(Common Lisp)
Lisp/始めの一歩/経験者用チュートリアル - Wikibooks

上記は翻訳版で、オリジナルは英語版↓
Common Lisp/First steps/Beginner tutorial - Wikibooks, open books for an open world

ネット上には、かつての書籍が無料公開されていてDL可能になっている。ありがたい。
On Lisp DLサイト
http://www.paulgraham.com/onlisptext.html
和訳サイト (野田氏による翻訳サイト)
http://www.asahi-net.or.jp/~kc7k-nd/onlispjhtml/


「Paradigms of Artificial Intelligence Programming」
DLサイト
Release Original sources · norvig/paip-lisp · GitHub
1章「Introduction to Lisp」は簡潔にまとまっていてわかりやすいと思う。


現在のところ、購入できる書籍としては、Practical Common Lispが一般的なのだろうか。。

CommonLispの書籍 Practical Common Lisp(洋書)
Practical Common Lisp

Practical Common Lisp

Practical Common Lisp

  • 作者:Seibel, Peter
  • 発売日: 2014/12/02
  • メディア: ペーパーバック


電子出版されているのは以下(PayPalで支払ってダウンロードする)
Common Lisp人工知能プログラミング」
本のまえがき | 本の購入サイト
熱い著者によるCommon Lisp本と思われます。言語編、AIプログラム編(だったか)と、上下に分かれている。かなり深い所まで解説されているようです。特に再帰プログラミングの解説にも注力とあります。かなり詳しく書かれていると推測されますが、内容がほとんど分からない。見本もあるにはあるけど。

CommonLispが業界標準だろうけど、手軽に動かすのにEmacsLispを使う手もある(と自分は思うのだが)。。
以下はEmacsLispのマニュアルと手引き(Emacsの上で動く)

Programming in Emacs Lisp: Programming in Emacs Lisp
GNU Emacs Lispリファレンスマニュアル: Top

学生のころは、培風館LISP(ウインストン著)で勉強したけど、、今は絶版だ(中古では買えるようだが)。MACLISPだし。今はCommon Lispに改定されているようだ。

Lisp

Lisp

楽しく学べそうなLand of Lisp。高いけど、本屋では売られているようで、内容を確認できる。

Land of Lisp

Land of Lisp



最も基本となるcar/cdr/consとリスト内部構造の解説記事
http://www.math.s.chiba-u.ac.jp/~matsu/lisp/emacs-lisp-intro-jp_8.html
http://www.math.s.chiba-u.ac.jp/~matsu/lisp/emacs-lisp-intro-jp_10.html#SEC124


A Road to Common Lisp 翻訳 · GitHub




LISP学習パス(案)

[LISP概要を理解](LISPという言語を俯瞰、LISPってどうよ)
   LISPとはどんな言語なのか?なぜLISP?何がいの?その特徴は?

[LISP実行環境を整える](手を動かさないと染み込まない)
   Windows/Linux/macOS上にLISPインタプリタを導入

[LISPの初歩の初歩を打ち込んでみる](最初はシンボルとリスト操作に専念)
     基本5関数(atom,eq,car,cdr,cons)  (Pure Lisp)
     http://www.wbricken.com/pdfs/02teach/05proglang/14-purelisp.pdf

 シンボル、 t/nil
    car cdr  cons
    bind
    cond  prog let atom boundp
    atom
  リスト操作してみる

[ループ、条件式、defunを使ってみる](アルゴリズムが表現できるようになる)
  (順次、選択、反復をLISPでどう表現するのかが分かればLISPでアルゴリズムが表現できるはず。。きっと)
[LISPのデータ構造(リスト内部表現)を学ぶ](LISPを深く理解)

[LISPが扱えるデータ型は?](いろんなデータが扱えてまともな言語にする)

[再帰してみる](LISPの本領発揮というか)

[ややこしい概念に取り組んでみる]
 ・マクロ式
 ・動的スコープ、静的スコープ
 ・etc

[次の課題を探してみる]
 ・データ型
 ・オブジェクト指向
 ・Lambda式
 ・mapcar等
 ・パッケージ
 ・ライブラリ
 ・デバッグ

■追記
あらためて、LISP解説記事を読み直していると、多くのサンプルはCommonLispベースで紹介されており、自分が知らない概念や表記がいろいろある。自分のLISPスキル習得はMACLISPで始まりCommonLispを少しかじって、EmacsLispで凍結している。入社から5年ぐらいまではEmacsLispで自分のEmacs環境をせっせとカスタマイズしていた。それ以降は仕事で使う主力言語もC、PerlJavaC++,Pythonと時代とともに移り変わってきた。(Cは組み込み関連でずっと使い続けてきたが、、最近は組み込みもMicroPythonに頼っている・・・エディタはEmacsからMeadowになってEmacsLispはちょっとした計算で使ってるけど、EmacsLispでプログラムすることはない)
そう思うと、主力言語は時代とともに移り変わり、一つの言語を長い時間かけてとことん学びつくすというのは難しいと思えるのだ(自分が飽き性なせいか??言語だって年が経つと仕様が変わるし。。)

■参考URL
Emacs-Lisp入門 2021

OpenECHOを使って照明用ECHONET Liteコントローラを試作

OpenECHOのサンプルを探していたが、大抵がProcessingを含む形式だった。一番シンプルな、Processingを含まないサンプルがarmadillo社様サイトで公開されていたので、それをベースに少し修正した。(オリジナルは2つのjavaだったが、それをうまくビルドする方法が分からず。antとか使うのだろうか)
実行環境は、Ubuntuです。

file: Ellight.java ( EL Light)

sumi@uburp4:~/lang/java$ cat  Ellight.java
import com.sonycsl.echo.Echo;
import com.sonycsl.echo.eoj.device.DeviceObject;
import com.sonycsl.echo.processing.defaults.DefaultNodeProfile;
import com.sonycsl.echo.eoj.device.housingfacilities.GeneralLighting;

import java.io.IOException;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;

class MyLighting extends GeneralLighting {
    byte[] mStatus = {0x31};
    byte[] mMode = {0x41};
    byte[] mLocation = {0x39};
    byte[] mVersion = {0x01, 0x01, 0x61, 0x00};
    byte[] mFaultStatus = {0x42};
    byte[] mManufacturerCode = {0x12, 0x34, 0xFE};

    @Override
    protected boolean setOperationStatus(byte[] edt) {
        Runtime rt = Runtime.getRuntime();
        Process pr = null;
        try {
            /* LEDをON/OFF */
            if (edt[0] == 0x30)
                pr = rt.exec(new String[]
                             { "/bin/sh", "-c",
                               "echo 255 > /sys/class/leds/green/brightness" });
            else if (edt[0] == 0x31)
                pr = rt.exec(new String[]
                             { "/bin/sh", "-c",
                               "echo 0 > /sys/class/leds/green/brightness" });
            if (pr != null)
                pr.waitFor();
            inform().reqInformOperationStatus().send();
        } catch (InterruptedException | IOException ex) {
            Logger.getLogger(MyLighting.class.getName()).log(Level.SEVERE,null, ex);
        }
        return true;
    }
    @Override
    protected byte[] getOperationStatus() {
        try {
            Runtime rt = Runtime.getRuntime();
            Process pr = rt.exec("cat /sys/class/leds/green/brightness");
            InputStream is = pr.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String line = br.readLine();
            if (line != null && line.equals("0"))
                mStatus[0] = 0x31;
            else
                mStatus[0] = 0x30;
        } catch (IOException ex) {
            Logger.getLogger(MyLighting.class.getName()).log(Level.SEVERE, null, ex);
        }
        return mStatus;
    }
    @Override
    protected boolean setLightingModeSetting(byte[] edt) {
        mMode[0] = edt[0];
        try {
            inform().reqInformLightingModeSetting().send();
        } catch (IOException ex) {
            Logger.getLogger(MyLighting.class.getName()).log(Level.SEVERE,
                                                             null, ex);
        }
        return true;
    }
    @Override
    protected byte[] getLightingModeSetting() {
        return mMode;
    }
    @Override
    protected boolean setInstallationLocation(byte[] edt) {
        mLocation[0] = edt[0];
        try {
            inform().reqInformInstallationLocation().send();
        } catch (IOException ex) {
            Logger.getLogger(MyLighting.class.getName()).log(Level.SEVERE,
                                                             null, ex);
        }
        return true;
    }
    @Override
    protected byte[] getInstallationLocation() {
        return mLocation;
    }
    @Override
    protected byte[] getStandardVersionInformation() {
        return mVersion;
    }
    @Override
    protected byte[] getFaultStatus() {
        return mFaultStatus;
    }
    @Override
    protected byte[] getManufacturerCode() {
        return mManufacturerCode;
    }
}

public class Ellight {
    public static void main(String[] args)
            throws InterruptedException, IOException {
        Echo.start(new DefaultNodeProfile(),
                   new DeviceObject[]{new MyLighting()});
    }
}

同一ディレクトリに、OpenECHO.jarがあるとして、以下でビルドと実行

$  javac -cp OpenECHO.jar Ellight.java
$  java -cp /<path>/OpenECHO.jar:  Ellight

上記で照明デバイスとしてECHONET Liteプロトコルで待ち受けてくれる
別のPCから動作テストを実施. ECHONET Liteプロトコルで、上記デバイス
Location(機器設置場所)を問い合わせ(自作のEL通信ツール)
Javaソースで設定したLocation: 0x39が返却されるのを確認

$ ./elp.py get 029001@192.168.10.120  Location
GET(0x62) EPC... Location
108100010EF00102900162018100
|10|81|0001|0EF001|029001|62(Get)|01|81(Location)_00_|
waiting...
----------------------------------------
------------------------------
from ('192.168.10.120', 3610)
------------------------------
msg:
108100010290010ef0017201810139
|10|81|0001|029001|0ef001|72(Get_Res)|01|81(Location)_01_39|

■ソースを参照したサイト
https://armadillo.atmark-techno.com/howto/a400-openecho-howto
「Armadillo-400シリーズでECHONET Lite対応一般照明機器」
上記Javaソースは、上記armadillo社様サイトより引用、一部改修してテストしました。
シンプルなサンプルソースなので分かりやすい!!]


■メモ
kaden emulatorをソースから動かす方法

(1)Processingを落とす
(2)echonetフォルダ配下にechonet.pdeを置く
(3)走らせると、ライブラリがないと怒られる
   3-1controlP5がない・・・ライブラリのマネージ機能でContorlP5を入れる
          documents\Processing\libraries\contorolP5\library\controlP5.jar が入る
   3-2sonyのライブラリ(OpenECHO)はGitから落とす。
          Githubの  SonyCSL/OpenECHOの processing/libraryにOpenECHOのjarがあるのでそれを落として入れる
    documents\Processing\libraries\OpenECHOlibrary\OpenECHO.jar 

メモ:EchonetLiteのINF_REQ(0x63)をUDP Multicast で送信して受信するPythonサンプル

マイナーすぎるのでほぼ自分用忘備録
仕様:EchonetLiteのINF_REQ(0x63)をUDP Multicastで送信して、同一プログラム内で受信もするPythonサンプルコード(受信側はUDP Unicast/Multicastの両方のパケットが受信可能)
お礼とお詫び:いろんな人のソースを参考に作ったので引用元がもうわからず。サンプルソースを公開してくださった皆様ありがとうございます。ミキシングされてしまい、どれをどう引用したか、もうわかりません。すみません
課題:送信用、受信用2つのUDPソケットを開いていますが、このような実装が正しいのかどうか自信ありません。エラーにはならずに動いてはいる。何をもって正しい実装と言えるのか、判断の根拠が分からない。2つのソケットで同じローカルIPをbindしているけど、、ポートが違うからそれは問題ないのだろうと推測。ポートを共用してはいないので許されるのだろう。。多分。。はぁ。。自信ない。。

#!/usr/bin/python3

import time
import threading
import socket
import config
import parse

def receive_packet(dummy):
    print("------starting receive thread-----")
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind((config.LOCAL_ADDR, config.ECHONET_PORT))
    sock.setsockopt(socket.IPPROTO_IP,
                socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(config.MULTICAST_GROUP) + socket.inet_aton(config.LOCAL_ADDR))

    msg = None
    msg, address = sock.recvfrom(128)
    print(f"from: {address}")
    parse.dump(msg.hex())
    sock.close()  
    print("------end of receive thread-----")


def ELP_infreq():

    # create socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # bind
    print(f"listen at:{config.LOCAL_ADDR} port:{config.ECHONET_PORT}")
    sock.setsockopt(socket.IPPROTO_IP, 
    		socket.IP_MULTICAST_IF, 
    		socket.inet_aton(config.LOCAL_ADDR))
    p = threading.Thread(target = receive_packet, args = (None, ))
    print(f"multicast:{config.MULTICAST_GROUP} port:{config.ECHONET_PORT}")
    msg = "1081000205FF010EF0016301D600"   # infreq D6(ins list) ask to node prop
    parse.dump(msg)
    msg = bytes.fromhex(msg)
    p.start()
    sock.sendto(msg, (config.MULTICAST_GROUP, config.ECHONET_PORT))
    sock.close()
    
    #receive(sock)
    p.join()

    print("bye")
    

ELP_infreq()

以下実行例

$ ./sendrec_infreq_mul.py
listen at:192.168.10.152 port:3610
multicast:224.0.23.0 port:3610
|10|81|0002|05FF01|0EF001|63(INF_REQ)|01|D6()_00_|
------starting receive thread-----
from: ('192.168.10.150', 49154)
|10|81|0002|0ef001|05ff01|73(INF)|01|d6()_07_0205ff01013009|
------end of receive thread-----
bye

メディアコンバータ*1からINF(0x73)+自ノードインスタンスリスト(0xd6)の返信が得られています。メディアコンバータには、0x5ff01(コントローラ)と、0x013009(エアコン)がぶら下がっている。。と。。

*1:商品名は、Panasonic製の無線ゲートウェイ(メディアコンバータ)型番:CF-TC7B

iOS版 ECHONET Lite機器走査アプリがボチボチ動く

UDP Multicastの申請が許可されて、iOSUDP Multicastを投げてECHONET Lite機器を探し出すアプリを開発中。
なんとかiOS側から INF_REQ(0x63)をMulticastして、ECHONET Lite機器(メディアコンバータ)からINF(0x73)が返却されるところまではできた。
以下はWindowsで動かしているUDP受信プログラム

iOSからのINF_REQ (UDP Multicast)
---------------
[10][81][00][00][05][FF][01][0E][F0][01][63][01][D5][00]
---------------
メディアコンバータからの応答(UDP Unicast)
---------------
[10][81][00][00][0E][F0][01][05][FF][01][73][01][D5][07][02][05][FF][01][01][30][09]
---------------

以下はXCodeのコンソール表示

2020-12-19 23:23:08.171906+0900 ELDeviceFinder[393:13442] [] nw_listener_socket_inbox_create_socket IP_DROP_MEMBERSHIP 224.0.23.0:3610 failed [49: Can't assign requested address]
Group entered state waiting(POSIXErrorCode: Network is down)
waiting
POSIXErrorCode: Network is down
Group entered state ready
ready
send...UDP
Send complete with error nil
Received message from Optional(192.168.10.156:58850)        <<  iOSからのMulticastを自分自身で受信
Optional(14 bytes)
Received message from Optional(192.168.10.164:49154)        <<  EL機器(メディアコンバータ)からの返信
Optional(21 bytes)
bye
Group entered state cancelled
cancelled

今後の実装は、、
複数の機器からポツポツとUDPで返ってくるのを非同期で受信して、GUI上でリスト形式で機器一覧を表示させる。
補足:
INF_REQをMulticastするのはVer1.0系の実装とのことで、Ver1.1系ではMulticastでGET 投げるらしい。が、、機器との正しい通信プロトコルを守らないと、GETでMulticastで投げても機器からは応答がない。