ocoge/blocks/sensors/amg8833/index.js

466 lines
17 KiB
JavaScript
Raw Normal View History

Blockly.Msg["UGJ_DRAW_GRIDEYEDATA_TITLE"] = "赤外線アレイセンサ画像表示 %1 温度データ %2 温度範囲上限 %3 %4 温度範囲下限 %5 %6";
Blockly.Msg["UGJ_DRAW_GRIDEYEDATA_TOOLTIP"] = "AMG8833の温度データを、画像としてキャンバスに描画します。「着色」をチェックすると、温度範囲で設定されている色をつけて表示します。";
Blockly.Msg["UGJ_GRIDEYE_INIT_TITLE"] = "赤外線アレイセンサ(アドレス: %1 )を初期化";
Blockly.Msg["UGJ_GRIDEYE_INIT_TOOLTIP"] = "赤外線アレイセンサ AMG8833 の使用準備をします。";
Blockly.Msg["UGJ_GRIDEYE_THERMISTOR_TITLE"] = "赤外線アレイセンサ本体温度";
Blockly.Msg["UGJ_GRIDEYE_THERMISTOR_TOOLTIP"] = "AMG8833に内蔵されたサーミスタ温度センサの値を取得します。";
Blockly.Msg["UGJ_GRIDEYE_READ_TITLE"] = "赤外線アレイセンサの値";
Blockly.Msg["UGJ_GRIDEYE_READ_TOOLTIP"] = "AMG8833から読み取った温度データを、8x8の配列で取得します。";
Blockly.Msg["UGJ_GRIDEYE_STOP_TITLE"] = "赤外線アレイセンサから切断";
Blockly.Msg["UGJ_GRIDEYE_STOP_TOOLTIP"] = "センサーとの接続を停止します。";
Blockly.Msg["UGJ_GRIDEYE_CANVAS_CREATE_TITLE"] = "赤外線アレイセンサデータ表示キャンバスを作成";
Blockly.Msg["UGJ_GRIDEYE_CANVAS_CREATE_TOOLTIP"] = "ディスプレイエリアにAMG8833データ表示用キャンバスを作成します。";
Blockly.Msg["UGJ_TEACHABLE_MACHINE_TITLE"] = "TensorFlow.jsによる画像分類器の準備";
Blockly.Msg["UGJ_TEACHABLE_MACHINE_TOOLTIP"] = "TensorFlow.jsにMobileNet, KNN Classifierを読み込んで、画像認識分類を行う準備をします。";
Blockly.Msg["UGJ_GRIDEYE_PREDICT_CLASS_TITLE"] = "赤外線アレイセンサの画像で推論を行う";
Blockly.Msg["UGJ_GRIDEYE_PREDICT_CLASS_TOOLTIP"] = "キャンバスに表示されたAMG8833の画像を元に画像分類の推論を行います。推論の結果として定義済みのラベルを返します。";
Blockly.Msg["UGJ_GRIDEYE_ADD_EXAMPLE_TITLE"] = "赤外線アレイセンサの画像にラベル %1 をつけてデータセットへ追加";
Blockly.Msg["UGJ_GRIDEYE_ADD_EXAMPLE_TOOLTIP"] = "キャンバスに表示されているAMG8833の画像にラベルクラス名をつけてデータセットへ追加します。";
Blockly.Msg["UGJ_TENSORSET_STRINGIFY_TITLE"] = "学習したクラスデータセットを文字列に変換";
Blockly.Msg["UGJ_TENSORSET_STRINGIFY_TOOLTIP"] = "学習したクラスデータセットを文字列に変換して保存します。";
Blockly.Msg["UGJ_TENSORSET_PARSE_TITLE"] = "クラスデータ文字列 %1 を画像分類器にセット";
Blockly.Msg["UGJ_TENSORSET_PARSE_TOOLTIP"] = "JSONテキストをパースして画像分類器に戻します。";
/******************* */
/** Init Grid-Eye ** */
/******************* */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_grideye_init",
"message0": "%{BKY_UGJ_GRIDEYE_INIT_TITLE}",
"args0": [
{
"type": "field_dropdown",
"name": "addr",
"options": [
[
"0x68",
"0x68"
],
[
"0x69",
"0x69"
]
]
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"tooltip": "%{BKY_UGJ_GRIDEYE_INIT_TOOLTIP}",
"helpUrl": "",
"style": "sensor_blocks"
}]);
Blockly.JavaScript['ugj_grideye_init'] = function (block) {
var dropdown_addr = block.getFieldValue('addr');
Blockly.JavaScript.provideFunction_(
'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_backend + `');`]
);
let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'amg8833', 'AMG8833x.js');
Blockly.JavaScript.provideFunction_(
'require_amg8833', [`const _amg8833 = require('${modpath}');`]
);
var code = `await _amg8833.init(${apptool.i2c_bus}, ${dropdown_addr}, window.addEventListener);\n`;
return code;//
};
/********************** */
/** Grid-Eye 本体温度 ** */
/********************** */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_grideye_thermistor",
"message0": "%{BKY_UGJ_GRIDEYE_THERMISTOR_TITLE}",
"output": "Number",
"tooltip": "%{BKY_UGJ_GRIDEYE_THERMISTOR_TOOLTIP}",
"helpUrl": "",
"style": "sensor_blocks"
}]);
Blockly.JavaScript['ugj_grideye_thermistor'] = function (block) {
var code = `await _amg8833.read_thermistor()`;
return [code, Blockly.JavaScript.ORDER_NONE];
};
/**************************** */
/** Read Temperature Array ** */
/**************************** */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_grideye_read",
"message0": "%{BKY_UGJ_GRIDEYE_READ_TITLE}",
"inputsInline": true,
"output": "Array",
"tooltip": "%{BKY_UGJ_GRIDEYE_READ_TOOLTIP}",
"helpUrl": "",
"style": "sensor_blocks"
}]);
Blockly.JavaScript['ugj_grideye_read'] = function (block) {
var code = 'await _amg8833.read_temp_array()';
return [code, Blockly.JavaScript.ORDER_ATOMIC];
};
/******************* */
/** Stop Grid-Eye ** */
/******************* */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_grideye_stop",
"message0": "%{BKY_UGJ_GRIDEYE_STOP_TITLE}",
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"tooltip": "%{BKY_UGJ_GRIDEYE_STOP_TOOLTIP}",
"helpUrl": "",
"style": "sensor_blocks"
}]);
Blockly.JavaScript['ugj_grideye_stop'] = function (block) {
var code = 'await _amg8833.stop();\n';
return code;
};
/***************************** */
/** GridEye 表示キャンバス作成 ** */
/***************************** */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_grideye_canvas_create",
"message0": "%{BKY_UGJ_GRIDEYE_CANVAS_CREATE_TITLE}",
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"tooltip": "%{BKY_UGJ_GRIDEYE_CANVAS_CREATE_TOOLTIP}",
"helpUrl": "",
"style": "multimedia_blocks"
}]);
Blockly.JavaScript['ugj_grideye_canvas_create'] = function (block) {
var code = `let _grideye_canvas = document.createElement('canvas');
_grideye_canvas.setAttribute('width', 8);
_grideye_canvas.setAttribute('height', 8);
_grideye_canvas.className = 'subdisplay';
_grideye_canvas.style.width = '160px';
_grideye_canvas.style.height = '160px';
_grideye_canvas.id = 'subcanvas';
document.getElementById('display_area').appendChild(_grideye_canvas);
_grideye_ctx = _grideye_canvas.getContext('2d', {willReadFrequently: true});
_grideye_imgData = _grideye_ctx.createImageData(8, 8);
`;
return code;
};
/********************************************** */
/** Draw IR Array Data to Image Data ** */
/********************************************** */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_draw_grideyedata",
"message0": "%{BKY_UGJ_DRAW_GRIDEYEDATA_TITLE}",
"args0": [
{
"type": "input_dummy"
},
{
"type": "input_value",
"name": "amg8833data",
"check": "Array",
"align": "RIGHT"
},
{
"type": "field_colour",
"name": "color_high",
"colour": "#ff0000"
},
{
"type": "input_value",
"name": "temp_high",
"check": "Number",
"align": "RIGHT"
},
{
"type": "field_colour",
"name": "color_low",
"colour": "#3333ff"
},
{
"type": "input_value",
"name": "temp_low",
"check": "Number",
"align": "RIGHT"
}
],
"inputsInline": false,
"previousStatement": null,
"nextStatement": null,
"tooltip": "%{BKY_UGJ_DRAW_GRIDEYEDATA_TOOLTIP}",
"helpUrl": "",
"style": "multimedia_blocks"
}]);
Blockly.JavaScript['ugj_draw_grideyedata'] = function (block) {
var value_amg8833data = Blockly.JavaScript.valueToCode(block, 'amg8833data', Blockly.JavaScript.ORDER_ATOMIC);
var colour_color_high = block.getFieldValue('color_high');
var value_temp_high = Blockly.JavaScript.valueToCode(block, 'temp_high', Blockly.JavaScript.ORDER_ATOMIC);
var colour_color_low = block.getFieldValue('color_low');
var value_temp_low = Blockly.JavaScript.valueToCode(block, 'temp_low', Blockly.JavaScript.ORDER_ATOMIC);
var functionName = Blockly.JavaScript.provideFunction_(
'_mapVal',
['const ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + ' = (val, inMin, inMax, outMin, outMax) => {',
`return (val - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;`,
'}'
]
);
// 温度カラー
let hr, hg, hb, lr, lg, lb;
hr = '0x' + colour_color_high.slice(1, 3);
hg = '0x' + colour_color_high.slice(3, 5);
hb = '0x' + colour_color_high.slice(5, 7);
lr = '0x' + colour_color_low.slice(1, 3);
lg = '0x' + colour_color_low.slice(3, 5);
lb = '0x' + colour_color_low.slice(5, 7);
var code = ` const _color_range = [[${lr}, ${hr}], [${lg}, ${hg}], [${lb}, ${hb}]];
let _grideye_data = ${value_amg8833data};//読み取りブロックを入力に直接接続できるようにする
for (let raw = 0; raw < _grideye_canvas.height; raw++) {
for (let col = 0; col < _grideye_canvas.width; col++) {
for (let rgb = 0; rgb < 3; rgb++) {
let pixel = ${functionName}(_grideye_data[raw][col], ${value_temp_low}, ${value_temp_high}, _color_range[rgb][0], _color_range[rgb][1]);
_grideye_imgData.data[((raw * _grideye_canvas.width * 4) + col * 4) + rgb] = pixel;
}
_grideye_imgData.data[((raw * _grideye_canvas.width * 4) + col * 4) + 3] = 0xff;
}
}
_grideye_ctx.putImageData(_grideye_imgData, 0, 0);
`;
return code;
};
/**************************** */
/** Teachable Machine を開始** */
/**************************** */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_teachable_machine",
"message0": "%{BKY_UGJ_TEACHABLE_MACHINE_TITLE}",
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"tooltip": "%{BKY_UGJ_TEACHABLE_MACHINE_TOOLTIP}",
"helpUrl": "",
"style": "multimedia_blocks"
}]);
Blockly.JavaScript['ugj_teachable_machine'] = function (block) {
Blockly.JavaScript.provideFunction_(
'require_ts', [`const _tf = require('@tensorflow/tfjs-node');`]
);
Blockly.JavaScript.provideFunction_(
'require_mobilenet', [`const _mobilenet = require('@tensorflow-models/mobilenet');`]
);
Blockly.JavaScript.provideFunction_(
'require_knn', [`const _knnClassifier = require('@tensorflow-models/knn-classifier');`]
);
var code = ` const _net = await _mobilenet.load({ version: 1, alpha: 0.25 }); // 高速・低精度
const _classifier = _knnClassifier.create();
`;
return code;
};
/************************* */
/** GridEye で推論を行う ** */
/************************* */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_grideye_predict_class",
"message0": "%{BKY_UGJ_GRIDEYE_PREDICT_CLASS_TITLE}",
"inputsInline": true,
"output": "Number",
"tooltip": "%{BKY_UGJ_GRIDEYE_PREDICT_CLASS_TOOLTIP}",
"helpUrl": "",
"style": "multimedia_blocks"
}]);
Blockly.JavaScript['ugj_grideye_predict_class'] = function (block) {
var functionName = Blockly.JavaScript.provideFunction_( // left output にするための関数化
'_predictClass',
[
`if (_confidence === undefined) var _confidence;`,
`const ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_} = async (img, clsfr, mblnet) => {`,
`if (clsfr.getNumClasses() > 0) {`,
`const result = await clsfr.predictClass(mblnet.infer(img, 'conv_preds'));`,
`_confidence = result.confidences[result.label];`,
`return result.label;`,
`}`,
`else return 0;`,
`}`
]
);
var code = `await ${functionName}(_grideye_canvas, _classifier, _net)`;
return [code, Blockly.JavaScript.ORDER_NONE];
};
/******************************************** */
/** ラベルをつけて Example をデータセットに追加 ** */
/******************************************** */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_grideye_add_example",
"message0": "%{BKY_UGJ_GRIDEYE_ADD_EXAMPLE_TITLE}",
"args0": [
{
"type": "input_value",
"name": "class_id",
"check": "Number"
}
],
"inputsInline": true,
"previousStatement": null,
"nextStatement": null,
"tooltip": "%{BKY_UGJ_GRIDEYE_ADD_EXAMPLE_TOOLTIP}",
"helpUrl": "",
"style": "multimedia_blocks"
}]);
Blockly.JavaScript['ugj_grideye_add_example'] = function (block) {
var value_class_id = Blockly.JavaScript.valueToCode(block, 'class_id', Blockly.JavaScript.ORDER_ATOMIC);
var code = `_classifier.addExample (_net.infer(_grideye_canvas, true), ${value_class_id});`;
return code;
};
/*************************** */
/** 学習したクラスを文字列化 ** */
/*************************** */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_tensorset_stringify",
"message0": "%{BKY_UGJ_TENSORSET_STRINGIFY_TITLE}",
"output": null,
"tooltip": "%{BKY_UGJ_TENSORSET_STRINGIFY_TOOLTIP}",
"helpUrl": "",
"style": "multimedia_blocks"
}]);
Blockly.JavaScript['ugj_tensorset_stringify'] = function (block) {
var code = `JSON.stringify( Object.entries(_classifier.getClassifierDataset()).map(([label, data])=>[label, Array.from(data.dataSync()), data.shape]) )`;
return [code, Blockly.JavaScript.ORDER_NONE];
};
/***************************************** */
/** jsonをデータセットに戻して分類器にセット ** */
/***************************************** */
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_tensorset_parse",
"message0": "%{BKY_UGJ_TENSORSET_PARSE_TITLE}",
"args0": [
{
"type": "input_value",
"name": "class_data_json",
"check": "String"
}
],
"previousStatement": null,
"nextStatement": null,
"tooltip": "%{BKY_UGJ_TENSORSET_PARSE_TOOLTIP}",
"helpUrl": "",
"style": "multimedia_blocks"
}]);
Blockly.JavaScript['ugj_tensorset_parse'] = function (block) {
var value_class_data_json = Blockly.JavaScript.valueToCode(block, 'class_data_json', Blockly.JavaScript.ORDER_ATOMIC);
var code = `_classifier.setClassifierDataset( Object.fromEntries( JSON.parse(${value_class_data_json}).map(([label, data, shape])=>[label, _tf.tensor(data, shape)]) ) );`;
return code;
};
// 温度ブロック(ツールボックス上非表示)
Blockly.defineBlocksWithJsonArray([{
"type": "ugj_temp",
"message0": "%1",
"args0": [
{
"type": "field_slider",
"name": "temp",
"value": 15,
"min": 0,
"max": 40,
"precision": 1
}
],
"inputsInline": true,
"output": "Number",
"tooltip": "",
"helpUrl": "",
"style": "math_blocks"
}]);
Blockly.JavaScript['ugj_temp'] = function (block) {
var number_temp = block.getFieldValue('temp');
var code = `${number_temp}`;
return [code, Blockly.JavaScript.ORDER_NONE];
};
flyout_contents = flyout_contents.concat([
{
"kind": "label",
"text": "赤外線アレイセンサーサーマルカメラAMG8833",
"web-line": "4.0",
"web-line-width": "200"
},
{
"kind": "block",
"type": "ugj_grideye_init",
"fields": {
"addr": "0x69"
}
},
{
"kind": "block",
"type": "ugj_grideye_thermistor"
},
{
"kind": "block",
"type": "ugj_grideye_read"
},
{
"kind": "block",
"type": "ugj_grideye_stop"
},
{
"kind": "block",
"type": "ugj_grideye_canvas_create"
},
{
"kind": "block",
"type": "ugj_draw_grideyedata",
"fields": {
"color_high": "#ff0000",
"color_low": "#3333ff"
},
"inputs": {
"temp_high": {
"shadow": {
"type": "ugj_temp",
"fields": {
"temp": "28"
}
}
},
"temp_low": {
"shadow": {
"type": "ugj_temp",
"fields": {
"temp": "15"
}
}
}
}
},
{
"kind": "block",
"type": "ugj_teachable_machine"
},
{
"kind": "block",
"type": "ugj_grideye_predict_class"
},
{
"kind": "block",
"type": "ugj_grideye_add_example",
"inputs": {
"class_id": {
"shadow": {
"type": "math_number",
"fields": {
"NUM": "0"
}
}
}
}
},
{
"kind": "block",
"type": "ugj_tensorset_stringify"
},
{
"kind": "block",
"type": "ugj_tensorset_parse"
}
]);