diff --git a/blocks/sensors/amg8833/AMG8833x.js b/blocks/sensors/amg8833/AMG8833x.js new file mode 100644 index 0000000..32cc5bc --- /dev/null +++ b/blocks/sensors/amg8833/AMG8833x.js @@ -0,0 +1,55 @@ +'use strict'; + +const err_msg = 'AMG8833 is already opened. Please close old connection to use new one.'; +const pig = require('@ocogeclub/pigpio'); + +let pi = -1; +let i2c_hand = -1; +exports.init = async (i2c_bus, i2c_addr, win = null) => { + if (win !== null) { + win.addEventListener('beforeunload', async () => { + await exports.stop(); + }); + } + if (pi >= 0) { throw new Error(err_msg); return; } + pi = await pig._pigpio_start('', ''); + console.log('pi=' + pi); + if (i2c_hand >= 0) { throw new Error(err_msg); return; } + i2c_hand = await pig._i2c_open(pi, i2c_bus, i2c_addr); + console.log('i2c_hand=' + i2c_hand); + await pig._i2c_write_byte_data(pi, i2c_hand, 0x00, 0x00); //Normal mode + await pig._i2c_write_byte_data(pi, i2c_hand, 0x02, 0x00); //10FPS +} + +exports.read_thermistor = async () => { + let temp = await pig._i2c_read_word_data(pi, i2c_hand, 0x0e); + return temp * 0.0625; +} + +exports.read_temp_array = async () => { + let linedata = []; + for (let i = 0; i < 8; i++) { + let data = await pig._i2c_read_i2c_block_data(pi, i2c_hand, 0x80 + 0x10 * i, 16); + let oneline = []; + for (let j = 0; j < 8; j++) { + oneline.push(((data[2 * j + 1] & 0x07) * 256 + data[2 * j]) * 0.25); + } + linedata.push(oneline); + } + return linedata; +} + +exports.stop = async () => { + if (i2c_hand >= 0) { + await pig._i2c_close(pi, i2c_hand); + i2c_hand = -1; + } + if (pi >= 0) { + await pig._pigpio_stop(pi); + pi = -1; + } +} + +/* +* This code was ported from https://www.denshi.club/pc/raspi/5raspberry-pi-zeroiot381i2c-amg8833.html +*/ diff --git a/blocks/sensors/amg8833/index.js b/blocks/sensors/amg8833/index.js new file mode 100644 index 0000000..465e75e --- /dev/null +++ b/blocks/sensors/amg8833/index.js @@ -0,0 +1,425 @@ +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 ** */ +/******************* */ +var ugjGridEyeInitDefinition = { + "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.Blocks['ugj_grideye_init'] = { + init: function () { + this.jsonInit(ugjGridEyeInitDefinition); + } +}; +Blockly.JavaScript['ugj_grideye_init'] = function (block) { + var dropdown_addr = block.getFieldValue('addr'); + Blockly.JavaScript.provideFunction_( + 'require_gpio', [`const _pi = require('@ocogeclub/` + elutil.gpio_backend + `');`] + ); + let modpath = elutil.path.join(elutil.blocks_sensors_dir, 'amg8833', 'AMG8833x.js'); + Blockly.JavaScript.provideFunction_( + 'require_amg8833', [`const _amg8833 = require('${modpath}');`] + ); + var code = `await _amg8833.init(${elutil.i2c_bus}, ${dropdown_addr}, window);\n`; + return code;// +}; +/********************** */ +/** Grid-Eye 本体温度 ** */ +/********************** */ +var ugjGridEyeThermistorDefinition = { + "type": "ugj_grideye_thermistor", + "message0": "%{BKY_UGJ_GRIDEYE_THERMISTOR_TITLE}", + "output": "Number", + "tooltip": "%{BKY_UGJ_GRIDEYE_THERMISTOR_TOOLTIP}", + "helpUrl": "", + "style": "sensor_blocks" +}; +Blockly.Blocks['ugj_grideye_thermistor'] = { + init: function () { + this.jsonInit(ugjGridEyeThermistorDefinition); + } +}; +Blockly.JavaScript['ugj_grideye_thermistor'] = function (block) { + var code = `await _amg8833.read_thermistor()`; + return [code, Blockly.JavaScript.ORDER_NONE]; +}; + +/**************************** */ +/** Read Temperature Array ** */ +/**************************** */ +var ugjGridEyeReadDefinition = { + "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.Blocks['ugj_grideye_read'] = { + init: function () { + this.jsonInit(ugjGridEyeReadDefinition); + } +}; +Blockly.JavaScript['ugj_grideye_read'] = function (block) { + var code = 'await _amg8833.read_temp_array()'; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; +/******************* */ +/** Stop Grid-Eye ** */ +/******************* */ +var ugjGridEyeStopDefinition = { + "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.Blocks['ugj_grideye_stop'] = { + init: function () { + this.jsonInit(ugjGridEyeStopDefinition); + } +}; +Blockly.JavaScript['ugj_grideye_stop'] = function (block) { + var code = 'await _amg8833.stop();\n'; + return code; +}; + + +/***************************** */ +/** GridEye 表示キャンバス作成 ** */ +/***************************** */ +var ugjGridEyeCanvasCreateDefinition = { + "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.Blocks['ugj_grideye_canvas_create'] = { + init: function () { + this.jsonInit(ugjGridEyeCanvasCreateDefinition); + } +}; +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'); + _grideye_imgData = _grideye_ctx.createImageData(8, 8); + `; + return code; +}; +/********************************************** */ +/** Draw IR Array Data to Image Data ** */ +/********************************************** */ +var ugjDrawGrideyedataDefinition = { + "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.Blocks['ugj_draw_grideyedata'] = { + init: function () { + this.jsonInit(ugjDrawGrideyedataDefinition); + } +}; +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 を開始** */ +/**************************** */ +var ugjTeachableMachineDefinition = { + "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.Blocks['ugj_teachable_machine'] = { + init: function () { + this.jsonInit(ugjTeachableMachineDefinition); + } +}; +Blockly.JavaScript['ugj_teachable_machine'] = function (block) { + Blockly.JavaScript.provideFunction_( + 'require_ts', [`const _tf = require('@tensorflow/tfjs');`] + // 'require_ts', [`const _tf = require('@tensorflow/tfjs-node');`] + ); + Blockly.JavaScript.provideFunction_( + 'require_wasm', [`const _wasm = require('@tensorflow/tfjs-backend-wasm');`] + ); + 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 = `await _tf.setBackend('wasm'); + const _net = await _mobilenet.load({ version: 1, alpha: 0.25 }); // 高速・低精度 + const _classifier = _knnClassifier.create(); + `; + return code; +}; +/************************* */ +/** GridEye で推論を行う ** */ +/************************* */ +var ugjGridEyePredictClassDefinition = { + "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.Blocks['ugj_grideye_predict_class'] = { + init: function () { + this.jsonInit(ugjGridEyePredictClassDefinition); + } +}; +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 をデータセットに追加 ** */ +/******************************************** */ +var ugjGridEyeAddExampleDefinition = { + "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.Blocks['ugj_grideye_add_example'] = { + init: function () { + this.jsonInit(ugjGridEyeAddExampleDefinition); + } +}; +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; +}; +/*************************** */ +/** 学習したクラスを文字列化 ** */ +/*************************** */ +var ugjTensorsetStringifyDefinition = { + "type": "ugj_tensorset_stringify", + "message0": "%{BKY_UGJ_TENSORSET_STRINGIFY_TITLE}", + "output": null, + "tooltip": "%{BKY_UGJ_TENSORSET_STRINGIFY_TOOLTIP}", + "helpUrl": "", + "style": "multimedia_blocks" +}; +Blockly.Blocks['ugj_tensorset_stringify'] = { + init: function () { + this.jsonInit(ugjTensorsetStringifyDefinition); + } +}; +Blockly.JavaScript['ugj_tensorset_stringify'] = function (block) { + Blockly.JavaScript.provideFunction_( + 'require_tensorset', [`const _Tensorset = require('tensorset');`] + ); + var code = `await _Tensorset.stringify(_classifier.getClassifierDataset())`; + return [code, Blockly.JavaScript.ORDER_NONE]; +}; +/***************************************** */ +/** jsonをデータセットに戻して分類器にセット ** */ +/***************************************** */ +var ugjTensorsetParseDefinition = { + "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.Blocks['ugj_tensorset_parse'] = { + init: function () { + this.jsonInit(ugjTensorsetParseDefinition); + } +}; +Blockly.JavaScript['ugj_tensorset_parse'] = function (block) { + Blockly.JavaScript.provideFunction_( + 'require_tensorset', [`const _Tensorset = require('tensorset');`] + ); + var value_class_data_json = Blockly.JavaScript.valueToCode(block, 'class_data_json', Blockly.JavaScript.ORDER_ATOMIC); + var code = `try { + let _class_dataset = _Tensorset.parse(${value_class_data_json}); + _classifier.setClassifierDataset(_class_dataset); + } catch (error) { + alert('Could not load class dataset.'); + } + `; + return code; +}; + + + diff --git a/blocks/sensors/amg8833/index.json b/blocks/sensors/amg8833/index.json new file mode 100644 index 0000000..d8842da --- /dev/null +++ b/blocks/sensors/amg8833/index.json @@ -0,0 +1,87 @@ +[ + { + "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": "math_number", + "fields": { + "NUM": "28" + } + } + }, + "temp_low": { + "shadow": { + "type": "math_number", + "fields": { + "NUM": "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" + } +] \ No newline at end of file diff --git a/blocks/sensors/bme280/BME280x.js b/blocks/sensors/bme280/BME280x.js new file mode 100644 index 0000000..5411eca --- /dev/null +++ b/blocks/sensors/bme280/BME280x.js @@ -0,0 +1,270 @@ +'use strict'; + +this.pig = null; +this.pi = null; + +this.i2cBusNo = null; +this.i2cAddress = null; +this.i2cHand = null; + +this.I2C_ADDRESS_B = 0x76; +this.I2C_ADDRESS_A = 0x77; +this.CHIP_ID = 0x58; + +this.REGISTER_DIG_T1 = 0x88; +this.REGISTER_DIG_T2 = 0x8A; +this.REGISTER_DIG_T3 = 0x8C; + +this.REGISTER_DIG_P1 = 0x8E; +this.REGISTER_DIG_P2 = 0x90; +this.REGISTER_DIG_P3 = 0x92; +this.REGISTER_DIG_P4 = 0x94; +this.REGISTER_DIG_P5 = 0x96; +this.REGISTER_DIG_P6 = 0x98; +this.REGISTER_DIG_P7 = 0x9A; +this.REGISTER_DIG_P8 = 0x9C; +this.REGISTER_DIG_P9 = 0x9E; + +this.REGISTER_DIG_H1 = 0xA1; +this.REGISTER_DIG_H2 = 0xE1; +this.REGISTER_DIG_H3 = 0xE3; +this.REGISTER_DIG_H4 = 0xE4; +this.REGISTER_DIG_H5 = 0xE5; +this.REGISTER_DIG_H6 = 0xE7; + +this.REGISTER_CHIPID = 0xD0; +this.REGISTER_RESET = 0xE0; + +this.REGISTER_CONTROL_HUM = 0xF2; +this.REGISTER_CONTROL = 0xF4; +this.REGISTER_PRESSURE_DATA = 0xF7; +this.REGISTER_TEMP_DATA = 0xFA; +this.REGISTER_HUMIDITY_DATA = 0xFD; + +exports.init = async (options) => { + this.pig = require('@ocogeclub/pigpio'); + this.pi = await this.pig._pigpio_start('', ''); + + this.i2cBusNo = (options && options.hasOwnProperty('i2cBusNo')) ? options.i2cBusNo : 1; + this.i2cAddress = (options && options.hasOwnProperty('i2cAddress')) ? options.i2cAddress : this.BME280_DEFAULT_I2C_ADDRESS(); + this.i2cHand = await this.pig._i2c_open(this.pi, this.i2cBusNo, this.i2cAddress); + + let r; + r = await this.pig._i2c_write_byte_data(this.pi, this.i2cHand, this.REGISTER_CHIPID, 0); + if (r < 0) return r; + let chipId = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_CHIPID); + if (chipId !== this.CHIP_ID_BME280() && + chipId !== this.CHIP_ID1_BMP280() && + chipId !== this.CHIP_ID2_BMP280() && + chipId !== this.CHIP_ID3_BMP280()) { + return `Unexpected BMx280 chip ID: 0x${chipId.toString(16).toUpperCase()}`; + } + // console.log(`Found BMx280 chip ID 0x${chipId.toString(16).toUpperCase()} on bus i2c-${this.i2cBusNo}, address 0x${this.i2cAddress.toString(16).toUpperCase()}`); + await this.loadCalibration(async (err) => { + if (err) { + return err; + } + // Humidity 16x oversampling + // + let r = await this.pig._i2c_write_byte_data(this.pi, this.i2cHand, this.REGISTER_CONTROL_HUM, 0b00000101); + if (r < 0) return `Humidity 16x oversampling error: ${r}`; + // Temperture/pressure 16x oversampling, normal mode + // + r = await this.pig._i2c_write_byte_data(this.pi, this.i2cHand, this.REGISTER_CONTROL, 0b10110111); + if (r < 0) return `Temperture/pressure 16x oversampling error: ${r}`; + + return 0; + }); +} + +// reset() +// +// Perform a power-on reset procedure. You will need to call init() following a reset() +// +exports.reset = async () => { + const POWER_ON_RESET_CMD = 0xB6; + let r = await this.pig._i2c_write_byte_data(this.pi, this.i2cHand, this.REGISTER_RESET, POWER_ON_RESET_CMD); + if (r < 0) return `cannot power-on reset: ${r}`; + else return 0; +} + +// cancel() +// +// Cancels the sensor and releases resources. +// +exports.cancel = async () => { + if (this.i2cHand >= 0) { + await this.pig._i2c_close(this.pi, this.i2cHand); + this.i2cHand = null; + await this.pig._pigpio_stop(this.pi); + this.pi = null; + } +} + +exports.readSensorData = async () => { + if (!this.cal) { + return 'You must first call bme280.init()'; + } + + // Grab temperature, humidity, and pressure in a single read + // + let buffer = await this.pig._i2c_read_i2c_block_data(this.pi, this.i2cHand, this.REGISTER_PRESSURE_DATA, 8); + if (!buffer) return `couldn't grab data`; + // Temperature (temperature first since we need t_fine for pressure and humidity) + // + let adc_T = this.uint20(buffer[3], buffer[4], buffer[5]); + let tvar1 = ((((adc_T >> 3) - (this.cal.dig_T1 << 1))) * this.cal.dig_T2) >> 11; + let tvar2 = (((((adc_T >> 4) - this.cal.dig_T1) * ((adc_T >> 4) - this.cal.dig_T1)) >> 12) * this.cal.dig_T3) >> 14; + let t_fine = tvar1 + tvar2; + + let temperature_C = ((t_fine * 5 + 128) >> 8) / 100; + + // Pressure + // + let adc_P = this.uint20(buffer[0], buffer[1], buffer[2]); + let pvar1 = t_fine / 2 - 64000; + let pvar2 = pvar1 * pvar1 * this.cal.dig_P6 / 32768; + pvar2 = pvar2 + pvar1 * this.cal.dig_P5 * 2; + pvar2 = pvar2 / 4 + this.cal.dig_P4 * 65536; + pvar1 = (this.cal.dig_P3 * pvar1 * pvar1 / 524288 + this.cal.dig_P2 * pvar1) / 524288; + pvar1 = (1 + pvar1 / 32768) * this.cal.dig_P1; + + let pressure_hPa = 0; + + if (pvar1 !== 0) { + let p = 1048576 - adc_P; + p = ((p - pvar2 / 4096) * 6250) / pvar1; + pvar1 = this.cal.dig_P9 * p * p / 2147483648; + pvar2 = p * this.cal.dig_P8 / 32768; + p = p + (pvar1 + pvar2 + this.cal.dig_P7) / 16; + + pressure_hPa = p / 100; + } + + // Humidity (available on the BME280, will be zero on the BMP280 since it has no humidity sensor) + // + let adc_H = this.uint16(buffer[6], buffer[7]); + + let h = t_fine - 76800; + h = (adc_H - (this.cal.dig_H4 * 64 + this.cal.dig_H5 / 16384 * h)) * + (this.cal.dig_H2 / 65536 * (1 + this.cal.dig_H6 / 67108864 * h * (1 + this.cal.dig_H3 / 67108864 * h))); + h = h * (1 - this.cal.dig_H1 * h / 524288); + + let humidity = (h > 100) ? 100 : (h < 0 ? 0 : h); + + return { + temperature_C: temperature_C, + humidity: humidity, + pressure_hPa: pressure_hPa + }; +} + +exports.loadCalibration = async (callback) => { + let buffer = await this.pig._i2c_read_i2c_block_data(this.pi, this.i2cHand, this.REGISTER_DIG_T1, 24); + if (buffer) { + let h1 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H1); + let h2 = await this.pig._i2c_read_word_data(this.pi, this.i2cHand, this.REGISTER_DIG_H2); + let h3 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H3); + let h4 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H4); + let h5 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H5); + let h5_1 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H5 + 1); + let h6 = await this.pig._i2c_read_byte_data(this.pi, this.i2cHand, this.REGISTER_DIG_H6); + + this.cal = { + dig_T1: this.uint16(buffer[1], buffer[0]), + dig_T2: this.int16(buffer[3], buffer[2]), + dig_T3: this.int16(buffer[5], buffer[4]), + + dig_P1: this.uint16(buffer[7], buffer[6]), + dig_P2: this.int16(buffer[9], buffer[8]), + dig_P3: this.int16(buffer[11], buffer[10]), + dig_P4: this.int16(buffer[13], buffer[12]), + dig_P5: this.int16(buffer[15], buffer[14]), + dig_P6: this.int16(buffer[17], buffer[16]), + dig_P7: this.int16(buffer[19], buffer[18]), + dig_P8: this.int16(buffer[21], buffer[20]), + dig_P9: this.int16(buffer[23], buffer[22]), + + dig_H1: h1, + dig_H2: h2, + dig_H3: h3, + dig_H4: (h4 << 4) | (h5 & 0xF), + dig_H5: (h5_1 << 4) | (h5 >> 4), + dig_H6: h6 + }; + + // console.log('BME280 cal = ' + JSON.stringify(this.cal, null, 2)); + await callback(); + } +} + +exports.BME280_DEFAULT_I2C_ADDRESS = () => { + return 0x77; +} + +exports.CHIP_ID1_BMP280 = () => { + return 0x56; +} + +exports.CHIP_ID2_BMP280 = () => { + return 0x57; +} + +exports.CHIP_ID3_BMP280 = () => { + return 0x58; +} + +exports.CHIP_ID_BME280 = () => { + return 0x60; +} + +exports.int16 = (msb, lsb) => { + let val = this.uint16(msb, lsb); + return val > 32767 ? (val - 65536) : val; +} + +exports.uint16 = (msb, lsb) => { + return msb << 8 | lsb; +} + +exports.uint20 = (msb, lsb, xlsb) => { + return ((msb << 8 | lsb) << 8 | xlsb) >> 4; +} + +exports.convertCelciusToFahrenheit = (c) => { + return c * 9 / 5 + 32; +} + +exports.convertHectopascalToInchesOfMercury = (hPa) => { + return hPa * 0.02952998751; +} + +exports.convertMetersToFeet = (m) => { + return m * 3.28084; +} + +exports.calculateHeatIndexCelcius = (temperature_C, humidity) => { + return -8.784695 + 1.61139411 * temperature_C + 2.33854900 * humidity + + -0.14611605 * temperature_C * humidity + -0.01230809 * Math.pow(temperature_C, 2) + + -0.01642482 * Math.pow(humidity, 2) + 0.00221173 * Math.pow(temperature_C, 2) * humidity + + 0.00072546 * temperature_C * Math.pow(humidity, 2) + + -0.00000358 * Math.pow(temperature_C, 2) * Math.pow(humidity, 2); +} + +exports.calculateDewPointCelcius = (temperature_C, humidity) => { + return 243.04 * (Math.log(humidity / 100.0) + ((17.625 * temperature_C) / (243.04 + temperature_C))) / + (17.625 - Math.log(humidity / 100.0) - ((17.625 * temperature_C) / (243.04 + temperature_C))); +} + +exports.calculateAltitudeMeters = (pressure_hPa, seaLevelPressure_hPa) => { + if (!seaLevelPressure_hPa) { + seaLevelPressure_hPa = 1013.25; + } + + return (1.0 - Math.pow(pressure_hPa / seaLevelPressure_hPa, (1 / 5.2553))) * 145366.45 * 0.3048; +} + + +/* + * This code was forked from skylarstein's bme280-sensor: https://github.com/skylarstein/bme280-sensor + */ \ No newline at end of file diff --git a/blocks/sensors/bme280/index.js b/blocks/sensors/bme280/index.js new file mode 100644 index 0000000..d76f568 --- /dev/null +++ b/blocks/sensors/bme280/index.js @@ -0,0 +1,56 @@ +/********* */ +/** BME280 */ +/********* */ +Blockly.Blocks['ugj_bme280'] = { + init: function () { + this.appendDummyInput() + .appendField("BME280(アドレス") + .appendField(new Blockly.FieldDropdown([["0x76", "0x76"], ["0x77", "0x77"]]), "addr") + .appendField(")の計測値を取得"); + this.setInputsInline(true); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setStyle('sensor_blocks'); + this.setTooltip("環境センサーBME280で、気温(摂氏)、湿度(%)、気圧(hPa)を計測します。計測結果は計測値ブロックで参照します。"); + this.setHelpUrl(""); + } +}; +Blockly.JavaScript['ugj_bme280'] = function (block) { + var dropdown_addr = block.getFieldValue('addr'); + Blockly.JavaScript.provideFunction_( + 'require_gpio', [`const _pi = require('@ocogeclub/` + elutil.gpio_backend + `');`] + ); + let modpath = elutil.path.join(elutil.blocks_sensors_dir, 'bme280', 'BME280x.js'); + Blockly.JavaScript.provideFunction_( + 'require_bme280', [`const _bme280 = require('${modpath}');`] + ); + var code = `const options = { + i2cBusNo: ${elutil.i2c_bus}, + i2cAddress: ${dropdown_addr} + }; + await _bme280.init(options); + let _thp = await _bme280.readSensorData(); + let _bmedata = []; + _bmedata[0] = Math.round(_thp.temperature_C * 10) / 10; + _bmedata[1] = Math.round(_thp.humidity * 10) / 10; + _bmedata[2] = Math.round(_thp.pressure_hPa); + await _bme280.cancel(); + `; + return code; +}; +Blockly.Blocks['ugj_bme280_data'] = { + init: function () { + this.appendDummyInput() + .appendField(new Blockly.FieldDropdown([["気温", "0"], ["湿度", "1"], ["気圧", "2"]]), "thp"); + this.setInputsInline(true); + this.setOutput(true, null); + this.setStyle('sensor_blocks'); + this.setTooltip("BME280 の計測値を返します。"); + this.setHelpUrl(""); + } +}; +Blockly.JavaScript['ugj_bme280_data'] = function (block) { + var dropdown_thp = block.getFieldValue('thp'); + var code = `_bmedata[${dropdown_thp}]`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; \ No newline at end of file diff --git a/blocks/sensors/bme280/index.json b/blocks/sensors/bme280/index.json new file mode 100644 index 0000000..7a4e319 --- /dev/null +++ b/blocks/sensors/bme280/index.json @@ -0,0 +1,22 @@ +[ + { + "kind": "label", + "text": "温湿度気圧センサー BME280", + "web-line": "4.0", + "web-line-width": "200" + }, + { + "kind": "block", + "type": "ugj_bme280", + "fields": { + "addr": "0x76" + } + }, + { + "kind": "block", + "type": "ugj_bme280_data", + "fields": { + "thp": "0" + } + } +] \ No newline at end of file diff --git a/blocks/sensors/dht11/dht11.py b/blocks/sensors/dht11/dht11.py new file mode 100644 index 0000000..55ebee9 --- /dev/null +++ b/blocks/sensors/dht11/dht11.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, print_function, unicode_literals + +import time +import pigpio + + +class DHT11(object): + """ + The DHT11 class is a stripped version of the DHT22 sensor code by joan2937. + You can find the initial implementation here: + - https://github.com/srounet/pigpio/tree/master/EXAMPLES/Python/DHT22_AM2302_SENSOR + + example code: + >>> pi = pigpio.pi() + >>> sensor = DHT11(pi, 4) # 4 is the data GPIO pin connected to your sensor + >>> for response in sensor: + .... print("Temperature: {}".format(response['temperature'])) + .... print("Humidity: {}".format(response['humidity'])) + """ + + def __init__(self, pi, gpio): + """ + pi (pigpio): an instance of pigpio + gpio (int): gpio pin number + """ + self.pi = pi + self.gpio = gpio + self.high_tick = 0 + self.bit = 40 + self.temperature = 0 + self.humidity = 0 + self.either_edge_cb = None + self.setup() + + def setup(self): + """ + Clears the internal gpio pull-up/down resistor. + Kills any watchdogs. + """ + self.pi.set_pull_up_down(self.gpio, pigpio.PUD_OFF) + self.pi.set_watchdog(self.gpio, 0) + self.register_callbacks() + + def register_callbacks(self): + """ + Monitors RISING_EDGE changes using callback. + """ + self.either_edge_cb = self.pi.callback( + self.gpio, pigpio.EITHER_EDGE, self.either_edge_callback + ) + + def either_edge_callback(self, gpio, level, tick): + """ + Either Edge callbacks, called each time the gpio edge changes. + Accumulate the 40 data bits from the dht11 sensor. + """ + level_handlers = { + pigpio.FALLING_EDGE: self._edge_FALL, + pigpio.RISING_EDGE: self._edge_RISE, + pigpio.EITHER_EDGE: self._edge_EITHER, + } + handler = level_handlers[level] + diff = pigpio.tickDiff(self.high_tick, tick) + handler(tick, diff) + + def _edge_RISE(self, tick, diff): + """ + Handle Rise signal. + """ + val = 0 + if diff >= 50: + val = 1 + if diff >= 200: # Bad bit? + self.checksum = 256 # Force bad checksum + + if self.bit >= 40: # Message complete + self.bit = 40 + elif self.bit >= 32: # In checksum byte + self.checksum = (self.checksum << 1) + val + if self.bit == 39: + # 40th bit received + self.pi.set_watchdog(self.gpio, 0) + total = self.humidity + self.temperature + # is checksum ok ? + if not (total & 255) == self.checksum: + raise + elif 16 <= self.bit < 24: # in temperature byte + self.temperature = (self.temperature << 1) + val + elif 0 <= self.bit < 8: # in humidity byte + self.humidity = (self.humidity << 1) + val + else: # skip header bits + pass + self.bit += 1 + + def _edge_FALL(self, tick, diff): + """ + Handle Fall signal. + """ + self.high_tick = tick + if diff <= 250000: + return + self.bit = -2 + self.checksum = 0 + self.temperature = 0 + self.humidity = 0 + + def _edge_EITHER(self, tick, diff): + """ + Handle Either signal. + """ + self.pi.set_watchdog(self.gpio, 0) + + def read(self): + """ + Start reading over DHT11 sensor. + """ + self.pi.write(self.gpio, pigpio.LOW) + time.sleep(0.017) # 17 ms + self.pi.set_mode(self.gpio, pigpio.INPUT) + self.pi.set_watchdog(self.gpio, 200) + time.sleep(0.2) + + def close(self): + """ + Stop reading sensor, remove callbacks. + """ + self.pi.set_watchdog(self.gpio, 0) + if self.either_edge_cb: + self.either_edge_cb.cancel() + self.either_edge_cb = None + + def __iter__(self): + """ + Support the iterator protocol. + """ + return self + + def __next__(self): + """ + Call the read method and return temperature and humidity informations. + """ + self.read() + response = {"humidity": self.humidity, "temperature": self.temperature} + return response + + +if __name__ == "__main__": + import sys + + args = sys.argv + pin = int(args[1]) + pi = pigpio.pi() + sensor = DHT11(pi, pin) + for d in sensor: + # print("temperature: {}".format(d["temperature"])) + # print("humidity: {}".format(d["humidity"])) + + print( + '{"temperature":' + + str(d["temperature"]) + + ',"humidity":' + + str(d["humidity"]) + + "}", + flush=True, + ) + break + # time.sleep(1) + sensor.close() diff --git a/blocks/sensors/dht11/index.js b/blocks/sensors/dht11/index.js new file mode 100644 index 0000000..112cf23 --- /dev/null +++ b/blocks/sensors/dht11/index.js @@ -0,0 +1,41 @@ +Blockly.Blocks['ugj_dht11'] = { + init: function () { + this.appendValueInput("pin") + .setCheck("Number") + .appendField("GPIO"); + this.appendDummyInput() + .appendField("の DHT11 センサーの値を取得"); + this.setInputsInline(true); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setTooltip(""); + this.setHelpUrl("気温・湿度センサーDHT11で、気温(摂氏)、湿度(%)を計測します。計測値は計測値ブロックで参照します。"); + this.setStyle('sensor_blocks'); + } +}; +Blockly.JavaScript['ugj_dht11'] = function (block) { + var value_pin = Blockly.JavaScript.valueToCode(block, 'pin', Blockly.JavaScript.ORDER_ATOMIC); + let pypath = elutil.path.join(elutil.blocks_sensors_dir, 'dht11', 'dht11.py'); + var code = `let _th = require('child_process').spawnSync('python3', ['${pypath}', '${value_pin}']).stdout.toString(); + let _dhtdata = JSON.parse(_th);`; + // let _dht11data[0] = + // console.log('t=' + obj.temperature); + // console.log('h=' + obj.humidity); + return code; +}; +Blockly.Blocks['ugj_dht11_data'] = { + init: function () { + this.appendDummyInput() + .appendField(new Blockly.FieldDropdown([["気温", "temperature"], ["湿度", "humidity"]]), "th"); + this.setInputsInline(true); + this.setOutput(true, null); + this.setStyle('sensor_blocks'); + this.setTooltip("DHT11 の計測値を返します。"); + this.setHelpUrl(""); + } +}; +Blockly.JavaScript['ugj_dht11_data'] = function (block) { + var dropdown_th = block.getFieldValue('th'); + var code = `_dhtdata.${dropdown_th}`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; \ No newline at end of file diff --git a/blocks/sensors/dht11/index.json b/blocks/sensors/dht11/index.json new file mode 100644 index 0000000..7e4b061 --- /dev/null +++ b/blocks/sensors/dht11/index.json @@ -0,0 +1,29 @@ +[ + { + "kind": "label", + "text": "温湿度センサー DHT11", + "web-line": "4.0", + "web-line-width": "200" + }, + { + "kind": "block", + "type": "ugj_dht11", + "inputs": { + "pin": { + "shadow": { + "type": "math_number", + "fields": { + "NUM": "8" + } + } + } + } + }, + { + "kind": "block", + "type": "ugj_dht11_data", + "fields": { + "th": "temperature" + } + } +] \ No newline at end of file diff --git a/blocks/sensors/paj7620/PAJ7620x.js b/blocks/sensors/paj7620/PAJ7620x.js new file mode 100644 index 0000000..78c4cdb --- /dev/null +++ b/blocks/sensors/paj7620/PAJ7620x.js @@ -0,0 +1,471 @@ +"use strict"; + +const sleep = sec => + new Promise(r => setTimeout(r, sec * 1000)); + +const GES_REACTION_TIME = .100; // default:0.5 // You can adjust the reaction time according to the actual circumstance. +const GES_ENTRY_TIME = .050; // default:0.8 // When you want to recognize the Forward/Backward gestures, your gestures' reaction time must less than GES_ENTRY_TIME(0.8s). +const GES_QUIT_TIME = 1.000; + +const BANK0 = 0; +const BANK1 = 1; + +const PAJ7620_ADDR_BASE = 0x00; + +//REGISTER BANK SELECT +const PAJ7620_REGITER_BANK_SEL = (PAJ7620_ADDR_BASE + 0xEF); //W + +//DEVICE ID +// const PAJ7620_ID = 0x73; + +//REGISTER BANK 0 +const PAJ7620_ADDR_SUSPEND_CMD = (PAJ7620_ADDR_BASE + 0x3); //W +const PAJ7620_ADDR_GES_PS_DET_MASK_0 = (PAJ7620_ADDR_BASE + 0x41); //RW +const PAJ7620_ADDR_GES_PS_DET_MASK_1 = (PAJ7620_ADDR_BASE + 0x42); //RW +const PAJ7620_ADDR_GES_PS_DET_FLAG_0 = (PAJ7620_ADDR_BASE + 0x43); //R +const PAJ7620_ADDR_GES_PS_DET_FLAG_1 = (PAJ7620_ADDR_BASE + 0x44); //R +const PAJ7620_ADDR_STATE_INDICATOR = (PAJ7620_ADDR_BASE + 0x45); //R +const PAJ7620_ADDR_PS_HIGH_THRESHOLD = (PAJ7620_ADDR_BASE + 0x69); //RW +const PAJ7620_ADDR_PS_LOW_THRESHOLD = (PAJ7620_ADDR_BASE + 0x6A); //RW +const PAJ7620_ADDR_PS_APPROACH_STATE = (PAJ7620_ADDR_BASE + 0x6B); //R +const PAJ7620_ADDR_PS_RAW_DATA = (PAJ7620_ADDR_BASE + 0x6C); //R + +//REGISTER BANK 1 +const PAJ7620_ADDR_PS_GAIN = (PAJ7620_ADDR_BASE + 0x44); //RW +const PAJ7620_ADDR_IDLE_S1_STEP_0 = (PAJ7620_ADDR_BASE + 0x67); //RW +const PAJ7620_ADDR_IDLE_S1_STEP_1 = (PAJ7620_ADDR_BASE + 0x68); //RW +const PAJ7620_ADDR_IDLE_S2_STEP_0 = (PAJ7620_ADDR_BASE + 0x69); //RW +const PAJ7620_ADDR_IDLE_S2_STEP_1 = (PAJ7620_ADDR_BASE + 0x6A); //RW +const PAJ7620_ADDR_OP_TO_S1_STEP_0 = (PAJ7620_ADDR_BASE + 0x6B); //RW +const PAJ7620_ADDR_OP_TO_S1_STEP_1 = (PAJ7620_ADDR_BASE + 0x6C); //RW +const PAJ7620_ADDR_OP_TO_S2_STEP_0 = (PAJ7620_ADDR_BASE + 0x6D); //RW +const PAJ7620_ADDR_OP_TO_S2_STEP_1 = (PAJ7620_ADDR_BASE + 0x6E); //RW +const PAJ7620_ADDR_OPERATION_ENABLE = (PAJ7620_ADDR_BASE + 0x72); //RW + +//PAJ7620_REGITER_BANK_SEL +const PAJ7620_BANK0 = 0; +const PAJ7620_BANK1 = 1; + +//PAJ7620_ADDR_SUSPEND_CMD +const PAJ7620_I2C_WAKEUP = 1; +const PAJ7620_I2C_SUSPEND = 0; + +//PAJ7620_ADDR_OPERATION_ENABLE +const PAJ7620_ENABLE = 1; +const PAJ7620_DISABLE = 0; + +//ADC, delete +const REG_ADDR_RESULT = 0x00; +const REG_ADDR_ALERT = 0x01; +const REG_ADDR_CONFIG = 0x02; +const REG_ADDR_LIMITL = 0x03; +const REG_ADDR_LIMITH = 0x04; +const REG_ADDR_HYST = 0x05; +const REG_ADDR_CONVL = 0x06; +const REG_ADDR_CONVH = 0x07; + +const GES_RIGHT_FLAG = 1 << 0; +const GES_LEFT_FLAG = 1 << 1; +const GES_UP_FLAG = 1 << 2; +const GES_DOWN_FLAG = 1 << 3; +const GES_FORWARD_FLAG = 1 << 4; +const GES_BACKWARD_FLAG = 1 << 5; +const GES_CLOCKWISE_FLAG = 1 << 6; +const GES_COUNT_CLOCKWISE_FLAG = 1 << 7; +const GES_WAVE_FLAG = 1 << 0; + +//Gesture output +exports.FORWARD = 1; +exports.BACKWARD = 2; +exports.RIGHT = 3; +exports.LEFT = 4; +exports.UP = 5; +exports.DOWN = 6; +exports.CLOCKWISE = 7; +exports.ANTI_CLOCKWISE = 8; +exports.WAVE = 9; + +//Initial register state +const initRegisterArray = [ + [0xEF, 0x00], + [0x32, 0x29], + [0x33, 0x01], + [0x34, 0x00], + [0x35, 0x01], + [0x36, 0x00], + [0x37, 0x07], + [0x38, 0x17], + [0x39, 0x06], + [0x3A, 0x12], + [0x3F, 0x00], + [0x40, 0x02], + [0x41, 0xFF], + [0x42, 0x01], + [0x46, 0x2D], + [0x47, 0x0F], + [0x48, 0x3C], + [0x49, 0x00], + [0x4A, 0x1E], + [0x4B, 0x00], + [0x4C, 0x20], + [0x4D, 0x00], + [0x4E, 0x1A], + [0x4F, 0x14], + [0x50, 0x00], + [0x51, 0x10], + [0x52, 0x00], + [0x5C, 0x02], + [0x5D, 0x00], + [0x5E, 0x10], + [0x5F, 0x3F], + [0x60, 0x27], + [0x61, 0x28], + [0x62, 0x00], + [0x63, 0x03], + [0x64, 0xF7], + [0x65, 0x03], + [0x66, 0xD9], + [0x67, 0x03], + [0x68, 0x01], + [0x69, 0xC8], + [0x6A, 0x40], + [0x6D, 0x04], + [0x6E, 0x00], + [0x6F, 0x00], + [0x70, 0x80], + [0x71, 0x00], + [0x72, 0x00], + [0x73, 0x00], + [0x74, 0xF0], + [0x75, 0x00], + [0x80, 0x42], + [0x81, 0x44], + [0x82, 0x04], + [0x83, 0x20], + [0x84, 0x20], + [0x85, 0x00], + [0x86, 0x10], + [0x87, 0x00], + [0x88, 0x05], + [0x89, 0x18], + [0x8A, 0x10], + [0x8B, 0x01], + [0x8C, 0x37], + [0x8D, 0x00], + [0x8E, 0xF0], + [0x8F, 0x81], + [0x90, 0x06], + [0x91, 0x06], + [0x92, 0x1E], + [0x93, 0x0D], + [0x94, 0x0A], + [0x95, 0x0A], + [0x96, 0x0C], + [0x97, 0x05], + [0x98, 0x0A], + [0x99, 0x41], + [0x9A, 0x14], + [0x9B, 0x0A], + [0x9C, 0x3F], + [0x9D, 0x33], + [0x9E, 0xAE], + [0x9F, 0xF9], + [0xA0, 0x48], + [0xA1, 0x13], + [0xA2, 0x10], + [0xA3, 0x08], + [0xA4, 0x30], + [0xA5, 0x19], + [0xA6, 0x10], + [0xA7, 0x08], + [0xA8, 0x24], + [0xA9, 0x04], + [0xAA, 0x1E], + [0xAB, 0x1E], + [0xCC, 0x19], + [0xCD, 0x0B], + [0xCE, 0x13], + [0xCF, 0x64], + [0xD0, 0x21], + [0xD1, 0x0F], + [0xD2, 0x88], + [0xE0, 0x01], + [0xE1, 0x04], + [0xE2, 0x41], + [0xE3, 0xD6], + [0xE4, 0x00], + [0xE5, 0x0C], + [0xE6, 0x0A], + [0xE7, 0x00], + [0xE8, 0x00], + [0xE9, 0x00], + [0xEE, 0x07], + [0xEF, 0x01], + [0x00, 0x1E], + [0x01, 0x1E], + [0x02, 0x0F], + [0x03, 0x10], + [0x04, 0x02], + [0x05, 0x00], + [0x06, 0xB0], + [0x07, 0x04], + [0x08, 0x0D], + [0x09, 0x0E], + [0x0A, 0x9C], + [0x0B, 0x04], + [0x0C, 0x05], + [0x0D, 0x0F], + [0x0E, 0x02], + [0x0F, 0x12], + [0x10, 0x02], + [0x11, 0x02], + [0x12, 0x00], + [0x13, 0x01], + [0x14, 0x05], + [0x15, 0x07], + [0x16, 0x05], + [0x17, 0x07], + [0x18, 0x01], + [0x19, 0x04], + [0x1A, 0x05], + [0x1B, 0x0C], + [0x1C, 0x2A], + [0x1D, 0x01], + [0x1E, 0x00], + [0x21, 0x00], + [0x22, 0x00], + [0x23, 0x00], + [0x25, 0x01], + [0x26, 0x00], + [0x27, 0x39], + [0x28, 0x7F], + [0x29, 0x08], + [0x30, 0x03], + [0x31, 0x00], + [0x32, 0x1A], + [0x33, 0x1A], + [0x34, 0x07], + [0x35, 0x07], + [0x36, 0x01], + [0x37, 0xFF], + [0x38, 0x36], + [0x39, 0x07], + [0x3A, 0x00], + [0x3E, 0xFF], + [0x3F, 0x00], + [0x40, 0x77], + [0x41, 0x40], + [0x42, 0x00], + [0x43, 0x30], + [0x44, 0xA0], + [0x45, 0x5C], + [0x46, 0x00], + [0x47, 0x00], + [0x48, 0x58], + [0x4A, 0x1E], + [0x4B, 0x1E], + [0x4C, 0x00], + [0x4D, 0x00], + [0x4E, 0xA0], + [0x4F, 0x80], + [0x50, 0x00], + [0x51, 0x00], + [0x52, 0x00], + [0x53, 0x00], + [0x54, 0x00], + [0x57, 0x80], + [0x59, 0x10], + [0x5A, 0x08], + [0x5B, 0x94], + [0x5C, 0xE8], + [0x5D, 0x08], + [0x5E, 0x3D], + [0x5F, 0x99], + [0x60, 0x45], + [0x61, 0x40], + [0x63, 0x2D], + [0x64, 0x02], + [0x65, 0x96], + [0x66, 0x00], + [0x67, 0x97], + [0x68, 0x01], + [0x69, 0xCD], + [0x6A, 0x01], + [0x6B, 0xB0], + [0x6C, 0x04], + [0x6D, 0x2C], + [0x6E, 0x01], + [0x6F, 0x32], + [0x71, 0x00], + [0x72, 0x01], + [0x73, 0x35], + [0x74, 0x00], + [0x75, 0x33], + [0x76, 0x31], + [0x77, 0x01], + [0x7C, 0x84], + [0x7D, 0x03], + [0x7E, 0x01] +]; + +//Enable debug message +const debug = 1; + +const err_msg = 'AMG8833 is already opened. Please close old connection to use new one.'; +let pig = -1; +let pi = -1; +let i2c_hand = -1; +//Initialize the sensors +exports.init = async (i2c_bus, i2c_addr, win = null) => { + if (win !== null) { + win.addEventListener('beforeunload', async () => { + await exports.stop(); + }); + } + pig = require('@ocogeclub/pigpio'); + if (pi >= 0) { throw new Error(err_msg); return; } + pi = await pig._pigpio_start('', ''); + if (i2c_hand >= 0) { throw new Error(err_msg); return; } + i2c_hand = await pig._i2c_open(pi, i2c_bus, i2c_addr); + if (debug) + console.log("pi=" + pi + ", i2c_hand=" + i2c_hand); + + await sleep(.001); + await paj7620SelectBank(BANK0); + await paj7620SelectBank(BANK0); + + let data0 = (await paj7620ReadReg(0, 1))[0]; + let data1 = (await paj7620ReadReg(1, 1))[0]; + if (debug) + console.log("data0:" + data0 + ", data1:" + data1); + if (data0 != 0x20) //or data1 <> 0x76 + console.log("Error with sensor"); + //return 0xff + if (data0 == 0x20) + console.log("wake-up finish."); + + for (let i = 0; i < initRegisterArray.length; i += 1) + await paj7620WriteReg(initRegisterArray[i][0], initRegisterArray[i][1]); + + await paj7620SelectBank(BANK0); + + console.log("Paj7620 initialize register finished."); + +} + +// Write a byte to a register on the Gesture sensor +const paj7620WriteReg = async (addr, cmd) => + await pig._i2c_write_word_data(pi, i2c_hand, addr, cmd); + +//Select a register bank on the Gesture Sensor +const paj7620SelectBank = async bank => { + if (bank == BANK0) + await paj7620WriteReg(PAJ7620_REGITER_BANK_SEL, PAJ7620_BANK0); +} + +//Read a block of bytes of length "qty" starting at address "addr" from the Gesture sensor +const paj7620ReadReg = async (addr, qty) => { + return await pig._i2c_read_i2c_block_data(pi, i2c_hand, addr, qty); +} + +//Return a vlaue from the gestire sensor which can be used in a program +// 0:nothing +// 1:Forward +// 2:Backward +// 3:Right +// 4:Left +// 5:Up +// 6:Down +// 7:Clockwise +// 8:anti-clockwise +// 9:wave +exports.return_gesture = async () => { + + let data = (await paj7620ReadReg(0x43, 1))[0]; + if (data == GES_RIGHT_FLAG) { + await sleep(GES_ENTRY_TIME); + data = (await paj7620ReadReg(0x43, 1))[0]; + if (data == GES_FORWARD_FLAG) { + return 1; + } + else if (data == GES_BACKWARD_FLAG) { + return 2; + } + else + return 3; + } + + else if (data == GES_LEFT_FLAG) { + await sleep(GES_ENTRY_TIME); + data = (await paj7620ReadReg(0x43, 1))[0]; + if (data == GES_FORWARD_FLAG) { + return 1; + } + else if (data == GES_BACKWARD_FLAG) { + return 2; + } + else + return 4; + } + + else if (data == GES_UP_FLAG) { + await sleep(GES_ENTRY_TIME); + data = (await paj7620ReadReg(0x43, 1))[0]; + if (data == GES_FORWARD_FLAG) { + return 1; + } + else if (data == GES_BACKWARD_FLAG) { + return 2; + } + else + return 5; + } + + else if (data == GES_DOWN_FLAG) { + await sleep(GES_ENTRY_TIME); + data = (await paj7620ReadReg(0x43, 1))[0]; + if (data == GES_FORWARD_FLAG) { + return 1; + } + else if (data == GES_BACKWARD_FLAG) { + return 2; + } + else + return 6; + } + else if (data == GES_FORWARD_FLAG) { + return 1; + } + + else if (data == GES_BACKWARD_FLAG) { + return 2; + } + + else if (data == GES_CLOCKWISE_FLAG) + return 7; + + else if (data == GES_COUNT_CLOCKWISE_FLAG) + return 8; + + else { + let data1 = (await paj7620ReadReg(0x44, 1))[0]; + if (data1 == GES_WAVE_FLAG) + return 9; + } + return 0; +} + +exports.stop = async () => { + if (i2c_hand >= 0) { + await pig._i2c_close(pi, i2c_hand); + i2c_hand = -1; + } + if (pi >= 0) { + await pig._pigpio_stop(pi); + pi = -1; + } +} + +/* + * This code was ported from "Grove - Gesture Sensor v1.0 Python library and examples": https://github.com/DexterInd/GrovePi/tree/master/Software/Python/grove_gesture_sensor + */ \ No newline at end of file diff --git a/blocks/sensors/paj7620/index.js b/blocks/sensors/paj7620/index.js new file mode 100644 index 0000000..fc42956 --- /dev/null +++ b/blocks/sensors/paj7620/index.js @@ -0,0 +1,89 @@ +/********** */ +/** PAJ7620 */ +/********** */ +Blockly.Msg["UGJ_GESTURE_INIT_TITLE"] = "ジェスチャーセンサー(アドレス: %1 )を初期化"; +Blockly.Msg["UGJ_GESTURE_INIT_TOOLTIP"] = "PAJ7620 ジェスチャーセンサーを使用する準備をします。"; +Blockly.Msg["UGJ_GESTURE_READ_TITLE"] = "ジェスチャーの値"; +Blockly.Msg["UGJ_GESTURE_READ_TOOLTIP"] = "センサーから現在のジェスチャーの値(0〜9)を読み込みます"; +Blockly.Msg["UGJ_GESTURE_STOP_TITLE"] = "ジェスチャーセンサーから切断"; +Blockly.Msg["UGJ_GESTURE_STOP_TOOLTIP"] = "センサーとの接続を停止します。"; + +var ugjGestureInitDefinition = { + "type": "ugj_gesture_init", + "message0": "%{BKY_UGJ_GESTURE_INIT_TITLE}", + "args0": [ + { + "type": "input_value", + "name": "i2c_addr", + "check": "Number" + } + ], + "inputsInline": true, + "previousStatement": null, + "nextStatement": null, + "tooltip": "%{BKY_UGJ_GESTURE_INIT_TOOLTIP}", + "helpUrl": "", + "style": "sensor_blocks" +}; +Blockly.Blocks['ugj_gesture_init'] = { + init: function () { + this.jsonInit(ugjGestureInitDefinition); + } +}; +Blockly.JavaScript['ugj_gesture_init'] = function (block) { + var value_i2c_addr = Blockly.JavaScript.valueToCode(block, 'i2c_addr', Blockly.JavaScript.ORDER_ATOMIC); + Blockly.JavaScript.provideFunction_( + 'require_gpio', [`const _pi = require('@ocogeclub/` + elutil.gpio_backend + `');`] + ); + let modpath = elutil.path.join(elutil.blocks_sensors_dir, 'paj7620', 'PAJ7620x.js'); + Blockly.JavaScript.provideFunction_( + 'require_paj7620', [`const _paj7620 = require('${modpath}');`] + ); + var code = `await _paj7620.init(${elutil.i2c_bus}, ${value_i2c_addr}, window); + `; + return code; +}; + +/****************** */ +/** Gesture Read ** */ +/****************** */ +var ugjGestureReadDefinition = { + "type": "ugj_gesture_read", + "message0": "%{BKY_UGJ_GESTURE_READ_TITLE}", + "inputsInline": true, + "output": "Number", + "tooltip": "%{BKY_UGJ_GESTURE_READ_TOOLTIP}", + "helpUrl": "https://ocoge.club/sensors/paj7620.html", + "style": "sensor_blocks" +}; +Blockly.Blocks['ugj_gesture_read'] = { + init: function () { + this.jsonInit(ugjGestureReadDefinition); + } +}; +Blockly.JavaScript['ugj_gesture_read'] = function (block) { + var code = 'await _paj7620.return_gesture()'; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; +/****************** */ +/** Gesture Stop ** */ +/****************** */ +var ugjGestureStopDefinition = { + "type": "ugj_gesture_stop", + "message0": "%{BKY_UGJ_GESTURE_STOP_TITLE}", + "inputsInline": true, + "previousStatement": null, + "nextStatement": null, + "tooltip": "%{BKY_UGJ_GESTURE_STOP_TOOLTIP}", + "helpUrl": "", + "style": "sensor_blocks" +}; +Blockly.Blocks['ugj_gesture_stop'] = { + init: function () { + this.jsonInit(ugjGestureStopDefinition); + } +}; +Blockly.JavaScript['ugj_gesture_stop'] = function (block) { + var code = 'await _paj7620.stop();\n'; + return code; +}; diff --git a/blocks/sensors/paj7620/index.json b/blocks/sensors/paj7620/index.json new file mode 100644 index 0000000..fdf9602 --- /dev/null +++ b/blocks/sensors/paj7620/index.json @@ -0,0 +1,37 @@ +[ + { + "kind": "label", + "text": "ジェスチャーセンサー PAJ7620", + "web-line": "4.0", + "web-line-width": "200" + }, + { + "kind": "block", + "type": "ugj_gesture_init", + "inputs": { + "i2c_addr": { + "shadow": { + "type": "ugj_hextodec", + "inputs": { + "hex": { + "shadow": { + "type": "text", + "fields": { + "TEXT": "73" + } + } + } + } + } + } + } + }, + { + "kind": "block", + "type": "ugj_gesture_read" + }, + { + "kind": "block", + "type": "ugj_gesture_stop" + } +] \ No newline at end of file diff --git a/index.js b/index.js index 9231916..4467728 100644 --- a/index.js +++ b/index.js @@ -372,14 +372,55 @@ const ugj_createCode = (args) => { } // ブロックスクリプト実行 +// var is_running = false; +// var worker; +// const ugj_runCode = async () => { +// const { +// Worker, +// isMainThread, +// setEnvironmentData, +// getEnvironmentData, +// } = require('worker_threads'); +// const stop_icon = ` 停止`; +// const run_icon = ` 実行`; +// document.activeElement.blur(); //実行ボタンからフォーカスを外す:エンターキー押下が悪さをするため + +// let btnel = document.getElementById('runbtn'); +// if (is_running) { +// worker.terminate(); +// } else { +// if (isMainThread) { +// btnel.innerHTML = stop_icon; +// let code = ugj_createCode({}); +// setEnvironmentData('code', code); +// worker = new Worker(__filename); +// // worker.on('exit', (excode) => { +// // btnel.innerHTML = run_icon; +// // }); +// // } else { +// // let AsyncFunction = Object.getPrototypeOf(async function () { }).constructor; +// // let ocogeFunc = new AsyncFunction(getEnvironmentData('code')); +// // await ocogeFunc().catch(e => { console.error(e); }); +// } +// } + +// is_running = !is_running; +// if (is_running) btnel.innerHTML = stop_icon; +// else btnel.innerHTML = run_icon; + + +// } const ugj_runCode = async () => { document.activeElement.blur(); //実行ボタンからフォーカスを外す:エンターキー押下が悪さをするため + let btnel = document.getElementById('runbtn'); + btnel.disabled = true; // let code = ugj_createCode({ 'async': true }); // await eval(code).catch(e => { alert(e); }); let AsyncFunction = Object.getPrototypeOf(async function () { }).constructor let ocogeFunc = new AsyncFunction(ugj_createCode({})); await ocogeFunc().catch(e => { window.alert(e); }); console.log('Code Execution done.'); + btnel.disabled = false; } // コードをダイアログで表示・保存 @@ -413,7 +454,7 @@ const ugj_showCode = () => { // document... と ugj_... と elutil... をコメントアウト(ブラウザ関連部分の追放という意味では不完全なので注意) // あと正規表現もいい加減 if (chkbox_cli.checked && ext == 'js') - code = code.replace(/const appendDiv[^#]*\/\/#/gm, 'const blackboardWrite = text => console.log(text);').replace('window.alert', 'console.log').replace(/ugj_fukidashi(.*), \d+(\);)/gm, 'console.log$1$2').replace(/(^(?=.*document.)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*ugj_)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*elutil.)[^;]*;)/gm, '/* $1 */'); + code = code.replace(/const appendDiv[^#]*\/\/#/gm, 'const blackboardWrite = text => console.log(text);').replace('window.alert', 'console.log').replace(/_fukidashi(.*), \d+(\);)/gm, 'console.log$1$2').replace(/(^(?=.*document.)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*ugj_)[^;]*;)/gm, '/* $1 */').replace(/(^(?=.*elutil.)[^;]*;)/gm, '/* $1 */'); if (elutil.saveFile(code, ext) === false) { window.alert('保存できませんでした。'); } @@ -439,7 +480,7 @@ const ugj_showCode = () => { // フキダシ let ugj_fdTimeoutID = null; let ugj_fdRecentBox = null; -const ugj_fukidashi = (text, sec) => { +const _fukidashi = (text, sec) => { // Canvas Context const context = document.getElementById('canvas').getContext('2d'); // 吹き出しを消去する関数 @@ -628,34 +669,10 @@ window.onload = () => { // 背景canvas ugj_canvasBgImg(elutil.getMascotFilePath(), -1, -1); } -window.onbeforeunload = () => { +window.addEventListener('beforeunload', () => { + // window.onbeforeunload = () => { ugj_saveWorkspace(); elutil.savePrefsToLS(); - elutil.killAllChildren(); elutil.cleanupGPIO(); -} - -// センサーブロック -// ブロックデータ格納ディレクトリのリスト -const allDirents = elutil.fs.readdirSync(elutil.blocks_sensors_dir, { withFileTypes: true }); -const blocks_list = allDirents.filter(dirent => dirent.isDirectory()).map(({ name }) => name); -// センサーカテゴリのインスタンス -var category_sensors = workspace.getToolbox().getToolboxItemById('category_sensors'); -var flyout_contents = []; // フライアウトのjsonのリスト -for (const sensor_dir of blocks_list) { //ディレクトリ巡り - if (sensor_dir.charAt(0) == '.') continue; //隠しディレクトリをスキップ - // フライアウトのjsonを取得してパース、リストに追加 - let fname = elutil.path.join(elutil.blocks_sensors_dir, sensor_dir, 'index.json'); - let json_text = elutil.fs.readFileSync(fname); - let obj = JSON.parse(json_text); - flyout_contents = flyout_contents.concat(obj); - // ブロック定義のスクリプト要素をbody要素の最後に追加 - fname = elutil.path.join(elutil.blocks_sensors_dir, sensor_dir, 'index.js'); - let script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = fname; - document.body.appendChild(script); -} -// センサーカテゴリのフライアウトをアップデート -category_sensors.updateFlyoutContents(flyout_contents); - + // } +}); diff --git a/index_elutil.js b/index_elutil.js index 2e1bdd7..57227c4 100644 --- a/index_elutil.js +++ b/index_elutil.js @@ -10,7 +10,7 @@ const ugj_const = { library_dirname: 'lib', document_root: 'Documents', executable_path: '.ocogeclub/apps/', - blocks_sensors_dir: '.ocogeclub/sensors/', + blocks_sensors_dir: 'blocks/sensors/', localStorage_fname: 'ocoge.json', error_ja_all: 'エラーが発生しました。\n『おこげ倶楽部』までお問い合わせください。', pig: 'pigpio', @@ -35,7 +35,9 @@ class elUtil { this.doc_root = this.path.join(process.env["HOME"], ugj_const.document_root); this.doc_current = this.path.join(process.env["HOME"], ugj_const.document_root); this.executable_path = this.path.join(process.env["HOME"], ugj_const.executable_path); - this.blocks_sensors_dir = this.path.join(process.env["HOME"], ugj_const.blocks_sensors_dir); + // this.blocks_sensors_dir = this.path.join(process.env["HOME"], ugj_const.blocks_sensors_dir); + const EventEmitter = require('events'); + this.ugjEmitter = new EventEmitter(); } // static init = async () => { // return new elUtil(await elUtil.get_app_path()); @@ -45,6 +47,43 @@ class elUtil { this.app_path = await this.ipcRenderer.invoke('get_app_path'); this.mascotFilePath = this.path.join(this.app_path, ugj_const.mascot_dirname, ugj_const.mascot_defname); this.library_path = this.path.join(this.app_path, ugj_const.library_dirname); + this.blocks_sensors_dir = this.path.join(this.app_path, ugj_const.blocks_sensors_dir); + this.loadSensorblocks(); + } + + // センサーブロックのロード + loadSensorblocks() { + // ディレクトリの有無 + if (!this.fs.existsSync(this.blocks_sensors_dir)) return; + // ブロックデータ格納ディレクトリのリスト + const allDirents = this.fs.readdirSync(this.blocks_sensors_dir, { withFileTypes: true }); + const blocks_list = allDirents.filter(dirent => dirent.isDirectory()).map(({ name }) => name); + // センサーカテゴリのインスタンス + let category_sensors = workspace.getToolbox().getToolboxItemById('category_sensors'); + let flyout_contents = []; // フライアウトのjsonのリスト + for (let sensor_dir of blocks_list) { //ディレクトリ巡り + if (sensor_dir.charAt(0) == '.') continue; //隠しディレクトリをスキップ + // フライアウトのjsonを取得してパース、リストに追加 + let fname = this.path.join(this.blocks_sensors_dir, sensor_dir, 'index.json'); + let json_text = this.fs.readFileSync(fname); + let obj = JSON.parse(json_text); + flyout_contents = flyout_contents.concat(obj); + // ブロック定義のスクリプト要素をbody要素の最後に追加 + fname = this.path.join(this.blocks_sensors_dir, sensor_dir, 'index.js'); + let script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = fname; + document.body.appendChild(script); + } + let lastline = [{ + "kind": "label", + "text": " ", + "web-line": "4.0", + "web-line-width": "200" + }]; + flyout_contents = flyout_contents.concat(lastline); + // センサーカテゴリのフライアウトをアップデート + category_sensors.updateFlyoutContents(flyout_contents); } @@ -200,24 +239,10 @@ class elUtil { } } - // 子プロセス関連 - // 新しい子プロセスを作成し、配列に保存 - addChild(child) { - this.children.push(child); - } - // 全ての子プロセスを殺し、配列をクリア - killAllChildren() { - this.children.forEach(function (child) { - child.kill(); - }); - this.children = []; - } - // GPIO 関連:リロードでGPIOをロックしたままハンドルを失うのを防ぐ cleanupGPIO() { + // this.ugjEmitter.emit('device_stop');//デバイス停止イベント require('@ocogeclub/' + this.gpio_backend).close_all_handle(); - // require('@ocogeclub/paj7620').stop(); - // require('@ocogeclub/amg8833').stop(); } // 設定(保存ファイルパスと未保存フラグ)をローカルストレージに保存 @@ -318,6 +343,11 @@ class brUtil { setWsChanged() { ; } killAllChildren() { ; } cleanupGPIO() { ; } + path = { + join: function (a = '', b = '', c = '', d = '', e = '') { + return a + b + c + d + e; + } + } } // Electron 動作 / ブラウザ動作自動判別 @@ -339,11 +369,11 @@ if (!is_el) { var require = module_name => { let block; switch (module_name) { - case '@tensorflow/tfjs-node': + case '@tensorflow/tfjs': block = 'TensorFlow'; break; - case '@vladmandic/face-api': - block = 'Face-API'; + case '@tensorflow-models/blazeface': + block = '顔認識'; break; case 'axios': block = 'URLを取得'; @@ -354,15 +384,6 @@ if (!is_el) { case '@ocogeclub/pigpio': block = 'GPIO'; break; - case '@ocogeclub/bme280': - block = 'BME280'; - break; - case '@ocogeclub/amg8833': - block = '赤外線アレイセンサ'; - break; - case '@ocogeclub/paj7620': - block = 'ジェスチャーセンサー'; - break; case 'fs': block = 'ファイル'; break; diff --git a/package-lock.json b/package-lock.json index 05419b2..57dd4dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,22 +1,21 @@ { "name": "ocoge", - "version": "0.1.5", + "version": "0.1.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ocoge", - "version": "0.1.5", + "version": "0.1.6", "license": "ISC", "dependencies": { - "@ocogeclub/amg8833": "file:local_modules/@ocogeclub/amg8833", "@ocogeclub/pigpio": "file:local_modules/@ocogeclub/pigpio", "@tensorflow-models/blazeface": "^0.0.7", "@tensorflow-models/knn-classifier": "^1.2.2", "@tensorflow-models/mobilenet": "^2.1.0", "@tensorflow/tfjs": "^3.13.0", "@tensorflow/tfjs-backend-wasm": "^3.13.0", - "axios": "^0.25.0", + "axios": "^0.26.0", "electron-squirrel-startup": "^1.0.0", "nodemailer": "^6.7.2", "tensorset": "^1.2.9" @@ -27,12 +26,13 @@ "@electron-forge/maker-rpm": "^6.0.0-beta.63", "@electron-forge/maker-squirrel": "6.0.0-beta.33", "@electron-forge/maker-zip": "^6.0.0-beta.63", - "electron": "^17.0.0", + "electron": "^17.1.0", "electron-rebuild": "^3.2.7" } }, "local_modules/@ocogeclub/amg8833": { "version": "0.0.1", + "extraneous": true, "license": "MIT", "dependencies": { "@ocogeclub/pigpio": "file:../pigpio" @@ -1420,10 +1420,6 @@ "node": ">=10" } }, - "node_modules/@ocogeclub/amg8833": { - "resolved": "local_modules/@ocogeclub/amg8833", - "link": true - }, "node_modules/@ocogeclub/pigpio": { "resolved": "local_modules/@ocogeclub/pigpio", "link": true @@ -1959,11 +1955,11 @@ "dev": true }, "node_modules/axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", + "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", "dependencies": { - "follow-redirects": "^1.14.7" + "follow-redirects": "^1.14.8" } }, "node_modules/balanced-match": { @@ -2829,9 +2825,9 @@ } }, "node_modules/electron": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-17.0.0.tgz", - "integrity": "sha512-3UXcBQMwbMWdPvGHaSdPMluHrd+/bc+K143MyvE5zVZ+S1XCHt4sau7dj6svJHns5llN0YG/c6h/vRfadIp8Zg==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-17.1.0.tgz", + "integrity": "sha512-X/qdldmQ8lA15NmeraubWCTtMeTO8K9Ser0wtSCgOXVh53Sr1Ea0VQQ7Q9LuGgWRVz4qtr40cntuEdM8icdmTw==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -3708,6 +3704,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, "optional": true, "dependencies": { "iconv-lite": "^0.6.2" @@ -3717,6 +3714,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -6896,7 +6894,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true + "dev": true }, "node_modules/seedrandom": { "version": "2.4.3", @@ -9169,12 +9167,6 @@ } } }, - "@ocogeclub/amg8833": { - "version": "file:local_modules/@ocogeclub/amg8833", - "requires": { - "@ocogeclub/pigpio": "file:../pigpio" - } - }, "@ocogeclub/pigpio": { "version": "file:local_modules/@ocogeclub/pigpio", "requires": { @@ -9207,20 +9199,17 @@ "@tensorflow-models/blazeface": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/@tensorflow-models/blazeface/-/blazeface-0.0.7.tgz", - "integrity": "sha512-+hInPkvHJoubfiXlmNuF3SCucZvU6W1PMC25IV99NSAftJUpKvLokfF93iX8UkOFQCXkPFbnLKacGfGlbjgvMw==", - "requires": {} + "integrity": "sha512-+hInPkvHJoubfiXlmNuF3SCucZvU6W1PMC25IV99NSAftJUpKvLokfF93iX8UkOFQCXkPFbnLKacGfGlbjgvMw==" }, "@tensorflow-models/knn-classifier": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@tensorflow-models/knn-classifier/-/knn-classifier-1.2.2.tgz", - "integrity": "sha512-QRnkCf7ErOxSRtvJ6yCwhlLREPcBJGaXRanF46f0iY6ii3Sybjb6Ux0qnNPTrHZChD0izPa3Z4GQEgSAykiHkQ==", - "requires": {} + "integrity": "sha512-QRnkCf7ErOxSRtvJ6yCwhlLREPcBJGaXRanF46f0iY6ii3Sybjb6Ux0qnNPTrHZChD0izPa3Z4GQEgSAykiHkQ==" }, "@tensorflow-models/mobilenet": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@tensorflow-models/mobilenet/-/mobilenet-2.1.0.tgz", - "integrity": "sha512-JjqT9ijHDFA2FEpUGWg7H2lQ0GrMuE2VmiCRBYmUew6b4JKht8LXDjG5HxZh95YH6c/25sZWTpGeHbquloH+hw==", - "requires": {} + "integrity": "sha512-JjqT9ijHDFA2FEpUGWg7H2lQ0GrMuE2VmiCRBYmUew6b4JKht8LXDjG5HxZh95YH6c/25sZWTpGeHbquloH+hw==" }, "@tensorflow/tfjs": { "version": "3.13.0", @@ -9274,8 +9263,7 @@ "@tensorflow/tfjs-converter": { "version": "3.13.0", "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-3.13.0.tgz", - "integrity": "sha512-H2VpDTv9Ve0HBt7ttzz46DmnsPaiT0B+yJjVH3NebGZbgY9C8boBgJIsdyqfiqEWBS3WxF8h4rh58Hv5XXMgaQ==", - "requires": {} + "integrity": "sha512-H2VpDTv9Ve0HBt7ttzz46DmnsPaiT0B+yJjVH3NebGZbgY9C8boBgJIsdyqfiqEWBS3WxF8h4rh58Hv5XXMgaQ==" }, "@tensorflow/tfjs-core": { "version": "3.13.0", @@ -9303,8 +9291,7 @@ "@tensorflow/tfjs-layers": { "version": "3.13.0", "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-3.13.0.tgz", - "integrity": "sha512-kTWJ/+9fbNCMDA9iQjDMYHmWivsiWz8CKNSOZdeCW7tiBwF1EiREBVQXMk1JI11ngQa8f+rYSLs7rkhp3SYl5Q==", - "requires": {} + "integrity": "sha512-kTWJ/+9fbNCMDA9iQjDMYHmWivsiWz8CKNSOZdeCW7tiBwF1EiREBVQXMk1JI11ngQa8f+rYSLs7rkhp3SYl5Q==" }, "@tootallnate/once": { "version": "1.1.2", @@ -9618,11 +9605,11 @@ "dev": true }, "axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", + "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", "requires": { - "follow-redirects": "^1.14.7" + "follow-redirects": "^1.14.8" } }, "balanced-match": { @@ -10273,9 +10260,9 @@ } }, "electron": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-17.0.0.tgz", - "integrity": "sha512-3UXcBQMwbMWdPvGHaSdPMluHrd+/bc+K143MyvE5zVZ+S1XCHt4sau7dj6svJHns5llN0YG/c6h/vRfadIp8Zg==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-17.1.0.tgz", + "integrity": "sha512-X/qdldmQ8lA15NmeraubWCTtMeTO8K9Ser0wtSCgOXVh53Sr1Ea0VQQ7Q9LuGgWRVz4qtr40cntuEdM8icdmTw==", "dev": true, "requires": { "@electron/get": "^1.13.0", @@ -10948,6 +10935,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, "optional": true, "requires": { "iconv-lite": "^0.6.2" @@ -10957,6 +10945,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -13432,7 +13421,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true + "dev": true }, "seedrandom": { "version": "2.4.3", @@ -13883,8 +13872,7 @@ "@tensorflow/tfjs-converter": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-2.8.6.tgz", - "integrity": "sha512-Uv4YC66qjVC9UwBxz0IeLZ8KS2CReh63WlGRtHcSwDEYiwsa7cvp9H6lFSSPT7kiJmrK6JtHeJGIVcTuNnSt9w==", - "requires": {} + "integrity": "sha512-Uv4YC66qjVC9UwBxz0IeLZ8KS2CReh63WlGRtHcSwDEYiwsa7cvp9H6lFSSPT7kiJmrK6JtHeJGIVcTuNnSt9w==" }, "@tensorflow/tfjs-core": { "version": "2.8.6", @@ -13910,8 +13898,7 @@ "@tensorflow/tfjs-layers": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-2.8.6.tgz", - "integrity": "sha512-fdZ0i/R2dIKmy8OB5tBAsm5IbAHfJpI6AlbjxpgoU3aWj1HCdDo+pMji928MkDJhP01ISgFTgw/7PseGNaUflw==", - "requires": {} + "integrity": "sha512-fdZ0i/R2dIKmy8OB5tBAsm5IbAHfJpI6AlbjxpgoU3aWj1HCdDo+pMji928MkDJhP01ISgFTgw/7PseGNaUflw==" }, "@types/webgl2": { "version": "0.0.5", diff --git a/package.json b/package.json index e5ebcfd..1f22d11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocoge", - "version": "0.1.5", + "version": "0.1.6", "description": "ブロックベースビジュアルプログラム開発・実行環境", "main": "main.js", "scripts": { @@ -27,18 +27,17 @@ "@electron-forge/maker-rpm": "^6.0.0-beta.63", "@electron-forge/maker-squirrel": "6.0.0-beta.33", "@electron-forge/maker-zip": "^6.0.0-beta.63", - "electron": "^17.0.0", + "electron": "^17.1.0", "electron-rebuild": "^3.2.7" }, "dependencies": { - "@ocogeclub/amg8833": "file:local_modules/@ocogeclub/amg8833", "@ocogeclub/pigpio": "file:local_modules/@ocogeclub/pigpio", "@tensorflow-models/blazeface": "^0.0.7", "@tensorflow-models/knn-classifier": "^1.2.2", "@tensorflow-models/mobilenet": "^2.1.0", "@tensorflow/tfjs": "^3.13.0", "@tensorflow/tfjs-backend-wasm": "^3.13.0", - "axios": "^0.25.0", + "axios": "^0.26.0", "electron-squirrel-startup": "^1.0.0", "nodemailer": "^6.7.2", "tensorset": "^1.2.9" diff --git a/ugj_blocks.js b/ugj_blocks.js index 7594fa3..166cbf6 100644 --- a/ugj_blocks.js +++ b/ugj_blocks.js @@ -1174,7 +1174,7 @@ Blockly.JavaScript['ugj_canvas_say'] = function (block) { var value_say = Blockly.JavaScript.valueToCode(block, 'say', Blockly.JavaScript.ORDER_ATOMIC); var value_sec = Blockly.JavaScript.valueToCode(block, 'sec', Blockly.JavaScript.ORDER_ATOMIC); var code = [ - `ugj_fukidashi(String(${value_say}), ${value_sec});`, + `_fukidashi(String(${value_say}), ${value_sec});`, '' ].join('\n'); return code; @@ -1431,294 +1431,6 @@ Blockly.JavaScript['ugj_canvas_drawrect'] = function (block) { return code; }; -/***************************** */ -/** GridEye 表示キャンバス作成 ** */ -/***************************** */ -var ugjGridEyeCanvasCreateDefinition = { - "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.Blocks['ugj_grideye_canvas_create'] = { - init: function () { - this.jsonInit(ugjGridEyeCanvasCreateDefinition); - } -}; -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'); -_grideye_imgData = _grideye_ctx.createImageData(8, 8); -`; - return code; -}; -/********************************************** */ -/** Draw IR Array Data to Image Data ** */ -/********************************************** */ -var ugjDrawGrideyedataDefinition = { - "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.Blocks['ugj_draw_grideyedata'] = { - init: function () { - this.jsonInit(ugjDrawGrideyedataDefinition); - } -}; -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 を開始** */ -/**************************** */ -var ugjTeachableMachineDefinition = { - "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.Blocks['ugj_teachable_machine'] = { - init: function () { - this.jsonInit(ugjTeachableMachineDefinition); - } -}; -Blockly.JavaScript['ugj_teachable_machine'] = function (block) { - Blockly.JavaScript.provideFunction_( - 'require_ts', [`const _tf = require('@tensorflow/tfjs');`] - // 'require_ts', [`const _tf = require('@tensorflow/tfjs-node');`] - ); - Blockly.JavaScript.provideFunction_( - 'require_wasm', [`const _wasm = require('@tensorflow/tfjs-backend-wasm');`] - ); - 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 = `await _tf.setBackend('wasm'); - const _net = await _mobilenet.load({ version: 1, alpha: 0.25 }); // 高速・低精度 - const _classifier = _knnClassifier.create(); - `; - return code; -}; -/************************* */ -/** GridEye で推論を行う ** */ -/************************* */ -var ugjGridEyePredictClassDefinition = { - "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.Blocks['ugj_grideye_predict_class'] = { - init: function () { - this.jsonInit(ugjGridEyePredictClassDefinition); - } -}; -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 をデータセットに追加 ** */ -/******************************************** */ -var ugjGridEyeAddExampleDefinition = { - "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.Blocks['ugj_grideye_add_example'] = { - init: function () { - this.jsonInit(ugjGridEyeAddExampleDefinition); - } -}; -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; -}; -/*************************** */ -/** 学習したクラスを文字列化 ** */ -/*************************** */ -var ugjTensorsetStringifyDefinition = { - "type": "ugj_tensorset_stringify", - "message0": "%{BKY_UGJ_TENSORSET_STRINGIFY_TITLE}", - "output": null, - "tooltip": "%{BKY_UGJ_TENSORSET_STRINGIFY_TOOLTIP}", - "helpUrl": "", - "style": "multimedia_blocks" -}; -Blockly.Blocks['ugj_tensorset_stringify'] = { - init: function () { - this.jsonInit(ugjTensorsetStringifyDefinition); - } -}; -Blockly.JavaScript['ugj_tensorset_stringify'] = function (block) { - Blockly.JavaScript.provideFunction_( - 'require_tensorset', [`const _Tensorset = require('tensorset');`] - ); - var code = `await _Tensorset.stringify(_classifier.getClassifierDataset())`; - return [code, Blockly.JavaScript.ORDER_NONE]; -}; -/***************************************** */ -/** jsonをデータセットに戻して分類器にセット ** */ -/***************************************** */ -var ugjTensorsetParseDefinition = { - "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.Blocks['ugj_tensorset_parse'] = { - init: function () { - this.jsonInit(ugjTensorsetParseDefinition); - } -}; -Blockly.JavaScript['ugj_tensorset_parse'] = function (block) { - Blockly.JavaScript.provideFunction_( - 'require_tensorset', [`const _Tensorset = require('tensorset');`] - ); - var value_class_data_json = Blockly.JavaScript.valueToCode(block, 'class_data_json', Blockly.JavaScript.ORDER_ATOMIC); - var code = `try { - let _class_dataset = _Tensorset.parse(${value_class_data_json}); - _classifier.setClassifierDataset(_class_dataset); - } catch (error) { - alert('Could not load class dataset.'); - } -`; - return code; -}; - - /****************************** */ /** KeyUpDown Event Listener ** */ /****************************** */ @@ -2415,7 +2127,7 @@ Blockly.JavaScript['ugj_event_answer'] = function (block) { // var variable_answer = Blockly.JavaScript.nameDB_.getName(block.getFieldValue('answer'), Blockly.Variables.NAME_TYPE); var statements_do = Blockly.JavaScript.statementToCode(block, 'do'); var code = [ - `ugj_fukidashi(${value_question}, 0);`, + `_fukidashi(${value_question}, 0);`, `_inputForm = document.getElementById('inputForm');`, `_inputBox = document.getElementById('inputBox');`, "_inputForm.style.display = 'inline-block'", @@ -2493,18 +2205,16 @@ Blockly.Blocks['ugj_spawn'] = { Blockly.JavaScript['ugj_spawn'] = function (block) { var value_childprocess = Blockly.JavaScript.valueToCode(block, 'childprocess', Blockly.JavaScript.ORDER_NONE); var value_data = Blockly.JavaScript.valueToCode(block, 'data', Blockly.JavaScript.ORDER_ATOMIC); - // var variable_data = Blockly.JavaScript.nameDB_.getName(block.getFieldValue('data'), Blockly.Variables.NAME_TYPE); var statements_do = Blockly.JavaScript.statementToCode(block, 'do'); + var code = [ `let _child = require('child_process').spawn(${value_childprocess});`, - `elutil.addChild(_child);`, "_child.stderr.on('data', _data => { console.error(_data.toString()) })", "_child.stdout.on('data', async _data => {", `${value_data} = _data.toString();`, statements_do, "})", - "_child.on('close', (_code, _signal) => { if (_code !== 0) { console.error(`process exited with code ${_code}, signal ${_signal}`)}", - "})", + `window.addEventListener( 'beforeunload', function() { _child.kill() }, false );`, '' ].join("\n"); return code;