CNX SoftwareによるGL-S200 Thread Border Routerキットの使い始めレビュー
先週は3枚のnRF52840 Thread Dev Boardが付属するGL.iNet GL-S200 Thread Border Routerキットのハードウェアを確認しました。今回はキットを実際に使う時間が取れたため、レビュー後編として使い始めたときの体験を紹介します。
GL-S200の初期セットアップ
WANポートをモデムルーターに接続したEthernet Switchに、LANポートをノートパソコンに接続し、デフォルトIPアドレス(192.168.8.1)でWebインターフェースにアクセスできるようにしました。GL-S200は、年初にレビューしたBeryl AXルーターなど、他のGL.iNetルーターと同じAdmin Panelを使用します。
ウィザードが表示され、言語の選択とAdmin Panelの新しいパスワード設定を行います。完了すると、おなじみのGL.iNet Admin Panel 4.x.xにアクセスできます。
ウィザードを完了するとシステムは自動的にインターネットへ接続し、本体中央のRGB LEDが緑色に変わります。
Threadネットワーク
まず左側のパネルでTHREAD MESHを開き、Threadネットワークを有効にします。
Enableをクリックすると、3つのIPv6アドレスが表示されます。
-
リンクローカルアドレス - 1回の無線通信で到達できるすべてのインターフェース
-
Local Address –
Threadネットワーク外から到達できるすべてのインターフェース(OpenThreadのドキュメントではGlobal Addressと呼ばれているようです)GL-iNetからの更新:グローバルIPv6インターネットへのアクセスは、現在OT/OTBRではまだサポートされていません。
OpenThreadの公式回答も確認できます: https://github.com/openthread/openthread/discussions/8794
ローカルアドレスはOMR(off-mesh-routable)アドレスで、Wi-Fi/Ethernet側からThreadデバイスにアクセスするために使用できます。
-
Mesh-Local EID - 同じThreadネットワーク内で到達できるすべてのインターフェース
独自のThreadデバイスをお持ちの場合は、Thread CommissioningメニューでデバイスのEUI64と事前共有キーを入力できます。GL.iNetは、Thread Dev Board(以下TBD)を扱いやすくするため、Webインターフェースによりシンプルな専用セクションを用意しています。
GL DEV BOARD->Devicesセクションで Add Devicesボタンをクリックし、Applyを選択します。いずれかのボードのSW2スイッチを押すと、そのボードがGL-S200 Thread Border Routerに自動的に追加されます。
A0+A1タイプを選択してボードに名前を付けます。ほかの2枚のボードでも同じ操作を行うと、3枚すべての温度、気圧、湿度、照度の値がAdmin Panelに表示されます。
GL-S200のファームウェア更新
ここで、忘れていたことを思い出しました。GL.iNetから、GL-S200向けの新しいファームウェアを手動で適用するよう案内を受けていました。そこで「SYSTEM->Upgrade」に移動して更新します。
新しいファームウェアはOTAサーバーに配信されていないため、Admin PanelではFirmware 4.1.3 Release 5が最新と表示されます。しかし、2023年3月6日にビルドされた新しいファームウェアのダウンロードリンクが提供されました。
ファイルをダウンロードし、Local UpgradeセクションでGL-S200 Thread Border Routerにアップロードしました。
検証に成功したため、Installをクリックしました。これでデバイスにFirmware 4.1.3 Release7がインストールされました。
Thread Dev Boardのファームウェア更新
このファームウェア更新を進める中で、Thread Dev Boardもバージョン1.1.5から1.1.9へ更新できることに気付きました。実際に更新したところ、問題なく完了しました。
Threadトポロジー
Threadはメッシュネットワーキングを利用します。ノードがルーターの近くにある場合はルーターへ直接接続し、通信範囲外または信号が弱い場合は、ノード自体がルーターとして動作できます。すべてのデバイスを机の上に置いた状態では、
予想どおり、ネットワークはスター型トポロジーになりました。
そこでボード同士を離し、1枚は屋外に置いて、一直線に配置してみました。
温度と照度がより高い、屋外に配置したボードを簡単に確認できます。
トポロジー図の「Self」をクリックすると、Thread Border Routerに接続されたノードの信号強度も確認できます。しかし、Thread Dev Boardをルーターとして使うことはできませんでした。通信範囲外または少なくとも信号の弱い場所にするため、屋外へ持ち出してみましたが、メッシュネットワーキングは期待どおりには動作しませんでした。GL.iNetのエンジニアに問い合わせたところ、この機能にはThreadルーター用ファームウェアが必要だとの回答を得ました。
現在、標準のTBDファームウェアはThreadエンドデバイスタイプであるため、デバイス同士を直接接続できません。本日中にThreadルーター用ファームウェアと、対応するTBDの書き込みガイドを提供します。
ルーター用ファームウェアに加えて、mcumgr >のLinuxおよびWindows用ユーティリティも送られてきました。
Thread Dev Boardの1枚をLinux(Ubuntu 22.04)PCに接続しました。
bt -lを実行すると、次のように表示されます。
$ bt -l
port | age (sec) | device | driver | description
------+------------+------------+------------------+----------------------
* 0 | 1137 | ttyUSB0 | ch341-uart | USB Serial
シリアルポートが検出されたため、次のコマンドで現在のファームウェア状態を確認できます。
$ sudo ./mcumgr --conntype serial --connstring=/dev/ttyUSB0,baud=460800 image list
Error: NMP timeout
jaufranc@cnx-laptop-4:~/edev/GL-S200/TBD upgrade$ sudo ./mcumgr --conntype serial --connstring=/dev/ttyUSB0,baud=460800 image list
Images:
image=0 slot=0
version: 1.1.9
bootable: true
flags: active confirmed
hash: 88b4725af7975e0b60915c320a82082bb2dda923b736f0e31d2c7d4d67cac3d3
image=0 slot=1
version: 1.1.5
bootable: true
flags:
hash: 4d8548e6910e7214d8325a7d1a49a8390fb25cce983bdb0b15bfecc657f7860b
Split status: N/A (0)
1回目は「NMP timeout」エラーで失敗しましたが、2回目は成功しました。Admin Panelで更新したFirmware 1.1.9はslot 0に、以前のFirmware 1.1.5はslot 1にあります。次に、ルーター用ファームウェアの書き込みを試します。
$ sudo ./mcumgr --conntype serial --connstring=/dev/ttyUSB0,baud=460800 image upload GL-Thread-Dev-Board-FTD-OTA-v1.1.9.bin
426.58 KiB / 426.58 KiB [========================] 100.00% 8.93 KiB/s 47s
Done
もう一度確認してみます。
$ sudo ./mcumgr --conntype serial --connstring=/dev/ttyUSB0,baud=460800 image list
Images:
image=0 slot=0
version: 1.1.9
bootable: true
flags: active confirmed
hash: 88b4725af7975e0b60915c320a82082bb2dda923b736f0e31d2c7d4d67cac3d3
image=0 slot=1
version: 1.1.9
bootable: true
flags:
hash: 7dbb8a9867a000bb968099593bc1f6c2c7bb28ab458719f499364c4a77feca9f
Split status: N/A (0)
Done
slot 0のファームウェアは現在動作中のもので、slot 1のファームウェアは先ほど書き込んだルーター版です。次のようにして新しいファームウェアに切り替えられます。
sudo ./mcumgr --conntype serial --connstring=/dev/ttyUSB0,baud=460800 image test 7dbb8a9867a000bb968
次にボードを再起動し、7dbbで始まるハッシュの新しいファームウェアがslot 0にあることを確認します。
$ sudo ./mcumgr --conntype serial --connstring=/dev/ttyUSB0,baud=460800 image list
Images:
image=0 slot=0
version: 1.1.9
bootable: true
flags: active confirmed
hash: 7dbb8a9867a000bb968099593bc1f6c2c7bb28ab458719f499364c4a77feca9f
image=0 slot=1
version: 1.1.9
bootable: true
flags:
hash: 88b4725af7975e0b60915c320a82082bb2dda923b736f0e31d2c7d4d67cac3d3
Split status: N/A (0)
ボードを再びAdmin Panelに追加すると、Thread Leader(GL-S200)、2台のEnd Device、そしてルーター用ファームウェアを書き込んだRouter(ボード)が表示されます。
すべてのEnd Deviceは机の上にありGL-S200に接続しているため、Board 1のルーターには接続されていません。そこで、Board 1とEnd Deviceの1台をGL-S200 Thread Border Routerから12 meters離れた別室へ移動しました。しかし、トポロジーは変わりませんでした。接続は「固定」されるようで、Threadノードは単に信号強度が高いからではなく、通信範囲外になった場合にのみ別のRouterへ再接続するようです。
しかし、GL-S200の電源を切って再起動すると、2台のEnd Deviceはネットワーク内で「Thread Leader」となったBoard 1のルーターに再接続しました。
つまり、実際に機能します。ただし、Board 2はGL-S200 Thread Border Router(self)から10 centimeters、Board 1は12 meters離れているにもかかわらず、しばらく待ってもBoard 2はGL-S200へ再接続しませんでした。言い換えると、接続が維持されている限り、より近く信号が強い別のルーターがあっても、Threadノードは再接続しません。これはバッテリー寿命の点でも理にかなっていると思います。
そこで、Board 2(End Device)とBoard 1(Router)をGL-S200の通信範囲外へ少し持ち出し、Board 1、続いてBoard 2の順に電源を入れて接続を確認してから戻りました。上図のとおり機能し、Board 2はRouterとして機能するBoard 1を経由して、GL-S200の「Thread Leader」に接続しました。
GL Thread Dev Boardの情報
特定デバイスのAction列にある3つのドットをクリックすると、Device Detail、View Records、Edit Device、Get Code、Reset Deviceなどの追加オプションを利用できます。
デバイス情報
EUI-64、名前、IPv6アドレス、Extended MACアドレス(通常の48-bitではなく64-bit)、ファームウェアバージョン、センサーデータを確認できます。
View Records
ボードを24時間以上稼働させた後、「View Records」メニューで特定ボードのセンサー値のグラフを確認しました。
ほとんど問題なさそうですが、左下を見ると9 pmの値がある一方で、なぜかグラフは11 pmからしか始まりません。これはすべてのボードで同じです。
時間別グラフではさらに状態が悪く見えます。左側には常にデータポイントが表示されるため、データ欠落ではなく表示上の不具合のようです。
コード例
独自のThreadデバイスを監視・制御する場合、「Get Code」セクションが最も重要かもしれません。5つのコード例が提供されています。
サンプルを実行するには、SSH経由でGL-S200 Thread Border Routerへ接続する必要があります。まずは、センサーからデータを読み取るコードです。
root@GL-S200:~# /usr/bin/demo_get_sensor_data
1 dev_id=9483c44004df0679 connected=1 temperature=31.300811 humidity=43.409729 brightness=0 pressure=97.817392
2 dev_id=9483c47d79c19475 connected=1 temperature=32.742767 humidity=53.620910 brightness=0 pressure=97.814472
3 dev_id=9483c4ec736c4f68 connected=1 temperature=31.292800 humidity=42.652893 brightness=0 press
EUI-64を使用して特定のデバイスを選択することもできます。
root@GL-S200:~# /usr/bin/demo_get_sensor_data 9483c44004df0679
dev_id=9483c44004df0679 connected=1 temperature=31.300811 humidity=43.409729 brightness=0 pre
参考としてソースコードを示します。
#!/bin/sh
# Note, this file is stored in /usr/bin/demo_get_sensor_data
# Usage: demo_get_sensor_data <dev_id>
# It will print all sensor data if <dev_id> is null, and the <dev_id> is the EUI64 of the device, which saved in /etc/config/thread_devices
. /usr/share/libubox/jshn.sh
dev_id=$1
if [ -z "$dev_id" ]; then
res=$(curl -s localhost/rpc -d '{"jsonrpc":"2.0","id":"0","method":"call","params":["","gw","get_device_list",{}]}')
json_init
json_load "$res"
json_select result
json_select device_list
idx=1
while json_is_a ${idx} object; do
json_select ${idx}
json_get_var dev_id dev_id
json_get_var connected connected
json_select dev_data
json_get_var val_temp temperature
json_get_var val_humi humidity
json_get_var val_brightness brightness
json_get_var val_pressure pressure
echo "$idx dev_id=$dev_id connected=$connected temperature=$val_temp humidity=$val_humi brightness=$val_brightness pressure=$val_pressure"
json_select ..
json_select ..
idx=$((idx + 1))
done
else
res=$(curl -s localhost/rpc -d "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"call\",\"params\":[\"\",\"gw\",\"get_device_status\",{\"dev_id\":\"$dev_id\"}]}")
json_init
json_load "$res"
json_select result
json_get_var dev_id dev_id
json_get_var connected connected
json_select dev_data
json_get_var val_temp temperature
json_get_var val_humi humidity
json_get_var val_brightness brightness
json_get_var val_pressure pressure
echo "dev_id=$dev_id connected=$connected temperature=$val_temp humidity=$val_humi brightness=$val_brightness pressure=$val_pressure"
fi
ほかのサンプルも試してみます。
ls /usr/bin/demo*
/usr/bin/demo_control_gpio /usr/bin/demo_sensor_trigger
/usr/bin/demo_control_led /usr/bin/demo_user_action_trigger
/usr/bin/demo_get_sensor_data
2つ目のサンプルでは、EUI-64デバイスIDで選択したボード上の2つのRGB LEDを制御します。
root@GL-S200:~# /usr/bin/demo_control_led 9483c44004df0679
LED OFF
LED ON
LED OFF
LED TOGGLE
Changle LED Color to Red light
Changle LED Color to Orange light
Changle LED Color to Yellow light
Changle LED Color to Green light
Changle LED Color to Blue light
Changle LED Color to Indigo light
Changle LED Color to Purple light
Changle LED Color to White light
動作の様子は、以下の短い動画デモで確認できます。
GPIO制御サンプルでは、特定のボードの一部のI/Oを出力モードに設定し、LowおよびHighを切り替えます。
root@GL-S200:~# /usr/bin/demo_control_gpio 9483c44004df0679
Get GPIO Status
GPIO 0.15: 0
GPIO 0.16: 0
GPIO 0.17: 0
GPIO 0.20: 0
Set GPIO 0.15/0.16/0.17/0.20 to output low level
Get GPIO Status
GPIO 0.15: 0
GPIO 0.16: 0
GPIO 0.17: 0
GPIO 0.20: 0
Set GPIO 0.15/0.16/0.17/0.20 to output high level
Get GPIO Status
GPIO 0.15: 1
GPIO 0.16: 1
GPIO 0.17: 1
GPIO 0.20: 1
LEDとGPIOを制御する最後の2つのサンプルでは、coap_cli(「小さなCoAP実装」)コマンドを使用します。たとえば、LEDをオフにするには次のようにします。
coap_cli -B 2 -N -e "$ctl_cmd_led_off" -m put coap://[$addr]/cmd 1>/dev/null 2>/
coap_cliの使用方法:
root@GL-S200:~# coap_cli
coap_cli v4.2.1 -- a small CoAP implementation
Copyright (C) 2010-2019 Olaf Bergmann <bergmann@tzi.org> and others
TLS Library: None
Usage: coap_cli [-a addr] [-b [num,]size] [-e text] [-f file] [-l loss]
[-m method] [-o file] [-p port] [-r] [-s duration] [-t type]
[-v num] [-A type] [-B seconds] [-K interval] [-N] [-O num,text]
[-P addr[:port]] [-T token] [-U]
[[-k key] [-u user]]
[[-c certfile] [-C cafile] [-R root_cafile]] URI
URI can be an absolute URI or a URI prefixed with scheme and host
General Options
-a addr The local interface address to use
-b [num,]size Block size to be used in GET/PUT/POST requests
(value must be a multiple of 16 not larger than 1024)
If num is present, the request chain will start at
block num
-e text Include text as payload (use percent-encoding for
non-ASCII characters)
-f file File to send with PUT/POST (use '-' for STDIN)
-l list Fail to send some datagrams specified by a comma
separated list of numbers or number ranges
(for debugging only)
-l loss% Randomly fail to send datagrams with the specified
probability - 100% all datagrams, 0% no datagrams
-m method Request method (get|put|post|delete|fetch|patch|ipatch),
default is 'get'
-o file Output received data to this file (use '-' for STDOUT)
-p port Listen on specified port
-r Use reliable protocol (TCP or TLS)
-s duration Subscribe to / Observe resource for given duration
in seconds
-t type Content format for given resource for PUT/POST
-v num Verbosity level (default 3, maximum is 9). Above 7,
there is increased verbosity in GnuTLS logging
-A type Accepted media type
-B seconds Break operation after waiting given seconds
(default is 90)
-K interval send a ping after interval seconds of inactivity
-N Send NON-confirmable message
-O num,text Add option num with contents text to request. If the
text begins with 0x, then the hex text is converted to
binary data
-P addr[:port] Use proxy (automatically adds Proxy-Uri option to
request)
-T token Include specified token
-U Never include Uri-Host or Uri-Port options
PSK Options (if supported by underlying (D)TLS library)
-k key Pre-shared key for the specified user
-u user User identity for pre-shared key mode
PKI Options (if supported by underlying (D)TLS library)
-c certfile PEM file containing both CERTIFICATE and PRIVATE KEY
This argument requires (D)TLS with PKI to be available
-C cafile PEM file containing the CA Certificate that was used to
sign the certfile. This will trigger the validation of
the server certificate. If certfile is self-signed (as
defined by '-c certfile'), then you need to have on the
command line the same filename for both the certfile and
cafile (as in '-c certfile -C certfile') to trigger
validation
-R root_cafile PEM file containing the set of trusted root CAs that
are to be used to validate the server certificate.
The '-C cafile' does not have to be in this list and is
'trusted' for the verification.
Alternatively, this can point to a directory containing
a set of CA PEM files
Examples:
coap-client -m get coap://[::1]/
coap-client -m get coap://[::1]/.well-known/core
coap-client -m get coap+tcp://[::1]/.well-known/core
coap-client -m get coaps://[::1]/.well-known/core
coap-client -m get coaps+tcp://[::1]/.well-known/core
coap-client -m get -T cafe coap://[::1]/time
echo -n 1000 | coap-client -m put -T cafe coap://[::1]/time -f -
アクショントリガーのデモでは、ノブの状態が報告されます。
root@GL-S200:~# /usr/bin/demo_user_action_trigger 9483c44004df0679
2023/03/19 17:38:14 info (/usr/bin/demo_user_action_trigger:34) 9483c44004df0679: Press the knob.
2023/03/19 17:38:16 info (/usr/bin/demo_user_action_trigger:34) 9483c44004df0679: Press the knob.
2023/03/19 17:38:18 info (/usr/bin/demo_user_action_trigger:32) 9483c44004df0679: Turn the knob forward
2023/03/19 17:38:19 info (/usr/bin/demo_user_action_trigger:32) 9483c44004df0679: Turn the knob forward
2023/03/19 17:38:20 info (/usr/bin/demo_user_action_trigger:32) 9483c44004df0679: Turn the knob forward
2023/03/19 17:38:20 info (/usr/bin/demo_user_action_trigger:32) 9483c44004df0679: Turn the knob reverse
2023/03/19 17:38:21 info (/usr/bin/demo_user_action_trigger:32) 9483c44004df0679: Turn the knob reverse
2023/03/19 17:38:21 info (/usr/bin/demo_user_action_trigger:32) 9483c44004df0679: Turn the knob reverse
2023/03/19 17:38:23 info (/usr/bin/demo_user_action_trigger:34) 9483c44004df0679: Press the knob.
2023/03/19 17:38:25 info (/usr/bin/demo_user_action_trigger:32) 9483c44004df0679: Turn the
このコードは/usr/bin/ecoバイナリに依存しています。[更新:eco-lua Luaインタープリタープロジェクトのようです。コメント欄を参照してください。]
#!/usr/bin/env eco
-- Note, this file is stored in /usr/bin/demo_user_action_trigger, It listens for events reported when the user operates the knob.
-- Usage: demo_sensor_trigger
local ubus = require 'eco.ubus'
local sys = require 'eco.sys'
local cjson = require 'cjson'
local log = require "glog"
local OPT_TURNING_THE_KNOB = 1
local OPT_PRESS_THE_KNOB = 2
log.level(log.LOG_INFO)
sys.signal(sys.SIGINT, function()
print('
Got SIGINT, now quit')
eco.unloop()
end)
local con, err = ubus.connect()
if not con then
error(err)
end
con:listen('user_active_trigger', function(ev, msg)
local event_data = cjson.encode(msg)
log.debug('Recievd user active trigger ' .. ev .. ' ' .. event_data)
local trigger, operation, direction = msg.trigger, msg.operation, msg.direction
if operation == OPT_TURNING_THE_KNOB then
log.info(trigger .. ': Turn the knob ' .. direction)
elseif operation == OPT_PRESS_THE_KNOB then
log.info(trigger .. ': Press the knob.')
else
log.err('Unknown operation!')
end
end)
GL-S200 Thread Border Routerに接続されたすべてのThread Dev BoardのPIRセンサーを監視するセンサートリガーのサンプルも同様です。
root@GL-S200:~# /usr/bin/demo_sensor_trigger
2023/03/19 17:47:46 info (/usr/bin/demo_sensor_trigger:31) 9483c44004df0679: Human body detected.
2023/03/19 17:47:46 info (/usr/bin/demo_sensor_trigger:31) 9483c4ec736c4f68: Human body detected.
2023/03/19 17:47:49 info (/usr/bin/demo_sensor_trigger:31) 9483c4ec736c4f68: Human body detected.
2023/03/19 17:47:52 info (/usr/bin/demo_sensor_trigger:31) 9483c44004df0679: Human body detected.
2023/03/19 17:48:15 info (/usr/bin/demo_sensor_trigger:31) 9483c47d79c19475: Human body detected.
これらのサンプルは、独自のスクリプトを作り始める際に役立つでしょう。
GL-S200の「Automations」
GL DEV BOARDSセクションには「Automations」セクションもあります。
残りの3枚目のボードのノブを使って、2枚のThread Dev Board上のRGB LEDを制御してみます。
オートメーションに名前を付けた後、ユーザーアクションを選択し、ノブとして使用するボード「Board 2」を選びます。
「Turning the knob」を選択し、次に「Device(s) Action」を選択しました。
ほかの2枚のボードを制御したいため、「Board 1」と「Board 2」を選択しました。
次に、選択できるのは「Change Color」だけなので、それを選択してApplyをクリックします。
しかし動作せず、ノブをどちらの方向に回してもRGB LEDはオフのままでした。色を変更する前にRGB LEDをオンにする必要があると分かったため、ノブを押したときにLEDのオン・オフを切り替える別のオートメーションを作成しました。
これで動作しました。
AutomationセクションはWebhookにも対応しているため、動きを検知したときにDiscordで通知を受け取るオートメーションを作成することにしました。そのためにDiscordサーバーを作成し、https://discord.com/api/webhooks/xxxxというWebhook URLを取得しました。Discordの手順に従って設定しています。
次に、Sensor Triggerを選択して別のオートメーションを作成しました。
次に「Human Body Detected」(PIRモーションセンサーがトリガーされたことを意味します)を選択します。
そして「Webhook」を選択します。
ここでWebhook URLを追加し、Jsonを選択して、次のような内容を追加しました。
{"content":"OMG! Somebody has just entered the bedroom!"}
最後にApplyをクリックし、キッチンとオフィスでも動きを検知するために同じ手順を繰り返しました。
これでDiscordを開き、家の中を歩き回って3枚すべてのボードのPIRモーションセンサーをトリガーできます。
成功しました。Automationセクションは非常に便利ですが、GL.iNet Thread Dev Boardでのみ動作するようです。独自のThreadデバイスでも使えるようにソリューションを更新するため、ソースコードを共有する予定があるか尋ねましたが、この質問については回答を得られませんでした。
その他
ほかのGL.iNetネットワークデバイスと同様に、GL-S200はリモートアクセス用のGoodCloudに対応しています。ただし、ThreadとBluetooth部分はサポートされていないため、用途は限定的です。
当初はMode SwitchボタンをBLEとThreadの切り替えに使用するものだと思っていましたが、以前のルーターと同様にWireGuardまたはOpenVPNのオン・オフを切り替えるよう設定できます。
時間の都合でBluetooth部分はテストしていませんが、インターフェースと使用感は、昨年レビューしたGL-S10 BLE to MQTT bridgeと似ていると予想しています。
興味深い体験でした。評価およびベータテスト用にGL-S200 Thread Border Routerキットを送付してくださったGL.iNetに感謝します。今回受け取ったキットは送料別$154.00で予約注文できます。すでに独自のThreadノードをお持ちの場合、GL-S200単体は予約期間中は$79で販売され、その後は$99になります。
|
Jean-Luc Aufranc (CNXSoft)Jean-Lucは2010年に副業としてCNX Softwareを始め、その後ソフトウェアエンジニアリングマネージャーの職を辞め、2011年後半から日々のニュースとレビューの執筆にフルタイムで取り組んでいます。 |
About GL.iNet
Founded in 2010, GL.iNet is a leading provider of OpenWrt-based routers and innovative remote networking solutions. From compact travel routers to cutting-edge remote KVMs, GL.iNet delivers award-winning networking technology designed for performance and security.
GL.iNet gives people and organizations the power to connect with confidence. By putting control in the hands of users, GL.iNet supports the productivity and collaboration that shape a Good Life.