From 7a2997c35ad7eedd81569cfffaeb685568689876 Mon Sep 17 00:00:00 2001 From: ocogeclub Date: Tue, 11 Apr 2023 22:25:50 +0900 Subject: [PATCH] =?UTF-8?q?Orange=20Pi=205=20+=20rgpio=20=E3=81=A7=20GPIO?= =?UTF-8?q?=20=E3=82=84=20I2C=20=E3=81=AE=E4=B8=80=E9=83=A8=E5=8B=95?= =?UTF-8?q?=E4=BD=9C=E3=82=92=E7=A2=BA=E8=AA=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .npmrc | 1 + apptool.js | 35 +- blocks/blocks.js | 6 +- .../{AMG8833x.js => AMG8833x_pigpio.js} | 0 blocks/sensors/amg8833/AMG8833x_rgpio.js | 55 +++ blocks/sensors/amg8833/index.js | 8 +- blocks/sensors/bme280/index.js | 2 +- blocks/sensors/paj7620/index.js | 2 +- local_modules/rgpio/binding.gyp | 12 + local_modules/rgpio/index.js | 70 ++++ local_modules/rgpio/package.json | 11 + local_modules/rgpio/rgpio.cpp | 337 ++++++++++++++++++ main.js | 94 +++-- package-lock.json | 58 ++- package.json | 7 +- 15 files changed, 635 insertions(+), 63 deletions(-) create mode 100644 .npmrc rename blocks/sensors/amg8833/{AMG8833x.js => AMG8833x_pigpio.js} (100%) create mode 100644 blocks/sensors/amg8833/AMG8833x_rgpio.js create mode 100644 local_modules/rgpio/binding.gyp create mode 100644 local_modules/rgpio/index.js create mode 100644 local_modules/rgpio/package.json create mode 100644 local_modules/rgpio/rgpio.cpp diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..e9ee3cb --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true \ No newline at end of file diff --git a/apptool.js b/apptool.js index a8cb114..0b7220b 100644 --- a/apptool.js +++ b/apptool.js @@ -14,7 +14,7 @@ const ugj_const = { localStorage_fname: 'ocoge.json', error_ja_all: 'エラーが発生しました。\n『おこげ倶楽部』までお問い合わせください。', pig: 'pigpio', - lg: 'lgpio', // 対応未定 + rg: 'rgpio', i2c_defbus: '1', // 文字列リテラルで指定 lang: 'js', dev_hash: '4e9205f9b7e571bec1aa52ab7871f420684fcf96149672a4d550a95863d6b072' @@ -31,7 +31,7 @@ class appTool { this.saveFilepath = null; this.wsChanged = false; this.children = []; - this.gpio_backend = ugj_const.pig; + this.gpio_lib = ugj_const.pig; this.i2c_bus = ugj_const.i2c_defbus; this.lang = ugj_const.lang; this.doc_root = this.path.join(process.env["HOME"], ugj_const.document_root); @@ -207,7 +207,7 @@ class appTool { // GPIO 関連:リロードでGPIOをロックしたままハンドルを失うのを防ぐ cleanupGPIO() { // this.ugjEmitter.emit('device_stop');//デバイス停止イベント - require('@ocoge.club/' + this.gpio_backend).close_all_handle(); + require('@ocoge.club/' + this.gpio_lib).close_all_handle(); } // 設定(保存ファイルパスと未保存フラグ)をローカルストレージに保存 @@ -221,7 +221,8 @@ class appTool { 'mascotFilePath': this.mascotFilePath, 'doc_current': this.doc_current, 'i2c_bus': this.i2c_bus, - 'lang': this.lang + 'lang': this.lang, + 'gpio_lib': this.gpio_lib }; let s = JSON.stringify(o); localStorage.setItem(ugj_const.localStorage_fname, s); @@ -242,8 +243,15 @@ class appTool { this.ipcRenderer.send('i2c_check_menu', 'i2c-' + this.i2c_bus); } if (o.lang) { - this.setLang(o.lang); - this.ipcRenderer.send('lang_check_menu', this.lang); + let l; + if (o.lang == 'js' || o.gpio_lib) { + l = o.lang + '-' + o.gpio_lib; + } else { + l = o.lang; + } + this.setLang(l); + console.log(`l= ${l}`); + this.ipcRenderer.send('lang_check_menu', l); } } } @@ -261,11 +269,18 @@ class appTool { this.i2c_bus = n; } - // 言語変更 + // 言語/GPIOライブラリ変更 setLang(l) { - this.lang = l; + if (l == 'js-pigpio') { + this.lang = 'js'; + this.gpio_lib = ugj_const.pig; + } else if (l == 'js-rgpio') { + this.lang = 'js'; + this.gpio_lib = ugj_const.rg; + } else this.lang = 'py'; + const exp = document.getElementById('dlgExport'); - if (l == 'js') { + if (this.lang == 'js') { exp.innerText = 'ファイルへ保存'; exp.title = 'ソースコードをファイルに保存します。'; } else { @@ -300,7 +315,7 @@ class appTool { class webTool { constructor() { // GPIOブロックは使えません - this.gpio_backend = ugj_const.pig; + this.gpio_lib = ugj_const.pig; this.lang = 'js'; this.blocks_dir = ugj_const.blocks_dir; } diff --git a/blocks/blocks.js b/blocks/blocks.js index e2ee9b3..ae00eec 100644 --- a/blocks/blocks.js +++ b/blocks/blocks.js @@ -185,7 +185,7 @@ Blockly.Blocks['ugj_gpio_open'] = { }; Blockly.JavaScript['ugj_gpio_open'] = function (block) { Blockly.JavaScript.provideFunction_( - 'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_backend + `');`] + 'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_lib + `');`] ); var code = `await _pi.gpio_open();\n`; // return code; @@ -540,7 +540,7 @@ Blockly.JavaScript['ugj_serial_open'] = function (block) { // var value_tty = Blockly.JavaScript.valueToCode(block, 'tty', Blockly.JavaScript.ORDER_ATOMIC); var dropdown_baud = block.getFieldValue('baud'); Blockly.JavaScript.provideFunction_( - 'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_backend + `');`] + 'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_lib + `');`] ); var code = `await _pi.serial_open('/dev/serial0', ${dropdown_baud});\n`; return code; @@ -680,7 +680,7 @@ Blockly.Blocks['ugj_i2c_open'] = { Blockly.JavaScript['ugj_i2c_open'] = function (block) { var value_i2c_address = Blockly.JavaScript.valueToCode(block, 'i2c_address', Blockly.JavaScript.ORDER_ATOMIC); Blockly.JavaScript.provideFunction_( - 'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_backend + `');`] + 'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_lib + `');`] ); var code = `await _pi.i2c_open(${apptool.i2c_bus}, ${value_i2c_address});\n`; return code; diff --git a/blocks/sensors/amg8833/AMG8833x.js b/blocks/sensors/amg8833/AMG8833x_pigpio.js similarity index 100% rename from blocks/sensors/amg8833/AMG8833x.js rename to blocks/sensors/amg8833/AMG8833x_pigpio.js diff --git a/blocks/sensors/amg8833/AMG8833x_rgpio.js b/blocks/sensors/amg8833/AMG8833x_rgpio.js new file mode 100644 index 0000000..4f554c3 --- /dev/null +++ b/blocks/sensors/amg8833/AMG8833x_rgpio.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('@ocoge.club/rgpio'); + +let pi = -1; +let i2c_hand = -1; +exports.init = async (i2c_bus, i2c_addr, wael = null) => { + if (wael !== null) { + wael('beforeunload', async () => { + await exports.stop(); + }); + } + if (pi >= 0) { throw new Error(err_msg); return; } + pi = await pig._rgpiod_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, 0); + 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._rgpiod_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 index 14d4541..6ee5644 100644 --- a/blocks/sensors/amg8833/index.js +++ b/blocks/sensors/amg8833/index.js @@ -53,10 +53,10 @@ Blockly.defineBlocksWithJsonArray([{ }]); 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_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_lib + `');`] + // ); + let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'amg8833', `AMG8833x_${apptool.gpio_lib}.js`); Blockly.JavaScript.provideFunction_( 'require_amg8833', [`const _amg8833 = require('${modpath}');`] ); diff --git a/blocks/sensors/bme280/index.js b/blocks/sensors/bme280/index.js index b892356..469707a 100644 --- a/blocks/sensors/bme280/index.js +++ b/blocks/sensors/bme280/index.js @@ -18,7 +18,7 @@ Blockly.Blocks['ugj_bme280'] = { Blockly.JavaScript['ugj_bme280'] = function (block) { var dropdown_addr = block.getFieldValue('addr'); Blockly.JavaScript.provideFunction_( - 'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_backend + `');`] + 'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_lib + `');`] ); let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'bme280', 'BME280x.js'); Blockly.JavaScript.provideFunction_( diff --git a/blocks/sensors/paj7620/index.js b/blocks/sensors/paj7620/index.js index cd25cc6..1118175 100644 --- a/blocks/sensors/paj7620/index.js +++ b/blocks/sensors/paj7620/index.js @@ -33,7 +33,7 @@ Blockly.Blocks['ugj_gesture_init'] = { 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('@ocoge.club/` + apptool.gpio_backend + `');`] + 'require_gpio', [`const _pi = require('@ocoge.club/` + apptool.gpio_lib + `');`] ); let modpath = apptool.path.join(apptool.blocks_dir, 'sensors', 'paj7620', 'PAJ7620x.js'); Blockly.JavaScript.provideFunction_( diff --git a/local_modules/rgpio/binding.gyp b/local_modules/rgpio/binding.gyp new file mode 100644 index 0000000..38b2cbd --- /dev/null +++ b/local_modules/rgpio/binding.gyp @@ -0,0 +1,12 @@ +{ + "targets": [ + { + "target_name": "rgpio", + "sources": ["rgpio.cpp"], + "defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"], + "include_dirs": [" { + sbc = await module.exports._rgpiod_start('', port); + if (sbc < 0) return sbc; + for (let i = 0; i < CHIP_COUNT; i++) { + gpiochip_hand.push(await module.exports._gpiochip_open(sbc, i)); + } + return gpiochip_hand; +} +module.exports.gpio_close = async () => { + for (let i = 0; i < CHIP_COUNT; i++) { + await module.exports._gpiochip_close(sbc, gpiochip_hand[i]); + } + gpiochip_hand = []; + await module.exports._rgpiod_stop(sbc); + sbc = -1; +} +module.exports.gpio_set_output = async gpio => { + let chip = Math.floor(gpio / 32); + let pin = Math.floor(gpio % 32); + if (gpiochip_hand[chip] >= 0) return await module.exports._gpio_claim_output(sbc, gpiochip_hand[chip], pin); +} +module.exports.gpio_write = async (gpio, value) => { + let chip = Math.floor(gpio / 32); + let pin = Math.floor(gpio % 32); + if (gpiochip_hand[chip] >= 0) return await module.exports._gpio_write(sbc, gpiochip_hand[chip], pin, value); +} +module.exports.i2c_open = async (i2c_bus, i2c_address) => { + if (i2c_hand >= 0) await module.exports._i2c_close(i2c_hand); // 勝手に閉じる + i2c_hand = await module.exports._i2c_open(sbc, i2c_bus, i2c_address, 0); + return i2c_hand; +} +module.exports.i2c_close = async () => { + if (i2c_hand >= 0) await module.exports._i2c_close(sbc, i2c_hand); + i2c_hand = -1; +} +module.exports.i2c_write_byte_data = async (reg, byte_val) => { + if (i2c_hand >= 0) return await module.exports._i2c_write_byte_data(sbc, i2c_hand, reg, byte_val); +} +module.exports.i2c_write_i2c_block_data = async (reg, data) => { + if (i2c_hand >= 0) return await module.exports._i2c_write_i2c_block_data(sbc, i2c_hand, reg, Buffer.from(data)); +} +module.exports.i2c_read_word_data = async reg => { + if (i2c_hand >= 0) return await module.exports._i2c_read_word_data(sbc, i2c_hand, reg); +} + +// 終了処理 +module.exports.close_all_handle = async () => { + await module.exports.gpio_close(); + // await module.exports.serial_close(); + await module.exports.i2c_close(); +} \ No newline at end of file diff --git a/local_modules/rgpio/package.json b/local_modules/rgpio/package.json new file mode 100644 index 0000000..a1b4947 --- /dev/null +++ b/local_modules/rgpio/package.json @@ -0,0 +1,11 @@ +{ + "name": "@ocoge.club/rgpio", + "version": "0.0.1", + "main": "index.js", + "private": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^1.7.1" + } +} \ No newline at end of file diff --git a/local_modules/rgpio/rgpio.cpp b/local_modules/rgpio/rgpio.cpp new file mode 100644 index 0000000..a082176 --- /dev/null +++ b/local_modules/rgpio/rgpio.cpp @@ -0,0 +1,337 @@ +/** lgpio を Node.js から利用するモジュール ** */ +/** 関数名・書式は lgpio Python に準拠 ******************* */ + +#include +#include +#include +#include + +using namespace Napi; + +// rgpio デーモンに接続 +Promise _rgpiodStart(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 2) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + if (!info[0].IsString() || !info[1].IsString()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + std::string ipaddr = info[0].As().Utf8Value(); + std::string port = info[1].As().Utf8Value(); + deferred.Resolve(Number::New(env, rgpiod_start(ipaddr.c_str(), port.c_str()))); + } + return deferred.Promise(); +} +// rgpioデーモンとの接続を閉じる +Promise _rgpiodStop(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 1) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + if (!info[0].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + rgpiod_stop(sbc); + deferred.Resolve(env.Null()); + } + return deferred.Promise(); +} + +// gpiochipデバイスを開く +Promise _gpiochipOpen(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 2) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + if (!info[0].IsNumber() || !info[1].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + int gpioDev = info[1].As().Int32Value(); + deferred.Resolve(Number::New(env, gpiochip_open(sbc, gpioDev))); + } + return deferred.Promise(); +} + +// gpiochipデバイスを閉じる +Promise _gpiochipClose(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 2) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + if (!info[0].IsNumber() || !info[1].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + int handle = info[1].As().Int32Value(); + deferred.Resolve(Number::New(env, gpiochip_close(sbc, handle))); + } + return deferred.Promise(); +} + +// GPIO のモードを出力にする(ことを要求?) +Promise _gpioClaimOutput(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 3) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + if (!info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + int handle = info[1].As().Int32Value(); + int gpio = info[2].As().Int32Value(); + + deferred.Resolve(Number::New(env, gpio_claim_output(sbc, handle, 0, gpio, 0))); + } + return deferred.Promise(); +} + +// GPIO のモードを入力にする(ことを要求?) +// GPIOの電圧を読む + +// GPIO の電圧をセットする +Promise _gpioWrite(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 4) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + if (!info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsNumber() || !info[3].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + int handle = info[1].As().Int32Value(); + int gpio = info[2].As().Int32Value(); + int value = info[3].As().Int32Value(); + + deferred.Resolve(Number::New(env, gpio_write(sbc, handle, gpio, value))); + } + return deferred.Promise(); +} + +// I2Cバスアドレスのデバイスのハンドルを返す +Promise _i2cOpen(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 4) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + else if (!info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsNumber() || !info[3].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + unsigned int i2c_bus = info[1].As().Uint32Value(); + unsigned int i2c_addr = info[2].As().Uint32Value(); + int flags = 0; + deferred.Resolve(Number::New(env, i2c_open(sbc, i2c_bus, i2c_addr, flags))); + } + return deferred.Promise(); +} +// オープン済みI2Cハンドルを閉じる +Promise _i2cClose(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 2) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + else if (!info[0].IsNumber() || !info[1].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + unsigned int handle = info[1].As().Uint32Value(); + deferred.Resolve(Number::New(env, i2c_close(sbc, handle))); + } + return deferred.Promise(); +} + +// I2Cハンドルに関連付けられているデバイスの指定されたレジスタに1バイトを書き込む +Promise _i2cWriteByteData(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 4) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + else if (!info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsNumber() || !info[3].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + unsigned int handle = info[1].As().Uint32Value(); + unsigned int i2c_reg = info[2].As().Uint32Value(); + unsigned int bVal = info[3].As().Uint32Value(); + deferred.Resolve(Number::New(env, i2c_write_byte_data(sbc, handle, i2c_reg, bVal))); + } + return deferred.Promise(); +} + +// I2Cハンドルに関連付けられているデバイスの指定されたレジスタからcountバイトを読み込む。countは1~32。 +Promise _i2cReadI2cBlockData(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 4) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + else if (!info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsNumber() || !info[3].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + unsigned int handle = info[1].As().Uint32Value(); + unsigned int i2cReg = info[2].As().Uint32Value(); + unsigned int count = info[3].As().Uint32Value(); + char buf[count]; + int rxCount = i2c_read_i2c_block_data(sbc, handle, i2cReg, buf, count); + auto outBuf = Buffer::Copy(env, buf, rxCount); + deferred.Resolve(outBuf); + } + return deferred.Promise(); +} + +// I2Cハンドルに関連付けられているデバイスの指定されたレジスタに最大32バイトのデータを書き込む。 +Promise _i2cWriteI2cBlockData(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 4) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + else if (!info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsNumber() || !info[3].IsBuffer()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + unsigned int handle = info[1].As().Uint32Value(); + unsigned int i2c_reg = info[2].As().Uint32Value(); + auto buf = info[3].As>(); + + unsigned int count = buf.Length(); + deferred.Resolve(Number::New(env, i2c_write_i2c_block_data(sbc, handle, i2c_reg, buf.Data(), count))); + } + return deferred.Promise(); +} + +// I2Cハンドルに関連付けられているデバイスの指定されたレジスタから単一の16ビットワードを読み取る +Promise _i2cReadWordData(const CallbackInfo &info) +{ + Env env = info.Env(); + auto deferred = Napi::Promise::Deferred::New(env); + if (info.Length() != 3) + { + deferred.Reject( + TypeError::New(env, "Invalid argument count").Value()); + } + else if (!info[0].IsNumber() || !info[1].IsNumber() || !info[2].IsNumber()) + { + deferred.Reject( + Napi::TypeError::New(env, "Invalid argument types").Value()); + } + else + { + int sbc = info[0].As().Int32Value(); + unsigned int handle = info[1].As().Uint32Value(); + unsigned int i2c_reg = info[2].As().Uint32Value(); + deferred.Resolve(Number::New(env, i2c_read_word_data(sbc, handle, i2c_reg))); + } + return deferred.Promise(); +} + +Object +Init(Env env, Object exports) +{ + exports.Set(String::New(env, "_rgpiod_start"), Function::New(env, _rgpiodStart)); + exports.Set(String::New(env, "_rgpiod_stop"), Function::New(env, _rgpiodStop)); + exports.Set(String::New(env, "_gpiochip_open"), Function::New(env, _gpiochipOpen)); + exports.Set(String::New(env, "_gpiochip_close"), Function::New(env, _gpiochipClose)); + exports.Set(String::New(env, "_gpio_claim_output"), Function::New(env, _gpioClaimOutput)); + exports.Set(String::New(env, "_gpio_write"), Function::New(env, _gpioWrite)); + exports.Set(String::New(env, "_i2c_open"), Function::New(env, _i2cOpen)); + exports.Set(String::New(env, "_i2c_close"), Function::New(env, _i2cClose)); + exports.Set(String::New(env, "_i2c_write_byte_data"), Function::New(env, _i2cWriteByteData)); + exports.Set(String::New(env, "_i2c_read_i2c_block_data"), Function::New(env, _i2cReadI2cBlockData)); + exports.Set(String::New(env, "_i2c_write_i2c_block_data"), Function::New(env, _i2cWriteI2cBlockData)); + exports.Set(String::New(env, "_i2c_read_word_data"), Function::New(env, _i2cReadWordData)); + return exports; +} + +NODE_API_MODULE(rgpio, Init) \ No newline at end of file diff --git a/main.js b/main.js index b069805..7e9a3d9 100644 --- a/main.js +++ b/main.js @@ -53,14 +53,14 @@ function createWindow() { } const toggleCheck_i2c = checked_id => { - const menus = ['i2c-1', 'i2c-6']; + const menus = ['i2c-1', 'i2c-5', 'i2c-6']; menus.forEach(id => { menu.getMenuItemById(id).checked = (id == checked_id); }) } const toggleCheck_lang = checked_id => { - const menus = ['js', 'py']; + const menus = ['js-pigpio', 'js-rgpio', 'py']; menus.forEach(id => { menu.getMenuItemById(id).checked = (id == checked_id); }) @@ -155,48 +155,32 @@ let template = [ { label: "Settings", submenu: [ - { - label: "i2c bus", - submenu: [ - { - label: "1", - id: "i2c-1", - type: 'checkbox', - checked: true, - click: (item, focusedWindow) => { - if (focusedWindow) { - toggleCheck_i2c("i2c-1"); - focusedWindow.webContents.executeJavaScript('apptool.setI2cbusNo("1")'); - } - } - }, - { - label: "6", - id: "i2c-6", - type: 'checkbox', - click: (item, focusedWindow) => { - if (focusedWindow) { - toggleCheck_i2c("i2c-6"); - focusedWindow.webContents.executeJavaScript('apptool.setI2cbusNo("6")'); - } - } - }, - - ] - }, { label: "Language", submenu: [ { - label: "JavaScript", - id: "js", + label: "JavaScript (pigpio)", + id: "js-pigpio", type: 'checkbox', checked: true, click: (item, focusedWindow) => { if (focusedWindow) { // menu.getMenuItemById('py').checked = false; - toggleCheck_lang("js"); - focusedWindow.webContents.executeJavaScript('apptool.setLang("js")'); + toggleCheck_lang("js-pigpio"); + focusedWindow.webContents.executeJavaScript('apptool.setLang("js-pigpio")'); + } + } + }, + { + label: "JavaScript (rgpio)", + id: "js-rgpio", + type: 'checkbox', + checked: true, + click: (item, focusedWindow) => { + if (focusedWindow) { + // menu.getMenuItemById('py').checked = false; + toggleCheck_lang("js-rgpio"); + focusedWindow.webContents.executeJavaScript('apptool.setLang("js-rgpio")'); } } }, @@ -215,6 +199,46 @@ let template = [ ] + }, + { + label: "i2c bus", + submenu: [ + { + label: "1", + id: "i2c-1", + type: 'checkbox', + checked: true, + click: (item, focusedWindow) => { + if (focusedWindow) { + toggleCheck_i2c("i2c-1"); + focusedWindow.webContents.executeJavaScript('apptool.setI2cbusNo("1")'); + } + } + }, + { + label: "5", + id: "i2c-5", + type: 'checkbox', + click: (item, focusedWindow) => { + if (focusedWindow) { + toggleCheck_i2c("i2c-5"); + focusedWindow.webContents.executeJavaScript('apptool.setI2cbusNo("5")'); + } + } + }, + { + label: "6", + id: "i2c-6", + type: 'checkbox', + click: (item, focusedWindow) => { + if (focusedWindow) { + toggleCheck_i2c("i2c-6"); + focusedWindow.webContents.executeJavaScript('apptool.setI2cbusNo("6")'); + } + } + }, + + ] } ] }, diff --git a/package-lock.json b/package-lock.json index 5b73d03..f7f25e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,15 @@ { "name": "ocoge", - "version": "0.1.10", + "version": "0.1.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ocoge", - "version": "0.1.10", + "version": "0.1.11", "license": "ISC", "dependencies": { "@blockly/field-slider": "^4.0.12", - "@ocoge.club/pigpio": "file:local_modules/pigpio", "@tensorflow-models/blazeface": "^0.0.7", "@tensorflow-models/knn-classifier": "^1.2.4", "@tensorflow-models/mobilenet": "^2.1.0", @@ -29,6 +28,10 @@ "@electron-forge/maker-deb": "^6.1.1", "@electron/rebuild": "^3.2.10", "electron": "^24.0.0" + }, + "optionalDependencies": { + "@ocoge.club/pigpio": "file:local_modules/pigpio", + "@ocoge.club/rgpio": "file:local_modules/rgpio" } }, "node_modules/@blockly/field-slider": { @@ -668,6 +671,7 @@ "resolved": "file:local_modules/pigpio", "hasInstallScript": true, "license": "MIT", + "optional": true, "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^1.7.1" @@ -676,7 +680,25 @@ "node_modules/@ocoge.club/pigpio/node_modules/node-addon-api": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==" + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "optional": true + }, + "node_modules/@ocoge.club/rgpio": { + "version": "0.0.1", + "resolved": "file:local_modules/rgpio", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^1.7.1" + } + }, + "node_modules/@ocoge.club/rgpio/node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "optional": true }, "node_modules/@sindresorhus/is": { "version": "4.6.0", @@ -1347,6 +1369,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, "dependencies": { "file-uri-to-path": "1.0.0" } @@ -2741,7 +2764,8 @@ "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true }, "node_modules/filename-reserved-regex": { "version": "2.0.0", @@ -6779,6 +6803,7 @@ }, "@ocoge.club/pigpio": { "version": "0.0.1", + "optional": true, "requires": { "bindings": "^1.5.0", "node-addon-api": "^1.7.1" @@ -6787,7 +6812,24 @@ "node-addon-api": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==" + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "optional": true + } + } + }, + "@ocoge.club/rgpio": { + "version": "0.0.1", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "node-addon-api": "^1.7.1" + }, + "dependencies": { + "node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "optional": true } } }, @@ -7320,6 +7362,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, "requires": { "file-uri-to-path": "1.0.0" } @@ -8356,7 +8399,8 @@ "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true }, "filename-reserved-regex": { "version": "2.0.0", diff --git a/package.json b/package.json index 9297173..d401acf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocoge", - "version": "0.1.10", + "version": "0.1.11", "description": "ブロックベースビジュアルプログラム開発・実行環境", "main": "main.js", "scripts": { @@ -29,7 +29,6 @@ }, "dependencies": { "@blockly/field-slider": "^4.0.12", - "@ocoge.club/pigpio": "file:local_modules/pigpio", "@tensorflow-models/blazeface": "^0.0.7", "@tensorflow-models/knn-classifier": "^1.2.4", "@tensorflow-models/mobilenet": "^2.1.0", @@ -43,6 +42,10 @@ "nodemailer": "^6.9.1", "prismjs": "^1.29.0" }, + "optionalDependencies": { + "@ocoge.club/pigpio": "file:local_modules/pigpio", + "@ocoge.club/rgpio": "file:local_modules/rgpio" + }, "config": { "forge": { "packagerConfig": {},