みえちゃう?! 赤外線アレイセンサーAMG8833【電子工作始めますか?】
ピッ!体温32℃! お店や会社の入り口で見かけるポ○コツ体温計を自分で作ってみましょう。
今回もI2Cを使ったセンサーモジュールを動かしてみます。
使うのはこのセンサーです。
パナソニック製の赤外線アレイセンサGrid-EYE AMG8833のブレークアウト基板です。
赤外線アレイセンサGrid-EYEは電源電圧と増幅率の違いで型番の末尾2桁が異なる製品があります。AMG8833は3.3V電源のハイゲインタイプです。3.3VなのでRaspberry PiのI2C端子とつなげばすぐに動かせます。
AMG8833は縦横8x8の64箇所の温度を赤外線で測定します。測定間隔は10fpsと高速です。測定温度範囲は0℃~80℃となっています。
配線
配線は電源とI2Cの4本をつなぐだけです。AMG8833は温度変化で割り込みをかける機能もあるのでそれを使う場合はもう一本GPIOとつなぎます。ここでは割り込みを使いません。
接続はこうです。
5ピンのコネクタを作ります。ピンを自作すると接触不良が多いです。ジャンパケーブルを使うのも良いでしょう。電源は3.3Vです。5Vと間違えないように。
I2Cのアドレスは0x68です。基板のジャンパを変えると0x69にもできます。
ドライバ
AMG8833はArduinoとかM5Stackとかワンボードマイコンで動かす例が多いです。
Raspberry Piで動かすPythonのプログラムを公開しているメーカーもあります。
Python & CircuitPython | AdafruitIt's easy to use the AMG8833 sensor with Python or CircuitPython and the Adafruit CircuitPython AMG88xx module.
私はPythonが嫌いなので今回もNode-REDを使いたいです。つまりJavaScript。前回の湿度センサーではNode.jsのモジュールが公開されていて簡単に使えたのですがAMG8833にはNode.jsのモジュールが見当たりません。
しかたないのでNode-REDでAMG8833と直接I2Cで通信して動かすことにします。ドライバがやってくれる部分を自分で作ります。難しそうに思えますがAMG8833は電源を入れれば動くので温度データを読み出すだけで使えます。
I2CでICと通信する
多くのICはI2Cというシリアル通信で動作の設定やデータのやり取りを行います。
ICにはレジスタというメモリーがあり、レジスタの値で動作を変えたり、結果をレジスタに置いたりしています。I2Cはこのレジスターの読み書きをします。
I2Cのプロトコル自体のプログラムはライブラリになっているでしょうから作りません。I2Cのライブラリへレジスタのアドレスとデータの読み書きをするコマンドを送る部分を作ります。
Node-REDでもI2C通信をするノードが公開されています。
A Node-RED node to talk to the Raspiberry PI's I2C port
このノードをNode-REDに追加します。
AMG8833のレジスタ
I2C通信をするにもICのレジスタがどうなっているか知らなければ何もできません。これはICのデータシートに書いてあるでしょう。ただし簡易なデータシートには書いてないかも。
AMG8833の場合はデータシートに書いてありませんでした。別紙で用意されているようです。
Conta™ サーモグラフィー AMG8833搭載 | スイッチサイエンスAMG8833の参考仕様書
この仕様書にレジスタの内容が説明されています。
測定した温度のデータはアドレス0x80から0xFFに保存されています。
このアドレスのデータを読み出せば温度データが得られます。2つのアドレスのデータ16bitで1つの温度を表します。温度データは12bitの2の補数で保存されています。
12bitの2の補数をJavaScriptやPythonで扱える数値に変換します。その数値を0.25倍すると温度になります。
AMG8833用フロー
いろいろ説明を読んでもわからないでしょうから、サンプルのフローを用意しました。
Node-REDにコピペして動かしてください。動いたらゆっくり何をしているか見ると良いでしょう。取得した温度はダッシュボードにヒートマップで表示しています。
このフローでは追加のノードを使っています。
ノードを追加してください。
フローのコードはこちら
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"id": "c392c0ed.646ce", | |
"type": "tab", | |
"label": "AMG8833", | |
"disabled": false, | |
"info": "" | |
}, | |
{ | |
"id": "d1e3dbe0.432138", | |
"type": "inject", | |
"z": "c392c0ed.646ce", | |
"name": "Start", | |
"props": [ | |
{ | |
"p": "payload" | |
} | |
], | |
"repeat": "", | |
"crontab": "", | |
"once": true, | |
"onceDelay": 0.1, | |
"topic": "", | |
"payload": "", | |
"payloadType": "str", | |
"x": 110, | |
"y": 100, | |
"wires": [ | |
[ | |
"fd5ce31a.93427" | |
] | |
] | |
}, | |
{ | |
"id": "dbf65e7b.07876", | |
"type": "inject", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"props": [ | |
{ | |
"p": "payload" | |
} | |
], | |
"repeat": "1", | |
"crontab": "", | |
"once": true, | |
"onceDelay": "1", | |
"topic": "", | |
"payload": "", | |
"payloadType": "str", | |
"x": 110, | |
"y": 420, | |
"wires": [ | |
[ | |
"5d3bd7b8.1e1fb8" | |
] | |
] | |
}, | |
{ | |
"id": "9a3e1bdc.9af668", | |
"type": "function", | |
"z": "c392c0ed.646ce", | |
"name": "Temperature REG.", | |
"func": "const address = msg.address;\n\nvar commands=[];\n\nfor (let i=0x80; i<=0xFF; i+=0x10) {\n if (i == 0xF0) {\n commands.push({'address': address, 'command': i, 'bytes': 16, 'complete': true});\n } else {\n commands.push({'address': address, 'command': i, 'bytes': 16,});\n }\n}\n\n\nreturn [commands];", | |
"outputs": 1, | |
"noerr": 0, | |
"initialize": "", | |
"finalize": "", | |
"x": 490, | |
"y": 240, | |
"wires": [ | |
[ | |
"458187d9.b5d6b8" | |
] | |
] | |
}, | |
{ | |
"id": "833a407b.40739", | |
"type": "join", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"mode": "custom", | |
"build": "array", | |
"property": "payload", | |
"propertyType": "msg", | |
"key": "address", | |
"joiner": "", | |
"joinerType": "str", | |
"accumulate": false, | |
"timeout": "", | |
"count": "", | |
"reduceRight": false, | |
"reduceExp": "", | |
"reduceInit": "", | |
"reduceInitType": "num", | |
"reduceFixup": "", | |
"x": 310, | |
"y": 300, | |
"wires": [ | |
[ | |
"6bba91d4.dc88a" | |
] | |
] | |
}, | |
{ | |
"id": "6bba91d4.dc88a", | |
"type": "function", | |
"z": "c392c0ed.646ce", | |
"name": "Concat & Flip", | |
"func": "var temperature = msg.payload;\n\n\nvar flip= [];\nfor (let i=0; i<temperature.length; i++) {\n flip = flip.concat(temperature[temperature.length - i - 1]);\n}\nmsg.payload = flip;\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"initialize": "", | |
"finalize": "", | |
"x": 460, | |
"y": 300, | |
"wires": [ | |
[ | |
"14522f6d.5e4171", | |
"cd890b11.a46b78" | |
] | |
] | |
}, | |
{ | |
"id": "624a3ed2.af5eb", | |
"type": "function", | |
"z": "c392c0ed.646ce", | |
"name": "Thermistor REG.", | |
"func": "const address = msg.address;\n\nvar commands=[];\n\n//commands.push({'address': address, 'command': 0x0E});\ncommands.push({'address': address, 'command': 0x0E, 'bytes': 2,'complete': true});\n\nreturn [commands];", | |
"outputs": 1, | |
"noerr": 0, | |
"initialize": "", | |
"finalize": "", | |
"x": 450, | |
"y": 420, | |
"wires": [ | |
[ | |
"c8010a3b.d1c548" | |
] | |
] | |
}, | |
{ | |
"id": "fcf15388.cf2a4", | |
"type": "function", | |
"z": "c392c0ed.646ce", | |
"name": "Calc Value", | |
"func": "var raw = msg.payload;\n\nvar temperature = Array(raw.length / 2).fill();\nfor (let i=0; i<temperature.length; i++) {\n j = i << 1; // Multiply 2\n var sign = raw[j+1] & 0x08;\n var val = ((raw[j+1] & 0x07) << 8) | raw[j] ;\n if (sign != 0) {\n //負\n val = val - 0x07FF -1;\n }\n temperature[i] = val*0.0625;\n}\n\nmsg.payload = temperature;\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"initialize": "", | |
"finalize": "", | |
"x": 670, | |
"y": 460, | |
"wires": [ | |
[ | |
"357e277c.27db98" | |
] | |
] | |
}, | |
{ | |
"id": "653ffbba.1def44", | |
"type": "inject", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"props": [ | |
{ | |
"p": "payload" | |
} | |
], | |
"repeat": "", | |
"crontab": "", | |
"once": false, | |
"onceDelay": "1", | |
"topic": "", | |
"payload": "", | |
"payloadType": "str", | |
"x": 110, | |
"y": 240, | |
"wires": [ | |
[ | |
"a008b366.7ea8c" | |
] | |
] | |
}, | |
{ | |
"id": "7194c86f.1a3fc8", | |
"type": "comment", | |
"z": "c392c0ed.646ce", | |
"name": "Initial reset", | |
"info": "", | |
"x": 80, | |
"y": 60, | |
"wires": [] | |
}, | |
{ | |
"id": "1804e8cd.2d0727", | |
"type": "comment", | |
"z": "c392c0ed.646ce", | |
"name": "Thermistor", | |
"info": "", | |
"x": 80, | |
"y": 380, | |
"wires": [] | |
}, | |
{ | |
"id": "76521262.83f06c", | |
"type": "comment", | |
"z": "c392c0ed.646ce", | |
"name": "Temperature", | |
"info": "", | |
"x": 90, | |
"y": 200, | |
"wires": [] | |
}, | |
{ | |
"id": "2c3e31d8.5983ee", | |
"type": "debug", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"active": false, | |
"tosidebar": true, | |
"console": false, | |
"tostatus": false, | |
"complete": "false", | |
"statusVal": "", | |
"statusType": "auto", | |
"x": 890, | |
"y": 300, | |
"wires": [] | |
}, | |
{ | |
"id": "711982c4.d7b21c", | |
"type": "inject", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"props": [ | |
{ | |
"p": "payload" | |
} | |
], | |
"repeat": "", | |
"crontab": "", | |
"once": true, | |
"onceDelay": 0.1, | |
"topic": "", | |
"payload": "1", | |
"payloadType": "num", | |
"x": 110, | |
"y": 580, | |
"wires": [ | |
[ | |
"a3a501fb.26fb6" | |
] | |
] | |
}, | |
{ | |
"id": "7e55e177.4b9ea", | |
"type": "comment", | |
"z": "c392c0ed.646ce", | |
"name": "FPS", | |
"info": "", | |
"x": 70, | |
"y": 500, | |
"wires": [] | |
}, | |
{ | |
"id": "456926fb.7cb5e8", | |
"type": "inject", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"props": [ | |
{ | |
"p": "payload" | |
} | |
], | |
"repeat": "", | |
"crontab": "", | |
"once": true, | |
"onceDelay": 0.1, | |
"topic": "", | |
"payload": "0", | |
"payloadType": "num", | |
"x": 110, | |
"y": 540, | |
"wires": [ | |
[ | |
"a3a501fb.26fb6" | |
] | |
] | |
}, | |
{ | |
"id": "a3a501fb.26fb6", | |
"type": "ui_switch", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"label": "1fps", | |
"tooltip": "", | |
"group": "38a41d52.5ca782", | |
"order": 1, | |
"width": "2", | |
"height": "1", | |
"passthru": true, | |
"decouple": "false", | |
"topic": "topic", | |
"topicType": "msg", | |
"style": "", | |
"onvalue": "1", | |
"onvalueType": "num", | |
"onicon": "", | |
"oncolor": "", | |
"offvalue": "0", | |
"offvalueType": "num", | |
"officon": "", | |
"offcolor": "", | |
"animate": false, | |
"x": 230, | |
"y": 560, | |
"wires": [ | |
[ | |
"92a4f6c6.c46a88" | |
] | |
] | |
}, | |
{ | |
"id": "d7fb5178.67a0a", | |
"type": "function", | |
"z": "c392c0ed.646ce", | |
"name": "FPS REG.", | |
"func": "//msg.address = 0x68;\nmsg.command =\"0x02\";\n\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"initialize": "", | |
"finalize": "", | |
"x": 550, | |
"y": 560, | |
"wires": [ | |
[ | |
"b428fb78.299b38" | |
] | |
] | |
}, | |
{ | |
"id": "357e277c.27db98", | |
"type": "ui_text", | |
"z": "c392c0ed.646ce", | |
"group": "38a41d52.5ca782", | |
"order": 2, | |
"width": "3", | |
"height": "1", | |
"name": "", | |
"label": "Thermistor", | |
"format": "{{msg.payload[0] }}", | |
"layout": "row-left", | |
"x": 890, | |
"y": 460, | |
"wires": [] | |
}, | |
{ | |
"id": "a56b72d3.96834", | |
"type": "catch", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"scope": null, | |
"uncaught": true, | |
"x": 470, | |
"y": 40, | |
"wires": [ | |
[ | |
"ec5fa817.7df378", | |
"fd7a52e.b10dab" | |
] | |
] | |
}, | |
{ | |
"id": "ec5fa817.7df378", | |
"type": "debug", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"active": true, | |
"tosidebar": true, | |
"console": false, | |
"tostatus": false, | |
"complete": "error", | |
"targetType": "jsonata", | |
"statusVal": "", | |
"statusType": "auto", | |
"x": 880, | |
"y": 40, | |
"wires": [] | |
}, | |
{ | |
"id": "150e6fc0.fd8c4", | |
"type": "ui_text", | |
"z": "c392c0ed.646ce", | |
"group": "38a41d52.5ca782", | |
"order": 5, | |
"width": "0", | |
"height": "0", | |
"name": "", | |
"label": "", | |
"format": "{{msg.payload}}", | |
"layout": "row-left", | |
"x": 870, | |
"y": 80, | |
"wires": [] | |
}, | |
{ | |
"id": "fd7a52e.b10dab", | |
"type": "change", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "payload", | |
"pt": "msg", | |
"to": "error.message", | |
"tot": "jsonata" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 660, | |
"y": 60, | |
"wires": [ | |
[ | |
"150e6fc0.fd8c4" | |
] | |
] | |
}, | |
{ | |
"id": "614d2678.a9fef8", | |
"type": "function", | |
"z": "c392c0ed.646ce", | |
"name": "Initialize REG.", | |
"func": "//msg.address = 0x68;\nmsg.command =\"0x01\";\nmsg.payload = 63;\n\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"initialize": "", | |
"finalize": "", | |
"x": 540, | |
"y": 140, | |
"wires": [ | |
[ | |
"6f00e502.aa22dc" | |
] | |
] | |
}, | |
{ | |
"id": "7aa93c8.a864ec4", | |
"type": "change", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "payload", | |
"pt": "msg", | |
"to": "", | |
"tot": "str" | |
}, | |
{ | |
"t": "set", | |
"p": "reset", | |
"pt": "msg", | |
"to": "true", | |
"tot": "bool" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 660, | |
"y": 100, | |
"wires": [ | |
[ | |
"150e6fc0.fd8c4", | |
"14522f6d.5e4171" | |
] | |
] | |
}, | |
{ | |
"id": "9a1bc00a.2394b", | |
"type": "ui_button", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"group": "38a41d52.5ca782", | |
"order": 6, | |
"width": "0", | |
"height": "0", | |
"passthru": false, | |
"label": "RESET", | |
"tooltip": "", | |
"color": "", | |
"bgcolor": "", | |
"icon": "", | |
"payload": "", | |
"payloadType": "str", | |
"topic": "topic", | |
"topicType": "msg", | |
"x": 100, | |
"y": 140, | |
"wires": [ | |
[ | |
"fd5ce31a.93427" | |
] | |
] | |
}, | |
{ | |
"id": "cd890b11.a46b78", | |
"type": "ui_heat_map", | |
"z": "c392c0ed.646ce", | |
"group": "38a41d52.5ca782", | |
"order": 3, | |
"width": "10", | |
"height": "10", | |
"name": "", | |
"rows": "8", | |
"columns": "8", | |
"minMax": false, | |
"minimumValue": "20", | |
"maximumValue": "35", | |
"backgroundType": "image", | |
"backgroundColor": "#b0b0b0", | |
"radius": "90", | |
"opacity": "0", | |
"blur": "1", | |
"showValues": false, | |
"gridType": "vals", | |
"valuesDecimals": 0, | |
"showLegend": false, | |
"legendType": "vals", | |
"legendDecimals": "1", | |
"legendCount": "4", | |
"x": 680, | |
"y": 340, | |
"wires": [] | |
}, | |
{ | |
"id": "458187d9.b5d6b8", | |
"type": "i2c in", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"busno": "1", | |
"address": "", | |
"command": "", | |
"count": "16", | |
"x": 650, | |
"y": 240, | |
"wires": [ | |
[ | |
"256cafc9.9b2ff" | |
] | |
] | |
}, | |
{ | |
"id": "c8010a3b.d1c548", | |
"type": "i2c in", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"busno": "1", | |
"address": "", | |
"command": "", | |
"count": "2", | |
"x": 610, | |
"y": 420, | |
"wires": [ | |
[ | |
"fcf15388.cf2a4" | |
] | |
] | |
}, | |
{ | |
"id": "6f00e502.aa22dc", | |
"type": "i2c out", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"busno": "1", | |
"address": "", | |
"command": "", | |
"payload": "payload", | |
"payloadType": "msg", | |
"count": "1", | |
"x": 690, | |
"y": 140, | |
"wires": [ | |
[ | |
"7aa93c8.a864ec4", | |
"9a3e1bdc.9af668" | |
] | |
] | |
}, | |
{ | |
"id": "b428fb78.299b38", | |
"type": "i2c out", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"busno": "1", | |
"address": "", | |
"command": "", | |
"payload": "payload", | |
"payloadType": "msg", | |
"count": "1", | |
"x": 690, | |
"y": 560, | |
"wires": [ | |
[] | |
] | |
}, | |
{ | |
"id": "fd5ce31a.93427", | |
"type": "change", | |
"z": "c392c0ed.646ce", | |
"name": "Set I2C Address", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "address", | |
"pt": "flow", | |
"to": "104", | |
"tot": "num" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 280, | |
"y": 100, | |
"wires": [ | |
[ | |
"cab1265c.e5afc8" | |
] | |
] | |
}, | |
{ | |
"id": "92a4f6c6.c46a88", | |
"type": "change", | |
"z": "c392c0ed.646ce", | |
"name": "Get I2C Address", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "address", | |
"pt": "msg", | |
"to": "address", | |
"tot": "flow" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 380, | |
"y": 560, | |
"wires": [ | |
[ | |
"d7fb5178.67a0a" | |
] | |
] | |
}, | |
{ | |
"id": "5d3bd7b8.1e1fb8", | |
"type": "change", | |
"z": "c392c0ed.646ce", | |
"name": "Get I2C Address", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "address", | |
"pt": "msg", | |
"to": "address", | |
"tot": "flow" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 260, | |
"y": 420, | |
"wires": [ | |
[ | |
"624a3ed2.af5eb" | |
] | |
] | |
}, | |
{ | |
"id": "a008b366.7ea8c", | |
"type": "change", | |
"z": "c392c0ed.646ce", | |
"name": "Get I2C Address", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "address", | |
"pt": "msg", | |
"to": "address", | |
"tot": "flow" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 260, | |
"y": 240, | |
"wires": [ | |
[ | |
"9a3e1bdc.9af668" | |
] | |
] | |
}, | |
{ | |
"id": "cab1265c.e5afc8", | |
"type": "change", | |
"z": "c392c0ed.646ce", | |
"name": "Get I2C Address", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "address", | |
"pt": "msg", | |
"to": "address", | |
"tot": "flow" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 360, | |
"y": 140, | |
"wires": [ | |
[ | |
"614d2678.a9fef8" | |
] | |
] | |
}, | |
{ | |
"id": "14522f6d.5e4171", | |
"type": "delay", | |
"z": "c392c0ed.646ce", | |
"name": "", | |
"pauseType": "rate", | |
"timeout": "100", | |
"timeoutUnits": "milliseconds", | |
"rate": "1", | |
"nbRateUnits": "0.1", | |
"rateUnits": "second", | |
"randomFirst": "1", | |
"randomLast": "5", | |
"randomUnits": "seconds", | |
"drop": false, | |
"x": 670, | |
"y": 300, | |
"wires": [ | |
[ | |
"2c3e31d8.5983ee", | |
"9a3e1bdc.9af668" | |
] | |
] | |
}, | |
{ | |
"id": "256cafc9.9b2ff", | |
"type": "function", | |
"z": "c392c0ed.646ce", | |
"name": "Calc Value", | |
"func": "var raw = msg.payload;\n\nvar temperature = Array(raw.length/2).fill();\nfor (let i=0; i<temperature.length; i++) {\n j = i << 1; // Multiply 2\n var sign = raw[j+1] & 0x08;\n var val = ((raw[j+1] & 0x07) << 8) | raw[j] ;\n if (sign != 0) {\n // negative\n val = val - 0x0800;\n }\n temperature[i] = val*0.25;\n}\n\n\nmsg.payload = temperature;\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"initialize": "", | |
"finalize": "", | |
"x": 170, | |
"y": 300, | |
"wires": [ | |
[ | |
"833a407b.40739" | |
] | |
] | |
}, | |
{ | |
"id": "38a41d52.5ca782", | |
"type": "ui_group", | |
"name": "Thermal Image", | |
"tab": "dd1d391f.548498", | |
"order": 1, | |
"disp": true, | |
"width": "10", | |
"collapse": false | |
}, | |
{ | |
"id": "dd1d391f.548498", | |
"type": "ui_tab", | |
"name": "Thermal Imaging", | |
"icon": "image", | |
"disabled": false, | |
"hidden": false | |
} | |
] |
コメント
コメントを投稿